summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/android.txt121
-rw-r--r--Documentation/cgroups/cgroups.txt9
-rw-r--r--Documentation/cpu-freq/governors.txt85
-rw-r--r--Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt23
-rw-r--r--Documentation/devicetree/bindings/arm/pmu.txt3
-rw-r--r--Documentation/devicetree/bindings/clock/scpi.txt34
-rw-r--r--Documentation/devicetree/bindings/dma/qcom_bam_dma.txt4
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-pl061.txt31
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt31
-rw-r--r--Documentation/devicetree/bindings/mailbox/mailbox.txt38
-rw-r--r--Documentation/devicetree/bindings/mfd/qcom,rpm.txt263
-rw-r--r--Documentation/devicetree/bindings/mtd/gpmc-nand.txt2
-rw-r--r--Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt28
-rw-r--r--Documentation/devicetree/bindings/phy/qcom-phy.txt23
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt212
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt179
-rw-r--r--Documentation/filesystems/proc.txt6
-rw-r--r--Documentation/mailbox.txt122
-rw-r--r--Documentation/sync.txt75
-rw-r--r--Documentation/sysctl/vm.txt16
-rw-r--r--Documentation/trace/ftrace.txt29
-rw-r--r--MAINTAINERS13
-rw-r--r--Makefile16
-rw-r--r--android/configs/README15
-rw-r--r--android/configs/android-base.cfg140
-rw-r--r--android/configs/android-recommended.cfg121
-rw-r--r--arch/arm/Kconfig34
-rw-r--r--arch/arm/Kconfig.debug35
-rw-r--r--arch/arm/Makefile2
-rw-r--r--arch/arm/boot/Makefile1
-rw-r--r--arch/arm/boot/compressed/atags_to_fdt.c18
-rw-r--r--arch/arm/boot/compressed/head.S2
-rw-r--r--arch/arm/boot/dts/Makefile11
-rw-r--r--arch/arm/boot/dts/bcm11351.dtsi3
-rw-r--r--arch/arm/boot/dts/dra7.dtsi16
-rw-r--r--arch/arm/boot/dts/hi3620-hi4511.dts639
-rw-r--r--arch/arm/boot/dts/hi3620.dtsi105
-rw-r--r--arch/arm/boot/dts/hip04-d01.dts32
-rw-r--r--arch/arm/boot/dts/hip04.dtsi373
-rw-r--r--arch/arm/boot/dts/hisi-x5hd2-dkb.dts31
-rw-r--r--arch/arm/boot/dts/hisi-x5hd2.dtsi370
-rw-r--r--arch/arm/boot/dts/imx53-qsrb.dts8
-rw-r--r--arch/arm/boot/dts/imx6dl-hummingboard.dts4
-rw-r--r--arch/arm/boot/dts/imx6qdl-cubox-i.dtsi19
-rw-r--r--arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi2
-rw-r--r--arch/arm/boot/dts/omap3-beagle.dts1
-rw-r--r--arch/arm/boot/dts/omap3430-sdp.dts2
-rw-r--r--arch/arm/boot/dts/omap54xx-clocks.dtsi16
-rw-r--r--arch/arm/boot/dts/qcom-apq8064-ifc6410.dts74
-rw-r--r--arch/arm/boot/dts/qcom-apq8064.dtsi405
-rw-r--r--arch/arm/boot/dts/qcom-apq8084-ifc6540.dts12
-rw-r--r--arch/arm/boot/dts/qcom-apq8084-mtp.dts6
-rw-r--r--arch/arm/boot/dts/qcom-apq8084.dtsi18
-rw-r--r--arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts158
-rw-r--r--arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts164
-rw-r--r--arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts176
-rw-r--r--arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts170
-rw-r--r--arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts182
-rw-r--r--arch/arm/boot/dts/rtsm_ve-motherboard.dtsi322
-rw-r--r--arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts232
-rw-r--r--arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts316
-rw-r--r--arch/arm/boot/dts/twl6030.dtsi4
-rw-r--r--arch/arm/boot/dts/vexpress-v2m-rs1.dtsi37
-rw-r--r--arch/arm/boot/dts/vexpress-v2m.dtsi37
-rw-r--r--arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts4
-rw-r--r--arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts102
-rw-r--r--arch/arm/boot/dts/vexpress-v2p-ca5s.dts4
-rw-r--r--arch/arm/boot/dts/vexpress-v2p-ca9.dts31
-rw-r--r--arch/arm/common/Kconfig50
-rw-r--r--arch/arm/common/Makefile2
-rw-r--r--arch/arm/common/fiq_debugger.c1376
-rw-r--r--arch/arm/common/fiq_debugger_ringbuf.h94
-rw-r--r--arch/arm/common/fiq_glue.S118
-rw-r--r--arch/arm/common/fiq_glue_setup.c147
-rw-r--r--arch/arm/configs/ep93xx_defconfig1
-rw-r--r--arch/arm/configs/hisi_defconfig (renamed from arch/arm/configs/hi3xxx_defconfig)33
-rw-r--r--arch/arm/configs/multi_v7_defconfig24
-rw-r--r--arch/arm/include/asm/cacheflush.h1
-rw-r--r--arch/arm/include/asm/cputype.h3
-rw-r--r--arch/arm/include/asm/elf.h1
-rw-r--r--arch/arm/include/asm/fiq_debugger.h64
-rw-r--r--arch/arm/include/asm/fiq_glue.h33
-rw-r--r--arch/arm/include/asm/hardirq.h2
-rw-r--r--arch/arm/include/asm/hardware/cache-l2x0.h3
-rw-r--r--arch/arm/include/asm/hardware/coresight.h50
-rw-r--r--arch/arm/include/asm/irq.h3
-rw-r--r--arch/arm/include/asm/mach/mmc.h28
-rw-r--r--arch/arm/include/asm/mcpm.h5
-rw-r--r--arch/arm/include/asm/pmu.h12
-rw-r--r--arch/arm/include/asm/smp.h2
-rw-r--r--arch/arm/include/asm/smp_plat.h15
-rw-r--r--arch/arm/include/asm/topology.h3
-rw-r--r--arch/arm/include/debug/samsung.S9
-rw-r--r--arch/arm/kernel/entry-header.S29
-rw-r--r--arch/arm/kernel/etm.c678
-rw-r--r--arch/arm/kernel/irq.c10
-rw-r--r--arch/arm/kernel/kgdb.c4
-rw-r--r--arch/arm/kernel/module.c1
-rw-r--r--arch/arm/kernel/perf_event.c19
-rw-r--r--arch/arm/kernel/perf_event_cpu.c116
-rw-r--r--arch/arm/kernel/perf_event_v7.c47
-rw-r--r--arch/arm/kernel/process.c127
-rw-r--r--arch/arm/kernel/setup.c13
-rw-r--r--arch/arm/kernel/smp.c58
-rw-r--r--arch/arm/kernel/topology.c27
-rw-r--r--arch/arm/mach-bcm/Makefile1
-rw-r--r--arch/arm/mach-bcm/bcm_kona_smc.c9
-rw-r--r--arch/arm/mach-bcm/brcmstb.h19
-rw-r--r--arch/arm/mach-bcm/headsmp-brcmstb.S33
-rw-r--r--arch/arm/mach-bcm/platsmp-brcmstb.c363
-rw-r--r--arch/arm/mach-exynos/Kconfig1
-rw-r--r--arch/arm/mach-exynos/common.h4
-rw-r--r--arch/arm/mach-exynos/exynos.c6
-rw-r--r--arch/arm/mach-exynos/firmware.c2
-rw-r--r--arch/arm/mach-exynos/headsmp.S6
-rw-r--r--arch/arm/mach-exynos/mcpm-exynos.c1
-rw-r--r--arch/arm/mach-exynos/platsmp.c4
-rw-r--r--arch/arm/mach-exynos/pm.c4
-rw-r--r--arch/arm/mach-exynos/pm_domains.c6
-rw-r--r--arch/arm/mach-highbank/highbank.c2
-rw-r--r--arch/arm/mach-hisi/Kconfig9
-rw-r--r--arch/arm/mach-hisi/Makefile1
-rw-r--r--arch/arm/mach-hisi/hisilicon.c9
-rw-r--r--arch/arm/mach-hisi/platmcpm.c386
-rw-r--r--arch/arm/mach-omap2/Kconfig1
-rw-r--r--arch/arm/mach-omap2/board-flash.c2
-rw-r--r--arch/arm/mach-omap2/gpmc-nand.c3
-rw-r--r--arch/arm/mach-omap2/gpmc.c7
-rw-r--r--arch/arm/mach-omap2/id.c2
-rw-r--r--arch/arm/mach-omap2/omap-headsmp.S13
-rw-r--r--arch/arm/mach-omap2/omap_device.c2
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.c7
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_7xx_data.c22
-rw-r--r--arch/arm/mach-omap2/sleep44xx.S23
-rw-r--r--arch/arm/mach-omap2/soc.h6
-rw-r--r--arch/arm/mach-qcom/Kconfig2
-rw-r--r--arch/arm/mach-qcom/board.c31
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7790.c4
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7791.c2
-rw-r--r--arch/arm/mach-shmobile/clock-sh73a0.c2
-rw-r--r--arch/arm/mach-vexpress/Kconfig1
-rw-r--r--arch/arm/mach-vexpress/spc.c14
-rw-r--r--arch/arm/mach-vexpress/v2m.c32
-rw-r--r--arch/arm/mm/abort-ev6.S6
-rw-r--r--arch/arm/mm/abort-ev7.S6
-rw-r--r--arch/arm/mm/cache-v6.S17
-rw-r--r--arch/arm/mm/fault.c4
-rw-r--r--arch/arm/plat-samsung/cpu.c8
-rw-r--r--arch/arm/plat-samsung/pm-common.c8
-rw-r--r--arch/arm/plat-samsung/pm-gpio.c66
-rw-r--r--arch/arm64/Kconfig2
-rw-r--r--arch/arm64/boot/dts/Makefile3
-rw-r--r--arch/arm64/boot/dts/fvp-base-gicv2-psci.dts227
-rw-r--r--arch/arm64/boot/dts/fvp-base-gicv2legacy-psci.dts227
-rw-r--r--arch/arm64/boot/dts/fvp-base-gicv3-psci.dts236
-rw-r--r--arch/arm64/boot/dts/fvp-foundation-gicv2-psci.dts201
-rw-r--r--arch/arm64/boot/dts/fvp-foundation-gicv2legacy-psci.dts201
-rw-r--r--arch/arm64/boot/dts/fvp-foundation-gicv3-psci.dts210
-rw-r--r--arch/arm64/boot/dts/fvp-foundation-motherboard.dtsi197
-rw-r--r--arch/arm64/boot/dts/juno.dts486
-rw-r--r--arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi34
-rw-r--r--arch/arm64/include/asm/bL_switcher.h54
-rw-r--r--arch/arm64/kernel/topology.c155
-rw-r--r--arch/unicore32/kernel/signal.c9
-rw-r--r--arch/x86/include/asm/idle.h7
-rw-r--r--arch/x86/kernel/process.c17
-rw-r--r--arch/xtensa/Kconfig92
-rw-r--r--arch/xtensa/Makefile7
-rw-r--r--arch/xtensa/boot/dts/kc705.dts5
-rw-r--r--arch/xtensa/configs/common_defconfig1
-rw-r--r--arch/xtensa/configs/iss_defconfig3
-rw-r--r--arch/xtensa/configs/s6105_defconfig1
-rw-r--r--arch/xtensa/include/asm/cacheflush.h2
-rw-r--r--arch/xtensa/include/asm/fixmap.h30
-rw-r--r--arch/xtensa/include/asm/highmem.h40
-rw-r--r--arch/xtensa/include/asm/page.h14
-rw-r--r--arch/xtensa/include/asm/pgtable.h7
-rw-r--r--arch/xtensa/include/asm/uaccess.h5
-rw-r--r--arch/xtensa/include/uapi/asm/ioctls.h19
-rw-r--r--arch/xtensa/include/uapi/asm/unistd.h5
-rw-r--r--arch/xtensa/kernel/align.S128
-rw-r--r--arch/xtensa/kernel/entry.S54
-rw-r--r--arch/xtensa/kernel/pci-dma.c12
-rw-r--r--arch/xtensa/kernel/traps.c5
-rw-r--r--arch/xtensa/kernel/vectors.S8
-rw-r--r--arch/xtensa/kernel/vmlinux.lds.S4
-rw-r--r--arch/xtensa/mm/cache.c77
-rw-r--r--arch/xtensa/mm/highmem.c41
-rw-r--r--arch/xtensa/mm/misc.S116
-rw-r--r--arch/xtensa/mm/mmu.c38
-rw-r--r--block/genhd.c17
-rw-r--r--block/partition-generic.c11
-rw-r--r--drivers/Kconfig4
-rw-r--r--drivers/Makefile3
-rw-r--r--drivers/ata/Kconfig10
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/ahci.h2
-rw-r--r--drivers/ata/ahci_qcom.c89
-rw-r--r--drivers/base/power/main.c43
-rw-r--r--drivers/bus/arm-cci.c1
-rw-r--r--drivers/char/Kconfig17
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/dcc_tty.c326
-rw-r--r--drivers/char/hw_random/omap-rng.c4
-rw-r--r--drivers/char/mem.c17
-rw-r--r--drivers/clk/Kconfig10
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/bcm/clk-bcm281xx.c13
-rw-r--r--drivers/clk/bcm/clk-kona-setup.c99
-rw-r--r--drivers/clk/bcm/clk-kona.c310
-rw-r--r--drivers/clk/bcm/clk-kona.h36
-rw-r--r--drivers/clk/clk-scpi.c309
-rw-r--r--drivers/clk/hisilicon/clk-hix5hd2.c236
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c4
-rw-r--r--drivers/clk/samsung/clk-pll.c122
-rw-r--r--drivers/clocksource/samsung_pwm_timer.c16
-rw-r--r--drivers/cpufreq/Kconfig28
-rw-r--r--drivers/cpufreq/Kconfig.arm12
-rw-r--r--drivers/cpufreq/Makefile2
-rw-r--r--drivers/cpufreq/arm_big_little.c4
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c1343
-rw-r--r--drivers/cpufreq/exynos4210-cpufreq.c12
-rw-r--r--drivers/cpufreq/exynos4x12-cpufreq.c12
-rw-r--r--drivers/cpufreq/exynos5250-cpufreq.c12
-rw-r--r--drivers/cpufreq/exynos5440-cpufreq.c28
-rw-r--r--drivers/cpufreq/highbank-cpufreq.c2
-rw-r--r--drivers/cpufreq/scpi-cpufreq.c99
-rw-r--r--drivers/cpuidle/cpuidle-big_little.c13
-rw-r--r--drivers/cpuidle/governors/menu.c7
-rw-r--r--drivers/crypto/omap-aes.c8
-rw-r--r--drivers/crypto/omap-sham.c4
-rw-r--r--drivers/dma/qcom_bam_dma.c177
-rw-r--r--drivers/gator/Kconfig38
-rw-r--r--drivers/gator/LICENSE339
-rw-r--r--drivers/gator/Makefile81
-rw-r--r--drivers/gator/gator.h149
-rw-r--r--drivers/gator/gator_annotate.c186
-rw-r--r--drivers/gator/gator_annotate_kernel.c202
-rw-r--r--drivers/gator/gator_backtrace.c208
-rw-r--r--drivers/gator/gator_buffer.c168
-rw-r--r--drivers/gator/gator_buffer_write.c80
-rw-r--r--drivers/gator/gator_cookies.c437
-rw-r--r--drivers/gator/gator_events_armv6.c237
-rw-r--r--drivers/gator/gator_events_armv7.c314
-rw-r--r--drivers/gator/gator_events_block.c163
-rw-r--r--drivers/gator/gator_events_ccn-504.c346
-rw-r--r--drivers/gator/gator_events_irq.c165
-rw-r--r--drivers/gator/gator_events_l2c-310.c208
-rw-r--r--drivers/gator/gator_events_mali_4xx.c669
-rw-r--r--drivers/gator/gator_events_mali_4xx.h18
-rw-r--r--drivers/gator/gator_events_mali_common.c69
-rw-r--r--drivers/gator/gator_events_mali_common.h78
-rw-r--r--drivers/gator/gator_events_mali_t6xx.c567
-rw-r--r--drivers/gator/gator_events_mali_t6xx_hw.c913
-rw-r--r--drivers/gator/gator_events_mali_t6xx_hw_test.c55
-rw-r--r--drivers/gator/gator_events_meminfo.c409
-rw-r--r--drivers/gator/gator_events_mmapped.c209
-rw-r--r--drivers/gator/gator_events_net.c172
-rw-r--r--drivers/gator/gator_events_perf_pmu.c587
-rw-r--r--drivers/gator/gator_events_sched.c113
-rw-r--r--drivers/gator/gator_events_scorpion.c669
-rw-r--r--drivers/gator/gator_events_threads.c115
-rw-r--r--drivers/gator/gator_fs.c365
-rw-r--r--drivers/gator/gator_hrtimer_gator.c80
-rw-r--r--drivers/gator/gator_iks.c197
-rw-r--r--drivers/gator/gator_main.c1467
-rw-r--r--drivers/gator/gator_marshaling.c362
-rw-r--r--drivers/gator/gator_trace_gpu.c333
-rw-r--r--drivers/gator/gator_trace_power.c203
-rw-r--r--drivers/gator/gator_trace_sched.c297
-rw-r--r--drivers/gator/mali/mali_dd_gator_api.h40
-rw-r--r--drivers/gator/mali/mali_mjollnir_profiling_gator_api.h163
-rw-r--r--drivers/gator/mali/mali_utgard_profiling_gator_api.h201
-rw-r--r--drivers/gator/mali_t6xx.mk39
-rw-r--r--drivers/gpio/gpio-pl061.c21
-rw-r--r--drivers/gpio/gpio-samsung.c98
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.c8
-rw-r--r--drivers/hid/hid-input.c10
-rw-r--r--drivers/hid/hid-multitouch.c24
-rw-r--r--drivers/i2c/busses/Kconfig16
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-hix5hd2.c577
-rw-r--r--drivers/iio/industrialio-event.c4
-rw-r--r--drivers/input/Kconfig9
-rw-r--r--drivers/input/Makefile1
-rw-r--r--drivers/input/evdev.c68
-rw-r--r--drivers/input/keyboard/omap4-keypad.c10
-rw-r--r--drivers/input/keyreset.c239
-rw-r--r--drivers/input/misc/Kconfig25
-rw-r--r--drivers/input/misc/Makefile3
-rw-r--r--drivers/input/misc/gpio_axis.c192
-rw-r--r--drivers/input/misc/gpio_event.c228
-rw-r--r--drivers/input/misc/gpio_input.c390
-rw-r--r--drivers/input/misc/gpio_matrix.c441
-rw-r--r--drivers/input/misc/gpio_output.c97
-rw-r--r--drivers/input/misc/hi6421_pwrkey.c153
-rw-r--r--drivers/input/misc/keychord.c391
-rw-r--r--drivers/input/touchscreen/Kconfig11
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/atmel_mXT224E.c1473
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/exynos-combiner.c8
-rw-r--r--drivers/irqchip/irq-hip04.c424
-rw-r--r--drivers/mailbox/Kconfig27
-rw-r--r--drivers/mailbox/Makefile7
-rw-r--r--drivers/mailbox/arm_mhu.c320
-rw-r--r--drivers/mailbox/arm_mhu.h33
-rw-r--r--drivers/mailbox/mailbox.c467
-rw-r--r--drivers/mailbox/pl320-ipc.c2
-rw-r--r--drivers/mailbox/scpi_protocol.c350
-rw-r--r--drivers/mfd/Kconfig23
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/hi6421-pmic-core.c310
-rw-r--r--drivers/mfd/pm8921-core.c36
-rw-r--r--drivers/mfd/qcom_rpm.c596
-rw-r--r--drivers/mfd/ssbi.c7
-rw-r--r--drivers/misc/Kconfig4
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/uid_stat.c152
-rw-r--r--drivers/mmc/card/Kconfig9
-rw-r--r--drivers/mmc/card/block.c33
-rw-r--r--drivers/mmc/core/Kconfig15
-rw-r--r--drivers/mmc/core/core.c55
-rw-r--r--drivers/mmc/core/host.c7
-rw-r--r--drivers/mmc/core/sd.c83
-rw-r--r--drivers/mmc/core/sdio.c112
-rw-r--r--drivers/mmc/core/sdio_bus.c13
-rwxr-xr-x[-rw-r--r--]drivers/mmc/core/sdio_io.c33
-rw-r--r--drivers/mmc/host/Kconfig11
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/dw_mmc.c42
-rw-r--r--drivers/mmc/host/dw_mmc.h26
-rw-r--r--drivers/mmc/host/mmci.c86
-rw-r--r--drivers/mmc/host/mmci_qcom_dml.c177
-rw-r--r--drivers/mmc/host/mmci_qcom_dml.h31
-rw-r--r--drivers/mmc/host/omap.c4
-rw-r--r--drivers/mmc/host/omap_hsmmc.c4
-rw-r--r--drivers/mtd/nand/Kconfig10
-rw-r--r--drivers/mtd/nand/omap2.c16
-rw-r--r--drivers/net/ethernet/hisilicon/Kconfig9
-rw-r--r--drivers/net/ethernet/hisilicon/Makefile1
-rw-r--r--drivers/net/ethernet/hisilicon/hip04_eth.c783
-rw-r--r--drivers/net/ethernet/hisilicon/hip04_mdio.c186
-rw-r--r--drivers/net/ppp/Kconfig17
-rw-r--r--drivers/net/ppp/Makefile2
-rw-r--r--drivers/net/ppp/pppolac.c449
-rw-r--r--drivers/net/ppp/pppopns.c428
-rw-r--r--drivers/net/tun.c6
-rw-r--r--drivers/net/wireless/Kconfig5
-rw-r--r--drivers/net/wireless/ath/ath6kl/sdio.c1
-rw-r--r--drivers/of/address.c108
-rw-r--r--drivers/pci/host/Makefile1
-rw-r--r--drivers/pci/host/pci-qcom.c968
-rw-r--r--drivers/pinctrl/qcom/Kconfig23
-rw-r--r--drivers/pinctrl/qcom/Makefile2
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-pmic.c1532
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c876
-rw-r--r--drivers/power/power_supply_sysfs.c4
-rw-r--r--drivers/regulator/Kconfig19
-rw-r--r--drivers/regulator/Makefile3
-rw-r--r--drivers/regulator/hi6421-regulator.c559
-rw-r--r--drivers/regulator/qcom_rpm-regulator.c787
-rw-r--r--drivers/rtc/Kconfig11
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-hi6421.c259
-rw-r--r--drivers/soc/qcom/qcom_gsbi.c46
-rw-r--r--drivers/spi/spi-au1550.c2
-rw-r--r--drivers/spi/spi-davinci.c16
-rw-r--r--drivers/spi/spi-dw-pci.c2
-rw-r--r--drivers/spi/spi-dw.c2
-rw-r--r--drivers/spi/spi-omap2-mcspi.c3
-rw-r--r--drivers/spi/spi-pxa2xx.c1
-rw-r--r--drivers/spi/spi-rockchip.c4
-rw-r--r--drivers/spi/spi-rspi.c94
-rw-r--r--drivers/spi/spi-sh-msiof.c71
-rw-r--r--drivers/spi/spi.c1
-rw-r--r--drivers/staging/android/Kconfig9
-rw-r--r--drivers/staging/android/TODO10
-rw-r--r--drivers/staging/android/ashmem.c20
-rw-r--r--drivers/staging/android/binder.c32
-rw-r--r--drivers/staging/android/ion/ion_priv.h1
-rw-r--r--drivers/staging/android/lowmemorykiller.c110
-rw-r--r--drivers/switch/Kconfig15
-rw-r--r--drivers/switch/Makefile4
-rw-r--r--drivers/switch/switch_class.c174
-rw-r--r--drivers/switch/switch_gpio.c172
-rw-r--r--drivers/thermal/samsung/exynos_tmu.c2
-rw-r--r--drivers/tty/serial/8250/8250_dw.c57
-rw-r--r--drivers/tty/serial/samsung.c43
-rw-r--r--drivers/tty/serial/samsung.h4
-rw-r--r--drivers/tty/serial/serial_core.c3
-rw-r--r--drivers/usb/gadget/composite.c10
-rw-r--r--drivers/usb/gadget/function/f_fs.c90
-rw-r--r--drivers/usb/gadget/function/f_rndis.c56
-rw-r--r--drivers/usb/gadget/function/u_serial.c1
-rw-r--r--drivers/usb/gadget/legacy/Kconfig20
-rw-r--r--drivers/usb/gadget/legacy/Makefile2
-rw-r--r--drivers/usb/gadget/legacy/android.c1561
-rw-r--r--drivers/usb/gadget/legacy/f_accessory.c1204
-rw-r--r--drivers/usb/gadget/legacy/f_audio_source.c828
-rw-r--r--drivers/usb/gadget/legacy/f_mtp.c1287
-rw-r--r--drivers/usb/gadget/udc/udc-core.c10
-rw-r--r--drivers/usb/host/Kconfig7
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-h20ahb.c341
-rw-r--r--drivers/usb/host/ehci-hcd.c11
-rw-r--r--drivers/usb/host/ehci-omap.c4
-rw-r--r--drivers/usb/musb/musb_io.h18
-rw-r--r--drivers/usb/phy/Kconfig8
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/otg-wakelock.c173
-rw-r--r--drivers/usb/phy/phy-msm-usb.c13
-rw-r--r--drivers/video/Kconfig6
-rw-r--r--drivers/video/Makefile4
-rw-r--r--drivers/video/adf/Kconfig14
-rw-r--r--drivers/video/adf/Makefile15
-rw-r--r--drivers/video/adf/adf.c1167
-rw-r--r--drivers/video/adf/adf.h71
-rw-r--r--drivers/video/adf/adf_client.c811
-rw-r--r--drivers/video/adf/adf_fbdev.c651
-rw-r--r--drivers/video/adf/adf_fops.c957
-rw-r--r--drivers/video/adf/adf_fops.h37
-rw-r--r--drivers/video/adf/adf_fops32.c217
-rw-r--r--drivers/video/adf/adf_fops32.h78
-rw-r--r--drivers/video/adf/adf_format.c280
-rw-r--r--drivers/video/adf/adf_memblock.c149
-rw-r--r--drivers/video/adf/adf_sysfs.c296
-rw-r--r--drivers/video/adf/adf_sysfs.h33
-rw-r--r--drivers/video/adf/adf_trace.h93
-rw-r--r--drivers/video/fbdev/Kconfig15
-rw-r--r--drivers/video/fbdev/Makefile1
-rw-r--r--drivers/video/fbdev/arm-hdlcd.c844
-rw-r--r--drivers/video/fbdev/omap2/dss/dispc.c4
-rw-r--r--drivers/video/fbdev/omap2/dss/dsi.c4
-rw-r--r--drivers/video/fbdev/omap2/dss/dss.c4
-rw-r--r--drivers/video/fbdev/omap2/dss/hdmi.h4
-rw-r--r--drivers/video/fbdev/omap2/dss/venc.c4
-rw-r--r--drivers/video/fbdev/omap2/omapfb/omapfb-main.c10
-rw-r--r--drivers/video/fbdev/omap2/vrfb.c6
-rw-r--r--drivers/video/vexpress-dvi.c229
-rw-r--r--drivers/w1/masters/ds2482.c47
-rw-r--r--fs/eventpoll.c4
-rw-r--r--fs/fs-writeback.c2
-rw-r--r--fs/fuse/dev.c6
-rw-r--r--fs/locks.c2
-rw-r--r--fs/proc/base.c39
-rw-r--r--fs/proc/task_mmu.c62
-rw-r--r--include/dt-bindings/clock/bcm281xx.h3
-rw-r--r--include/dt-bindings/clock/hix5hd2-clock.h27
-rw-r--r--include/dt-bindings/mfd/qcom,rpm.h142
-rw-r--r--include/dt-bindings/pinctrl/qcom,pmic-gpio.h142
-rw-r--r--include/dt-bindings/pinctrl/qcom,pmic-mpp.h70
-rw-r--r--include/linux/Kbuild2
-rw-r--r--include/linux/amba/mmci.h12
-rw-r--r--include/linux/android_aid.h28
-rw-r--r--include/linux/arm-hdlcd.h122
-rw-r--r--include/linux/cgroup.h2
-rw-r--r--include/linux/cpu.h7
-rw-r--r--include/linux/cpufreq.h3
-rw-r--r--include/linux/gpio_event.h170
-rw-r--r--include/linux/hid.h4
-rw-r--r--include/linux/if_pppolac.h23
-rw-r--r--include/linux/if_pppopns.h23
-rw-r--r--include/linux/if_pppox.h21
-rw-r--r--include/linux/kernel.h3
-rw-r--r--include/linux/keychord.h23
-rw-r--r--include/linux/keyreset.h28
-rw-r--r--include/linux/mailbox_client.h46
-rw-r--r--include/linux/mailbox_controller.h135
-rw-r--r--include/linux/mfd/hi6421-pmic.h99
-rw-r--r--include/linux/mfd/pm8921-core.h32
-rw-r--r--include/linux/mfd/qcom_rpm.h12
-rw-r--r--include/linux/mm.h3
-rw-r--r--include/linux/mm_types.h14
-rw-r--r--include/linux/mmc/host.h17
-rw-r--r--include/linux/mmc/pm.h1
-rwxr-xr-x[-rw-r--r--]include/linux/mmc/sdio_func.h10
-rw-r--r--include/linux/mtd/map.h12
-rw-r--r--include/linux/netfilter/xt_qtaguid.h13
-rw-r--r--include/linux/netfilter/xt_quota2.h25
-rw-r--r--include/linux/nmi.h5
-rw-r--r--include/linux/of_address.h14
-rw-r--r--include/linux/pl320-ipc.h (renamed from include/linux/mailbox.h)0
-rw-r--r--include/linux/platform_data/ds2482.h21
-rw-r--r--include/linux/platform_data/mtd-nand-omap2.h13
-rw-r--r--include/linux/power_supply.h4
-rw-r--r--include/linux/sched.h3
-rw-r--r--include/linux/scpi_protocol.h30
-rw-r--r--include/linux/security.h29
-rw-r--r--include/linux/serial_core.h1
-rw-r--r--include/linux/spi/spi.h7
-rw-r--r--include/linux/switch.h53
-rw-r--r--include/linux/uid_stat.h29
-rw-r--r--include/linux/usb/f_accessory.h23
-rw-r--r--include/linux/usb/f_mtp.h23
-rw-r--r--include/linux/wakelock.h67
-rw-r--r--include/linux/wakeup_reason.h23
-rw-r--r--include/linux/wifi_tiwlan.h27
-rw-r--r--include/linux/wlan_plat.h27
-rw-r--r--include/net/activity_stats.h25
-rw-r--r--include/net/tcp.h3
-rw-r--r--include/trace/events/cpufreq_interactive.h112
-rw-r--r--include/trace/events/gpu.h143
-rw-r--r--include/trace/events/mmc.h91
-rw-r--r--include/trace/events/power.h19
-rw-r--r--include/uapi/linux/if_pppolac.h33
-rw-r--r--include/uapi/linux/if_pppopns.h32
-rw-r--r--include/uapi/linux/if_pppox.h6
-rw-r--r--include/uapi/linux/input.h7
-rw-r--r--include/uapi/linux/keychord.h52
-rw-r--r--include/uapi/linux/netfilter/xt_IDLETIMER.h8
-rw-r--r--include/uapi/linux/netfilter/xt_socket.h5
-rw-r--r--include/uapi/linux/prctl.h2
-rw-r--r--include/uapi/linux/sockios.h1
-rw-r--r--include/uapi/linux/usb/f_accessory.h146
-rw-r--r--include/uapi/linux/usb/f_mtp.h61
-rw-r--r--include/uapi/video/adf.h321
-rw-r--r--include/video/adf.h484
-rw-r--r--include/video/adf_client.h61
-rw-r--r--include/video/adf_fbdev.h59
-rw-r--r--include/video/adf_format.h26
-rw-r--r--include/video/adf_memblock.h20
-rw-r--r--kernel/cpu.c20
-rw-r--r--kernel/debug/debug_core.c12
-rw-r--r--kernel/debug/kdb/kdb_io.c12
-rw-r--r--kernel/fork.c19
-rw-r--r--kernel/irq/pm.c12
-rw-r--r--kernel/panic.c7
-rw-r--r--kernel/power/Kconfig15
-rw-r--r--kernel/power/Makefile3
-rw-r--r--kernel/power/suspend.c15
-rw-r--r--kernel/power/suspend_time.c111
-rw-r--r--kernel/power/wakelock.c7
-rw-r--r--kernel/power/wakeup_reason.c140
-rw-r--r--kernel/printk/printk.c8
-rw-r--r--kernel/sched/core.c27
-rw-r--r--kernel/sys.c152
-rw-r--r--kernel/sysctl.c17
-rw-r--r--kernel/trace/Kconfig3
-rw-r--r--kernel/trace/Makefile1
-rw-r--r--kernel/trace/gpu-traces.c23
-rw-r--r--kernel/trace/trace.c98
-rw-r--r--kernel/trace/trace.h2
-rw-r--r--kernel/trace/trace_functions_graph.c52
-rw-r--r--kernel/trace/trace_output.c182
-rw-r--r--kernel/watchdog.c123
-rw-r--r--lib/Kconfig.debug15
-rw-r--r--linaro/configs/android.conf47
-rw-r--r--linaro/configs/arndale.conf100
-rw-r--r--linaro/configs/arndale_octa.conf89
-rw-r--r--linaro/configs/beaglebone.conf22
-rw-r--r--linaro/configs/big-LITTLE-IKS.conf4
-rw-r--r--linaro/configs/bigendian.conf4
-rw-r--r--linaro/configs/capri.conf90
-rw-r--r--linaro/configs/debug.conf1
-rw-r--r--linaro/configs/distribution.conf62
-rw-r--r--linaro/configs/gcov.conf3
-rw-r--r--linaro/configs/hi3xxx.conf18
-rw-r--r--linaro/configs/highbank.conf42
-rw-r--r--linaro/configs/hugepage.conf3
-rw-r--r--linaro/configs/ifc6410.conf326
-rw-r--r--linaro/configs/imx5.conf105
-rw-r--r--linaro/configs/kvm-guest.conf17
-rw-r--r--linaro/configs/kvm-host.conf13
-rw-r--r--linaro/configs/linaro-base.conf95
-rw-r--r--linaro/configs/linaro-base64.conf3
-rw-r--r--linaro/configs/lt-arndale.conf1
-rw-r--r--linaro/configs/lt-arndale_octa.conf1
-rw-r--r--linaro/configs/multi_v7.conf151
-rw-r--r--linaro/configs/netns.conf2
-rw-r--r--linaro/configs/null.conf1
-rw-r--r--linaro/configs/omap2plus.conf205
-rw-r--r--linaro/configs/omap4.conf194
-rw-r--r--linaro/configs/origen.conf89
-rw-r--r--linaro/configs/panda.conf11
-rw-r--r--linaro/configs/u8500.conf83
-rw-r--r--linaro/configs/uprobes.conf7
-rw-r--r--linaro/configs/vexpress-tuning.conf1
-rw-r--r--linaro/configs/vexpress.conf65
-rw-r--r--linaro/configs/vexpress64.conf59
-rw-r--r--linaro/configs/xen.conf7
-rw-r--r--mm/madvise.c3
-rw-r--r--mm/mempolicy.c2
-rw-r--r--mm/mlock.c3
-rw-r--r--mm/mmap.c44
-rw-r--r--mm/mprotect.c3
-rw-r--r--mm/page_alloc.c35
-rw-r--r--mm/shmem.c13
-rw-r--r--mm/vmscan.c43
-rw-r--r--net/Kconfig16
-rw-r--r--net/Makefile1
-rw-r--r--net/activity_stats.c119
-rw-r--r--net/bluetooth/af_bluetooth.c29
-rw-r--r--net/ipv4/Makefile1
-rw-r--r--net/ipv4/af_inet.c20
-rw-r--r--net/ipv4/devinet.c8
-rw-r--r--net/ipv4/sysctl_net_ipv4.c22
-rw-r--r--net/ipv4/sysfs_net_ipv4.c88
-rw-r--r--net/ipv4/tcp.c119
-rw-r--r--net/ipv4/tcp_input.c1
-rw-r--r--net/ipv4/tcp_output.c2
-rw-r--r--net/ipv6/af_inet6.c37
-rw-r--r--net/ipv6/exthdrs_core.c13
-rw-r--r--net/netfilter/Kconfig42
-rw-r--r--net/netfilter/Makefile2
-rw-r--r--net/netfilter/xt_IDLETIMER.c78
-rw-r--r--net/netfilter/xt_qtaguid.c3026
-rw-r--r--net/netfilter/xt_qtaguid_internal.h352
-rw-r--r--net/netfilter/xt_qtaguid_print.c566
-rw-r--r--net/netfilter/xt_qtaguid_print.h120
-rw-r--r--net/netfilter/xt_quota2.c391
-rw-r--r--net/netfilter/xt_socket.c73
-rw-r--r--net/rfkill/Kconfig5
-rw-r--r--net/rfkill/core.c4
-rw-r--r--net/wireless/scan.c2
-rw-r--r--scripts/Makefile.lib7
-rw-r--r--scripts/package/builddeb14
-rw-r--r--security/capability.c24
-rw-r--r--security/commoncap.c11
-rw-r--r--security/security.c20
-rw-r--r--security/selinux/hooks.c66
-rw-r--r--security/selinux/include/classmap.h1
-rw-r--r--tools/gator/Makefile4
-rw-r--r--tools/gator/daemon/Android.mk65
-rw-r--r--tools/gator/daemon/Application.mk1
-rw-r--r--tools/gator/daemon/Buffer.cpp391
-rw-r--r--tools/gator/daemon/Buffer.h102
-rw-r--r--tools/gator/daemon/CapturedXML.cpp144
-rw-r--r--tools/gator/daemon/CapturedXML.h26
-rw-r--r--tools/gator/daemon/Child.cpp346
-rw-r--r--tools/gator/daemon/Child.h33
-rw-r--r--tools/gator/daemon/Config.h17
-rw-r--r--tools/gator/daemon/ConfigurationXML.cpp217
-rw-r--r--tools/gator/daemon/ConfigurationXML.h38
-rw-r--r--tools/gator/daemon/Counter.h65
-rw-r--r--tools/gator/daemon/Driver.cpp15
-rw-r--r--tools/gator/daemon/Driver.h48
-rw-r--r--tools/gator/daemon/DriverSource.cpp321
-rw-r--r--tools/gator/daemon/DriverSource.h57
-rw-r--r--tools/gator/daemon/DynBuf.cpp139
-rw-r--r--tools/gator/daemon/DynBuf.h52
-rw-r--r--tools/gator/daemon/EventsXML.cpp76
-rw-r--r--tools/gator/daemon/EventsXML.h21
-rw-r--r--tools/gator/daemon/ExternalSource.cpp215
-rw-r--r--tools/gator/daemon/ExternalSource.h49
-rw-r--r--tools/gator/daemon/FSDriver.cpp212
-rw-r--r--tools/gator/daemon/FSDriver.h44
-rw-r--r--tools/gator/daemon/Fifo.cpp130
-rw-r--r--tools/gator/daemon/Fifo.h48
-rw-r--r--tools/gator/daemon/Hwmon.cpp352
-rw-r--r--tools/gator/daemon/Hwmon.h45
-rw-r--r--tools/gator/daemon/KMod.cpp110
-rw-r--r--tools/gator/daemon/KMod.h27
-rw-r--r--tools/gator/daemon/LocalCapture.cpp131
-rw-r--r--tools/gator/daemon/LocalCapture.h26
-rw-r--r--tools/gator/daemon/Logging.cpp78
-rw-r--r--tools/gator/daemon/Logging.h36
-rw-r--r--tools/gator/daemon/Makefile25
-rw-r--r--tools/gator/daemon/Makefile_aarch6412
-rw-r--r--tools/gator/daemon/MaliVideoDriver.cpp253
-rw-r--r--tools/gator/daemon/MaliVideoDriver.h50
-rw-r--r--tools/gator/daemon/Monitor.cpp68
-rw-r--r--tools/gator/daemon/Monitor.h33
-rw-r--r--tools/gator/daemon/OlySocket.cpp282
-rw-r--r--tools/gator/daemon/OlySocket.h55
-rw-r--r--tools/gator/daemon/OlyUtility.cpp227
-rw-r--r--tools/gator/daemon/OlyUtility.h42
-rw-r--r--tools/gator/daemon/PerfBuffer.cpp139
-rw-r--r--tools/gator/daemon/PerfBuffer.h39
-rw-r--r--tools/gator/daemon/PerfDriver.cpp409
-rw-r--r--tools/gator/daemon/PerfDriver.h60
-rw-r--r--tools/gator/daemon/PerfGroup.cpp226
-rw-r--r--tools/gator/daemon/PerfGroup.h57
-rw-r--r--tools/gator/daemon/PerfSource.cpp275
-rw-r--r--tools/gator/daemon/PerfSource.h54
-rw-r--r--tools/gator/daemon/Proc.cpp215
-rw-r--r--tools/gator/daemon/Proc.h17
-rw-r--r--tools/gator/daemon/Sender.cpp134
-rw-r--r--tools/gator/daemon/Sender.h42
-rw-r--r--tools/gator/daemon/SessionData.cpp194
-rw-r--r--tools/gator/daemon/SessionData.h86
-rw-r--r--tools/gator/daemon/SessionXML.cpp111
-rw-r--r--tools/gator/daemon/SessionXML.h42
-rw-r--r--tools/gator/daemon/Source.cpp33
-rw-r--r--tools/gator/daemon/Source.h40
-rw-r--r--tools/gator/daemon/StreamlineSetup.cpp272
-rw-r--r--tools/gator/daemon/StreamlineSetup.h50
-rw-r--r--tools/gator/daemon/UEvent.cpp76
-rw-r--r--tools/gator/daemon/UEvent.h36
-rw-r--r--tools/gator/daemon/UserSpaceSource.cpp89
-rw-r--r--tools/gator/daemon/UserSpaceSource.h38
-rw-r--r--tools/gator/daemon/c++.cpp40
-rw-r--r--tools/gator/daemon/common.mk52
-rw-r--r--tools/gator/daemon/defaults.xml67
-rw-r--r--tools/gator/daemon/escape.c75
-rw-r--r--tools/gator/daemon/events-ARM11.xml39
-rw-r--r--tools/gator/daemon/events-ARM11MPCore.xml26
-rw-r--r--tools/gator/daemon/events-CCI-400.xml98
-rw-r--r--tools/gator/daemon/events-CCN-504.xml113
-rw-r--r--tools/gator/daemon/events-Cortex-A12.xml86
-rw-r--r--tools/gator/daemon/events-Cortex-A15.xml68
-rw-r--r--tools/gator/daemon/events-Cortex-A5.xml36
-rw-r--r--tools/gator/daemon/events-Cortex-A53.xml87
-rw-r--r--tools/gator/daemon/events-Cortex-A57.xml87
-rw-r--r--tools/gator/daemon/events-Cortex-A7.xml43
-rw-r--r--tools/gator/daemon/events-Cortex-A8.xml52
-rw-r--r--tools/gator/daemon/events-Cortex-A9.xml65
-rw-r--r--tools/gator/daemon/events-Filesystem.xml11
-rw-r--r--tools/gator/daemon/events-Krait-architected.xml22
-rw-r--r--tools/gator/daemon/events-L2C-310.xml18
-rw-r--r--tools/gator/daemon/events-Linux.xml16
-rw-r--r--tools/gator/daemon/events-Mali-4xx.xml245
-rw-r--r--tools/gator/daemon/events-Mali-T6xx.xml46
-rw-r--r--tools/gator/daemon/events-Mali-T6xx_hw.xml91
-rw-r--r--tools/gator/daemon/events-Mali-V500.xml29
-rw-r--r--tools/gator/daemon/events-Perf-Hardware.xml12
-rw-r--r--tools/gator/daemon/events-Scorpion.xml107
-rw-r--r--tools/gator/daemon/events-ScorpionMP.xml90
-rw-r--r--tools/gator/daemon/events_footer.xml1
-rw-r--r--tools/gator/daemon/events_header.xml2
-rw-r--r--tools/gator/daemon/k/perf_event.3.12.h792
l---------tools/gator/daemon/k/perf_event.h1
-rw-r--r--tools/gator/daemon/libsensors/COPYING.LGPL502
-rw-r--r--tools/gator/daemon/libsensors/access.c561
-rw-r--r--tools/gator/daemon/libsensors/access.h33
-rw-r--r--tools/gator/daemon/libsensors/conf-lex.c2881
-rw-r--r--tools/gator/daemon/libsensors/conf-lex.l372
-rw-r--r--tools/gator/daemon/libsensors/conf-parse.c2042
-rw-r--r--tools/gator/daemon/libsensors/conf-parse.h84
-rw-r--r--tools/gator/daemon/libsensors/conf-parse.y347
-rw-r--r--tools/gator/daemon/libsensors/conf.h34
-rw-r--r--tools/gator/daemon/libsensors/data.c278
-rw-r--r--tools/gator/daemon/libsensors/data.h184
-rw-r--r--tools/gator/daemon/libsensors/error.c92
-rw-r--r--tools/gator/daemon/libsensors/error.h74
-rw-r--r--tools/gator/daemon/libsensors/general.c85
-rw-r--r--tools/gator/daemon/libsensors/general.h39
-rw-r--r--tools/gator/daemon/libsensors/init.c341
-rw-r--r--tools/gator/daemon/libsensors/init.h28
-rw-r--r--tools/gator/daemon/libsensors/scanner.h32
-rw-r--r--tools/gator/daemon/libsensors/sensors.h311
-rw-r--r--tools/gator/daemon/libsensors/sysfs.c926
-rw-r--r--tools/gator/daemon/libsensors/sysfs.h43
-rw-r--r--tools/gator/daemon/libsensors/version.h1
-rw-r--r--tools/gator/daemon/main.cpp547
-rw-r--r--tools/gator/daemon/mxml/COPYING507
-rw-r--r--tools/gator/daemon/mxml/config.h96
-rw-r--r--tools/gator/daemon/mxml/mxml-attr.c319
-rw-r--r--tools/gator/daemon/mxml/mxml-entity.c460
-rw-r--r--tools/gator/daemon/mxml/mxml-file.c3082
-rw-r--r--tools/gator/daemon/mxml/mxml-get.c471
-rw-r--r--tools/gator/daemon/mxml/mxml-index.c662
-rw-r--r--tools/gator/daemon/mxml/mxml-node.c807
-rw-r--r--tools/gator/daemon/mxml/mxml-private.c331
-rw-r--r--tools/gator/daemon/mxml/mxml-private.h50
-rw-r--r--tools/gator/daemon/mxml/mxml-search.c287
-rw-r--r--tools/gator/daemon/mxml/mxml-set.c349
-rw-r--r--tools/gator/daemon/mxml/mxml-string.c476
-rw-r--r--tools/gator/daemon/mxml/mxml.h329
-rw-r--r--tools/gator/debian/changelog172
-rw-r--r--tools/gator/debian/compat1
-rw-r--r--tools/gator/debian/control38
-rw-r--r--tools/gator/debian/copyright15
-rw-r--r--tools/gator/debian/dkms.conf.in7
-rw-r--r--tools/gator/debian/gator-daemon.install1
-rw-r--r--tools/gator/debian/gator-daemon.postrm41
-rw-r--r--tools/gator/debian/gator-daemon.preinst36
-rw-r--r--tools/gator/debian/gator-daemon.upstart10
-rwxr-xr-xtools/gator/debian/gator-module-dkms.install.in2
-rwxr-xr-xtools/gator/debian/gator-module-dkms.postinst49
-rwxr-xr-xtools/gator/debian/gator-module-dkms.prerm29
-rwxr-xr-xtools/gator/debian/rules15
-rwxr-xr-xtools/gator/debian/scripts/do-packaging104
-rw-r--r--tools/perf/Makefile.perf4
774 files changed, 94651 insertions, 1845 deletions
diff --git a/Documentation/android.txt b/Documentation/android.txt
new file mode 100644
index 000000000000..0f40a78b045f
--- /dev/null
+++ b/Documentation/android.txt
@@ -0,0 +1,121 @@
+ =============
+ A N D R O I D
+ =============
+
+Copyright (C) 2009 Google, Inc.
+Written by Mike Chan <mike@android.com>
+
+CONTENTS:
+---------
+
+1. Android
+ 1.1 Required enabled config options
+ 1.2 Required disabled config options
+ 1.3 Recommended enabled config options
+2. Contact
+
+
+1. Android
+==========
+
+Android (www.android.com) is an open source operating system for mobile devices.
+This document describes configurations needed to run the Android framework on
+top of the Linux kernel.
+
+To see a working defconfig look at msm_defconfig or goldfish_defconfig
+which can be found at http://android.git.kernel.org in kernel/common.git
+and kernel/msm.git
+
+
+1.1 Required enabled config options
+-----------------------------------
+After building a standard defconfig, ensure that these options are enabled in
+your .config or defconfig if they are not already. Based off the msm_defconfig.
+You should keep the rest of the default options enabled in the defconfig
+unless you know what you are doing.
+
+ANDROID_PARANOID_NETWORK
+ASHMEM
+CONFIG_FB_MODE_HELPERS
+CONFIG_FONT_8x16
+CONFIG_FONT_8x8
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+DAB
+EARLYSUSPEND
+FB
+FB_CFB_COPYAREA
+FB_CFB_FILLRECT
+FB_CFB_IMAGEBLIT
+FB_DEFERRED_IO
+FB_TILEBLITTING
+HIGH_RES_TIMERS
+INOTIFY
+INOTIFY_USER
+INPUT_EVDEV
+INPUT_GPIO
+INPUT_MISC
+LEDS_CLASS
+LEDS_GPIO
+LOCK_KERNEL
+LkOGGER
+LOW_MEMORY_KILLER
+MISC_DEVICES
+NEW_LEDS
+NO_HZ
+POWER_SUPPLY
+PREEMPT
+RAMFS
+RTC_CLASS
+RTC_LIB
+SWITCH
+SWITCH_GPIO
+TMPFS
+UID_STAT
+UID16
+USB_FUNCTION
+USB_FUNCTION_ADB
+USER_WAKELOCK
+VIDEO_OUTPUT_CONTROL
+WAKELOCK
+YAFFS_AUTO_YAFFS2
+YAFFS_FS
+YAFFS_YAFFS1
+YAFFS_YAFFS2
+
+
+1.2 Required disabled config options
+------------------------------------
+CONFIG_YAFFS_DISABLE_LAZY_LOAD
+DNOTIFY
+
+
+1.3 Recommended enabled config options
+------------------------------
+ANDROID_PMEM
+PSTORE_CONSOLE
+PSTORE_RAM
+SCHEDSTATS
+DEBUG_PREEMPT
+DEBUG_MUTEXES
+DEBUG_SPINLOCK_SLEEP
+DEBUG_INFO
+FRAME_POINTER
+CPU_FREQ
+CPU_FREQ_TABLE
+CPU_FREQ_DEFAULT_GOV_ONDEMAND
+CPU_FREQ_GOV_ONDEMAND
+CRC_CCITT
+EMBEDDED
+INPUT_TOUCHSCREEN
+I2C
+I2C_BOARDINFO
+LOG_BUF_SHIFT=17
+SERIAL_CORE
+SERIAL_CORE_CONSOLE
+
+
+2. Contact
+==========
+website: http://android.git.kernel.org
+
+mailing-lists: android-kernel@googlegroups.com
diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt
index 10c949b293e4..925b8201eba5 100644
--- a/Documentation/cgroups/cgroups.txt
+++ b/Documentation/cgroups/cgroups.txt
@@ -578,6 +578,15 @@ is completely unused; @cgrp->parent is still valid. (Note - can also
be called for a newly-created cgroup if an error occurs after this
subsystem's create() method has been called for the new cgroup).
+int allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
+(cgroup_mutex held by caller)
+
+Called prior to moving a task into a cgroup; if the subsystem
+returns an error, this will abort the attach operation. Used
+to extend the permission checks - if all subsystems in a cgroup
+return 0, the attach will be allowed to proceed, even if the
+default permission check (root or same user) fails.
+
int can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
(cgroup_mutex held by caller)
diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index 77ec21574fb1..2ed766ec75fc 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -28,6 +28,7 @@ Contents:
2.3 Userspace
2.4 Ondemand
2.5 Conservative
+2.6 Interactive
3. The Governor Interface in the CPUfreq Core
@@ -218,6 +219,90 @@ a decision on when to decrease the frequency while running in any
speed. Load for frequency increase is still evaluated every
sampling rate.
+2.6 Interactive
+---------------
+
+The CPUfreq governor "interactive" is designed for latency-sensitive,
+interactive workloads. This governor sets the CPU speed depending on
+usage, similar to "ondemand" and "conservative" governors, but with a
+different set of configurable behaviors.
+
+The tuneable values for this governor are:
+
+target_loads: CPU load values used to adjust speed to influence the
+current CPU load toward that value. In general, the lower the target
+load, the more often the governor will raise CPU speeds to bring load
+below the target. The format is a single target load, optionally
+followed by pairs of CPU speeds and CPU loads to target at or above
+those speeds. Colons can be used between the speeds and associated
+target loads for readability. For example:
+
+ 85 1000000:90 1700000:99
+
+targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until
+1.7GHz and above, at which load 99% is targeted. If speeds are
+specified these must appear in ascending order. Higher target load
+values are typically specified for higher speeds, that is, target load
+values also usually appear in an ascending order. The default is
+target load 90% for all speeds.
+
+min_sample_time: The minimum amount of time to spend at the current
+frequency before ramping down. Default is 80000 uS.
+
+hispeed_freq: An intermediate "hi speed" at which to initially ramp
+when CPU load hits the value specified in go_hispeed_load. If load
+stays high for the amount of time specified in above_hispeed_delay,
+then speed may be bumped higher. Default is the maximum speed
+allowed by the policy at governor initialization time.
+
+go_hispeed_load: The CPU load at which to ramp to hispeed_freq.
+Default is 99%.
+
+above_hispeed_delay: When speed is at or above hispeed_freq, wait for
+this long before raising speed in response to continued high load.
+The format is a single delay value, optionally followed by pairs of
+CPU speeds and the delay to use at or above those speeds. Colons can
+be used between the speeds and associated delays for readability. For
+example:
+
+ 80000 1300000:200000 1500000:40000
+
+uses delay 80000 uS until CPU speed 1.3 GHz, at which speed delay
+200000 uS is used until speed 1.5 GHz, at which speed (and above)
+delay 40000 uS is used. If speeds are specified these must appear in
+ascending order. Default is 20000 uS.
+
+timer_rate: Sample rate for reevaluating CPU load when the CPU is not
+idle. A deferrable timer is used, such that the CPU will not be woken
+from idle to service this timer until something else needs to run.
+(The maximum time to allow deferring this timer when not running at
+minimum speed is configurable via timer_slack.) Default is 20000 uS.
+
+timer_slack: Maximum additional time to defer handling the governor
+sampling timer beyond timer_rate when running at speeds above the
+minimum. For platforms that consume additional power at idle when
+CPUs are running at speeds greater than minimum, this places an upper
+bound on how long the timer will be deferred prior to re-evaluating
+load and dropping speed. For example, if timer_rate is 20000uS and
+timer_slack is 10000uS then timers will be deferred for up to 30msec
+when not at lowest speed. A value of -1 means defer timers
+indefinitely at all speeds. Default is 80000 uS.
+
+boost: If non-zero, immediately boost speed of all CPUs to at least
+hispeed_freq until zero is written to this attribute. If zero, allow
+CPU speeds to drop below hispeed_freq according to load as usual.
+Default is zero.
+
+boostpulse: On each write, immediately boost speed of all CPUs to
+hispeed_freq for at least the period of time specified by
+boostpulse_duration, after which speeds are allowed to drop below
+hispeed_freq according to load as usual.
+
+boostpulse_duration: Length of time to hold CPU speed at hispeed_freq
+on a write to boostpulse, before allowing speed to drop according to
+load as usual. Default is 80000 uS.
+
+
3. The Governor Interface in the CPUfreq Core
=============================================
diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
index 934f00025cc4..f717c7b48603 100644
--- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
+++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
@@ -5,6 +5,11 @@ Hi4511 Board
Required root node properties:
- compatible = "hisilicon,hi3620-hi4511";
+HiP04 D01 Board
+Required root node properties:
+ - compatible = "hisilicon,hip04-d01";
+
+
Hisilicon system controller
Required properties:
@@ -55,3 +60,21 @@ Example:
compatible = "hisilicon,pctrl";
reg = <0xfca09000 0x1000>;
};
+
+-----------------------------------------------------------------------
+Fabric:
+
+Required Properties:
+- compatible: "hisilicon,hip04-fabric";
+- reg: Address and size of Fabric
+
+-----------------------------------------------------------------------
+Bootwrapper boot method (software protocol on SMP):
+
+Required Properties:
+- compatible: "hisilicon,hip04-bootwrapper";
+- boot-method: Address and size of boot method.
+ [0]: bootwrapper physical address
+ [1]: bootwrapper size
+ [2]: relocation physical address
+ [3]: relocation size
diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
index 75ef91d08f3b..d83ab5736171 100644
--- a/Documentation/devicetree/bindings/arm/pmu.txt
+++ b/Documentation/devicetree/bindings/arm/pmu.txt
@@ -21,6 +21,9 @@ Required properties:
"qcom,krait-pmu"
- interrupts : 1 combined interrupt or 1 per core. If the interrupt is a per-cpu
interrupt (PPI) then 1 interrupt should be specified.
+- cluster : a phandle to the cluster to which it belongs
+ If there are more than one cluster with same CPU type
+ then there should be separate PMU nodes per cluster.
Optional properties:
diff --git a/Documentation/devicetree/bindings/clock/scpi.txt b/Documentation/devicetree/bindings/clock/scpi.txt
new file mode 100644
index 000000000000..b2b7035018f4
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/scpi.txt
@@ -0,0 +1,34 @@
+Device Tree Clock bindings for the clocks based on
+System Control and Power Interface (SCPI) Message Protocol
+
+This binding uses the common clock binding[1].
+
+Required properties:
+- compatible : shall be one of the following:
+ "arm,scpi-clks" - for the container node with all the clocks
+ based on the SCPI protocol
+ "arm,scpi-clk-indexed" - all the clocks that are variable and index
+ based. These clocks don't provide the full range between the
+ limits but only discrete points within the range. The firmware
+ provides the mapping for each such operating frequency and the
+ index associated with it.
+ "arm,scpi-clk-range" - all the clocks that are variable and provide
+ full range within the specified range
+
+Required properties for all clocks(all from common clock binding):
+- #clock-cells : ; shall be set to 0 or 1 depending on whether it has single
+ or multiple clock outputs.
+- clock-output-names : shall be the corresponding names of the outputs.
+- clock-indices: The identifyng number for the clocks in the node as expected
+ by the firmware. It can be non linear and hence provide the mapping
+ of identifiers into the clock-output-names array.
+- frequency-range: The allowed range of clock frequency supported specified
+ in the form of minimum and maximum limits(two u32 fields)
+ This is required only if compatible is "arm,scpi-clk-range"
+
+Clock consumers should specify the desired clocks they use with a
+"clocks" phandle cell. Consumers should also provide an additional ID
+in their clock property. This ID refers to the specific clock in the clock
+provider list.
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
diff --git a/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt
index d75a9d767022..802572f66077 100644
--- a/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt
+++ b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt
@@ -1,7 +1,9 @@
QCOM BAM DMA controller
Required properties:
-- compatible: must contain "qcom,bam-v1.4.0" for MSM8974
+- compatible: must contain one of the following:
+ "qcom,bam-v1.4.0" for MSM8974
+ "qcom,bam-v1.3.0" for APQ8064, IPQ8064, MSM8x64
- reg: Address range for DMA registers
- interrupts: Should contain the one interrupt shared by all channels
- #dma-cells: must be <1>, the cell in the dmas property of the client device
diff --git a/Documentation/devicetree/bindings/gpio/gpio-pl061.txt b/Documentation/devicetree/bindings/gpio/gpio-pl061.txt
new file mode 100644
index 000000000000..164b5ba2e0dc
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-pl061.txt
@@ -0,0 +1,31 @@
+PL061 GPIO controller bindings
+
+Required properties:
+- compatible:
+ - "arm,pl061", "arm,primecell".
+- #gpio-cells : Should be two.
+ - first cell is the gpio pin number
+ - second cell is used to specify the gpio polarity:
+ 0 = active high
+ 1 = active low
+- gpio-controller : Marks the device node as a GPIO controller.
+- interrupt-controller : Marks the device node as an interrupt controller.
+- #interrupt-cells : Should be two.
+ - first cell is the hw irq number
+ - second cell is used to specify the interrupt type:
+ 0 = default, unspecified type
+ 1 = rising edge triggered
+ 2 = falling edge triggered
+ 4 = high level triggered
+ 8 = low level triggered
+
+Example:
+ gpio0: gpio@fc806000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xfc806000 0x1000>;
+ interrupts = <0 64 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt b/Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt
new file mode 100644
index 000000000000..2ecc8a7f4401
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt
@@ -0,0 +1,31 @@
+I2C for Hisilicon hix5hd2 chipset platforms (3716,3719,3798...)
+
+Required properties:
+- compatible: Must be "hisilicon,hix5hd2-i2c"
+ Specifically, the following versions of the chipset are supported:
+ Hi3716CV200 (support six I2C module)
+ Hi3719CV100 (support six I2C module)
+ Hi3718CV100 (support six I2C module)
+ Hi3719MV100 (support two I2C module)
+ Hi3718MV100 (support two I2C module)
+- reg: physical base address of the controller and length of memory mapped
+ region.
+- interrupts: interrupt number to the cpu.
+- #address-cells = <1>;
+- #size-cells = <0>;
+- clocks: phandles to input clocks.
+
+Optional properties:
+- clock-frequency: Desired I2C bus frequency in Hz, otherwise defaults to 400000
+- Child nodes conforming to i2c bus binding
+
+Examples:
+I2C0@f8b10000 {
+ compatible = "hisilicon,hix5hd2-i2c";
+ reg = <0xf8b10000 0x1000>;
+ interrupts = <0 38 4>;
+ clocks = <&clock HIX5HD2_I2C0_RST>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+}
diff --git a/Documentation/devicetree/bindings/mailbox/mailbox.txt b/Documentation/devicetree/bindings/mailbox/mailbox.txt
new file mode 100644
index 000000000000..1a2cd3d266db
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/mailbox.txt
@@ -0,0 +1,38 @@
+* Generic Mailbox Controller and client driver bindings
+
+Generic binding to provide a way for Mailbox controller drivers to
+assign appropriate mailbox channel to client drivers.
+
+* Mailbox Controller
+
+Required property:
+- #mbox-cells: Must be at least 1. Number of cells in a mailbox
+ specifier.
+
+Example:
+ mailbox: mailbox {
+ ...
+ #mbox-cells = <1>;
+ };
+
+
+* Mailbox Client
+
+Required property:
+- mboxes: List of phandle and mailbox channel specifiers.
+
+Optional property:
+- mbox-names: List of identifier strings for each mailbox channel
+ required by the client. The use of this property
+ is discouraged in favor of using index in list of
+ 'mboxes' while requesting a mailbox. Instead the
+ platforms may define channel indices, in DT headers,
+ to something legible.
+
+Example:
+ pwr_cntrl: power {
+ ...
+ mbox-names = "pwr-ctrl", "rpc";
+ mboxes = <&mailbox 0
+ &mailbox 1>;
+ };
diff --git a/Documentation/devicetree/bindings/mfd/qcom,rpm.txt b/Documentation/devicetree/bindings/mfd/qcom,rpm.txt
new file mode 100644
index 000000000000..4ea03388fdd3
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/qcom,rpm.txt
@@ -0,0 +1,263 @@
+Qualcomm Resource Power Manager (RPM)
+
+This driver is used to interface with the Resource Power Manager (RPM) found in
+various Qualcomm platforms. The RPM allows each component in the system to vote
+for state of the system resources, such as clocks, regulators and bus
+frequencies.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,rpm-apq8064"
+ "qcom,rpm-msm8660"
+ "qcom,rpm-msm8960"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: two entries specifying the physical address and size of the
+ RPM's message ram
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: three entries specifying the RPM's:
+ 1. acknowledgement interrupt
+ 2. error interrupt
+ 3. wakeup interrupt
+
+- interrupt-names:
+ Usage: required
+ Value type: <string-array>
+ Definition: must be the three strings "ack", "err" and "wakeup", in order
+
+- #address-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 1
+
+- #size-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 0
+
+- qcom,ipc:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: three entries specifying:
+ - phandle to a syscon node representing the apcs registers
+ - u32 representing offset to the register within the syscon
+ - u32 representing the ipc bit within the register
+
+
+= SUBDEVICES
+
+The RPM exposes resources to its subnodes. The below bindings specify the set
+of valid subnodes that can operate on these resources.
+
+== Switch-mode Power Supply regulator
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,rpm-pm8058-smps"
+ "qcom,rpm-pm8901-ftsmps"
+ "qcom,rpm-pm8921-smps"
+ "qcom,rpm-pm8921-ftsmps"
+
+- reg:
+ Usage: required
+ Value type: <u32>
+ Definition: resource as defined in <dt-bindings/mfd/qcom,rpm.h>
+
+- bias-pull-down:
+ Usage: optional
+ Value type: <empty>
+ Definition: enable pull down of the regulator when inactive
+
+- qcom,switch-mode-frequency:
+ Usage: required
+ Value type: <u32>
+ Definition: Frequency (Hz) of the switch-mode power supply;
+ must be one of:
+ 19200000, 9600000, 6400000, 4800000, 3840000, 3200000,
+ 2740000, 2400000, 2130000, 1920000, 1750000, 1600000,
+ 1480000, 1370000, 1280000, 1200000
+
+- qcom,force-mode-none:
+ Usage: optional (default if no other qcom,force-mode is specified)
+ Value type: <empty>
+ Defintion: indicates that the regulator should not be forced to any
+ particular mode
+
+- qcom,force-mode-lpm:
+ Usage: optional
+ Value type: <empty>
+ Definition: indicates that the regulator should be forced to operate in
+ low-power-mode
+
+- qcom,force-mode-auto:
+ Usage: optional (only available for 8960/8064)
+ Value type: <empty>
+ Definition: indicates that the regulator should be automatically pick
+ operating mode
+
+- qcom,force-mode-hpm:
+ Usage: optional (only available for 8960/8064)
+ Value type: <empty>
+ Definition: indicates that the regulator should be forced to operate in
+ high-power-mode
+
+- qcom,force-mode-bypass: (only for 8960/8064)
+ Usage: optional (only available for 8960/8064)
+ Value type: <empty>
+ Definition: indicates that the regulator should be forced to operate in
+ bypass mode
+
+- qcom,power-mode-hysteretic:
+ Usage: optional
+ Value type: <empty>
+ Definition: indicates that the power supply should operate in hysteretic
+ mode (defaults to qcom,power-mode-pwm if not specified)
+
+- qcom,power-mode-pwm:
+ Usage: optional
+ Value type: <empty>
+ Definition: indicates that the power supply should operate in pwm mode
+
+Standard regulator bindings are used inside switch mode power supply subnodes.
+Check Documentation/devicetree/bindings/regulator/regulator.txt for more
+details.
+
+== Low-dropout regulator
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,rpm-pm8058-pldo"
+ "qcom,rpm-pm8058-nldo"
+ "qcom,rpm-pm8901-pldo"
+ "qcom,rpm-pm8901-nldo"
+ "qcom,rpm-pm8921-pldo"
+ "qcom,rpm-pm8921-nldo"
+ "qcom,rpm-pm8921-nldo1200"
+
+- reg:
+ Usage: required
+ Value type: <u32>
+ Definition: resource as defined in <dt-bindings/mfd/qcom,rpm.h>
+
+- bias-pull-down:
+ Usage: optional
+ Value type: <empty>
+ Definition: enable pull down of the regulator when inactive
+
+- qcom,force-mode-none:
+ Usage: optional (default if no other qcom,force-mode is specified)
+ Value type: <empty>
+ Defintion: indicates that the regulator should not be forced to any
+ particular mode
+
+- qcom,force-mode-lpm:
+ Usage: optional
+ Value type: <empty>
+ Definition: indicates that the regulator should be forced to operate in
+ low-power-mode
+
+- qcom,force-mode-auto:
+ Usage: optional (only available for 8960/8064)
+ Value type: <empty>
+ Definition: indicates that the regulator should be automatically pick
+ operating mode
+
+- qcom,force-mode-hpm:
+ Usage: optional (only available for 8960/8064)
+ Value type: <empty>
+ Definition: indicates that the regulator should be forced to operate in
+ high-power-mode
+
+- qcom,force-mode-bypass: (only for 8960/8064)
+ Usage: optional (only available for 8960/8064)
+ Value type: <empty>
+ Definition: indicates that the regulator should be forced to operate in
+ bypass mode
+
+Standard regulator bindings are used inside switch low-dropout regulator
+subnodes. Check Documentation/devicetree/bindings/regulator/regulator.txt for
+more details.
+
+== Negative Charge Pump
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,rpm-pm8058-ncp"
+ "qcom,rpm-pm8921-ncp"
+
+- reg:
+ Usage: required
+ Value type: <u32>
+ Definition: resource as defined in <dt-bindings/mfd/qcom,rpm.h>
+
+- qcom,switch-mode-frequency:
+ Usage: required
+ Value type: <u32>
+ Definition: Frequency (Hz) of the swith mode power supply;
+ must be one of:
+ 19200000, 9600000, 6400000, 4800000, 3840000, 3200000,
+ 2740000, 2400000, 2130000, 1920000, 1750000, 1600000,
+ 1480000, 1370000, 1280000, 1200000
+
+Standard regulator bindings are used inside negative charge pump regulator
+subnodes. Check Documentation/devicetree/bindings/regulator/regulator.txt for
+more details.
+
+== Switch
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,rpm-pm8058-switch"
+ "qcom,rpm-pm8901-switch"
+ "qcom,rpm-pm8921-switch"
+
+- reg:
+ Usage: required
+ Value type: <u32>
+ Definition: resource as defined in <dt-bindings/mfd/qcom/qcom,rpm.h>
+
+
+= EXAMPLE
+
+ #include <dt-bindings/mfd/qcom,rpm.h>
+
+ rpm@108000 {
+ compatible = "qcom,rpm-msm8960";
+ reg = <0x108000 0x1000>;
+ qcom,ipc = <&apcs 0x8 2>;
+
+ interrupts = <0 19 0>, <0 21 0>, <0 22 0>;
+ interrupt-names = "ack", "err", "wakeup";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pm8921_s1: pm8921-s1 {
+ compatible = "qcom,rpm-pm8921-smps";
+ reg = <QCOM_RPM_PM8921_S1>;
+
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+ regulator-always-on;
+
+ bias-pull-down;
+
+ qcom,switch-mode-frequency = <3200000>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt
index 65f4f7c43136..ee654e95d8ad 100644
--- a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt
@@ -22,7 +22,7 @@ Optional properties:
width of 8 is assumed.
- ti,nand-ecc-opt: A string setting the ECC layout to use. One of:
- "sw" <deprecated> use "ham1" instead
+ "sw" 1-bit Hamming ecc code via software
"hw" <deprecated> use "ham1" instead
"hw-romcode" <deprecated> use "ham1" instead
"ham1" 1-bit Hamming ecc code
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt b/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
new file mode 100644
index 000000000000..6bc542a58d37
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt
@@ -0,0 +1,28 @@
+* Hisilicon hip04 Ethernet Controller
+
+Required properties:
+- compatible: should be "hisilicon,hip04-mac".
+- reg: address and length of the register set for the device.
+- interrupts: interrupt for the device.
+- port: port number combined with the controller: range from 0 to 31.
+- speed: 100 (100M) or 1000 (1000M).
+
+Example:
+ ge0: ethernet@2800000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x2800000 0x10000>;
+ interrupts = <0 434 4>;
+ port = <0>;
+ speed = <1000>;
+ };
+
+* Hisilicon hip04 Ethernet ppe: control rx & rx fifos of all ethnet controllers
+
+Required properties:
+- compatible: should be "hisilicon,ppebase".
+
+Example:
+ ppebase: ppebase@28c0000 {
+ compatible = "hisilicon,ppebase";
+ reg = <0x28c0000 0x10000>;
+ };
diff --git a/Documentation/devicetree/bindings/phy/qcom-phy.txt b/Documentation/devicetree/bindings/phy/qcom-phy.txt
new file mode 100644
index 000000000000..76bfbd056202
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom-phy.txt
@@ -0,0 +1,23 @@
+Qualcomm IPQ806x SATA PHY Controller
+------------------------------------
+
+SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
+Each SATA PHY controller should have its own node.
+
+Required properties:
+- compatible: compatible list, contains "qcom,ipq806x-sata-phy"
+- reg: offset and length of the SATA PHY register set;
+- #phy-cells: must be zero
+- clocks: must be exactly one entry
+- clock-names: must be "cfg"
+
+Example:
+ sata_phy: sata-phy@1b400000 {
+ compatible = "qcom,ipq806x-sata-phy";
+ reg = <0x1b400000 0x200>;
+
+ clocks = <&gcc SATA_PHY_CFG_CLK>;
+ clock-names = "cfg";
+
+ #phy-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
new file mode 100644
index 000000000000..c2d01b44f385
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
@@ -0,0 +1,212 @@
+Qualcomm PMIC GPIO block
+
+This binding describes the GPIO block(s) found in the 8xxx series of pmics from
+Qualcomm.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,pm8018-gpio"
+ "qcom,pm8038-gpio"
+ "qcom,pm8058-gpio"
+ "qcom,pm8917-gpio"
+ "qcom,pm8921-gpio"
+ "qcom,pm8941-gpio"
+ "qcom,pma8084-gpio"
+
+- reg:
+ Usage: required
+ Value type: <u32>
+ Definition: Register base of the gpio block
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Must contain an array of encoded interrupt specifiers for
+ each available gpio
+
+- gpio-controller:
+ Usage: required
+ Value type: <none>
+ Definition: Mark the device node as a GPIO controller
+
+- #gpio-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: Must be 2;
+ the first cell will be used to define gpio number and the
+ second denotes the flags for this gpio
+
+Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
+a general description of GPIO and interrupt bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The pin configuration nodes act as a container for an abitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin or a list of pins. This configuration can include the
+mux function to select on those pin(s), and various pin configuration
+parameters, as listed below.
+
+
+SUBNODES:
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content.
+
+Each subnode only affects those parameters that are explicitly listed. In
+other words, a subnode that lists a mux function but no pin configuration
+parameters implies no information about any pin configuration parameters.
+Similarly, a pin subnode that describes a pullup parameter implies no
+information about e.g. the mux function.
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a pin configuration subnode:
+
+- pins:
+ Usage: required
+ Value type: <string-array>
+ Definition: List of gpio pins affected by the properties specified in
+ this subnode. Valid pins are:
+ gpio1-gpio6 for pm8018
+ gpio1-gpio12 for pm8038
+ gpio1-gpio40 for pm8058
+ gpio1-gpio38 for pm8917
+ gpio1-gpio44 for pm8921
+ gpio1-gpio36 for pm8941
+ gpio1-gpio22 for pma8084
+
+- function:
+ Usage: required
+ Value type: <string>
+ Definition: Specify the alternative function to be configured for the
+ specified pins. Valid values are:
+ "normal",
+ "paired",
+ "func1",
+ "func2",
+ "dtest1",
+ "dtest2",
+ "dtest3",
+ "dtest4"
+
+- bias-disable:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins should be configued as no pull.
+
+- bias-pull-down:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins should be configued as pull down.
+
+- bias-pull-up:
+ Usage: optional
+ Value type: <empty>
+ Definition: The specified pins should be configured as pull up.
+
+- qcom,pull-up-strength:
+ Usage: optional
+ Value type: <u32>
+ Definition: Specifies the strength to use for pull up, if selected.
+ Valid values are; as defined in
+ <dt-bindings/pinctrl/qcom,pmic-gpio.h>:
+ 1: 30uA (PMIC_GPIO_PULL_UP_30)
+ 2: 1.5uA (PMIC_GPIO_PULL_UP_1P5)
+ 3: 31.5uA (PMIC_GPIO_PULL_UP_31P5)
+ 4: 1.5uA + 30uA boost (PMIC_GPIO_PULL_UP_1P5_30)
+ If this property is ommited 30uA strength will be used if
+ pull up is selected
+
+- bias-high-impedance:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins will put in high-Z mode and disabled.
+
+- input-enable:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are put in input mode.
+
+- output-high:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in output mode, driven
+ high.
+
+- output-low:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in output mode, driven
+ low.
+
+- power-source:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects the power source for the specified pins. Valid
+ power sources are defined per chip in
+ <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+ xxxx_GPIO_L6, xxxx_GPIO_L5...
+
+- qcom,drive-strength:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects the drive strength for the specified pins. Value
+ drive strengths are:
+ 0: no (PMIC_GPIO_STRENGTH_NO)
+ 1: high (PMIC_GPIO_STRENGTH_HIGH) 0.9mA @ 1.8V - 1.9mA @ 2.6V
+ 2: medium (PMIC_GPIO_STRENGTH_MED) 0.6mA @ 1.8V - 1.25mA @ 2.6V
+ 3: low (PMIC_GPIO_STRENGTH_LOW) 0.15mA @ 1.8V - 0.3mA @ 2.6V
+ as defined in <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+- drive-push-pull:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in push-pull mode.
+
+- drive-open-drain:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in open-drain mode.
+
+
+Example:
+
+ pm8921_gpio: gpio@150 {
+ compatible = "qcom,pm8921-gpio";
+ reg = <0x150>;
+ interrupts = <192 1>, <193 1>, <194 1>,
+ <195 1>, <196 1>, <197 1>,
+ <198 1>, <199 1>, <200 1>,
+ <201 1>, <202 1>, <203 1>,
+ <204 1>, <205 1>, <206 1>,
+ <207 1>, <208 1>, <209 1>,
+ <210 1>, <211 1>, <212 1>,
+ <213 1>, <214 1>, <215 1>,
+ <216 1>, <217 1>, <218 1>,
+ <219 1>, <220 1>, <221 1>,
+ <222 1>, <223 1>, <224 1>,
+ <225 1>, <226 1>, <227 1>,
+ <228 1>, <229 1>, <230 1>,
+ <231 1>, <232 1>, <233 1>,
+ <234 1>, <235 1>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ pm8921_gpio_keys: gpio-keys {
+ volume-keys {
+ pins = "gpio20", "gpio21";
+ function = "normal";
+
+ input-enable;
+ bias-pull-up;
+ drive-push-pull;
+ qcom,drive-strength = <PMIC_GPIO_STRENGTH_NO>;
+ power-source = <PM8921_GPIO_S4>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
new file mode 100644
index 000000000000..0a645671d2d5
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
@@ -0,0 +1,179 @@
+Qualcomm PMIC Multi-Purpose Pin (MPP) block
+
+This binding describes the MPP block(s) found in the 8xxx series of pmics from
+Qualcomm.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,pm8841-mpp"
+ "qcom,pm8941-mpp"
+ "qcom,pma8084-mpp"
+
+- reg:
+ Usage: required
+ Value type: <u32>
+ Definition: Register base of the gpio block
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Must contain an array of encoded interrupt specifiers for
+ each available gpio
+
+- gpio-controller:
+ Usage: required
+ Value type: <none>
+ Definition: Mark the device node as a GPIO controller
+
+- #gpio-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: Must be 2;
+ the first cell will be used to define gpio number and the
+ second denotes the flags for this gpio
+
+Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
+a general description of GPIO and interrupt bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The pin configuration nodes act as a container for an abitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin or a list of pins. This configuration can include the
+mux function to select on those pin(s), and various pin configuration
+parameters, as listed below.
+
+
+SUBNODES:
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content.
+
+Each subnode only affects those parameters that are explicitly listed. In
+other words, a subnode that lists a mux function but no pin configuration
+parameters implies no information about any pin configuration parameters.
+Similarly, a pin subnode that describes a pullup parameter implies no
+information about e.g. the mux function.
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a pin configuration subnode:
+
+- pins:
+ Usage: required
+ Value type: <string-array>
+ Definition: List of gpio pins affected by the properties specified in
+ this subnode. Valid pins are:
+ mpp1-mpp4 for pm8841
+ mpp1-mpp8 for pm8941
+ mpp1-mpp4 for pma8084
+
+- function:
+ Usage: optional
+ Value type: <string>
+ Definition: Specify the alternative function to be configured for the
+ specified pins. Valid values are:
+ "normal",
+ "paired",
+ "dtest1",
+ "dtest2",
+ "dtest3",
+ "dtest4"
+
+- qcom,mode:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects MPP mode of operation: Digital Input, Digital Output,
+ Digital Input and Digital Output, Bidirectional Logic,
+ Analog Input, Analog Output, Current Sink Valid values are
+ defined in <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+ PMIC_MPP_MODE_DI, PMIC_MPP_MODE_DO, PMIC_MPP_MODE_DIO...
+
+- bias-disable:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins should be configued as no pull.
+
+- bias-pull-up:
+ Usage: optional
+ Value type: <u32>
+ Definition: The specified pins should be configued as pull up.
+ Valid values are 600, 10000 and 30000.
+
+- bias-high-impedance:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins will put in high-Z mode and disabled.
+
+- input-enable:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are put in input mode.
+
+- output-high:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in output mode, driven
+ high.
+
+- output-low:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in output mode, driven
+ low.
+
+- power-source:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects the power source for the specified pins. Valid only
+ when pin operate in Digital I/O mode ("gpio"). Valid
+ power sources are defined in
+ <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+
+- drive-strength:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects the drive strength for the specified pins. Value
+ drive strengths are: 5-40mA with 5mA step
+
+- qcom,amux-route:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects the source for analog input. Valid values are
+ defined in <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+ PMIC_MPP_AMUX_ROUTE_CH5, PMIC_MPP_AMUX_ROUTE_CH6...
+
+- qcom,vrefence:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects the analog output voltage reference. Valid values
+ are defined in <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+ PMIC_MPP_VREFERENCE_1V25, PMIC_MPP_VREFERENCE_0V625...
+
+Example:
+
+ mpps@a000 {
+ compatible = "qcom,pm8841-mpp";
+ reg = <0xa000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupts = <4 0xa0 0 0>, <4 0xa1 0 0>, <4 0xa2 0 0>, <4 0xa3 0 0>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pm8841_default>;
+
+ pm8841_default: default {
+ gpio {
+ pins = "mpp1", "mpp2", "mpp3", "mpp4";
+ function = "normal";
+
+ input-enable;
+
+ power-source = <PM8841_MPP_S3>;
+ bias-pull-up = <600>;
+ };
+ };
+ };
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
index eb8a10e22f7c..229b60e925f5 100644
--- a/Documentation/filesystems/proc.txt
+++ b/Documentation/filesystems/proc.txt
@@ -369,6 +369,8 @@ is not associated with a file:
[stack:1001] = the stack of the thread with tid 1001
[vdso] = the "virtual dynamic shared object",
the kernel system call handler
+ [anon:<name>] = an anonymous mapping that has been
+ named by userspace
or if empty, the mapping is anonymous.
@@ -419,6 +421,7 @@ KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 374 kB
VmFlags: rd ex mr mw me de
+Name: name from userspace
the first of these lines shows the same information as is displayed for the
mapping in /proc/PID/maps. The remaining lines show the size of the mapping
@@ -470,6 +473,9 @@ Note that there is no guarantee that every flag and associated mnemonic will
be present in all further kernel releases. Things get changed, the flags may
be vanished or the reverse -- new added.
+The "Name" field will only be present on a mapping that has been named by
+userspace, and will show the name passed in by userspace.
+
This file is only present if the CONFIG_MMU kernel configuration option is
enabled.
diff --git a/Documentation/mailbox.txt b/Documentation/mailbox.txt
new file mode 100644
index 000000000000..60f43ff629aa
--- /dev/null
+++ b/Documentation/mailbox.txt
@@ -0,0 +1,122 @@
+ The Common Mailbox Framework
+ Jassi Brar <jaswinder.singh@linaro.org>
+
+ This document aims to help developers write client and controller
+drivers for the API. But before we start, let us note that the
+client (especially) and controller drivers are likely going to be
+very platform specific because the remote firmware is likely to be
+proprietary and implement non-standard protocol. So even if two
+platforms employ, say, PL320 controller, the client drivers can't
+be shared across them. Even the PL320 driver might need to accommodate
+some platform specific quirks. So the API is meant mainly to avoid
+similar copies of code written for each platform. Having said that,
+nothing prevents the remote f/w to also be Linux based and use the
+same api there. However none of that helps us locally because we only
+ever deal at client's protocol level.
+ Some of the choices made during implementation are the result of this
+peculiarity of this "common" framework.
+
+
+
+ Part 1 - Controller Driver (See include/linux/mailbox_controller.h)
+
+ Allocate mbox_controller and the array of mbox_chan.
+Populate mbox_chan_ops, except peek_data() all are mandatory.
+The controller driver might know a message has been consumed
+by the remote by getting an IRQ or polling some hardware flag
+or it can never know (the client knows by way of the protocol).
+The method in order of preference is IRQ -> Poll -> None, which
+the controller driver should set via 'txdone_irq' or 'txdone_poll'
+or neither.
+
+
+ Part 2 - Client Driver (See include/linux/mailbox_client.h)
+
+ The client might want to operate in blocking mode (synchronously
+send a message through before returning) or non-blocking/async mode (submit
+a message and a callback function to the API and return immediately).
+
+
+struct demo_client {
+ struct mbox_client cl;
+ struct mbox_chan *mbox;
+ struct completion c;
+ bool async;
+ /* ... */
+};
+
+/*
+ * This is the handler for data received from remote. The behaviour is purely
+ * dependent upon the protocol. This is just an example.
+ */
+static void message_from_remote(struct mbox_client *cl, void *mssg)
+{
+ struct demo_client *dc = container_of(mbox_client,
+ struct demo_client, cl);
+ if (dc->aysnc) {
+ if (is_an_ack(mssg)) {
+ /* An ACK to our last sample sent */
+ return; /* Or do something else here */
+ } else { /* A new message from remote */
+ queue_req(mssg);
+ }
+ } else {
+ /* Remote f/w sends only ACK packets on this channel */
+ return;
+ }
+}
+
+static void sample_sent(struct mbox_client *cl, void *mssg, int r)
+{
+ struct demo_client *dc = container_of(mbox_client,
+ struct demo_client, cl);
+ complete(&dc->c);
+}
+
+static void client_demo(struct platform_device *pdev)
+{
+ struct demo_client *dc_sync, *dc_async;
+ /* The controller already knows async_pkt and sync_pkt */
+ struct async_pkt ap;
+ struct sync_pkt sp;
+
+ dc_sync = kzalloc(sizeof(*dc_sync), GFP_KERNEL);
+ dc_async = kzalloc(sizeof(*dc_async), GFP_KERNEL);
+
+ /* Populate non-blocking mode client */
+ dc_async->cl.dev = &pdev->dev;
+ dc_async->cl.rx_callback = message_from_remote;
+ dc_async->cl.tx_done = sample_sent;
+ dc_async->cl.tx_block = false;
+ dc_async->cl.tx_tout = 0; /* doesn't matter here */
+ dc_async->cl.knows_txdone = false; /* depending upon protocol */
+ dc_async->async = true;
+ init_completion(&dc_async->c);
+
+ /* Populate blocking mode client */
+ dc_sync->cl.dev = &pdev->dev;
+ dc_sync->cl.rx_callback = message_from_remote;
+ dc_sync->cl.tx_done = NULL; /* operate in blocking mode */
+ dc_sync->cl.tx_block = true;
+ dc_sync->cl.tx_tout = 500; /* by half a second */
+ dc_sync->cl.knows_txdone = false; /* depending upon protocol */
+ dc_sync->async = false;
+
+ /* ASync mailbox is listed second in 'mboxes' property */
+ dc_async->mbox = mbox_request_channel(&dc_async->cl, 1);
+ /* Populate data packet */
+ /* ap.xxx = 123; etc */
+ /* Send async message to remote */
+ mbox_send_message(dc_async->mbox, &ap);
+
+ /* Sync mailbox is listed first in 'mboxes' property */
+ dc_sync->mbox = mbox_request_channel(&dc_sync->cl, 0);
+ /* Populate data packet */
+ /* sp.abc = 123; etc */
+ /* Send message to remote in blocking mode */
+ mbox_send_message(dc_sync->mbox, &sp);
+ /* At this point 'sp' has been sent */
+
+ /* Now wait for async chan to be done */
+ wait_for_completion(&dc_async->c);
+}
diff --git a/Documentation/sync.txt b/Documentation/sync.txt
new file mode 100644
index 000000000000..a2d05e7fa193
--- /dev/null
+++ b/Documentation/sync.txt
@@ -0,0 +1,75 @@
+Motivation:
+
+In complicated DMA pipelines such as graphics (multimedia, camera, gpu, display)
+a consumer of a buffer needs to know when the producer has finished producing
+it. Likewise the producer needs to know when the consumer is finished with the
+buffer so it can reuse it. A particular buffer may be consumed by multiple
+consumers which will retain the buffer for different amounts of time. In
+addition, a consumer may consume multiple buffers atomically.
+The sync framework adds an API which allows synchronization between the
+producers and consumers in a generic way while also allowing platforms which
+have shared hardware synchronization primitives to exploit them.
+
+Goals:
+ * provide a generic API for expressing synchronization dependencies
+ * allow drivers to exploit hardware synchronization between hardware
+ blocks
+ * provide a userspace API that allows a compositor to manage
+ dependencies.
+ * provide rich telemetry data to allow debugging slowdowns and stalls of
+ the graphics pipeline.
+
+Objects:
+ * sync_timeline
+ * sync_pt
+ * sync_fence
+
+sync_timeline:
+
+A sync_timeline is an abstract monotonically increasing counter. In general,
+each driver/hardware block context will have one of these. They can be backed
+by the appropriate hardware or rely on the generic sw_sync implementation.
+Timelines are only ever created through their specific implementations
+(i.e. sw_sync.)
+
+sync_pt:
+
+A sync_pt is an abstract value which marks a point on a sync_timeline. Sync_pts
+have a single timeline parent. They have 3 states: active, signaled, and error.
+They start in active state and transition, once, to either signaled (when the
+timeline counter advances beyond the sync_pt’s value) or error state.
+
+sync_fence:
+
+Sync_fences are the primary primitives used by drivers to coordinate
+synchronization of their buffers. They are a collection of sync_pts which may
+or may not have the same timeline parent. A sync_pt can only exist in one fence
+and the fence's list of sync_pts is immutable once created. Fences can be
+waited on synchronously or asynchronously. Two fences can also be merged to
+create a third fence containing a copy of the two fences’ sync_pts. Fences are
+backed by file descriptors to allow userspace to coordinate the display pipeline
+dependencies.
+
+Use:
+
+A driver implementing sync support should have a work submission function which:
+ * takes a fence argument specifying when to begin work
+ * asynchronously queues that work to kick off when the fence is signaled
+ * returns a fence to indicate when its work will be done.
+ * signals the returned fence once the work is completed.
+
+Consider an imaginary display driver that has the following API:
+/*
+ * assumes buf is ready to be displayed.
+ * blocks until the buffer is on screen.
+ */
+ void display_buffer(struct dma_buf *buf);
+
+The new API will become:
+/*
+ * will display buf when fence is signaled.
+ * returns immediately with a fence that will signal when buf
+ * is no longer displayed.
+ */
+struct sync_fence* display_buffer(struct dma_buf *buf,
+ struct sync_fence *fence);
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index 4415aa915681..9aecaa51f735 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -29,6 +29,7 @@ Currently, these files are in /proc/sys/vm:
- dirty_writeback_centisecs
- drop_caches
- extfrag_threshold
+- extra_free_kbytes
- hugepages_treat_as_movable
- hugetlb_shm_group
- laptop_mode
@@ -225,6 +226,21 @@ fragmentation index is <= extfrag_threshold. The default value is 500.
==============================================================
+extra_free_kbytes
+
+This parameter tells the VM to keep extra free memory between the threshold
+where background reclaim (kswapd) kicks in, and the threshold where direct
+reclaim (by allocating processes) kicks in.
+
+This is useful for workloads that require low latency memory allocations
+and have a bounded burstiness in memory allocations, for example a
+realtime application that receives and transmits network traffic
+(causing in-kernel memory allocations) with a maximum total message burst
+size of 200MB may need 200MB of extra free memory to avoid direct reclaim
+related latencies.
+
+==============================================================
+
hugepages_treat_as_movable
This parameter controls whether we can allocate hugepages from ZONE_MOVABLE
diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt
index 4da42616939f..0344f76da571 100644
--- a/Documentation/trace/ftrace.txt
+++ b/Documentation/trace/ftrace.txt
@@ -2043,6 +2043,35 @@ will produce:
1) 1.449 us | }
+You can disable the hierarchical function call formatting and instead print a
+flat list of function entry and return events. This uses the format described
+in the Output Formatting section and respects all the trace options that
+control that formatting. Hierarchical formatting is the default.
+
+ hierachical: echo nofuncgraph-flat > trace_options
+ flat: echo funcgraph-flat > trace_options
+
+ ie:
+
+ # tracer: function_graph
+ #
+ # entries-in-buffer/entries-written: 68355/68355 #P:2
+ #
+ # _-----=> irqs-off
+ # / _----=> need-resched
+ # | / _---=> hardirq/softirq
+ # || / _--=> preempt-depth
+ # ||| / delay
+ # TASK-PID CPU# |||| TIMESTAMP FUNCTION
+ # | | | |||| | |
+ sh-1806 [001] d... 198.843443: graph_ent: func=_raw_spin_lock
+ sh-1806 [001] d... 198.843445: graph_ent: func=__raw_spin_lock
+ sh-1806 [001] d..1 198.843447: graph_ret: func=__raw_spin_lock
+ sh-1806 [001] d..1 198.843449: graph_ret: func=_raw_spin_lock
+ sh-1806 [001] d..1 198.843451: graph_ent: func=_raw_spin_unlock_irqrestore
+ sh-1806 [001] d... 198.843453: graph_ret: func=_raw_spin_unlock_irqrestore
+
+
You might find other useful features for this tracer in the
following "dynamic ftrace" section such as tracing only specific
functions or tasks.
diff --git a/MAINTAINERS b/MAINTAINERS
index 217dc542e808..ac2c39faaf4d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1279,8 +1279,13 @@ M: Heiko Stuebner <heiko@sntech.de>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-rockchip@lists.infradead.org
S: Maintained
+F: arch/arm/boot/dts/rk3*
F: arch/arm/mach-rockchip/
+F: drivers/clk/rockchip/
+F: drivers/i2c/busses/i2c-rk3x.c
F: drivers/*/*rockchip*
+F: drivers/*/*/*rockchip*
+F: sound/soc/rockchip/
ARM/SAMSUNG ARM ARCHITECTURES
M: Ben Dooks <ben-linux@fluff.org>
@@ -5737,6 +5742,14 @@ S: Maintained
F: drivers/net/macvlan.c
F: include/linux/if_macvlan.h
+MAILBOX API
+M: Jassi Brar <jassisinghbrar@gmail.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/mailbox/
+F: include/linux/mailbox_client.h
+F: include/linux/mailbox_controller.h
+
MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7
M: Michael Kerrisk <mtk.manpages@gmail.com>
W: http://www.kernel.org/doc/man-pages
diff --git a/Makefile b/Makefile
index 8926229f4635..a604c4bdb158 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 3
PATCHLEVEL = 17
SUBLEVEL = 0
-EXTRAVERSION = -rc2
+EXTRAVERSION = -rc3
NAME = Shuffling Zombie Juror
# *DOCUMENTATION*
@@ -201,17 +201,7 @@ else
_all: modules
endif
-ifeq ($(KBUILD_SRC),)
- # building in the source tree
- srctree := .
-else
- ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
- # building in a subdirectory of the source tree
- srctree := ..
- else
- srctree := $(KBUILD_SRC)
- endif
-endif
+srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),.)
objtree := .
src := $(srctree)
obj := $(objtree)
@@ -673,6 +663,7 @@ ifdef CONFIG_CC_STACKPROTECTOR_REGULAR
ifeq ($(call cc-option, $(stackp-flag)),)
$(warning Cannot use CONFIG_CC_STACKPROTECTOR_REGULAR: \
-fstack-protector not supported by compiler)
+ stackp-flag :=
endif
else
ifdef CONFIG_CC_STACKPROTECTOR_STRONG
@@ -680,6 +671,7 @@ ifdef CONFIG_CC_STACKPROTECTOR_STRONG
ifeq ($(call cc-option, $(stackp-flag)),)
$(warning Cannot use CONFIG_CC_STACKPROTECTOR_STRONG: \
-fstack-protector-strong not supported by compiler)
+ stackp-flag :=
endif
else
# Force off for distro compilers that enable stack protector by default.
diff --git a/android/configs/README b/android/configs/README
new file mode 100644
index 000000000000..8798731f8904
--- /dev/null
+++ b/android/configs/README
@@ -0,0 +1,15 @@
+The files in this directory are meant to be used as a base for an Android
+kernel config. All devices should have the options in android-base.cfg enabled.
+While not mandatory, the options in android-recommended.cfg enable advanced
+Android features.
+
+Assuming you already have a minimalist defconfig for your device, a possible
+way to enable these options would be:
+
+ ARCH=<arch> scripts/kconfig/merge_config.sh <path_to>/<device>_defconfig android/configs/android-base.cfg android/configs/android-recommended.cfg
+
+This will generate a .config that can then be used to save a new defconfig or
+compile a new kernel with Android features enabled.
+
+Because there is no tool to consistently generate these config fragments,
+lets keep them alphabetically sorted instead of random.
diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg
new file mode 100644
index 000000000000..04c7148cf637
--- /dev/null
+++ b/android/configs/android-base.cfg
@@ -0,0 +1,140 @@
+# KEEP ALPHABETICALLY SORTED
+# CONFIG_INET_LRO is not set
+# CONFIG_MODULES is not set
+# CONFIG_OABI_COMPAT is not set
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_INTF_ALARM_DEV=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ASHMEM=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_VERITY=y
+CONFIG_EMBEDDED=y
+CONFIG_FB=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_INET=y
+CONFIG_INET_ESP=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_TARGET_REJECT_SKERR=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_REJECT_SKERR=y
+CONFIG_NET=y
+CONFIG_NETDEVICES=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_KEY=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_NAT=y
+CONFIG_NO_HZ=y
+CONFIG_PACKET=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PPP=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PREEMPT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_STAGING=y
+CONFIG_SWITCH=y
+CONFIG_SYNC=y
+CONFIG_SYSVIPC=y
+CONFIG_TUN=y
+CONFIG_UNIX=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_XFRM_USER=y
diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg
new file mode 100644
index 000000000000..960b9de2860d
--- /dev/null
+++ b/android/configs/android-recommended.cfg
@@ -0,0 +1,121 @@
+# KEEP ALPHABETICALLY SORTED
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_NF_CONNTRACK_SIP is not set
+# CONFIG_PM_WAKELOCKS_GC is not set
+# CONFIG_VT is not set
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_COMPACTION=y
+CONFIG_DM_UEVENT=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_FUSE_FS=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HIDRAW=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_GREENASIA=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_GPIO=y
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_KEYRESET=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_TABLET=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_ION=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KSM=y
+CONFIG_LOGIG940_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGITECH_FF=y
+CONFIG_MD=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MSDOS_FS=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_PANTHERLORD_FF=y
+CONFIG_PERF_EVENTS=y
+CONFIG_PM_DEBUG=y
+CONFIG_PM_RUNTIME=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+CONFIG_POWER_SUPPLY=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
+CONFIG_SCHEDSTATS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_SND=y
+CONFIG_SOUND=y
+CONFIG_SUSPEND_TIME=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_TABLET_USB_WACOM=y
+CONFIG_TIMER_STATS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_UHID=y
+CONFIG_UID_STAT=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_USBNET=y
+CONFIG_VFAT_FS=y
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 32cbbd565902..3a63c38c0982 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1405,6 +1405,15 @@ config MCPM
for (multi-)cluster based systems, such as big.LITTLE based
systems.
+config MCPM_QUAD_CLUSTER
+ bool
+ depends on MCPM
+ help
+ To avoid wasting resources unnecessarily, MCPM only supports up
+ to 2 clusters by default.
+ Platforms with 3 or 4 clusters that use MCPM must select this
+ option to allow the additional clusters to be managed.
+
config BIG_LITTLE
bool "big.LITTLE support (Experimental)"
depends on CPU_V7 && SMP
@@ -1492,6 +1501,7 @@ config ARCH_NR_GPIO
default 352 if ARCH_VT8500
default 288 if ARCH_ROCKCHIP
default 264 if MACH_H4700
+ default 128 if ARCH_HIP04
default 0
help
Maximum number of GPIOs in the system.
@@ -1769,6 +1779,15 @@ config XEN
help
Say Y if you want to run Linux in a Virtual Machine on Xen on ARM.
+config ARM_FLUSH_CONSOLE_ON_RESTART
+ bool "Force flush the console on restart"
+ help
+ If the console is locked while the system is rebooted, the messages
+ in the temporary logbuffer would not have propogated to all the
+ console drivers. This option forces the console lock to be
+ released if it failed to be acquired, which will cause all the
+ pending messages to be flushed.
+
endmenu
menu "Boot options"
@@ -1799,6 +1818,21 @@ config DEPRECATED_PARAM_STRUCT
This was deprecated in 2001 and announced to live on for 5 years.
Some old boot loaders still use this way.
+config BUILD_ARM_APPENDED_DTB_IMAGE
+ bool "Build a concatenated zImage/dtb by default"
+ depends on OF
+ help
+ Enabling this option will cause a concatenated zImage and DTB to
+ be built by default (instead of a standalone zImage.) The image
+ will built in arch/arm/boot/zImage-dtb.<dtb name>
+
+config BUILD_ARM_APPENDED_DTB_IMAGE_NAME
+ string "Default dtb name"
+ depends on BUILD_ARM_APPENDED_DTB_IMAGE
+ help
+ name of the dtb to append when building a concatenated
+ zImage/dtb.
+
# Compressed boot loader in ROM. Yes, we really want to ask about
# TEXT and BSS so we preserve their values in the config files.
config ZBOOT_ROM_TEXT
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index b11ad54f8d17..341f36c15481 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -231,6 +231,14 @@ choice
Say Y here if you want kernel low-level debugging support
on Hix5hd2 UART.
+ config DEBUG_HIP04_UART
+ bool "Hisilicon HiP04 Debug UART"
+ depends on ARCH_HIP04
+ select DEBUG_UART_8250
+ help
+ Say Y here if you want kernel low-level debugging support
+ on HIP04 UART.
+
config DEBUG_HIGHBANK_UART
bool "Kernel low-level debugging messages via Highbank UART"
depends on ARCH_HIGHBANK
@@ -1040,22 +1048,11 @@ config DEBUG_LL_INCLUDE
default "debug/zynq.S" if DEBUG_ZYNQ_UART0 || DEBUG_ZYNQ_UART1
default "mach/debug-macro.S"
-# Compatibility options for PL01x
config DEBUG_UART_PL01X
- def_bool ARCH_EP93XX || \
- ARCH_INTEGRATOR || \
- ARCH_SPEAR3XX || \
- ARCH_SPEAR6XX || \
- ARCH_SPEAR13XX || \
- ARCH_VERSATILE
-
-# Compatibility options for 8250
+ bool
+
config DEBUG_UART_8250
- def_bool ARCH_DOVE || ARCH_EBSA110 || \
- (FOOTBRIDGE && !DEBUG_DC21285_PORT) || \
- ARCH_GEMINI || ARCH_IOP13XX || ARCH_IOP32X || \
- ARCH_IOP33X || ARCH_IXP4XX || \
- ARCH_LPC32XX || ARCH_MV78XX0 || ARCH_ORION5X || ARCH_RPC
+ bool
config DEBUG_UART_PHYS
hex "Physical base address of debug UART"
@@ -1109,6 +1106,7 @@ config DEBUG_UART_PHYS
default 0xd4017000 if DEBUG_MMP_UART2
default 0xd4018000 if DEBUG_MMP_UART3
default 0xe0000000 if ARCH_SPEAR13XX
+ default 0xe4007000 if DEBUG_HIP04_UART
default 0xf0000be0 if ARCH_EBSA110
default 0xf1012000 if DEBUG_MVEBU_UART_ALTERNATE
default 0xf1012000 if ARCH_DOVE || ARCH_MV78XX0 || \
@@ -1152,6 +1150,7 @@ config DEBUG_UART_VIRT
default 0xf7008000 if DEBUG_S3C24XX_UART && (DEBUG_S3C_UART2 || \
DEBUG_S3C2410_UART2)
default 0xf7fc9000 if DEBUG_BERLIN_UART
+ default 0xf8007000 if DEBUG_HIP04_UART
default 0xf8009000 if DEBUG_VEXPRESS_UART0_CA9
default 0xf8090000 if DEBUG_VEXPRESS_UART0_RS1
default 0xfa71e000 if DEBUG_QCOM_UARTDM
@@ -1247,6 +1246,14 @@ config EARLY_PRINTK
kernel low-level debugging functions. Add earlyprintk to your
kernel parameters to enable this console.
+config EARLY_PRINTK_DIRECT
+ bool "Early printk direct"
+ depends on DEBUG_LL
+ help
+ Say Y here if you want to have an early console using the
+ kernel low-level debugging functions and EARLY_PRINTK is
+ not early enough.
+
config OC_ETM
bool "On-chip ETM and ETB"
depends on ARM_AMBA
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 6f663254670f..dd458882d250 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -287,6 +287,8 @@ libs-y := arch/arm/lib/ $(libs-y)
# Default target when executing plain make
ifeq ($(CONFIG_XIP_KERNEL),y)
KBUILD_IMAGE := xipImage
+else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y)
+KBUILD_IMAGE := zImage-dtb.$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAME)
else
KBUILD_IMAGE := zImage
endif
diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
index ec2f8065f955..a2e18ef0bdb7 100644
--- a/arch/arm/boot/Makefile
+++ b/arch/arm/boot/Makefile
@@ -14,6 +14,7 @@
ifneq ($(MACHINE),)
include $(srctree)/$(MACHINE)/Makefile.boot
endif
+include $(srctree)/arch/arm/boot/dts/Makefile
# Note: the following conditions must always be true:
# ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)
diff --git a/arch/arm/boot/compressed/atags_to_fdt.c b/arch/arm/boot/compressed/atags_to_fdt.c
index 9448aa0c6686..34b448c0e9ae 100644
--- a/arch/arm/boot/compressed/atags_to_fdt.c
+++ b/arch/arm/boot/compressed/atags_to_fdt.c
@@ -97,6 +97,22 @@ static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
setprop_string(fdt, "/chosen", "bootargs", cmdline);
}
+static int fdt_overrides_atag_mem(void *fdt)
+{
+ const char *memory;
+ int len = 0;
+
+ memory = getprop(fdt, "/memory", "reg", &len);
+ if (memory) {
+ while (len--) {
+ if (*memory != '\0')
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/*
* Convert and fold provided ATAGs into the provided FDT.
*
@@ -182,7 +198,7 @@ int atags_to_fdt(void *atag_list, void *fdt, int total_space)
}
}
- if (memcount) {
+ if (memcount && !fdt_overrides_atag_mem(fdt)) {
setprop(fdt, "/memory", "reg", mem_reg_property,
4 * memcount * memsize);
}
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 413fd94b5301..16db367f4220 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -731,6 +731,8 @@ __armv7_mmu_cache_on:
bic r6, r6, #1 << 31 @ 32-bit translation system
bic r6, r6, #3 << 0 @ use only ttbr0
mcrne p15, 0, r3, c2, c0, 0 @ load page table pointer
+ mcrne p15, 0, r0, c8, c7, 0 @ flush I,D TLBs
+ mcr p15, 0, r0, c7, c5, 4 @ ISB
mcrne p15, 0, r1, c3, c0, 0 @ load domain access control
mcrne p15, 0, r6, c2, c0, 2 @ load ttb control
#endif
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index b8c5cd3ddeb9..001666de928a 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -90,6 +90,7 @@ dtb-$(CONFIG_ARCH_HI3xxx) += hi3620-hi4511.dtb
dtb-$(CONFIG_ARCH_HIX5HD2) += hisi-x5hd2-dkb.dtb
dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb \
ecx-2000.dtb
+dtb-$(CONFIG_ARCH_HIP04) += hip04-d01.dtb
dtb-$(CONFIG_ARCH_INTEGRATOR) += integratorap.dtb \
integratorcp.dtb
dtb-$(CONFIG_ARCH_KEYSTONE) += k2hk-evm.dtb \
@@ -341,6 +342,7 @@ dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb
dtb-$(CONFIG_ARCH_QCOM) += \
qcom-apq8064-ifc6410.dtb \
qcom-apq8074-dragonboard.dtb \
+ qcom-apq8084-ifc6540.dtb \
qcom-apq8084-mtp.dtb \
qcom-msm8660-surf.dtb \
qcom-msm8960-cdp.dtb
@@ -454,7 +456,14 @@ dtb-$(CONFIG_ARCH_VERSATILE) += versatile-ab.dtb \
dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
vexpress-v2p-ca9.dtb \
vexpress-v2p-ca15-tc1.dtb \
- vexpress-v2p-ca15_a7.dtb
+ vexpress-v2p-ca15_a7.dtb \
+ rtsm_ve-cortex_a9x2.dtb \
+ rtsm_ve-cortex_a9x4.dtb \
+ rtsm_ve-cortex_a15x1.dtb \
+ rtsm_ve-cortex_a15x2.dtb \
+ rtsm_ve-cortex_a15x4.dtb \
+ rtsm_ve-v2p-ca15x1-ca7x1.dtb \
+ rtsm_ve-v2p-ca15x4-ca7x4.dtb
dtb-$(CONFIG_ARCH_VIRT) += xenvm-4.2.dtb
dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
wm8505-ref.dtb \
diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi
index 2ddaa5136611..2299f7a3e470 100644
--- a/arch/arm/boot/dts/bcm11351.dtsi
+++ b/arch/arm/boot/dts/bcm11351.dtsi
@@ -274,7 +274,8 @@
"bsc1",
"bsc2",
"bsc3",
- "pwm";
+ "pwm",
+ "bsc3_apb";
};
ref_1m_clk: ref_1m {
diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index 97f603c4483d..d678152db4cb 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -245,7 +245,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio2: gpio@48055000 {
@@ -256,7 +256,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio3: gpio@48057000 {
@@ -267,7 +267,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio4: gpio@48059000 {
@@ -278,7 +278,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio5: gpio@4805b000 {
@@ -289,7 +289,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio6: gpio@4805d000 {
@@ -300,7 +300,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio7: gpio@48051000 {
@@ -311,7 +311,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio8: gpio@48053000 {
@@ -322,7 +322,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
uart1: serial@4806a000 {
diff --git a/arch/arm/boot/dts/hi3620-hi4511.dts b/arch/arm/boot/dts/hi3620-hi4511.dts
index fe623928f687..8b2c3f222c48 100644
--- a/arch/arm/boot/dts/hi3620-hi4511.dts
+++ b/arch/arm/boot/dts/hi3620-hi4511.dts
@@ -64,6 +64,607 @@
status = "ok";
};
+ i2c0: i2c@b08000 {
+ status = "ok";
+ pinctrl-names = "default", "idle";
+ pinctrl-0 = <&i2c0_pmx_func &i2c0_cfg_func>;
+ pinctrl-1 = <&i2c0_pmx_idle &i2c0_cfg_func>;
+ };
+
+ i2c1: i2c@b09000 {
+ status = "ok";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pmx_func &i2c1_cfg_func>;
+ ts_mxt224e: ts@4a {
+ compatible = "atmel,ts-mxt224e";
+ reg = <0x4a>;
+ ldo-supply = <&ldo6>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&ts_pin_cfg>;
+ atmel-ts,gpio-irq = <&gpio19 5 0>;
+ atmel-ts,gpio-reset = <&gpio19 4 0>;
+ /* min max: x y pressure width */
+ atmel-ts,abs = <0 719 0 1279 0 255 0 255>;
+ atmel-ts,cfg_t6 = /bits/ 8 <0 0 0 0 0 0>;
+ atmel-ts,cfg_t7 = /bits/ 8 <32 255 10>;
+ atmel-ts,cfg_t8 = /bits/ 8 <24 0 1 10 0 0 5 60 10 192>;
+ atmel-ts,cfg_t9 = /bits/ 8 <143 0 0 19 11 0 32 66 2 3 0 2 2 47 10 15 22 10 106 5
+ 207 2 0 0 0 0 161 40 183 64 30 20 0 0 1>;
+ atmel-ts,cfg_t15 = /bits/ 8 <0 0 0 0 0 0 0 0 0 0 0>;
+ atmel-ts,cfg_t19 = /bits/ 8 <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ atmel-ts,cfg_t23 = /bits/ 8 <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ atmel-ts,cfg_t25 = /bits/ 8 <0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ atmel-ts,cfg_t40 = /bits/ 8 <0 0 0 0 0>;
+ atmel-ts,cfg_t42 = /bits/ 8 <0 40 40 80 128 0 0 0>;
+ atmel-ts,cfg_t46 = /bits/ 8 <0 3 32 32 0 0 0 0 0>;
+ atmel-ts,cfg_t47 = /bits/ 8 <0 20 50 5 2 40 40 180 0 100>;
+ atmel-ts,cfg_t48 = /bits/ 8 <1 4 10 0 0 0 0 0 1 1 0 0 0 6 6 0 0 63 6 64
+ 10 0 20 5 0 38 0 20 0 0 0 0 0 0 0 40 2 2 2 32
+ 10 12 20 241 251 0 0 191 40 183 64 30 15 0>;
+ atmel-ts,object_crc = /bits/ 8 <0xFD 0x3B 0x8D>;
+ atmel-ts,cable_config = /bits/ 8 <70 30 32 32>;
+ atmel-ts,cable_config_t7 = /bits/ 8 <32 16 25>;
+ atmel-ts,cable_config_t8 = /bits/ 8 <24 0 5 5 0 0 5 60 10 192>;
+ atmel-ts,cable_config_T9 = /bits/ 8 <139 0 0 19 11 0 32 66 2 3 0 5 2 64 10
+ 12 20 10 106 5 207 2 0 0 0 0 161 40 183 64 30 20 0 0 0>;
+ atmel-ts,cable_config_t46 = /bits/ 8 <0 3 40 40 0 0 0 0 0>;
+ atmel-ts,cable_config_t48 = /bits/ 8 <1 128 114 0 0 0 0 0 1 2 0 0 0 6 6
+ 0 0 63 6 64 10 0 20 5 0 38 0 20 0 0 0 0 0 0 0
+ 40 2 2 2 32 10 12 20 241 251 0 0 191 40 183 64 30 15 0>;
+ atmel-ts,noise_config = /bits/ 8 <70 3 35>;
+ atmel-ts,filter_level = /bits/ 16 <0 0 539 539>;
+ atmel-ts,gcaf_level = /bits/ 8 <8 16 24 32 40>;
+ atmel-ts,atch_nor = /bits/ 8 <0 0 5 60 10 192>;
+ atmel-ts,atch_nor_20s = /bits/ 8 <0 0 255 1 0 0>;
+ };
+ };
+
+ dwmmc1@d04000 {
+ num-slots = <1>;
+ vmmc-supply = <&ldo0>;
+ vqmmc-supply = <&ldo5>;
+ /* emmc fifo register value is incorrect */
+ fifo-depth = <0x100>;
+ broken-cd;
+ supports-highspeed;
+ pinctrl-names = "default";
+ pinctrl-0 = <&emmc_pmx_func &emmc_cfg_func &emmc_cfg_clk_func>;
+ slot@0 {
+ reg = <0>;
+ bus-width = <8>;
+ disable-wp;
+ };
+ };
+
+ dwmmc0@d03000 {
+ num-slots = <1>;
+ vmmc-supply = <&ldo12>;
+ fifo-depth = <0x100>;
+ supports-highspeed;
+ cd-inverted;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
+ slot@0 {
+ reg = <0>;
+ bus-width = <4>;
+ disable-wp;
+ cd-gpios = <&gpio10 3 0>;
+ };
+ };
+
+ dwmmc2@d05000 {
+ status = "disabled";
+ };
+
+ dwmmc3@d06000 {
+ status = "disabled";
+ };
+ pmic: pmic@c00000 {
+ gpios = <&gpio19 7 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_int_cfg_func>;
+ ldo0: ldo@20 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO0";
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ hisilicon,hi6421-ctrl = <0x20 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x20 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <10000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo1: ldo@21 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO1";
+ regulator-min-microvolt = <1700000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x21 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x21 0x03>;
+ hisilicon,hi6421-n-voltages = <4>;
+ hisilicon,hi6421-vset-table = <1700000>, <1800000>,
+ <1900000>, <2000000>;
+ hisilicon,hi6421-off-on-delay-us = <10000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <5000>;
+ };
+
+ ldo2: ldo@22 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO2";
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x22 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x22 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1050000>, <1100000>,
+ <1150000>, <1200000>,
+ <1250000>, <1300000>,
+ <1350000>, <1400000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo3: ldo@23 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO3";
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x23 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x23 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1050000>, <1100000>,
+ <1150000>, <1200000>,
+ <1250000>, <1300000>,
+ <1350000>, <1400000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo4: ldo@24 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO4";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x24 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x24 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo5: ldo@25 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO5";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x25 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x25 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo6: ldo@26 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO6";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x26 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x26 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo7: ldo@27 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO7";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x27 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x27 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <5000>;
+ };
+
+ ldo8: ldo@28 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO8";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x28 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x28 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2600000>,
+ <2700000>, <2850000>,
+ <3000000>, <3300000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo9: ldo@29 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO9";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x29 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x29 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo10: ldo@2a {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO10";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2a 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2a 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo11: ldo@2b {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO11";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2b 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2b 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo12: ldo@2c {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO12";
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ hisilicon,hi6421-ctrl = <0x2c 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2c 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo13: ldo@2d {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO13";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2d 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2d 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo14: ldo@2e {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO14";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2e 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2e 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo15: ldo@2f {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO15";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3300000>;
+ hisilicon,hi6421-ctrl = <0x2f 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2f 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2600000>,
+ <2700000>, <2850000>,
+ <3000000>, <3300000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo16: ldo@30 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO16";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x30 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x30 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo17: ldo@31 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO17";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x31 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x31 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo18: ldo@32 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO18";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x32 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x32 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo19: ldo@33 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO19";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2a 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2a 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo20: ldo@34 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO20";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x34 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x34 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldoaudio: ldo@36 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDOAUDIO";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <3300000>;
+ hisilicon,hi6421-ctrl = <0x36 0x01 0x02>;
+ hisilicon,hi6421-vset = <0x36 0x70>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <2800000>, <2850000>,
+ <2900000>, <2950000>,
+ <3000000>, <3100000>,
+ <3200000>, <3300000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <5000>;
+ };
+
+ buck0: buck@0c {
+ compatible = "hisilicon,hi6421-buck012";
+ regulator-name = "BUCK0";
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1600000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x0c 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x0d 0x7f>;
+ hisilicon,hi6421-n-voltages = <128>;
+ hisilicon,hi6421-uv-step = <7086>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <300>;
+ };
+
+ buck1: buck@0e {
+ compatible = "hisilicon,hi6421-buck012";
+ regulator-name = "BUCK1";
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1600000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x0e 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x0f 0x7f>;
+ hisilicon,hi6421-n-voltages = <128>;
+ hisilicon,hi6421-uv-step = <7086>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <300>;
+ };
+
+ buck2: buck@10 {
+ compatible = "hisilicon,hi6421-buck012";
+ regulator-name = "BUCK2";
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1600000>;
+ regulator-boot-on;
+ hisilicon,hi6421-ctrl = <0x10 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x11 0x7f>;
+ hisilicon,hi6421-n-voltages = <128>;
+ hisilicon,hi6421-uv-step = <7086>;
+ hisilicon,hi6421-off-on-delay-us = <100>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ };
+
+ buck3: buck@12 {
+ compatible = "hisilicon,hi6421-buck345";
+ regulator-name = "BUCK3";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x12 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x13 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <950000>, <1050000>,
+ <1100000>, <1170000>,
+ <1134000>, <1150000>,
+ <1167000>, <1200000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ };
+
+ buck4: buck@14 {
+ compatible = "hisilicon,hi6421-buck345";
+ regulator-name = "BUCK4";
+ regulator-min-microvolt = <1150000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x14 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x15 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1150000>, <1200000>,
+ <1250000>, <1350000>,
+ <1700000>, <1800000>,
+ <1900000>, <2000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ };
+
+ buck5: buck@16 {
+ compatible = "hisilicon,hi6421-buck345";
+ regulator-name = "BUCK5";
+ regulator-min-microvolt = <1150000>;
+ regulator-max-microvolt = <1900000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x16 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x17 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1150000>, <1200000>,
+ <1250000>, <1350000>,
+ <1600000>, <1700000>,
+ <1800000>, <1900000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ };
+
+ onkey {
+ compatible = "hisilicon,hi6421-onkey";
+ interrupt-parent = <&pmic>;
+ interrupts = <7 0>, <6 0>, <5 0>, <4 0>;
+ interrupt-names = "down", "up", "hold 1s", "hold 10s";
+ };
+
+ rtc {
+ compatible = "hisilicon,hi6421-rtc";
+ interrupt-parent = <&pmic>;
+ interrupts = <0 0>;
+ };
+ }; /* end of pmic */
+
pmx0: pinmux@803000 {
pinctrl-names = "default";
pinctrl-0 = <&board_pmx_pins>;
@@ -74,6 +675,12 @@
0x100 0x0 /* USIM_CLK & USIM_DATA (IOMG63) */
>;
};
+ sd_pmx_pins: pinmux_sd_pins {
+ pinctrl-single,pins = <
+ 0x0bc 0x0 /* SD_CLK, SD_CMD, SD_DATA[0:2] */
+ 0x0c0 0x0 /* SD_DATA[3] */
+ >;
+ };
uart0_pmx_func: uart0_pmx_func {
pinctrl-single,pins = <
0x0f0 0x0
@@ -534,10 +1141,6 @@
emmc_cfg_func: emmc_cfg_func {
pinctrl-single,pins = <
0x0ac 0 /* eMMC_CMD (IOCFG40) */
- 0x0b0 0 /* eMMC_CLK (IOCFG41) */
- 0x058 0 /* NAND_CS3_N (IOCFG19) */
- 0x064 0 /* NAND_BUSY2_N (IOCFG22) */
- 0x068 0 /* NAND_BUSY3_N (IOCFG23) */
0x08c 0 /* NAND_DATA8 (IOCFG32) */
0x090 0 /* NAND_DATA9 (IOCFG33) */
0x094 0 /* NAND_DATA10 (IOCFG34) */
@@ -551,7 +1154,15 @@
pinctrl-single,bias-pullup = <1 1 0 1>;
pinctrl-single,drive-strength = <0x30 0xf0>;
};
- sd_cfg_func1: sd_cfg_func1 {
+ emmc_cfg_clk_func: pincfg_emmc_clk_func {
+ pinctrl-single,pins = <
+ 0x0b0 0 /* eMMC_CLK (IOCFG41) */
+ >;
+ pinctrl-single,bias-pulldown = <0 2 0 2>;
+ pinctrl-single,bias-pullup = <0 1 0 1>;
+ pinctrl-single,drive-strength = <0x30 0xf0>;
+ };
+ sd_cfg_func1: pincfg_sd_f1 {
pinctrl-single,pins = <
0x18c 0 /* SD_CLK (IOCFG107) */
0x190 0 /* SD_CMD (IOCFG108) */
@@ -562,13 +1173,14 @@
};
sd_cfg_func2: sd_cfg_func2 {
pinctrl-single,pins = <
+ 0x190 0 /* SD_CMD (IOCFG108) */
0x194 0 /* SD_DATA0 (IOCFG109) */
0x198 0 /* SD_DATA1 (IOCFG110) */
0x19c 0 /* SD_DATA2 (IOCFG111) */
0x1a0 0 /* SD_DATA3 (IOCFG112) */
>;
- pinctrl-single,bias-pulldown = <2 2 0 2>;
- pinctrl-single,bias-pullup = <0 1 0 1>;
+ pinctrl-single,bias-pulldown = <0 2 0 2>;
+ pinctrl-single,bias-pullup = <1 1 0 1>;
pinctrl-single,drive-strength = <0x70 0xf0>;
};
nand_cfg_func1: nand_cfg_func1 {
@@ -634,6 +1246,19 @@
pinctrl-single,bias-pulldown = <2 2 0 2>;
pinctrl-single,bias-pullup = <0 1 0 1>;
};
+ pmic_int_cfg_func: pincfg_pmic_func {
+ pinctrl-single,pins = <
+ 0x018 0 /* GPIO159 (IOCFG003) */
+ >;
+ };
+ /* TP_IRQ need pullup */
+ ts_pin_cfg: pincfg_ts_func {
+ pinctrl-single,pins = <
+ 0x010 0 /* GPIO157 (TP_IRQ) */
+ >;
+ pinctrl-single,bias-pulldown = <0 2 0 2>;
+ pinctrl-single,bias-pullup = <1 1 0 1>;
+ };
};
};
diff --git a/arch/arm/boot/dts/hi3620.dtsi b/arch/arm/boot/dts/hi3620.dtsi
index 6cbb62e5c6a9..55d2e894c3de 100644
--- a/arch/arm/boot/dts/hi3620.dtsi
+++ b/arch/arm/boot/dts/hi3620.dtsi
@@ -107,6 +107,21 @@
};
};
+ pctrl: pctrl@a09000 {
+ compatible = "hisilicon,pctrl";
+ ranges = <0 0xa09000 0x1000>;
+ reg = <0xa09000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ mmc_clock: clock@1 {
+ compatible = "hisilicon,hi3620-mmc-clock";
+ reg = <0 0x10>;
+ #clock-cells = <1>;
+ };
+ };
+
+
dual_timer0: dual_timer@800000 {
compatible = "arm,sp804", "arm,primecell";
reg = <0x800000 0x1000>;
@@ -532,6 +547,96 @@
clock-names = "apb_pclk";
};
+ i2c0: i2c@b08000 {
+ compatible = "snps,designware-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xb08000 0x1000>;
+ interrupts = <0 28 4>;
+ clocks = <&clock HI3620_I2CCLK0>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@b09000 {
+ compatible = "snps,designware-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xb09000 0x1000>;
+ interrupts = <0 29 4>;
+ clocks = <&clock HI3620_I2CCLK1>;
+ status = "disabled";
+ };
+
+ i2c2: i2c@b0c000 {
+ compatible = "snps,designware-i2c";
+ reg = <0xb0c000 0x1000>;
+ interrupts = <0 62 4>;
+ clocks = <&clock HI3620_I2CCLK2>;
+ status = "disabled";
+ };
+
+ i2c3: i2c@b0d000 {
+ compatible = "snps,designware-i2c";
+ reg = <0xb0d000 0x1000>;
+ interrupts = <0 63 4>;
+ clocks = <&clock HI3620_I2CCLK3>;
+ status = "disabled";
+ };
+
+ /* unremovable emmc as mmcblk0 */
+ dwmmc_1: dwmmc1@d04000 {
+ compatible = "hisilicon,hi4511-dw-mshc";
+ reg = <0xd04000 0x1000>;
+ interrupts = <0 17 4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&mmc_clock HI3620_MMC_CIUCLK1>, <&clock HI3620_DDRC_PER_CLK>;
+ clock-names = "ciu", "biu";
+ clock-freq-table =
+ <13000000 50000000 0 0 13000000 50000000 0 100000000>;
+
+ };
+
+ /* sd as mmcblk1 */
+ dwmmc_0: dwmmc0@d03000 {
+ compatible = "hisilicon,hi4511-dw-mshc";
+ reg = <0xd03000 0x1000>;
+ interrupts = <0 16 4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
+ clock-names = "ciu", "biu";
+ clock-freq-table =
+ <25000000 0 50000000 25000000 50000000 100000000 0 50000000>;
+ };
+
+ dwmmc_2: dwmmc2@d05000 {
+ compatible = "hisilicon,hi4511-dw-mshc";
+ reg = <0xd05000 0x1000>;
+ interrupts = <0 18 4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&mmc_clock HI3620_MMC_CIUCLK2>, <&clock HI3620_DDRC_PER_CLK>;
+ clock-freq-table =
+ <25000000 25000000 50000000 50000000>;
+ };
+
+ dwmmc_3: dwmmc3@d06000 {
+ compatible = "hisilicon,hi4511-dw-mshc";
+ reg = <0xd06000 0x1000>;
+ interrupts = <0 19 4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&mmc_clock HI3620_MMC_CIUCLK3>, <&clock HI3620_DDRC_PER_CLK>;
+ clock-freq-table =
+ <25000000 25000000 50000000 50000000>;
+ };
+ pmic: pmic@c00000 {
+ compatible = "hisilicon,hi6421-pmic";
+ reg = <0xc00000 0x0180>; /* 0x60 << 2 */
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
pmx0: pinmux@803000 {
compatible = "pinctrl-single";
reg = <0x803000 0x188>;
diff --git a/arch/arm/boot/dts/hip04-d01.dts b/arch/arm/boot/dts/hip04-d01.dts
new file mode 100644
index 000000000000..40a9e33c2654
--- /dev/null
+++ b/arch/arm/boot/dts/hip04-d01.dts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013-2014 Linaro Ltd.
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+
+/dts-v1/;
+
+#include "hip04.dtsi"
+
+/ {
+ /* memory bus is 64-bit */
+ #address-cells = <2>;
+ #size-cells = <2>;
+ model = "Hisilicon D01 Development Board";
+ compatible = "hisilicon,hip04-d01";
+
+ memory@00000000,10000000 {
+ device_type = "memory";
+ reg = <0x00000000 0x10000000 0x00000000 0xc0000000>,
+ <0x00000004 0xc0000000 0x00000003 0x40000000>;
+ };
+
+ soc {
+ uart0: uart@4007000 {
+ status = "ok";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/hip04.dtsi b/arch/arm/boot/dts/hip04.dtsi
new file mode 100644
index 000000000000..b3757a5bff67
--- /dev/null
+++ b/arch/arm/boot/dts/hip04.dtsi
@@ -0,0 +1,373 @@
+/*
+ * Hisilicon Ltd. HiP04 SoC
+ *
+ * Copyright (C) 2013-2014 Hisilicon Ltd.
+ * Copyright (C) 2013-2014 Linaro Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * 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.
+ */
+
+/ {
+ /* memory bus is 64-bit */
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ bootwrapper {
+ compatible = "hisilicon,hip04-bootwrapper";
+ boot-method = <0x10c00000 0x10000>, <0xe0000100 0x1000>;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu-map {
+ cluster0 {
+ core0 {
+ cpu = <&CPU0>;
+ };
+ core1 {
+ cpu = <&CPU1>;
+ };
+ core2 {
+ cpu = <&CPU2>;
+ };
+ core3 {
+ cpu = <&CPU3>;
+ };
+ };
+ cluster1 {
+ core0 {
+ cpu = <&CPU4>;
+ };
+ core1 {
+ cpu = <&CPU5>;
+ };
+ core2 {
+ cpu = <&CPU6>;
+ };
+ core3 {
+ cpu = <&CPU7>;
+ };
+ };
+ cluster2 {
+ core0 {
+ cpu = <&CPU8>;
+ };
+ core1 {
+ cpu = <&CPU9>;
+ };
+ core2 {
+ cpu = <&CPU10>;
+ };
+ core3 {
+ cpu = <&CPU11>;
+ };
+ };
+ cluster3 {
+ core0 {
+ cpu = <&CPU12>;
+ };
+ core1 {
+ cpu = <&CPU13>;
+ };
+ core2 {
+ cpu = <&CPU14>;
+ };
+ core3 {
+ cpu = <&CPU15>;
+ };
+ };
+ };
+ CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ };
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <1>;
+ };
+ CPU2: cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <2>;
+ };
+ CPU3: cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <3>;
+ };
+ CPU4: cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x100>;
+ };
+ CPU5: cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x101>;
+ };
+ CPU6: cpu@102 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x102>;
+ };
+ CPU7: cpu@103 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x103>;
+ };
+ CPU8: cpu@200 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x200>;
+ };
+ CPU9: cpu@201 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x201>;
+ };
+ CPU10: cpu@202 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x202>;
+ };
+ CPU11: cpu@203 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x203>;
+ };
+ CPU12: cpu@300 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x300>;
+ };
+ CPU13: cpu@301 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x301>;
+ };
+ CPU14: cpu@302 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x302>;
+ };
+ CPU15: cpu@303 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x303>;
+ };
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupt-parent = <&gic>;
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
+ };
+
+ clk_50m: clk_50m {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <50000000>;
+ };
+
+ clk_168m: clk_168m {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <168000000>;
+ };
+
+ soc {
+ /* It's a 32-bit SoC. */
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ interrupt-parent = <&gic>;
+ ranges = <0 0 0xe0000000 0x10000000>;
+
+ gic: interrupt-controller@c01000 {
+ compatible = "hisilicon,hip04-intc";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ interrupts = <1 9 0xf04>;
+
+ reg = <0xc01000 0x1000>, <0xc02000 0x1000>,
+ <0xc04000 0x2000>, <0xc06000 0x2000>;
+ };
+
+ sysctrl: sysctrl {
+ compatible = "hisilicon,sysctrl";
+ reg = <0x3e00000 0x00100000>;
+ };
+
+ fabric: fabric {
+ compatible = "hisilicon,hip04-fabric";
+ reg = <0x302a000 0x1000>;
+ };
+
+ dual_timer0: dual_timer@3000000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0x3000000 0x1000>;
+ interrupts = <0 224 4>;
+ clocks = <&clk_50m>, <&clk_50m>;
+ clock-names = "apb_pclk";
+ };
+
+ arm-pmu {
+ compatible = "arm,cortex-a15-pmu";
+ interrupts = <0 64 4>,
+ <0 65 4>,
+ <0 66 4>,
+ <0 67 4>,
+ <0 68 4>,
+ <0 69 4>,
+ <0 70 4>,
+ <0 71 4>,
+ <0 72 4>,
+ <0 73 4>,
+ <0 74 4>,
+ <0 75 4>,
+ <0 76 4>,
+ <0 77 4>,
+ <0 78 4>,
+ <0 79 4>;
+ };
+
+ uart0: uart@4007000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x4007000 0x1000>;
+ interrupts = <0 381 4>;
+ clocks = <&clk_168m>;
+ clock-names = "uartclk";
+ reg-shift = <2>;
+ status = "disabled";
+ };
+
+ sata0: sata@a000000 {
+ compatible = "hisilicon,hisi-ahci";
+ reg = <0xa000000 0x1000000>;
+ interrupts = <0 372 4>;
+ };
+
+ ppebase: ppebase@28c0000 {
+ compatible = "hisilicon,ppebase";
+ reg = <0x28c0000 0x10000>;
+ };
+
+ fe: ethernet@28b0000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x28b0000 0x10000>;
+ interrupts = <0 413 4>;
+ port = <31>;
+ speed = <100>;
+ id = <0>;
+ };
+
+ ge0: ethernet@2800000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x2800000 0x10000>;
+ interrupts = <0 412 4>;
+ port = <0>;
+ speed = <1000>;
+ id = <1>;
+ };
+ ge8: ethernet@2880000 {
+ compatible = "hisilicon,hip04-mac";
+ reg = <0x2880000 0x10000>;
+ interrupts = <0 420 4>;
+ port = <8>;
+ speed = <1000>;
+ id = <2>;
+ };
+
+ gpio@4003000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x4003000 0x1000>;
+
+ gpio3: gpio-controller@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ gpio-controller;
+ #gpio-cells = <2>;
+ snps,nr-gpios = <32>;
+ reg = <0>;
+ interrupt-parent = <&gic>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <0 392 4>;
+ };
+ };
+
+ gpio@4002000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x4002000 0x1000>;
+
+ gpio2: gpio-controller@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ gpio-controller;
+ #gpio-cells = <2>;
+ snps,nr-gpios = <32>;
+ reg = <0>;
+ interrupt-parent = <&gic>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <0 391 4>;
+ };
+ };
+
+ gpio@4001000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x4001000 0x1000>;
+
+ gpio1: gpio-controller@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ gpio-controller;
+ #gpio-cells = <2>;
+ snps,nr-gpios = <32>;
+ reg = <0>;
+ interrupt-parent = <&gic>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <0 390 4>;
+ };
+ };
+
+ gpio@4000000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x4000000 0x1000>;
+
+ gpio0: gpio-controller@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ gpio-controller;
+ #gpio-cells = <2>;
+ snps,nr-gpios = <32>;
+ reg = <0>;
+ interrupt-parent = <&gic>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <0 389 4>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/hisi-x5hd2-dkb.dts b/arch/arm/boot/dts/hisi-x5hd2-dkb.dts
index 05b44c272c9a..15292541ab46 100644
--- a/arch/arm/boot/dts/hisi-x5hd2-dkb.dts
+++ b/arch/arm/boot/dts/hisi-x5hd2-dkb.dts
@@ -51,3 +51,34 @@
&uart0 {
status = "okay";
};
+
+&gmac0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ phy-handle = <&phy2>;
+ phy-mode = "mii";
+ mac-address = [00 00 00 00 00 00];
+ status = "okay";
+
+ phy2: ethernet-phy@2 {
+ reg = <2>;
+ };
+};
+
+&gmac1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ phy-handle = <&phy1>;
+ phy-mode = "rgmii";
+ mac-address = [00 00 00 00 00 00];
+ status = "okay";
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+};
+
+&ahci {
+ phys = <&sata_phy>;
+ phy-names = "sata-phy";
+};
diff --git a/arch/arm/boot/dts/hisi-x5hd2.dtsi b/arch/arm/boot/dts/hisi-x5hd2.dtsi
index f85ba2924ff7..17d063717e07 100644
--- a/arch/arm/boot/dts/hisi-x5hd2.dtsi
+++ b/arch/arm/boot/dts/hisi-x5hd2.dtsi
@@ -131,6 +131,249 @@
clock-names = "apb_pclk";
status = "disabled";
};
+
+ gpio0: gpio@b20000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb20000 0x1000>;
+ interrupts = <0 108 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio1: gpio@b21000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb21000 0x1000>;
+ interrupts = <0 109 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio2: gpio@b22000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb22000 0x1000>;
+ interrupts = <0 110 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio3: gpio@b23000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb23000 0x1000>;
+ interrupts = <0 111 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio4: gpio@b24000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb24000 0x1000>;
+ interrupts = <0 112 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio5: gpio@004000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0x004000 0x1000>;
+ interrupts = <0 113 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio6: gpio@b26000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb26000 0x1000>;
+ interrupts = <0 114 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio7: gpio@b27000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb27000 0x1000>;
+ interrupts = <0 115 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio8: gpio@b28000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb28000 0x1000>;
+ interrupts = <0 116 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio9: gpio@b29000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb29000 0x1000>;
+ interrupts = <0 117 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio10: gpio@b2a000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb2a000 0x1000>;
+ interrupts = <0 118 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio11: gpio@b2b000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb2b000 0x1000>;
+ interrupts = <0 119 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio12: gpio@b2c000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb2c000 0x1000>;
+ interrupts = <0 120 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio13: gpio@b2d000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb2d000 0x1000>;
+ interrupts = <0 121 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio14: gpio@b2e000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb2e000 0x1000>;
+ interrupts = <0 122 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio15: gpio@b2f000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb2f000 0x1000>;
+ interrupts = <0 123 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio16: gpio@b30000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb30000 0x1000>;
+ interrupts = <0 124 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ gpio17: gpio@b31000 {
+ compatible = "arm,pl061", "arm,primecell";
+ reg = <0xb31000 0x1000>;
+ interrupts = <0 125 0x4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ clocks = <&clock HIX5HD2_FIXED_100M>;
+ clock-names = "apb_pclk";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ status = "disabled";
+ };
+
+ wdg0: wdg@a2c000 {
+ compatible = "arm,sp805", "arm,primecell";
+ arm,primecell-periphid = <0x00141805>;
+ reg = <0xa2c000 0x1000>;
+ interrupts = <0 29 4>;
+ clocks = <&clock HIX5HD2_WDG0_RST>;
+ clock-names = "apb_pclk";
+ };
};
local_timer@00a00600 {
@@ -166,5 +409,132 @@
#clock-cells = <1>;
};
};
+
+ /* unremovable emmc as mmcblk0 */
+ mmc: mmc@1830000 {
+ compatible = "snps,dw-mshc";
+ reg = <0x1830000 0x1000>;
+ interrupts = <0 35 4>;
+ clocks = <&clock HIX5HD2_MMC_CIU_RST>, <&clock HIX5HD2_MMC_BIU_CLK>;
+ clock-names = "ciu", "biu";
+ };
+
+ sd: mmc@1820000 {
+ compatible = "snps,dw-mshc";
+ reg = <0x1820000 0x1000>;
+ interrupts = <0 34 4>;
+ clocks = <&clock HIX5HD2_SD_CIU_RST>, <&clock HIX5HD2_SD_BIU_CLK>;
+ clock-names = "ciu","biu";
+ };
+
+ gmac0: ethernet@1840000 {
+ compatible = "hisilicon,hix5hd2-gmac";
+ reg = <0x1840000 0x1000>,<0x184300c 0x4>;
+ interrupts = <0 71 4>;
+ clocks = <&clock HIX5HD2_MAC0_CLK>;
+ status = "disabled";
+ };
+
+ gmac1: ethernet@1841000 {
+ compatible = "hisilicon,hix5hd2-gmac";
+ reg = <0x1841000 0x1000>,<0x1843010 0x4>;
+ interrupts = <0 72 4>;
+ clocks = <&clock HIX5HD2_MAC1_CLK>;
+ status = "disabled";
+ };
+
+ usb0: ehci@1890000 {
+ compatible = "generic-ehci";
+ reg = <0x1890000 0x1000>;
+ interrupts = <0 66 4>;
+ clocks = <&clock HIX5HD2_USB_CLK>;
+ };
+
+ usb1: ohci@1880000 {
+ compatible = "generic-ohci";
+ reg = <0x1880000 0x1000>;
+ interrupts = <0 67 4>;
+ clocks = <&clock HIX5HD2_USB_CLK>;
+ };
+
+ peripheral_ctrl: syscon@a20000 {
+ compatible = "syscon";
+ reg = <0xa20000 0x1000>;
+ };
+
+ sata_phy: phy@1900000 {
+ compatible = "hisilicon,hix5hd2-sata-phy";
+ reg = <0x1900000 0x10000>;
+ #phy-cells = <0>;
+ hisilicon,peripheral-syscon = <&peripheral_ctrl>;
+ hisilicon,power-reg = <0x8 10>;
+ };
+
+ ahci: sata@1900000 {
+ compatible = "hisilicon,hix5hd2-ahci";
+ reg = <0x1900000 0x10000>;
+ interrupts = <0 70 4>;
+ clocks = <&clock HIX5HD2_SATA_CLK>;
+ };
+
+ i2c0: i2c@b10000 {
+ compatible = "hisilicon,hix5hd2-i2c";
+ reg = <0xb10000 0x1000>;
+ interrupts = <0 38 4>;
+ clocks = <&clock HIX5HD2_I2C0_RST>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@b11000 {
+ compatible = "hisilicon,hix5hd2-i2c";
+ reg = <0xb11000 0x1000>;
+ interrupts = <0 39 4>;
+ clocks = <&clock HIX5HD2_I2C1_RST>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ i2c2: i2c@b12000 {
+ compatible = "hisilicon,hix5hd2-i2c";
+ reg = <0xb12000 0x1000>;
+ interrupts = <0 40 4>;
+ clocks = <&clock HIX5HD2_I2C2_RST>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ i2c3: i2c@b13000 {
+ compatible = "hisilicon,hix5hd2-i2c";
+ reg = <0xb13000 0x1000>;
+ interrupts = <0 41 4>;
+ clocks = <&clock HIX5HD2_I2C3_RST>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ i2c4: i2c@b16000 {
+ compatible = "hisilicon,hix5hd2-i2c";
+ reg = <0xb16000 0x1000>;
+ interrupts = <0 43 4>;
+ clocks = <&clock HIX5HD2_I2C4_RST>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ i2c5: i2c@b17000 {
+ compatible = "hisilicon,hix5hd2-i2c";
+ reg = <0xb17000 0x1000>;
+ interrupts = <0 44 4>;
+ clocks = <&clock HIX5HD2_I2C5_RST>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/imx53-qsrb.dts b/arch/arm/boot/dts/imx53-qsrb.dts
index f1bbf9a32991..82d623d05915 100644
--- a/arch/arm/boot/dts/imx53-qsrb.dts
+++ b/arch/arm/boot/dts/imx53-qsrb.dts
@@ -28,6 +28,12 @@
MX53_PAD_CSI0_DAT9__I2C1_SCL 0x400001ec
>;
};
+
+ pinctrl_pmic: pmicgrp {
+ fsl,pins = <
+ MX53_PAD_CSI0_DAT5__GPIO5_23 0x1e4 /* IRQ */
+ >;
+ };
};
};
@@ -38,6 +44,8 @@
pmic: mc34708@8 {
compatible = "fsl,mc34708";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pmic>;
reg = <0x08>;
interrupt-parent = <&gpio5>;
interrupts = <23 0x8>;
diff --git a/arch/arm/boot/dts/imx6dl-hummingboard.dts b/arch/arm/boot/dts/imx6dl-hummingboard.dts
index c8e51dd41b8f..71598546087f 100644
--- a/arch/arm/boot/dts/imx6dl-hummingboard.dts
+++ b/arch/arm/boot/dts/imx6dl-hummingboard.dts
@@ -58,7 +58,7 @@
sound-spdif {
compatible = "fsl,imx-audio-spdif";
- model = "imx-spdif";
+ model = "On-board SPDIF";
/* IMX6 doesn't implement this yet */
spdif-controller = <&spdif>;
spdif-out;
@@ -181,11 +181,13 @@
};
&usbh1 {
+ disable-over-current;
vbus-supply = <&reg_usbh1_vbus>;
status = "okay";
};
&usbotg {
+ disable-over-current;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hummingboard_usbotg_id>;
vbus-supply = <&reg_usbotg_vbus>;
diff --git a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi
index e8e781656b3f..6a524ca011e7 100644
--- a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi
@@ -61,7 +61,7 @@
sound-spdif {
compatible = "fsl,imx-audio-spdif";
- model = "imx-spdif";
+ model = "Integrated SPDIF";
/* IMX6 doesn't implement this yet */
spdif-controller = <&spdif>;
spdif-out;
@@ -130,16 +130,23 @@
fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>;
};
+ pinctrl_cubox_i_usbh1: cubox-i-usbh1 {
+ fsl,pins = <MX6QDL_PAD_GPIO_3__USB_H1_OC 0x1b0b0>;
+ };
+
pinctrl_cubox_i_usbh1_vbus: cubox-i-usbh1-vbus {
fsl,pins = <MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x4001b0b0>;
};
- pinctrl_cubox_i_usbotg_id: cubox-i-usbotg-id {
+ pinctrl_cubox_i_usbotg: cubox-i-usbotg {
/*
- * The Cubox-i pulls this low, but as it's pointless
+ * The Cubox-i pulls ID low, but as it's pointless
* leaving it as a pull-up, even if it is just 10uA.
*/
- fsl,pins = <MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059>;
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059
+ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0
+ >;
};
pinctrl_cubox_i_usbotg_vbus: cubox-i-usbotg-vbus {
@@ -173,13 +180,15 @@
};
&usbh1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cubox_i_usbh1>;
vbus-supply = <&reg_usbh1_vbus>;
status = "okay";
};
&usbotg {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_cubox_i_usbotg_id>;
+ pinctrl-0 = <&pinctrl_cubox_i_usbotg>;
vbus-supply = <&reg_usbotg_vbus>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi b/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi
index d16066608e21..db9f45b2c573 100644
--- a/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi
@@ -17,7 +17,7 @@
enet {
pinctrl_microsom_enet_ar8035: microsom-enet-ar8035 {
fsl,pins = <
- MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b8b0
MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
/* AR8035 reset */
MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x130b0
diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts
index 3c3e6da1deac..a9aae88b74f5 100644
--- a/arch/arm/boot/dts/omap3-beagle.dts
+++ b/arch/arm/boot/dts/omap3-beagle.dts
@@ -292,6 +292,7 @@
&uart3 {
pinctrl-names = "default";
pinctrl-0 = <&uart3_pins>;
+ interrupts-extended = <&intc 74 &omap3_pmx_core OMAP3_UART3_RX>;
};
&gpio1 {
diff --git a/arch/arm/boot/dts/omap3430-sdp.dts b/arch/arm/boot/dts/omap3430-sdp.dts
index 02f69f4a8fd3..9bad94efe1c8 100644
--- a/arch/arm/boot/dts/omap3430-sdp.dts
+++ b/arch/arm/boot/dts/omap3430-sdp.dts
@@ -107,7 +107,7 @@
#address-cells = <1>;
#size-cells = <1>;
reg = <1 0 0x08000000>;
- ti,nand-ecc-opt = "ham1";
+ ti,nand-ecc-opt = "sw";
nand-bus-width = <8>;
gpmc,cs-on-ns = <0>;
gpmc,cs-rd-off-ns = <36>;
diff --git a/arch/arm/boot/dts/omap54xx-clocks.dtsi b/arch/arm/boot/dts/omap54xx-clocks.dtsi
index e67a23b5d788..58c27466f012 100644
--- a/arch/arm/boot/dts/omap54xx-clocks.dtsi
+++ b/arch/arm/boot/dts/omap54xx-clocks.dtsi
@@ -367,10 +367,12 @@
l3_iclk_div: l3_iclk_div {
#clock-cells = <0>;
- compatible = "fixed-factor-clock";
+ compatible = "ti,divider-clock";
+ ti,max-div = <2>;
+ ti,bit-shift = <4>;
+ reg = <0x100>;
clocks = <&dpll_core_h12x2_ck>;
- clock-mult = <1>;
- clock-div = <1>;
+ ti,index-power-of-two;
};
gpu_l3_iclk: gpu_l3_iclk {
@@ -383,10 +385,12 @@
l4_root_clk_div: l4_root_clk_div {
#clock-cells = <0>;
- compatible = "fixed-factor-clock";
+ compatible = "ti,divider-clock";
+ ti,max-div = <2>;
+ ti,bit-shift = <8>;
+ reg = <0x100>;
clocks = <&l3_iclk_div>;
- clock-mult = <1>;
- clock-div = <1>;
+ ti,index-power-of-two;
};
slimbus1_slimbus_clk: slimbus1_slimbus_clk {
diff --git a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
index 7c2441d526bc..0be7613858a3 100644
--- a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
+++ b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
@@ -4,6 +4,11 @@
model = "Qualcomm APQ8064/IFC6410";
compatible = "qcom,apq8064-ifc6410", "qcom,apq8064";
+ memory{
+ device_type = "memory";
+ reg = <0x80000000 0x80000000>;
+ };
+
soc {
gsbi@16600000 {
status = "ok";
@@ -12,5 +17,74 @@
status = "ok";
};
};
+
+ /* OTG */
+ usb1_phy:phy@12500000 {
+ status = "ok";
+ };
+
+ usb3_phy:phy@12520000 {
+ status = "ok";
+ };
+
+ usb4_phy:phy@12530000 {
+ status = "ok";
+ };
+
+ gadget1:gadget@12500000 {
+ status = "ok";
+ };
+
+ /* OTG */
+ usb1: usb@12500000 {
+ status = "ok";
+ };
+
+ usb3: usb@12520000 {
+ status = "ok";
+ };
+
+ usb4: usb@12530000 {
+ status = "ok";
+ };
+
+ /* on board fixed 3.3v supply */
+ v3p3_pcieclk: v3p3-pcieclk {
+ compatible = "regulator-fixed";
+ regulator-name = "PCIE V3P3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ pci@1b500000 {
+ status = "ok";
+ pcie-clk-supply = <&v3p3_pcieclk>;
+ avdd-supply = <&pm8921_s3>;
+ vdd-supply = <&pm8921_lvs6>;
+ qcom,external-phy-refclk;
+ reset-gpio = <&qcom_pinmux 27 GPIO_ACTIVE_LOW>;
+ };
+
+ wlan0 {
+ compatible = "atheros,ath6kl";
+ reset-gpio = <&pm8921_gpio 42 GPIO_ACTIVE_LOW>;
+ };
+
+ amba {
+ /* eMMC */
+ sdcc1: sdcc@12400000 {
+ status = "okay";
+ };
+
+ /* External micro SD card */
+ sdcc3: sdcc@12180000 {
+ status = "okay";
+ };
+ /* WLAN */
+ sdcc4: sdcc@121c0000 {
+ status = "okay";
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index 92bf793622c3..ddba7d97f7cb 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -1,9 +1,13 @@
/dts-v1/;
-
#include "skeleton.dtsi"
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/qcom,gcc-msm8960.h>
#include <dt-bindings/soc/qcom,gsbi.h>
-
+#include <dt-bindings/mfd/qcom,rpm.h>
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+#include <dt-bindings/reset/qcom,gcc-msm8960.h>
+#include <dt-bindings/gpio/gpio.h>
/ {
model = "Qualcomm APQ8064";
compatible = "qcom,apq8064";
@@ -158,6 +162,49 @@
compatible = "qcom,ssbi";
reg = <0x00500000 0x1000>;
qcom,controller-type = "pmic-arbiter";
+
+ pmicintc: pmic@0 {
+ compatible = "qcom,pm8921";
+ interrupt-parent = <&qcom_pinmux>;
+ interrupts = <74 8>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pm8921_gpio: gpio@150 {
+ compatible = "qcom,pm8921-gpio";
+ reg = <0x150>;
+ interrupts = <192 1>, <193 1>, <194 1>,
+ <195 1>, <196 1>, <197 1>,
+ <198 1>, <199 1>, <200 1>,
+ <201 1>, <202 1>, <203 1>,
+ <204 1>, <205 1>, <206 1>,
+ <207 1>, <208 1>, <209 1>,
+ <210 1>, <211 1>, <212 1>,
+ <213 1>, <214 1>, <215 1>,
+ <216 1>, <217 1>, <218 1>,
+ <219 1>, <220 1>, <221 1>,
+ <222 1>, <223 1>, <224 1>,
+ <225 1>, <226 1>, <227 1>,
+ <228 1>, <229 1>, <230 1>,
+ <231 1>, <232 1>, <233 1>,
+ <234 1>, <235 1>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&wlan_default_gpios>;
+
+ wlan_default_gpios: wlan-gpios {
+ pios {
+ pins = "gpio43";
+ function = "normal";
+ bias-disable;
+ power-source = <PM8921_GPIO_S4>;
+ };
+ };
+ };
+ };
};
gcc: clock-controller@900000 {
@@ -166,5 +213,359 @@
#clock-cells = <1>;
#reset-cells = <1>;
};
+
+ apcs: syscon@2011000 {
+ compatible = "syscon";
+ reg = <0x2011000 0x1000>;
+ };
+
+ rpm@108000 {
+ compatible = "qcom,rpm-apq8064";
+ reg = <0x108000 0x1000>;
+ qcom,ipc = <&apcs 0x8 2>;
+
+ interrupts = <0 19 0>, <0 21 0>, <0 22 0>;
+ interrupt-names = "ack", "err", "wakeup";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pm8921_s3: pm8921-s3 {
+ compatible = "qcom,rpm-pm8921-smps";
+ reg = <QCOM_RPM_PM8921_S3>;
+
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1400000>;
+ qcom,boot-load = <49360>;
+ qcom,switch-mode-frequency = <3200000>;
+ regulator-always-on;
+ };
+
+ pm8921_s4: pm8921-s4 {
+ compatible = "qcom,rpm-pm8921-smps";
+ reg = <QCOM_RPM_PM8921_S4>;
+
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,boot-load = <200000>;
+ qcom,switch-mode-frequency = <3200000>;
+ regulator-always-on;
+ };
+
+ pm8921_l3: pm8921-l3 {
+ compatible = "qcom,rpm-pm8921-pldo";
+ reg = <QCOM_RPM_PM8921_L3>;
+
+ regulator-min-microvolt = <3050000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ qcom,boot-load = <50000>;
+ };
+
+ pm8921_l4: pm8921-l4 {
+ compatible = "qcom,rpm-pm8921-pldo";
+ reg = <QCOM_RPM_PM8921_L4>;
+
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ qcom,boot-load = <50000>;
+ };
+
+ pm8921_l23: pm8921-l23 {
+ compatible = "qcom,rpm-pm8921-pldo";
+ reg = <QCOM_RPM_PM8921_L23>;
+
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,boot-load = <50000>;
+ regulator-always-on;
+ };
+
+
+ pm8921_lvs6: pm8921-lvs6 {
+ compatible = "qcom,rpm-pm8921-switch";
+ reg = <QCOM_RPM_PM8921_LVS6>;
+ regulator-always-on;
+ };
+
+
+ };
+
+ /* PCIE */
+ qcom_pinmux: pinctrl@800000 {
+ compatible = "qcom,apq8064-pinctrl";
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpio = <150>;
+ interrupts = <GIC_SPI 16 0x4>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0x00800000 0x4000>;
+ sdc4_gpios: sdc4-gpios {
+ pios {
+ pins = "gpio63", "gpio64", "gpio65", "gpio66", "gpio67", "gpio68";
+ function = "sdc4";
+ };
+ };
+
+
+ };
+
+ pci@1b500000 {
+ compatible = "qcom,pcie-ipq8064";
+ reg = <0x1b500000 0x1000>, <0x1b502000 0x100>, <0x1b600000 0x80>;
+ reg-names = "base", "elbi", "parf";
+ status = "disabled";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ interrupts = <0 35 0x0
+ 0 36 0x0
+ 0 37 0x0
+ 0 38 0x0
+ 0 39 0x0
+ 0 238 0x0>;
+ interrupt-names = "irq1", "irq2", "irq3", "irq4", "iqr5", "msi";
+
+ resets = <&gcc PCIE_ACLK_RESET>,
+ <&gcc PCIE_HCLK_RESET>,
+ <&gcc PCIE_POR_RESET>,
+ <&gcc PCIE_PCI_RESET>,
+ <&gcc PCIE_PHY_RESET>;
+ reset-names = "axi", "ahb", "por", "pci", "phy";
+
+ clocks = <&gcc PCIE_A_CLK>,
+ <&gcc PCIE_H_CLK>,
+ <&gcc PCIE_PHY_REF_CLK>;
+ clock-names = "core", "iface", "phy";
+
+ ranges = <0x00000000 0 0 0x0ff00000 0 0x00100000 /* configuration space */
+ 0x81000000 0 0 0x0fe00000 0 0x00100000 /* downstream I/O */
+ 0x82000000 0 0 0x08000000 0 0x07e00000>; /* non-prefetchable memory */
+
+ };
+
+
+
+ usb1_phy:phy@12500000 {
+ compatible = "qcom,usb-otg-ci";
+ reg = <0x12500000 0x400>;
+ interrupts = <0 100 0>;
+ status = "disabled";
+ dr_mode = "host";
+
+ clocks = <&gcc USB_HS1_XCVR_CLK>,
+ <&gcc USB_HS1_H_CLK>;
+ clock-names = "core", "iface";
+
+ vddcx-supply = <&pm8921_s3>;
+ v3p3-supply = <&pm8921_l3>;
+ v1p8-supply = <&pm8921_l4>;
+
+ resets = <&gcc USB_HS1_RESET>;
+ reset-names = "link";
+ };
+
+ usb3_phy:phy@12520000 {
+ compatible = "qcom,usb-otg-ci";
+ reg = <0x12520000 0x400>;
+ interrupts = <0 188 0>;
+ status = "disabled";
+ dr_mode = "host";
+
+ clocks = <&gcc USB_HS3_XCVR_CLK>,
+ <&gcc USB_HS3_H_CLK>;
+ clock-names = "core", "iface";
+
+ vddcx-supply = <&pm8921_s3>;
+ v3p3-supply = <&pm8921_l3>;
+ v1p8-supply = <&pm8921_l23>;
+
+ resets = <&gcc USB_HS3_RESET>;
+ reset-names = "link";
+ };
+
+ usb4_phy:phy@12530000 {
+ compatible = "qcom,usb-otg-ci";
+ reg = <0x12530000 0x400>;
+ interrupts = <0 215 0>;
+ status = "disabled";
+ dr_mode = "host";
+
+ clocks = <&gcc USB_HS4_XCVR_CLK>,
+ <&gcc USB_HS4_H_CLK>;
+ clock-names = "core", "iface";
+
+ vddcx-supply = <&pm8921_s3>;
+ v3p3-supply = <&pm8921_l3>;
+ v1p8-supply = <&pm8921_l23>;
+
+ resets = <&gcc USB_HS4_RESET>;
+ reset-names = "link";
+ };
+
+ gadget1:gadget@12500000 {
+ compatible = "qcom,ci-hdrc";
+ reg = <0x12500000 0x400>;
+ status = "disabled";
+ dr_mode = "peripheral";
+ interrupts = <0 100 0>;
+ usb-phy = <&usb1_phy>;
+ };
+
+ usb1: usb@12500000 {
+ compatible = "qcom,ehci-host";
+ reg = <0x12500000 0x400>;
+ interrupts = <0 100 0>;
+ status = "disabled";
+ usb-phy = <&usb1_phy>;
+ };
+
+ usb3: usb@12520000 {
+ compatible = "qcom,ehci-host";
+ reg = <0x12520000 0x400>;
+ interrupts = <0 188 0>;
+ status = "disabled";
+ usb-phy = <&usb3_phy>;
+ };
+
+ usb4: usb@12530000 {
+ compatible = "qcom,ehci-host";
+ reg = <0x12530000 0x400>;
+ interrupts = <0 215 0>;
+ status = "disabled";
+ usb-phy = <&usb4_phy>;
+ };
+
+ sata_phy0:sata-phy@1b400000{
+ compatible = "qcom,apq8064-sata-phy";
+ reg = <0x1b400000 0x200>;
+ reg-names = "phy_mem";
+ clocks = <&gcc SATA_PHY_CFG_CLK>;
+ clock-names = "cfg";
+ #phy-cells = <0>;
+ };
+
+ sata0: sata@29000000 {
+ compatible = "qcom,msm-ahci";
+ reg = <0x29000000 0x180>;
+ interrupts = <0 209 0>;
+ clocks = <&gcc SFAB_SATA_S_H_CLK>, <&gcc SATA_H_CLK>,
+ <&gcc SATA_A_CLK>, <&gcc SATA_RXOOB_CLK>,
+ <&gcc SATA_PMALIVE_CLK>;
+
+ clock-names = "slave_iface", "iface",
+ "bus", "rxoob",
+ "core_pmalive";
+
+ phys = <&sata_phy0>;
+ phy-names = "sata-phy";
+ target-supply = <&pm8921_s4>;
+ };
+
+ /* Temporary fixed regulator */
+ vsdcc_fixed: vsdcc-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "SDCC Power";
+ regulator-min-microvolt = <2700000>;
+ regulator-max-microvolt = <2700000>;
+ regulator-always-on;
+ };
+
+ sdcc1bam:dma@12402000{
+ compatible = "qcom,bam-v1.3.0";
+ reg = <0x12402000 0x8000>;
+ interrupts = <0 98 0>;
+ clocks = <&gcc SDC1_H_CLK>;
+ clock-names = "bam_clk";
+ #dma-cells = <1>;
+ qcom,ee = <0>;
+ };
+
+ sdcc3bam:dma@12182000{
+ compatible = "qcom,bam-v1.3.0";
+ reg = <0x12182000 0x8000>;
+ interrupts = <0 96 0>;
+ clocks = <&gcc SDC3_H_CLK>;
+ clock-names = "bam_clk";
+ #dma-cells = <1>;
+ qcom,ee = <0>;
+ };
+
+ sdcc4bam:dma@121c2000{
+ compatible = "qcom,bam-v1.3.0";
+ reg = <0x121c2000 0x8000>;
+ interrupts = <0 95 0>;
+ clocks = <&gcc SDC4_H_CLK>;
+ clock-names = "bam_clk";
+ #dma-cells = <1>;
+ qcom,ee = <0>;
+ };
+
+ amba {
+ compatible = "arm,amba-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ sdcc1: sdcc@12400000 {
+ status = "disabled";
+ compatible = "arm,pl18x", "arm,primecell";
+ arm,primecell-periphid = <0x00051180>;
+ reg = <0x12400000 0x2000>;
+ interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "cmd_irq";
+ clocks = <&gcc SDC1_CLK>, <&gcc SDC1_H_CLK>;
+ clock-names = "mclk", "apb_pclk";
+ bus-width = <8>;
+ max-frequency = <96000000>;
+ non-removable;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ vmmc-supply = <&vsdcc_fixed>;
+ dmas = <&sdcc1bam 2>, <&sdcc1bam 1>;
+ dma-names = "tx", "rx";
+ };
+
+ sdcc3: sdcc@12180000 {
+ compatible = "arm,pl18x", "arm,primecell";
+ arm,primecell-periphid = <0x00051180>;
+ status = "disabled";
+ reg = <0x12180000 0x2000>;
+ interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "cmd_irq";
+ clocks = <&gcc SDC3_CLK>, <&gcc SDC3_H_CLK>;
+ clock-names = "mclk", "apb_pclk";
+ bus-width = <4>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ max-frequency = <192000000>;
+ no-1-8-v;
+ vmmc-supply = <&vsdcc_fixed>;
+ dmas = <&sdcc3bam 2>, <&sdcc3bam 1>;
+ dma-names = "tx", "rx";
+ };
+
+ sdcc4: sdcc@121c0000 {
+ compatible = "arm,pl18x", "arm,primecell";
+ arm,primecell-periphid = <0x00051180>;
+ status = "disabled";
+ reg = <0x121c0000 0x2000>;
+ interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "cmd_irq";
+ clocks = <&gcc SDC4_CLK>, <&gcc SDC4_H_CLK>;
+ clock-names = "mclk", "apb_pclk";
+ bus-width = <4>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ max-frequency = <48000000>;
+ vmmc-supply = <&vsdcc_fixed>;
+ vqmmc-supply = <&vsdcc_fixed>;
+ dmas = <&sdcc4bam 2>, <&sdcc4bam 1>;
+ dma-names = "tx", "rx";
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdc4_gpios>;
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom-apq8084-ifc6540.dts b/arch/arm/boot/dts/qcom-apq8084-ifc6540.dts
new file mode 100644
index 000000000000..e41cb8ac36ff
--- /dev/null
+++ b/arch/arm/boot/dts/qcom-apq8084-ifc6540.dts
@@ -0,0 +1,12 @@
+#include "qcom-apq8084.dtsi"
+
+/ {
+ model = "Qualcomm APQ8084/IFC6540";
+ compatible = "qcom,apq8084-ifc6540", "qcom,apq8084";
+
+ soc {
+ serial@f995e000 {
+ status = "okay";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom-apq8084-mtp.dts b/arch/arm/boot/dts/qcom-apq8084-mtp.dts
index 9dae3878b71d..8ecec58a9ff6 100644
--- a/arch/arm/boot/dts/qcom-apq8084-mtp.dts
+++ b/arch/arm/boot/dts/qcom-apq8084-mtp.dts
@@ -3,4 +3,10 @@
/ {
model = "Qualcomm APQ 8084-MTP";
compatible = "qcom,apq8084-mtp", "qcom,apq8084";
+
+ soc {
+ serial@f995e000 {
+ status = "okay";
+ };
+ };
};
diff --git a/arch/arm/boot/dts/qcom-apq8084.dtsi b/arch/arm/boot/dts/qcom-apq8084.dtsi
index e3e009a5912b..b5b156e328a0 100644
--- a/arch/arm/boot/dts/qcom-apq8084.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8084.dtsi
@@ -2,6 +2,8 @@
#include "skeleton.dtsi"
+#include <dt-bindings/clock/qcom,gcc-apq8084.h>
+
/ {
model = "Qualcomm APQ 8084";
compatible = "qcom,apq8084";
@@ -175,5 +177,21 @@
compatible = "qcom,pshold";
reg = <0xfc4ab000 0x4>;
};
+
+ gcc: clock-controller@fc400000 {
+ compatible = "qcom,gcc-apq8084";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ reg = <0xfc400000 0x4000>;
+ };
+
+ serial@f995e000 {
+ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+ reg = <0xf995e000 0x1000>;
+ interrupts = <0 114 0x0>;
+ clocks = <&gcc GCC_BLSP2_UART2_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
+ clock-names = "core", "iface";
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts
new file mode 100644
index 000000000000..ce6e936aba3e
--- /dev/null
+++ b/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts
@@ -0,0 +1,158 @@
+/*
+ * ARM Ltd. Fast Models
+ *
+ * Versatile Express (VE) system model
+ * ARMCortexA15x1CT
+ *
+ * RTSM_VE_Cortex_A15x1.lisa
+ */
+
+/dts-v1/;
+
+/ {
+ model = "RTSM_VE_CortexA15x1";
+ arm,vexpress,site = <0xf>;
+ compatible = "arm,rtsm_ve,cortex_a15x1", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0 0x80000000 0 0x80000000>;
+ };
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0 0x2c001000 0 0x1000>,
+ <0 0x2c002000 0 0x1000>,
+ <0 0x2c004000 0 0x2000>,
+ <0 0x2c006000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
+ };
+
+ dcc {
+ compatible = "arm,vexpress,config-bus";
+ arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+ osc@0 {
+ /* ACLK clock to the AXI master port on the test chip */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 0>;
+ freq-range = <30000000 50000000>;
+ #clock-cells = <0>;
+ clock-output-names = "extsaxiclk";
+ };
+
+ oscclk1: osc@1 {
+ /* Reference clock for the CLCD */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 1>;
+ freq-range = <10000000 80000000>;
+ #clock-cells = <0>;
+ clock-output-names = "clcdclk";
+ };
+
+ smbclk: oscclk2: osc@2 {
+ /* Reference clock for the test chip internal PLLs */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 2>;
+ freq-range = <33000000 100000000>;
+ #clock-cells = <0>;
+ clock-output-names = "tcrefclk";
+ };
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
+
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts
new file mode 100644
index 000000000000..3c5c9635df25
--- /dev/null
+++ b/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts
@@ -0,0 +1,164 @@
+/*
+ * ARM Ltd. Fast Models
+ *
+ * Versatile Express (VE) system model
+ * ARMCortexA15x2CT
+ *
+ * RTSM_VE_Cortex_A15x2.lisa
+ */
+
+/dts-v1/;
+
+/ {
+ model = "RTSM_VE_CortexA15x2";
+ arm,vexpress,site = <0xf>;
+ compatible = "arm,rtsm_ve,cortex_a15x2", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <1>;
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0 0x80000000 0 0x80000000>;
+ };
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0 0x2c001000 0 0x1000>,
+ <0 0x2c002000 0 0x1000>,
+ <0 0x2c004000 0 0x2000>,
+ <0 0x2c006000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
+ };
+
+ dcc {
+ compatible = "arm,vexpress,config-bus";
+ arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+ osc@0 {
+ /* ACLK clock to the AXI master port on the test chip */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 0>;
+ freq-range = <30000000 50000000>;
+ #clock-cells = <0>;
+ clock-output-names = "extsaxiclk";
+ };
+
+ oscclk1: osc@1 {
+ /* Reference clock for the CLCD */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 1>;
+ freq-range = <10000000 80000000>;
+ #clock-cells = <0>;
+ clock-output-names = "clcdclk";
+ };
+
+ smbclk: oscclk2: osc@2 {
+ /* Reference clock for the test chip internal PLLs */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 2>;
+ freq-range = <33000000 100000000>;
+ #clock-cells = <0>;
+ clock-output-names = "tcrefclk";
+ };
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
+
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts
new file mode 100644
index 000000000000..94728718e876
--- /dev/null
+++ b/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts
@@ -0,0 +1,176 @@
+/*
+ * ARM Ltd. Fast Models
+ *
+ * Versatile Express (VE) system model
+ * ARMCortexA15x4CT
+ *
+ * RTSM_VE_Cortex_A15x4.lisa
+ */
+
+/dts-v1/;
+
+/ {
+ model = "RTSM_VE_CortexA15x4";
+ arm,vexpress,site = <0xf>;
+ compatible = "arm,rtsm_ve,cortex_a15x4", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <1>;
+ };
+
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <2>;
+ };
+
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <3>;
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0 0x80000000 0 0x80000000>;
+ };
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0 0x2c001000 0 0x1000>,
+ <0 0x2c002000 0 0x1000>,
+ <0 0x2c004000 0 0x2000>,
+ <0 0x2c006000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
+ };
+
+ dcc {
+ compatible = "arm,vexpress,config-bus";
+ arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+ osc@0 {
+ /* ACLK clock to the AXI master port on the test chip */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 0>;
+ freq-range = <30000000 50000000>;
+ #clock-cells = <0>;
+ clock-output-names = "extsaxiclk";
+ };
+
+ oscclk1: osc@1 {
+ /* Reference clock for the CLCD */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 1>;
+ freq-range = <10000000 80000000>;
+ #clock-cells = <0>;
+ clock-output-names = "clcdclk";
+ };
+
+ smbclk: oscclk2: osc@2 {
+ /* Reference clock for the test chip internal PLLs */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 2>;
+ freq-range = <33000000 100000000>;
+ #clock-cells = <0>;
+ clock-output-names = "tcrefclk";
+ };
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
+
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts
new file mode 100644
index 000000000000..357344ba44b7
--- /dev/null
+++ b/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts
@@ -0,0 +1,170 @@
+/*
+ * ARM Ltd. Fast Models
+ *
+ * Versatile Express (VE) system model
+ * ARMCortexA9MPx2CT
+ *
+ * RTSM_VE_Cortex_A9x2.lisa
+ */
+
+/dts-v1/;
+
+/ {
+ model = "RTSM_VE_CortexA9x2";
+ arm,vexpress,site = <0xf>;
+ compatible = "arm,rtsm_ve,cortex_a9x2", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <0>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <1>;
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x80000000 0x80000000>;
+ };
+
+ scu@2c000000 {
+ compatible = "arm,cortex-a9-scu";
+ reg = <0x2c000000 0x58>;
+ };
+
+ timer@2c000600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0x2c000600 0x20>;
+ interrupts = <1 13 0xf04>;
+ };
+
+ watchdog@2c000620 {
+ compatible = "arm,cortex-a9-twd-wdt";
+ reg = <0x2c000620 0x20>;
+ interrupts = <1 14 0xf04>;
+ };
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0x2c001000 0x1000>,
+ <0x2c000100 0x100>;
+ };
+
+ dcc {
+ compatible = "arm,vexpress,config-bus";
+ arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+ osc@0 {
+ /* ACLK clock to the AXI master port on the test chip */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 0>;
+ freq-range = <30000000 50000000>;
+ #clock-cells = <0>;
+ clock-output-names = "extsaxiclk";
+ };
+
+ oscclk1: osc@1 {
+ /* Reference clock for the CLCD */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 1>;
+ freq-range = <10000000 80000000>;
+ #clock-cells = <0>;
+ clock-output-names = "clcdclk";
+ };
+
+ smbclk: oscclk2: osc@2 {
+ /* Reference clock for the test chip internal PLLs */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 2>;
+ freq-range = <33000000 100000000>;
+ #clock-cells = <0>;
+ clock-output-names = "tcrefclk";
+ };
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0x08000000 0x04000000>,
+ <1 0 0x14000000 0x04000000>,
+ <2 0 0x18000000 0x04000000>,
+ <3 0 0x1c000000 0x04000000>,
+ <4 0 0x0c000000 0x04000000>,
+ <5 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
+
diff --git a/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts b/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts
new file mode 100644
index 000000000000..42350e23d201
--- /dev/null
+++ b/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts
@@ -0,0 +1,182 @@
+/*
+ * ARM Ltd. Fast Models
+ *
+ * Versatile Express (VE) system model
+ * ARMCortexA9MPx4CT
+ *
+ * RTSM_VE_Cortex_A9x4.lisa
+ */
+
+/dts-v1/;
+
+/ {
+ model = "RTSM_VE_CortexA9x4";
+ arm,vexpress,site = <0xf>;
+ compatible = "arm,rtsm_ve,cortex_a9x4", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <0>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <1>;
+ };
+
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <2>;
+ };
+
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <3>;
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x80000000 0x80000000>;
+ };
+
+ scu@2c000000 {
+ compatible = "arm,cortex-a9-scu";
+ reg = <0x2c000000 0x58>;
+ };
+
+ timer@2c000600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0x2c000600 0x20>;
+ interrupts = <1 13 0xf04>;
+ };
+
+ watchdog@2c000620 {
+ compatible = "arm,cortex-a9-twd-wdt";
+ reg = <0x2c000620 0x20>;
+ interrupts = <1 14 0xf04>;
+ };
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0x2c001000 0x1000>,
+ <0x2c000100 0x100>;
+ };
+
+ dcc {
+ compatible = "arm,vexpress,config-bus";
+ arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+ osc@0 {
+ /* ACLK clock to the AXI master port on the test chip */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 0>;
+ freq-range = <30000000 50000000>;
+ #clock-cells = <0>;
+ clock-output-names = "extsaxiclk";
+ };
+
+ oscclk1: osc@1 {
+ /* Reference clock for the CLCD */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 1>;
+ freq-range = <10000000 80000000>;
+ #clock-cells = <0>;
+ clock-output-names = "clcdclk";
+ };
+
+ smbclk: oscclk2: osc@2 {
+ /* Reference clock for the test chip internal PLLs */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 2>;
+ freq-range = <33000000 100000000>;
+ #clock-cells = <0>;
+ clock-output-names = "tcrefclk";
+ };
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0x08000000 0x04000000>,
+ <1 0 0x14000000 0x04000000>,
+ <2 0 0x18000000 0x04000000>,
+ <3 0 0x1c000000 0x04000000>,
+ <4 0 0x0c000000 0x04000000>,
+ <5 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
+
diff --git a/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi b/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi
new file mode 100644
index 000000000000..f6535a284594
--- /dev/null
+++ b/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi
@@ -0,0 +1,322 @@
+/*
+ * ARM Ltd. Fast Models
+ *
+ * Versatile Express (VE) system model
+ * Motherboard component
+ *
+ * VEMotherBoard.lisa
+ */
+
+ motherboard {
+ compatible = "arm,vexpress,v2m-p1", "simple-bus";
+ arm,hbi = <0x190>;
+ arm,vexpress,site = <0>;
+ arm,v2m-memory-map = "rs1";
+ #address-cells = <2>; /* SMB chipselect number and offset */
+ #size-cells = <1>;
+ #interrupt-cells = <1>;
+ ranges;
+
+ flash@0,00000000 {
+ compatible = "arm,vexpress-flash", "cfi-flash";
+ reg = <0 0x00000000 0x04000000>,
+ <4 0x00000000 0x04000000>;
+ bank-width = <4>;
+ };
+
+ v2m_video_ram: vram@2,00000000 {
+ compatible = "arm,vexpress-vram";
+ reg = <2 0x00000000 0x00800000>;
+ };
+
+ ethernet@2,02000000 {
+ compatible = "smsc,lan91c111";
+ reg = <2 0x02000000 0x10000>;
+ interrupts = <15>;
+ };
+
+ iofpga@3,00000000 {
+ compatible = "arm,amba-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 3 0 0x200000>;
+
+ v2m_sysreg: sysreg@010000 {
+ compatible = "arm,vexpress-sysreg";
+ reg = <0x010000 0x1000>;
+
+ v2m_led_gpios: sys_led@08 {
+ compatible = "arm,vexpress-sysreg,sys_led";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ v2m_mmc_gpios: sys_mci@48 {
+ compatible = "arm,vexpress-sysreg,sys_mci";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+
+ v2m_sysctl: sysctl@020000 {
+ compatible = "arm,sp810", "arm,primecell";
+ reg = <0x020000 0x1000>;
+ clocks = <&v2m_refclk32khz>, <&v2m_refclk1mhz>, <&smbclk>;
+ clock-names = "refclk", "timclk", "apb_pclk";
+ #clock-cells = <1>;
+ clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3";
+ };
+
+ aaci@040000 {
+ compatible = "arm,pl041", "arm,primecell";
+ reg = <0x040000 0x1000>;
+ interrupts = <11>;
+ clocks = <&smbclk>;
+ clock-names = "apb_pclk";
+ };
+
+ mmci@050000 {
+ compatible = "arm,pl180", "arm,primecell";
+ reg = <0x050000 0x1000>;
+ interrupts = <9 10>;
+ cd-gpios = <&v2m_mmc_gpios 0 0>;
+ wp-gpios = <&v2m_mmc_gpios 1 0>;
+ max-frequency = <12000000>;
+ vmmc-supply = <&v2m_fixed_3v3>;
+ clocks = <&v2m_clk24mhz>, <&smbclk>;
+ clock-names = "mclk", "apb_pclk";
+ };
+
+ kmi@060000 {
+ compatible = "arm,pl050", "arm,primecell";
+ reg = <0x060000 0x1000>;
+ interrupts = <12>;
+ clocks = <&v2m_clk24mhz>, <&smbclk>;
+ clock-names = "KMIREFCLK", "apb_pclk";
+ };
+
+ kmi@070000 {
+ compatible = "arm,pl050", "arm,primecell";
+ reg = <0x070000 0x1000>;
+ interrupts = <13>;
+ clocks = <&v2m_clk24mhz>, <&smbclk>;
+ clock-names = "KMIREFCLK", "apb_pclk";
+ };
+
+ v2m_serial0: uart@090000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x090000 0x1000>;
+ interrupts = <5>;
+ clocks = <&v2m_clk24mhz>, <&smbclk>;
+ clock-names = "uartclk", "apb_pclk";
+ };
+
+ v2m_serial1: uart@0a0000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0a0000 0x1000>;
+ interrupts = <6>;
+ clocks = <&v2m_clk24mhz>, <&smbclk>;
+ clock-names = "uartclk", "apb_pclk";
+ };
+
+ v2m_serial2: uart@0b0000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0b0000 0x1000>;
+ interrupts = <7>;
+ clocks = <&v2m_clk24mhz>, <&smbclk>;
+ clock-names = "uartclk", "apb_pclk";
+ };
+
+ v2m_serial3: uart@0c0000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0c0000 0x1000>;
+ interrupts = <8>;
+ clocks = <&v2m_clk24mhz>, <&smbclk>;
+ clock-names = "uartclk", "apb_pclk";
+ };
+
+ wdt@0f0000 {
+ compatible = "arm,sp805", "arm,primecell";
+ reg = <0x0f0000 0x1000>;
+ interrupts = <0>;
+ clocks = <&v2m_refclk32khz>, <&smbclk>;
+ clock-names = "wdogclk", "apb_pclk";
+ };
+
+ v2m_timer01: timer@110000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0x110000 0x1000>;
+ interrupts = <2>;
+ clocks = <&v2m_sysctl 0>, <&v2m_sysctl 1>, <&smbclk>;
+ clock-names = "timclken1", "timclken2", "apb_pclk";
+ };
+
+ v2m_timer23: timer@120000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0x120000 0x1000>;
+ interrupts = <3>;
+ clocks = <&v2m_sysctl 2>, <&v2m_sysctl 3>, <&smbclk>;
+ clock-names = "timclken1", "timclken2", "apb_pclk";
+ };
+
+ rtc@170000 {
+ compatible = "arm,pl031", "arm,primecell";
+ reg = <0x170000 0x1000>;
+ interrupts = <4>;
+ clocks = <&smbclk>;
+ clock-names = "apb_pclk";
+ };
+
+ clcd@1f0000 {
+ compatible = "arm,pl111", "arm,primecell";
+ reg = <0x1f0000 0x1000>;
+ interrupt-names = "combined";
+ interrupts = <14>;
+ clocks = <&v2m_oscclk1>, <&smbclk>;
+ clock-names = "v2m:oscclk1", "apb_pclk";
+ memory-region = <&v2m_video_ram>;
+ max-memory-bandwidth = <130000000>; /* Bps, 1024x768@60 16bpp */
+
+ port {
+ v2m_clcd_pads: endpoint {
+ remote-endpoint = <&v2m_clcd_panel>;
+ arm,pl11x,tft-r0g0b0-pads = <16 8 0>;
+ };
+ };
+
+ panel {
+ compatible = "panel-dpi";
+
+ port {
+ v2m_clcd_panel: endpoint {
+ remote-endpoint = <&v2m_clcd_pads>;
+ };
+ };
+
+ panel-timing {
+ clock-frequency = <25175000>;
+ hactive = <640>;
+ hback-porch = <40>;
+ hfront-porch = <24>;
+ hsync-len = <96>;
+ vactive = <480>;
+ vback-porch = <32>;
+ vfront-porch = <11>;
+ vsync-len = <2>;
+ };
+ };
+ };
+
+ virtio_block@0130000 {
+ compatible = "virtio,mmio";
+ reg = <0x130000 0x200>;
+ interrupts = <42>;
+ };
+
+ };
+
+ v2m_fixed_3v3: fixedregulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ v2m_clk24mhz: clk24mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "v2m:clk24mhz";
+ };
+
+ v2m_refclk1mhz: refclk1mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <1000000>;
+ clock-output-names = "v2m:refclk1mhz";
+ };
+
+ v2m_refclk32khz: refclk32khz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <32768>;
+ clock-output-names = "v2m:refclk32khz";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ user@1 {
+ label = "v2m:green:user1";
+ gpios = <&v2m_led_gpios 0 0>;
+ linux,default-trigger = "heartbeat";
+ };
+
+ user@2 {
+ label = "v2m:green:user2";
+ gpios = <&v2m_led_gpios 1 0>;
+ linux,default-trigger = "mmc0";
+ };
+
+ user@3 {
+ label = "v2m:green:user3";
+ gpios = <&v2m_led_gpios 2 0>;
+ linux,default-trigger = "cpu0";
+ };
+
+ user@4 {
+ label = "v2m:green:user4";
+ gpios = <&v2m_led_gpios 3 0>;
+ linux,default-trigger = "cpu1";
+ };
+
+ user@5 {
+ label = "v2m:green:user5";
+ gpios = <&v2m_led_gpios 4 0>;
+ linux,default-trigger = "cpu2";
+ };
+
+ user@6 {
+ label = "v2m:green:user6";
+ gpios = <&v2m_led_gpios 5 0>;
+ linux,default-trigger = "cpu3";
+ };
+
+ user@7 {
+ label = "v2m:green:user7";
+ gpios = <&v2m_led_gpios 6 0>;
+ linux,default-trigger = "cpu4";
+ };
+
+ user@8 {
+ label = "v2m:green:user8";
+ gpios = <&v2m_led_gpios 7 0>;
+ linux,default-trigger = "cpu5";
+ };
+ };
+
+ mcc {
+ compatible = "arm,vexpress,config-bus";
+ arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+ v2m_oscclk1: osc@1 {
+ /* CLCD clock */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 1>;
+ freq-range = <23750000 63500000>;
+ #clock-cells = <0>;
+ clock-output-names = "v2m:oscclk1";
+ };
+
+ muxfpga@0 {
+ compatible = "arm,vexpress-muxfpga";
+ arm,vexpress-sysreg,func = <7 0>;
+ };
+
+ shutdown@0 {
+ compatible = "arm,vexpress-shutdown";
+ arm,vexpress-sysreg,func = <8 0>;
+ };
+ };
+ };
diff --git a/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts b/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts
new file mode 100644
index 000000000000..97ae02af4508
--- /dev/null
+++ b/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts
@@ -0,0 +1,232 @@
+/*
+ * ARM Ltd. Fast Models
+ *
+ * Versatile Express (VE) system model
+ * ARMCortexA15x4CT
+ * ARMCortexA7x4CT
+ * RTSM_VE_Cortex_A15x1_A7x1.lisa
+ */
+
+/dts-v1/;
+
+/memreserve/ 0xff000000 0x01000000;
+
+/ {
+ model = "RTSM_VE_CortexA15x1-A7x1";
+ arm,vexpress,site = <0xf>;
+ compatible = "arm,rtsm_ve,cortex_a15x1_a7x1", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ clusters {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cluster0: cluster@0 {
+ reg = <0>;
+// freqs = <500000000 600000000 700000000 800000000 900000000 1000000000 1100000000 1200000000>;
+ cores {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ core0: core@0 {
+ reg = <0>;
+ };
+
+ };
+ };
+
+ cluster1: cluster@1 {
+ reg = <1>;
+// freqs = <350000000 400000000 500000000 600000000 700000000 800000000 900000000 1000000000>;
+ cores {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ core1: core@0 {
+ reg = <0>;
+ };
+
+ };
+ };
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ cluster = <&cluster0>;
+ core = <&core0>;
+// clock-frequency = <1000000000>;
+ cci-control-port = <&cci_control1>;
+ };
+
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x100>;
+ cluster = <&cluster1>;
+ core = <&core1>;
+// clock-frequency = <800000000>;
+ cci-control-port = <&cci_control2>;
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0 0x80000000 0 0x80000000>;
+ };
+
+ cci@2c090000 {
+ compatible = "arm,cci-400", "arm,cci";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0 0x2c090000 0 0x1000>;
+ ranges = <0x0 0x0 0x2c090000 0x10000>;
+
+ cci_control1: slave-if@4000 {
+ compatible = "arm,cci-400-ctrl-if";
+ interface-type = "ace";
+ reg = <0x4000 0x1000>;
+ };
+
+ cci_control2: slave-if@5000 {
+ compatible = "arm,cci-400-ctrl-if";
+ interface-type = "ace";
+ reg = <0x5000 0x1000>;
+ };
+ };
+
+ dcscb@60000000 {
+ compatible = "arm,rtsm,dcscb";
+ reg = <0 0x60000000 0 0x1000>;
+ };
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0 0x2c001000 0 0x1000>,
+ <0 0x2c002000 0 0x1000>,
+ <0 0x2c004000 0 0x2000>,
+ <0 0x2c006000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
+ };
+
+ dcc {
+ compatible = "arm,vexpress,config-bus";
+ arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+ osc@0 {
+ /* ACLK clock to the AXI master port on the test chip */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 0>;
+ freq-range = <30000000 50000000>;
+ #clock-cells = <0>;
+ clock-output-names = "extsaxiclk";
+ };
+
+ oscclk1: osc@1 {
+ /* Reference clock for the CLCD */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 1>;
+ freq-range = <10000000 80000000>;
+ #clock-cells = <0>;
+ clock-output-names = "clcdclk";
+ };
+
+ smbclk: oscclk2: osc@2 {
+ /* Reference clock for the test chip internal PLLs */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 2>;
+ freq-range = <33000000 100000000>;
+ #clock-cells = <0>;
+ clock-output-names = "tcrefclk";
+ };
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
+
diff --git a/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts b/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts
new file mode 100644
index 000000000000..a3458320e1ca
--- /dev/null
+++ b/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts
@@ -0,0 +1,316 @@
+/*
+ * ARM Ltd. Fast Models
+ *
+ * Versatile Express (VE) system model
+ * ARMCortexA15x4CT
+ * ARMCortexA7x4CT
+ * RTSM_VE_Cortex_A15x4_A7x4.lisa
+ */
+
+/dts-v1/;
+
+/memreserve/ 0xff000000 0x01000000;
+
+/ {
+ model = "RTSM_VE_CortexA15x4-A7x4";
+ arm,vexpress,site = <0xf>;
+ compatible = "arm,rtsm_ve,cortex_a15x4_a7x4", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ clusters {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cluster0: cluster@0 {
+ reg = <0>;
+// freqs = <500000000 600000000 700000000 800000000 900000000 1000000000 1100000000 1200000000>;
+ cores {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ core0: core@0 {
+ reg = <0>;
+ };
+
+ core1: core@1 {
+ reg = <1>;
+ };
+
+ core2: core@2 {
+ reg = <2>;
+ };
+
+ core3: core@3 {
+ reg = <3>;
+ };
+
+ };
+ };
+
+ cluster1: cluster@1 {
+ reg = <1>;
+// freqs = <350000000 400000000 500000000 600000000 700000000 800000000 900000000 1000000000>;
+ cores {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ core4: core@0 {
+ reg = <0>;
+ };
+
+ core5: core@1 {
+ reg = <1>;
+ };
+
+ core6: core@2 {
+ reg = <2>;
+ };
+
+ core7: core@3 {
+ reg = <3>;
+ };
+
+ };
+ };
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ cluster = <&cluster0>;
+ core = <&core0>;
+// clock-frequency = <1000000000>;
+ cci-control-port = <&cci_control1>;
+ };
+
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <1>;
+ cluster = <&cluster0>;
+ core = <&core1>;
+// clock-frequency = <1000000000>;
+ cci-control-port = <&cci_control1>;
+ };
+
+ cpu2: cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <2>;
+ cluster = <&cluster0>;
+ core = <&core2>;
+// clock-frequency = <1000000000>;
+ cci-control-port = <&cci_control1>;
+ };
+
+ cpu3: cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <3>;
+ cluster = <&cluster0>;
+ core = <&core3>;
+// clock-frequency = <1000000000>;
+ cci-control-port = <&cci_control1>;
+ };
+
+ cpu4: cpu@4 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x100>;
+ cluster = <&cluster1>;
+ core = <&core4>;
+// clock-frequency = <800000000>;
+ cci-control-port = <&cci_control2>;
+ };
+
+ cpu5: cpu@5 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x101>;
+ cluster = <&cluster1>;
+ core = <&core5>;
+// clock-frequency = <800000000>;
+ cci-control-port = <&cci_control2>;
+ };
+
+ cpu6: cpu@6 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x102>;
+ cluster = <&cluster1>;
+ core = <&core6>;
+// clock-frequency = <800000000>;
+ cci-control-port = <&cci_control2>;
+ };
+
+ cpu7: cpu@7 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x103>;
+ cluster = <&cluster1>;
+ core = <&core7>;
+// clock-frequency = <800000000>;
+ cci-control-port = <&cci_control2>;
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0 0x80000000 0 0x80000000>;
+ };
+
+ cci@2c090000 {
+ compatible = "arm,cci-400", "arm,cci";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0 0x2c090000 0 0x1000>;
+ ranges = <0x0 0x0 0x2c090000 0x10000>;
+
+ cci_control1: slave-if@4000 {
+ compatible = "arm,cci-400-ctrl-if";
+ interface-type = "ace";
+ reg = <0x4000 0x1000>;
+ };
+
+ cci_control2: slave-if@5000 {
+ compatible = "arm,cci-400-ctrl-if";
+ interface-type = "ace";
+ reg = <0x5000 0x1000>;
+ };
+ };
+
+ dcscb@60000000 {
+ compatible = "arm,rtsm,dcscb";
+ reg = <0 0x60000000 0 0x1000>;
+ };
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0 0x2c001000 0 0x1000>,
+ <0 0x2c002000 0 0x1000>,
+ <0 0x2c004000 0 0x2000>,
+ <0 0x2c006000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
+ };
+
+ dcc {
+ compatible = "arm,vexpress,config-bus";
+ arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+ osc@0 {
+ /* ACLK clock to the AXI master port on the test chip */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 0>;
+ freq-range = <30000000 50000000>;
+ #clock-cells = <0>;
+ clock-output-names = "extsaxiclk";
+ };
+
+ oscclk1: osc@1 {
+ /* Reference clock for the CLCD */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 1>;
+ freq-range = <10000000 80000000>;
+ #clock-cells = <0>;
+ clock-output-names = "clcdclk";
+ };
+
+ smbclk: oscclk2: osc@2 {
+ /* Reference clock for the test chip internal PLLs */
+ compatible = "arm,vexpress-osc";
+ arm,vexpress-sysreg,func = <1 2>;
+ freq-range = <33000000 100000000>;
+ #clock-cells = <0>;
+ clock-output-names = "tcrefclk";
+ };
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
+
diff --git a/arch/arm/boot/dts/twl6030.dtsi b/arch/arm/boot/dts/twl6030.dtsi
index 2e3bd3172b23..55eb35f068fb 100644
--- a/arch/arm/boot/dts/twl6030.dtsi
+++ b/arch/arm/boot/dts/twl6030.dtsi
@@ -83,10 +83,6 @@
regulator-always-on;
};
- clk32kg: regulator-clk32kg {
- compatible = "ti,twl6030-clk32kg";
- };
-
twl_usb_comparator: usb-comparator {
compatible = "ti,twl6030-usb";
interrupts = <4>, <10>;
diff --git a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
index 756c986995a3..56823fa46cb7 100644
--- a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
+++ b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
@@ -41,7 +41,7 @@
bank-width = <4>;
};
- vram@2,00000000 {
+ v2m_video_ram: vram@2,00000000 {
compatible = "arm,vexpress-vram";
reg = <2 0x00000000 0x00800000>;
};
@@ -244,11 +244,44 @@
};
clcd@1f0000 {
+ status = "disabled";
compatible = "arm,pl111", "arm,primecell";
reg = <0x1f0000 0x1000>;
+ interrupt-names = "combined";
interrupts = <14>;
clocks = <&v2m_oscclk1>, <&smbclk>;
clock-names = "clcdclk", "apb_pclk";
+ memory-region = <&v2m_video_ram>;
+ max-memory-bandwidth = <50350000>; /* Bps, 640x480@60 16bpp */
+
+ port {
+ v2m_clcd_pads: endpoint {
+ remote-endpoint = <&v2m_clcd_panel>;
+ arm,pl11x,tft-r0g0b0-pads = <0 8 16>;
+ };
+ };
+
+ panel {
+ compatible = "panel-dpi";
+
+ port {
+ v2m_clcd_panel: endpoint {
+ remote-endpoint = <&v2m_clcd_pads>;
+ };
+ };
+
+ panel-timing {
+ clock-frequency = <25175000>;
+ hactive = <640>;
+ hback-porch = <40>;
+ hfront-porch = <24>;
+ hsync-len = <96>;
+ vactive = <480>;
+ vback-porch = <32>;
+ vfront-porch = <11>;
+ vsync-len = <2>;
+ };
+ };
};
};
@@ -350,7 +383,7 @@
/* CLCD clock */
compatible = "arm,vexpress-osc";
arm,vexpress-sysreg,func = <1 1>;
- freq-range = <23750000 63500000>;
+ freq-range = <23750000 65000000>;
#clock-cells = <0>;
clock-output-names = "v2m:oscclk1";
};
diff --git a/arch/arm/boot/dts/vexpress-v2m.dtsi b/arch/arm/boot/dts/vexpress-v2m.dtsi
index ba856d604fb7..58351efa2aad 100644
--- a/arch/arm/boot/dts/vexpress-v2m.dtsi
+++ b/arch/arm/boot/dts/vexpress-v2m.dtsi
@@ -40,7 +40,7 @@
bank-width = <4>;
};
- vram@3,00000000 {
+ v2m_video_ram: vram@3,00000000 {
compatible = "arm,vexpress-vram";
reg = <3 0x00000000 0x00800000>;
};
@@ -243,11 +243,44 @@
};
clcd@1f000 {
+ status = "disabled";
compatible = "arm,pl111", "arm,primecell";
reg = <0x1f000 0x1000>;
+ interrupt-names = "combined";
interrupts = <14>;
clocks = <&v2m_oscclk1>, <&smbclk>;
clock-names = "clcdclk", "apb_pclk";
+ memory-region = <&v2m_video_ram>;
+ max-memory-bandwidth = <50350000>; /* Bps, 640x480@60 16bpp */
+
+ port {
+ v2m_clcd_pads: endpoint {
+ remote-endpoint = <&v2m_clcd_panel>;
+ arm,pl11x,tft-r0g0b0-pads = <0 8 16>;
+ };
+ };
+
+ panel {
+ compatible = "panel-dpi";
+
+ port {
+ v2m_clcd_panel: endpoint {
+ remote-endpoint = <&v2m_clcd_pads>;
+ };
+ };
+
+ panel-timing {
+ clock-frequency = <25175000>;
+ hactive = <640>;
+ hback-porch = <40>;
+ hfront-porch = <24>;
+ hsync-len = <96>;
+ vactive = <480>;
+ vback-porch = <32>;
+ vfront-porch = <11>;
+ vsync-len = <2>;
+ };
+ };
};
};
@@ -349,7 +382,7 @@
/* CLCD clock */
compatible = "arm,vexpress-osc";
arm,vexpress-sysreg,func = <1 1>;
- freq-range = <23750000 63500000>;
+ freq-range = <23750000 65000000>;
#clock-cells = <0>;
clock-output-names = "v2m:oscclk1";
};
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
index 9420053acc14..7cbe38cecf6c 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
@@ -9,6 +9,8 @@
/dts-v1/;
+/memreserve/ 0xbf000000 0x01000000;
+
/ {
model = "V2P-CA15";
arm,hbi = <0x237>;
@@ -57,6 +59,8 @@
interrupts = <0 85 4>;
clocks = <&oscclk5>;
clock-names = "pxlclk";
+ mode = "1024x768-16@60";
+ framebuffer = <0 0xbf000000 0 0x01000000>;
};
memory-controller@2b0a0000 {
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
index a25c262326dc..a8c89b1cc0c8 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
@@ -9,6 +9,8 @@
/dts-v1/;
+/memreserve/ 0xff000000 0x01000000;
+
/ {
model = "V2P-CA15_CA7";
arm,hbi = <0x249>;
@@ -29,29 +31,60 @@
i2c1 = &v2m_i2c_pcie;
};
- cpus {
+ clusters {
#address-cells = <1>;
#size-cells = <0>;
- cpu0: cpu@0 {
- device_type = "cpu";
- compatible = "arm,cortex-a15";
+ cluster0: cluster@0 {
reg = <0>;
- cci-control-port = <&cci_control1>;
+ cores {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ core0: core@0 {
+ reg = <0>;
+ };
+
+ core1: core@1 {
+ reg = <1>;
+ };
+
+ };
};
- cpu1: cpu@1 {
- device_type = "cpu";
- compatible = "arm,cortex-a15";
+ cluster1: cluster@1 {
reg = <1>;
- cci-control-port = <&cci_control1>;
+ cores {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ core2: core@0 {
+ reg = <0>;
+ };
+
+ core3: core@1 {
+ reg = <1>;
+ };
+
+ core4: core@2 {
+ reg = <2>;
+ };
+ };
};
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
cpu2: cpu@2 {
device_type = "cpu";
compatible = "arm,cortex-a7";
reg = <0x100>;
cci-control-port = <&cci_control2>;
+ cluster = <&cluster1>;
+ core = <&core2>;
+ clock-frequency = <800000000>;
};
cpu3: cpu@3 {
@@ -59,6 +92,9 @@
compatible = "arm,cortex-a7";
reg = <0x101>;
cci-control-port = <&cci_control2>;
+ cluster = <&cluster1>;
+ core = <&core3>;
+ clock-frequency = <800000000>;
};
cpu4: cpu@4 {
@@ -66,12 +102,35 @@
compatible = "arm,cortex-a7";
reg = <0x102>;
cci-control-port = <&cci_control2>;
+ cluster = <&cluster1>;
+ core = <&core4>;
+ clock-frequency = <800000000>;
+ };
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ cci-control-port = <&cci_control1>;
+ cluster = <&cluster0>;
+ core = <&core0>;
+ clock-frequency = <1000000000>;
+ };
+
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <1>;
+ cci-control-port = <&cci_control1>;
+ cluster = <&cluster0>;
+ core = <&core1>;
+ clock-frequency = <1000000000>;
};
};
memory@80000000 {
device_type = "memory";
- reg = <0 0x80000000 0 0x40000000>;
+ reg = <0 0x80000000 0 0x80000000>;
};
wdt@2a490000 {
@@ -86,6 +145,8 @@
compatible = "arm,hdlcd";
reg = <0 0x2b000000 0 0x1000>;
interrupts = <0 85 4>;
+ mode = "1024x768-16@60";
+ framebuffer = <0 0xff000000 0 0x01000000>;
clocks = <&oscclk5>;
clock-names = "pxlclk";
};
@@ -127,6 +188,16 @@
interface-type = "ace";
reg = <0x5000 0x1000>;
};
+
+ pmu@9000 {
+ compatible = "arm,cci-400-pmu";
+ reg = <0x9000 0x5000>;
+ interrupts = <0 101 4>,
+ <0 102 4>,
+ <0 103 4>,
+ <0 104 4>,
+ <0 105 4>;
+ };
};
memory-controller@7ffd0000 {
@@ -164,12 +235,21 @@
<1 10 0xf08>;
};
- pmu {
+ pmu_a15 {
compatible = "arm,cortex-a15-pmu";
+ cluster = <&cluster0>;
interrupts = <0 68 4>,
<0 69 4>;
};
+ pmu_a7 {
+ compatible = "arm,cortex-a7-pmu";
+ cluster = <&cluster1>;
+ interrupts = <0 128 4>,
+ <0 129 4>,
+ <0 130 4>;
+ };
+
oscclk6a: oscclk6a {
/* Reference 24MHz clock */
compatible = "fixed-clock";
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts
index d2709b73316b..b4ee491530e2 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts
@@ -9,6 +9,8 @@
/dts-v1/;
+/memreserve/ 0xbf000000 0x01000000;
+
/ {
model = "V2P-CA5s";
arm,hbi = <0x225>;
@@ -59,6 +61,8 @@
interrupts = <0 85 4>;
clocks = <&oscclk3>;
clock-names = "pxlclk";
+ mode = "640x480-16@60";
+ framebuffer = <0xbf000000 0x01000000>;
};
memory-controller@2a150000 {
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca9.dts b/arch/arm/boot/dts/vexpress-v2p-ca9.dts
index 62d9b225dcce..1c7085d80a58 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca9.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca9.dts
@@ -70,9 +70,40 @@
clcd@10020000 {
compatible = "arm,pl111", "arm,primecell";
reg = <0x10020000 0x1000>;
+ interrupt-names = "combined";
interrupts = <0 44 4>;
clocks = <&oscclk1>, <&oscclk2>;
clock-names = "clcdclk", "apb_pclk";
+ max-memory-bandwidth = <130000000>; /* Bps, 1024x768@60 16bpp */
+
+ port {
+ clcd_pads: endpoint {
+ remote-endpoint = <&clcd_panel>;
+ arm,pl11x,tft-r0g0b0-pads = <16 8 0>;
+ };
+ };
+
+ panel {
+ compatible = "panel-dpi";
+
+ port {
+ clcd_panel: endpoint {
+ remote-endpoint = <&clcd_pads>;
+ };
+ };
+
+ panel-timing {
+ clock-frequency = <63500127>;
+ hactive = <1024>;
+ hback-porch = <152>;
+ hfront-porch = <48>;
+ hsync-len = <104>;
+ vactive = <768>;
+ vback-porch = <23>;
+ vfront-porch = <3>;
+ vsync-len = <4>;
+ };
+ };
};
memory-controller@100e0000 {
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index c3a4e9ceba34..4fb337ca0cb7 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -20,3 +20,53 @@ config SHARP_SCOOP
config TI_PRIV_EDMA
bool
+
+config FIQ_GLUE
+ bool
+ select FIQ
+
+config FIQ_DEBUGGER
+ bool "FIQ Mode Serial Debugger"
+ select FIQ
+ select FIQ_GLUE
+ default n
+ help
+ The FIQ serial debugger can accept commands even when the
+ kernel is unresponsive due to being stuck with interrupts
+ disabled.
+
+
+config FIQ_DEBUGGER_NO_SLEEP
+ bool "Keep serial debugger active"
+ depends on FIQ_DEBUGGER
+ default n
+ help
+ Enables the serial debugger at boot. Passing
+ fiq_debugger.no_sleep on the kernel commandline will
+ override this config option.
+
+config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
+ bool "Don't disable wakeup IRQ when debugger is active"
+ depends on FIQ_DEBUGGER
+ default n
+ help
+ Don't disable the wakeup irq when enabling the uart clock. This will
+ cause extra interrupts, but it makes the serial debugger usable with
+ on some MSM radio builds that ignore the uart clock request in power
+ collapse.
+
+config FIQ_DEBUGGER_CONSOLE
+ bool "Console on FIQ Serial Debugger port"
+ depends on FIQ_DEBUGGER
+ default n
+ help
+ Enables a console so that printk messages are displayed on
+ the debugger serial port as the occur.
+
+config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE
+ bool "Put the FIQ debugger into console mode by default"
+ depends on FIQ_DEBUGGER_CONSOLE
+ default n
+ help
+ If enabled, this puts the fiq debugger into console mode by default.
+ Otherwise, the fiq debugger will start out in debug mode.
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
index 70b1eff477b3..14e592312033 100644
--- a/arch/arm/common/Makefile
+++ b/arch/arm/common/Makefile
@@ -4,6 +4,8 @@
obj-y += firmware.o
+obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o
+obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o
obj-$(CONFIG_ICST) += icst.o
obj-$(CONFIG_SA1111) += sa1111.o
obj-$(CONFIG_DMABOUNCE) += dmabounce.o
diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c
new file mode 100644
index 000000000000..65b943c76300
--- /dev/null
+++ b/arch/arm/common/fiq_debugger.c
@@ -0,0 +1,1376 @@
+/*
+ * arch/arm/common/fiq_debugger.c
+ *
+ * Serial Debugger Interface accessed through an FIQ interrupt.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <stdarg.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/kernel_stat.h>
+#include <linux/kmsg_dump.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/wakelock.h>
+
+#include <asm/fiq_debugger.h>
+#include <asm/fiq_glue.h>
+#include <asm/stacktrace.h>
+
+#include <linux/uaccess.h>
+
+#include "fiq_debugger_ringbuf.h"
+
+#define DEBUG_MAX 64
+#define MAX_UNHANDLED_FIQ_COUNT 1000000
+
+#define MAX_FIQ_DEBUGGER_PORTS 4
+
+#define THREAD_INFO(sp) ((struct thread_info *) \
+ ((unsigned long)(sp) & ~(THREAD_SIZE - 1)))
+
+struct fiq_debugger_state {
+ struct fiq_glue_handler handler;
+
+ int fiq;
+ int uart_irq;
+ int signal_irq;
+ int wakeup_irq;
+ bool wakeup_irq_no_set_wake;
+ struct clk *clk;
+ struct fiq_debugger_pdata *pdata;
+ struct platform_device *pdev;
+
+ char debug_cmd[DEBUG_MAX];
+ int debug_busy;
+ int debug_abort;
+
+ char debug_buf[DEBUG_MAX];
+ int debug_count;
+
+ bool no_sleep;
+ bool debug_enable;
+ bool ignore_next_wakeup_irq;
+ struct timer_list sleep_timer;
+ spinlock_t sleep_timer_lock;
+ bool uart_enabled;
+ struct wake_lock debugger_wake_lock;
+ bool console_enable;
+ int current_cpu;
+ atomic_t unhandled_fiq_count;
+ bool in_fiq;
+
+ struct work_struct work;
+ spinlock_t work_lock;
+ char work_cmd[DEBUG_MAX];
+
+#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
+ spinlock_t console_lock;
+ struct console console;
+ struct tty_port tty_port;
+ struct fiq_debugger_ringbuf *tty_rbuf;
+ bool syslog_dumping;
+#endif
+
+ unsigned int last_irqs[NR_IRQS];
+ unsigned int last_local_timer_irqs[NR_CPUS];
+};
+
+#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
+struct tty_driver *fiq_tty_driver;
+#endif
+
+#ifdef CONFIG_FIQ_DEBUGGER_NO_SLEEP
+static bool initial_no_sleep = true;
+#else
+static bool initial_no_sleep;
+#endif
+
+#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE
+static bool initial_debug_enable = true;
+static bool initial_console_enable = true;
+#else
+static bool initial_debug_enable;
+static bool initial_console_enable;
+#endif
+
+static bool fiq_kgdb_enable;
+
+module_param_named(no_sleep, initial_no_sleep, bool, 0644);
+module_param_named(debug_enable, initial_debug_enable, bool, 0644);
+module_param_named(console_enable, initial_console_enable, bool, 0644);
+module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644);
+
+#ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
+static inline void enable_wakeup_irq(struct fiq_debugger_state *state) {}
+static inline void disable_wakeup_irq(struct fiq_debugger_state *state) {}
+#else
+static inline void enable_wakeup_irq(struct fiq_debugger_state *state)
+{
+ if (state->wakeup_irq < 0)
+ return;
+ enable_irq(state->wakeup_irq);
+ if (!state->wakeup_irq_no_set_wake)
+ enable_irq_wake(state->wakeup_irq);
+}
+static inline void disable_wakeup_irq(struct fiq_debugger_state *state)
+{
+ if (state->wakeup_irq < 0)
+ return;
+ disable_irq_nosync(state->wakeup_irq);
+ if (!state->wakeup_irq_no_set_wake)
+ disable_irq_wake(state->wakeup_irq);
+}
+#endif
+
+static bool inline debug_have_fiq(struct fiq_debugger_state *state)
+{
+ return (state->fiq >= 0);
+}
+
+static void debug_force_irq(struct fiq_debugger_state *state)
+{
+ unsigned int irq = state->signal_irq;
+
+ if (WARN_ON(!debug_have_fiq(state)))
+ return;
+ if (state->pdata->force_irq) {
+ state->pdata->force_irq(state->pdev, irq);
+ } else {
+ struct irq_chip *chip = irq_get_chip(irq);
+ if (chip && chip->irq_retrigger)
+ chip->irq_retrigger(irq_get_irq_data(irq));
+ }
+}
+
+static void debug_uart_enable(struct fiq_debugger_state *state)
+{
+ if (state->clk)
+ clk_enable(state->clk);
+ if (state->pdata->uart_enable)
+ state->pdata->uart_enable(state->pdev);
+}
+
+static void debug_uart_disable(struct fiq_debugger_state *state)
+{
+ if (state->pdata->uart_disable)
+ state->pdata->uart_disable(state->pdev);
+ if (state->clk)
+ clk_disable(state->clk);
+}
+
+static void debug_uart_flush(struct fiq_debugger_state *state)
+{
+ if (state->pdata->uart_flush)
+ state->pdata->uart_flush(state->pdev);
+}
+
+static void debug_putc(struct fiq_debugger_state *state, char c)
+{
+ state->pdata->uart_putc(state->pdev, c);
+}
+
+static void debug_puts(struct fiq_debugger_state *state, char *s)
+{
+ unsigned c;
+ while ((c = *s++)) {
+ if (c == '\n')
+ debug_putc(state, '\r');
+ debug_putc(state, c);
+ }
+}
+
+static void debug_prompt(struct fiq_debugger_state *state)
+{
+ debug_puts(state, "debug> ");
+}
+
+static void dump_kernel_log(struct fiq_debugger_state *state)
+{
+ char buf[512];
+ size_t len;
+ struct kmsg_dumper dumper = { .active = true };
+
+
+ kmsg_dump_rewind_nolock(&dumper);
+ while (kmsg_dump_get_line_nolock(&dumper, true, buf,
+ sizeof(buf) - 1, &len)) {
+ buf[len] = 0;
+ debug_puts(state, buf);
+ }
+}
+
+static char *mode_name(unsigned cpsr)
+{
+ switch (cpsr & MODE_MASK) {
+ case USR_MODE: return "USR";
+ case FIQ_MODE: return "FIQ";
+ case IRQ_MODE: return "IRQ";
+ case SVC_MODE: return "SVC";
+ case ABT_MODE: return "ABT";
+ case UND_MODE: return "UND";
+ case SYSTEM_MODE: return "SYS";
+ default: return "???";
+ }
+}
+
+static int debug_printf(void *cookie, const char *fmt, ...)
+{
+ struct fiq_debugger_state *state = cookie;
+ char buf[256];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ debug_puts(state, buf);
+ return state->debug_abort;
+}
+
+/* Safe outside fiq context */
+static int debug_printf_nfiq(void *cookie, const char *fmt, ...)
+{
+ struct fiq_debugger_state *state = cookie;
+ char buf[256];
+ va_list ap;
+ unsigned long irq_flags;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, 128, fmt, ap);
+ va_end(ap);
+
+ local_irq_save(irq_flags);
+ debug_puts(state, buf);
+ debug_uart_flush(state);
+ local_irq_restore(irq_flags);
+ return state->debug_abort;
+}
+
+static void dump_regs(struct fiq_debugger_state *state, unsigned *regs)
+{
+ debug_printf(state, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
+ regs[0], regs[1], regs[2], regs[3]);
+ debug_printf(state, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
+ regs[4], regs[5], regs[6], regs[7]);
+ debug_printf(state, " r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n",
+ regs[8], regs[9], regs[10], regs[11],
+ mode_name(regs[16]));
+ if ((regs[16] & MODE_MASK) == USR_MODE)
+ debug_printf(state, " ip %08x sp %08x lr %08x pc %08x "
+ "cpsr %08x\n", regs[12], regs[13], regs[14],
+ regs[15], regs[16]);
+ else
+ debug_printf(state, " ip %08x sp %08x lr %08x pc %08x "
+ "cpsr %08x spsr %08x\n", regs[12], regs[13],
+ regs[14], regs[15], regs[16], regs[17]);
+}
+
+struct mode_regs {
+ unsigned long sp_svc;
+ unsigned long lr_svc;
+ unsigned long spsr_svc;
+
+ unsigned long sp_abt;
+ unsigned long lr_abt;
+ unsigned long spsr_abt;
+
+ unsigned long sp_und;
+ unsigned long lr_und;
+ unsigned long spsr_und;
+
+ unsigned long sp_irq;
+ unsigned long lr_irq;
+ unsigned long spsr_irq;
+
+ unsigned long r8_fiq;
+ unsigned long r9_fiq;
+ unsigned long r10_fiq;
+ unsigned long r11_fiq;
+ unsigned long r12_fiq;
+ unsigned long sp_fiq;
+ unsigned long lr_fiq;
+ unsigned long spsr_fiq;
+};
+
+void __naked get_mode_regs(struct mode_regs *regs)
+{
+ asm volatile (
+ "mrs r1, cpsr\n"
+ "msr cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r13 - r14}\n"
+ "mrs r2, spsr\n"
+ "msr cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+ "stmia r0!, {r2, r8 - r14}\n"
+ "mrs r2, spsr\n"
+ "stmia r0!, {r2}\n"
+ "msr cpsr_c, r1\n"
+ "bx lr\n");
+}
+
+
+static void dump_allregs(struct fiq_debugger_state *state, unsigned *regs)
+{
+ struct mode_regs mode_regs;
+ dump_regs(state, regs);
+ get_mode_regs(&mode_regs);
+ debug_printf(state, " svc: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc);
+ debug_printf(state, " abt: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt);
+ debug_printf(state, " und: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und);
+ debug_printf(state, " irq: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq);
+ debug_printf(state, " fiq: r8 %08x r9 %08x r10 %08x r11 %08x "
+ "r12 %08x\n",
+ mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq,
+ mode_regs.r11_fiq, mode_regs.r12_fiq);
+ debug_printf(state, " fiq: sp %08x lr %08x spsr %08x\n",
+ mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq);
+}
+
+static void dump_irqs(struct fiq_debugger_state *state)
+{
+ int n;
+ struct irq_desc *desc;
+
+ debug_printf(state, "irqnr total since-last status name\n");
+ for_each_irq_desc(n, desc) {
+ struct irqaction *act = desc->action;
+ if (!act && !kstat_irqs(n))
+ continue;
+ debug_printf(state, "%5d: %10u %11u %8x %s\n", n,
+ kstat_irqs(n),
+ kstat_irqs(n) - state->last_irqs[n],
+ desc->status_use_accessors,
+ (act && act->name) ? act->name : "???");
+ state->last_irqs[n] = kstat_irqs(n);
+ }
+}
+
+struct stacktrace_state {
+ struct fiq_debugger_state *state;
+ unsigned int depth;
+};
+
+static int report_trace(struct stackframe *frame, void *d)
+{
+ struct stacktrace_state *sts = d;
+
+ if (sts->depth) {
+ debug_printf(sts->state,
+ " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n",
+ frame->pc, frame->pc, frame->lr, frame->lr,
+ frame->sp, frame->fp);
+ sts->depth--;
+ return 0;
+ }
+ debug_printf(sts->state, " ...\n");
+
+ return sts->depth == 0;
+}
+
+struct frame_tail {
+ struct frame_tail *fp;
+ unsigned long sp;
+ unsigned long lr;
+} __attribute__((packed));
+
+static struct frame_tail *user_backtrace(struct fiq_debugger_state *state,
+ struct frame_tail *tail)
+{
+ struct frame_tail buftail[2];
+
+ /* Also check accessibility of one struct frame_tail beyond */
+ if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) {
+ debug_printf(state, " invalid frame pointer %p\n", tail);
+ return NULL;
+ }
+ if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) {
+ debug_printf(state,
+ " failed to copy frame pointer %p\n", tail);
+ return NULL;
+ }
+
+ debug_printf(state, " %p\n", buftail[0].lr);
+
+ /* frame pointers should strictly progress back up the stack
+ * (towards higher addresses) */
+ if (tail >= buftail[0].fp)
+ return NULL;
+
+ return buftail[0].fp-1;
+}
+
+void dump_stacktrace(struct fiq_debugger_state *state,
+ struct pt_regs * const regs, unsigned int depth, void *ssp)
+{
+ struct frame_tail *tail;
+ struct thread_info *real_thread_info = THREAD_INFO(ssp);
+ struct stacktrace_state sts;
+
+ sts.depth = depth;
+ sts.state = state;
+ *current_thread_info() = *real_thread_info;
+
+ if (!current)
+ debug_printf(state, "current NULL\n");
+ else
+ debug_printf(state, "pid: %d comm: %s\n",
+ current->pid, current->comm);
+ dump_regs(state, (unsigned *)regs);
+
+ if (!user_mode(regs)) {
+ struct stackframe frame;
+ frame.fp = regs->ARM_fp;
+ frame.sp = regs->ARM_sp;
+ frame.lr = regs->ARM_lr;
+ frame.pc = regs->ARM_pc;
+ debug_printf(state,
+ " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n",
+ regs->ARM_pc, regs->ARM_pc, regs->ARM_lr, regs->ARM_lr,
+ regs->ARM_sp, regs->ARM_fp);
+ walk_stackframe(&frame, report_trace, &sts);
+ return;
+ }
+
+ tail = ((struct frame_tail *) regs->ARM_fp) - 1;
+ while (depth-- && tail && !((unsigned long) tail & 3))
+ tail = user_backtrace(state, tail);
+}
+
+static void do_ps(struct fiq_debugger_state *state)
+{
+ struct task_struct *g;
+ struct task_struct *p;
+ unsigned task_state;
+ static const char stat_nam[] = "RSDTtZX";
+
+ debug_printf(state, "pid ppid prio task pc\n");
+ read_lock(&tasklist_lock);
+ do_each_thread(g, p) {
+ task_state = p->state ? __ffs(p->state) + 1 : 0;
+ debug_printf(state,
+ "%5d %5d %4d ", p->pid, p->parent->pid, p->prio);
+ debug_printf(state, "%-13.13s %c", p->comm,
+ task_state >= sizeof(stat_nam) ? '?' : stat_nam[task_state]);
+ if (task_state == TASK_RUNNING)
+ debug_printf(state, " running\n");
+ else
+ debug_printf(state, " %08lx\n", thread_saved_pc(p));
+ } while_each_thread(g, p);
+ read_unlock(&tasklist_lock);
+}
+
+#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
+static void begin_syslog_dump(struct fiq_debugger_state *state)
+{
+ state->syslog_dumping = true;
+}
+
+static void end_syslog_dump(struct fiq_debugger_state *state)
+{
+ state->syslog_dumping = false;
+}
+#else
+extern int do_syslog(int type, char __user *bug, int count);
+static void begin_syslog_dump(struct fiq_debugger_state *state)
+{
+ do_syslog(5 /* clear */, NULL, 0);
+}
+
+static void end_syslog_dump(struct fiq_debugger_state *state)
+{
+ dump_kernel_log(state);
+}
+#endif
+
+static void do_sysrq(struct fiq_debugger_state *state, char rq)
+{
+ if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) {
+ debug_printf(state, "sysrq-g blocked\n");
+ return;
+ }
+ begin_syslog_dump(state);
+ handle_sysrq(rq);
+ end_syslog_dump(state);
+}
+
+#ifdef CONFIG_KGDB
+static void do_kgdb(struct fiq_debugger_state *state)
+{
+ if (!fiq_kgdb_enable) {
+ debug_printf(state, "kgdb through fiq debugger not enabled\n");
+ return;
+ }
+
+ debug_printf(state, "enabling console and triggering kgdb\n");
+ state->console_enable = true;
+ handle_sysrq('g');
+}
+#endif
+
+static void debug_schedule_work(struct fiq_debugger_state *state, char *cmd)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->work_lock, flags);
+ if (state->work_cmd[0] != '\0') {
+ debug_printf(state, "work command processor busy\n");
+ spin_unlock_irqrestore(&state->work_lock, flags);
+ return;
+ }
+
+ strlcpy(state->work_cmd, cmd, sizeof(state->work_cmd));
+ spin_unlock_irqrestore(&state->work_lock, flags);
+
+ schedule_work(&state->work);
+}
+
+static void debug_work(struct work_struct *work)
+{
+ struct fiq_debugger_state *state;
+ char work_cmd[DEBUG_MAX];
+ char *cmd;
+ unsigned long flags;
+
+ state = container_of(work, struct fiq_debugger_state, work);
+
+ spin_lock_irqsave(&state->work_lock, flags);
+
+ strlcpy(work_cmd, state->work_cmd, sizeof(work_cmd));
+ state->work_cmd[0] = '\0';
+
+ spin_unlock_irqrestore(&state->work_lock, flags);
+
+ cmd = work_cmd;
+ if (!strncmp(cmd, "reboot", 6)) {
+ cmd += 6;
+ while (*cmd == ' ')
+ cmd++;
+ if (cmd != '\0')
+ kernel_restart(cmd);
+ else
+ kernel_restart(NULL);
+ } else {
+ debug_printf(state, "unknown work command '%s'\n", work_cmd);
+ }
+}
+
+/* This function CANNOT be called in FIQ context */
+static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd)
+{
+ if (!strcmp(cmd, "ps"))
+ do_ps(state);
+ if (!strcmp(cmd, "sysrq"))
+ do_sysrq(state, 'h');
+ if (!strncmp(cmd, "sysrq ", 6))
+ do_sysrq(state, cmd[6]);
+#ifdef CONFIG_KGDB
+ if (!strcmp(cmd, "kgdb"))
+ do_kgdb(state);
+#endif
+ if (!strncmp(cmd, "reboot", 6))
+ debug_schedule_work(state, cmd);
+}
+
+static void debug_help(struct fiq_debugger_state *state)
+{
+ debug_printf(state, "FIQ Debugger commands:\n"
+ " pc PC status\n"
+ " regs Register dump\n"
+ " allregs Extended Register dump\n"
+ " bt Stack trace\n"
+ " reboot [<c>] Reboot with command <c>\n"
+ " reset [<c>] Hard reset with command <c>\n"
+ " irqs Interupt status\n"
+ " kmsg Kernel log\n"
+ " version Kernel version\n");
+ debug_printf(state, " sleep Allow sleep while in FIQ\n"
+ " nosleep Disable sleep while in FIQ\n"
+ " console Switch terminal to console\n"
+ " cpu Current CPU\n"
+ " cpu <number> Switch to CPU<number>\n");
+ debug_printf(state, " ps Process list\n"
+ " sysrq sysrq options\n"
+ " sysrq <param> Execute sysrq with <param>\n");
+#ifdef CONFIG_KGDB
+ debug_printf(state, " kgdb Enter kernel debugger\n");
+#endif
+}
+
+static void take_affinity(void *info)
+{
+ struct fiq_debugger_state *state = info;
+ struct cpumask cpumask;
+
+ cpumask_clear(&cpumask);
+ cpumask_set_cpu(get_cpu(), &cpumask);
+
+ irq_set_affinity(state->uart_irq, &cpumask);
+}
+
+static void switch_cpu(struct fiq_debugger_state *state, int cpu)
+{
+ if (!debug_have_fiq(state))
+ smp_call_function_single(cpu, take_affinity, state, false);
+ state->current_cpu = cpu;
+}
+
+static bool debug_fiq_exec(struct fiq_debugger_state *state,
+ const char *cmd, unsigned *regs, void *svc_sp)
+{
+ bool signal_helper = false;
+
+ if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) {
+ debug_help(state);
+ } else if (!strcmp(cmd, "pc")) {
+ debug_printf(state, " pc %08x cpsr %08x mode %s\n",
+ regs[15], regs[16], mode_name(regs[16]));
+ } else if (!strcmp(cmd, "regs")) {
+ dump_regs(state, regs);
+ } else if (!strcmp(cmd, "allregs")) {
+ dump_allregs(state, regs);
+ } else if (!strcmp(cmd, "bt")) {
+ dump_stacktrace(state, (struct pt_regs *)regs, 100, svc_sp);
+ } else if (!strncmp(cmd, "reset", 5)) {
+ cmd += 5;
+ while (*cmd == ' ')
+ cmd++;
+ if (*cmd) {
+ char tmp_cmd[32];
+ strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd));
+ machine_restart(tmp_cmd);
+ } else {
+ machine_restart(NULL);
+ }
+ } else if (!strcmp(cmd, "irqs")) {
+ dump_irqs(state);
+ } else if (!strcmp(cmd, "kmsg")) {
+ dump_kernel_log(state);
+ } else if (!strcmp(cmd, "version")) {
+ debug_printf(state, "%s\n", linux_banner);
+ } else if (!strcmp(cmd, "sleep")) {
+ state->no_sleep = false;
+ debug_printf(state, "enabling sleep\n");
+ } else if (!strcmp(cmd, "nosleep")) {
+ state->no_sleep = true;
+ debug_printf(state, "disabling sleep\n");
+ } else if (!strcmp(cmd, "console")) {
+ debug_printf(state, "console mode\n");
+ debug_uart_flush(state);
+ state->console_enable = true;
+ } else if (!strcmp(cmd, "cpu")) {
+ debug_printf(state, "cpu %d\n", state->current_cpu);
+ } else if (!strncmp(cmd, "cpu ", 4)) {
+ unsigned long cpu = 0;
+ if (strict_strtoul(cmd + 4, 10, &cpu) == 0)
+ switch_cpu(state, cpu);
+ else
+ debug_printf(state, "invalid cpu\n");
+ debug_printf(state, "cpu %d\n", state->current_cpu);
+ } else {
+ if (state->debug_busy) {
+ debug_printf(state,
+ "command processor busy. trying to abort.\n");
+ state->debug_abort = -1;
+ } else {
+ strcpy(state->debug_cmd, cmd);
+ state->debug_busy = 1;
+ }
+
+ return true;
+ }
+ if (!state->console_enable)
+ debug_prompt(state);
+
+ return signal_helper;
+}
+
+static void sleep_timer_expired(unsigned long data)
+{
+ struct fiq_debugger_state *state = (struct fiq_debugger_state *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->sleep_timer_lock, flags);
+ if (state->uart_enabled && !state->no_sleep) {
+ if (state->debug_enable && !state->console_enable) {
+ state->debug_enable = false;
+ debug_printf_nfiq(state, "suspending fiq debugger\n");
+ }
+ state->ignore_next_wakeup_irq = true;
+ debug_uart_disable(state);
+ state->uart_enabled = false;
+ enable_wakeup_irq(state);
+ }
+ wake_unlock(&state->debugger_wake_lock);
+ spin_unlock_irqrestore(&state->sleep_timer_lock, flags);
+}
+
+static void handle_wakeup(struct fiq_debugger_state *state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->sleep_timer_lock, flags);
+ if (state->wakeup_irq >= 0 && state->ignore_next_wakeup_irq) {
+ state->ignore_next_wakeup_irq = false;
+ } else if (!state->uart_enabled) {
+ wake_lock(&state->debugger_wake_lock);
+ debug_uart_enable(state);
+ state->uart_enabled = true;
+ disable_wakeup_irq(state);
+ mod_timer(&state->sleep_timer, jiffies + HZ / 2);
+ }
+ spin_unlock_irqrestore(&state->sleep_timer_lock, flags);
+}
+
+static irqreturn_t wakeup_irq_handler(int irq, void *dev)
+{
+ struct fiq_debugger_state *state = dev;
+
+ if (!state->no_sleep)
+ debug_puts(state, "WAKEUP\n");
+ handle_wakeup(state);
+
+ return IRQ_HANDLED;
+}
+
+static void debug_handle_console_irq_context(struct fiq_debugger_state *state)
+{
+#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
+ if (state->tty_port.ops) {
+ int i;
+ int count = fiq_debugger_ringbuf_level(state->tty_rbuf);
+ for (i = 0; i < count; i++) {
+ int c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0);
+ tty_insert_flip_char(&state->tty_port, c, TTY_NORMAL);
+ if (!fiq_debugger_ringbuf_consume(state->tty_rbuf, 1))
+ pr_warn("fiq tty failed to consume byte\n");
+ }
+ tty_flip_buffer_push(&state->tty_port);
+ }
+#endif
+}
+
+static void debug_handle_irq_context(struct fiq_debugger_state *state)
+{
+ if (!state->no_sleep) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->sleep_timer_lock, flags);
+ wake_lock(&state->debugger_wake_lock);
+ mod_timer(&state->sleep_timer, jiffies + HZ * 5);
+ spin_unlock_irqrestore(&state->sleep_timer_lock, flags);
+ }
+ debug_handle_console_irq_context(state);
+ if (state->debug_busy) {
+ debug_irq_exec(state, state->debug_cmd);
+ if (!state->console_enable)
+ debug_prompt(state);
+ state->debug_busy = 0;
+ }
+}
+
+static int debug_getc(struct fiq_debugger_state *state)
+{
+ return state->pdata->uart_getc(state->pdev);
+}
+
+static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state,
+ int this_cpu, void *regs, void *svc_sp)
+{
+ int c;
+ static int last_c;
+ int count = 0;
+ bool signal_helper = false;
+
+ if (this_cpu != state->current_cpu) {
+ if (state->in_fiq)
+ return false;
+
+ if (atomic_inc_return(&state->unhandled_fiq_count) !=
+ MAX_UNHANDLED_FIQ_COUNT)
+ return false;
+
+ debug_printf(state, "fiq_debugger: cpu %d not responding, "
+ "reverting to cpu %d\n", state->current_cpu,
+ this_cpu);
+
+ atomic_set(&state->unhandled_fiq_count, 0);
+ switch_cpu(state, this_cpu);
+ return false;
+ }
+
+ state->in_fiq = true;
+
+ while ((c = debug_getc(state)) != FIQ_DEBUGGER_NO_CHAR) {
+ count++;
+ if (!state->debug_enable) {
+ if ((c == 13) || (c == 10)) {
+ state->debug_enable = true;
+ state->debug_count = 0;
+ debug_prompt(state);
+ }
+ } else if (c == FIQ_DEBUGGER_BREAK) {
+ state->console_enable = false;
+ debug_puts(state, "fiq debugger mode\n");
+ state->debug_count = 0;
+ debug_prompt(state);
+#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
+ } else if (state->console_enable && state->tty_rbuf) {
+ fiq_debugger_ringbuf_push(state->tty_rbuf, c);
+ signal_helper = true;
+#endif
+ } else if ((c >= ' ') && (c < 127)) {
+ if (state->debug_count < (DEBUG_MAX - 1)) {
+ state->debug_buf[state->debug_count++] = c;
+ debug_putc(state, c);
+ }
+ } else if ((c == 8) || (c == 127)) {
+ if (state->debug_count > 0) {
+ state->debug_count--;
+ debug_putc(state, 8);
+ debug_putc(state, ' ');
+ debug_putc(state, 8);
+ }
+ } else if ((c == 13) || (c == 10)) {
+ if (c == '\r' || (c == '\n' && last_c != '\r')) {
+ debug_putc(state, '\r');
+ debug_putc(state, '\n');
+ }
+ if (state->debug_count) {
+ state->debug_buf[state->debug_count] = 0;
+ state->debug_count = 0;
+ signal_helper |=
+ debug_fiq_exec(state, state->debug_buf,
+ regs, svc_sp);
+ } else {
+ debug_prompt(state);
+ }
+ }
+ last_c = c;
+ }
+ if (!state->console_enable)
+ debug_uart_flush(state);
+ if (state->pdata->fiq_ack)
+ state->pdata->fiq_ack(state->pdev, state->fiq);
+
+ /* poke sleep timer if necessary */
+ if (state->debug_enable && !state->no_sleep)
+ signal_helper = true;
+
+ atomic_set(&state->unhandled_fiq_count, 0);
+ state->in_fiq = false;
+
+ return signal_helper;
+}
+
+static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp)
+{
+ struct fiq_debugger_state *state =
+ container_of(h, struct fiq_debugger_state, handler);
+ unsigned int this_cpu = THREAD_INFO(svc_sp)->cpu;
+ bool need_irq;
+
+ need_irq = debug_handle_uart_interrupt(state, this_cpu, regs, svc_sp);
+ if (need_irq)
+ debug_force_irq(state);
+}
+
+/*
+ * When not using FIQs, we only use this single interrupt as an entry point.
+ * This just effectively takes over the UART interrupt and does all the work
+ * in this context.
+ */
+static irqreturn_t debug_uart_irq(int irq, void *dev)
+{
+ struct fiq_debugger_state *state = dev;
+ bool not_done;
+
+ handle_wakeup(state);
+
+ /* handle the debugger irq in regular context */
+ not_done = debug_handle_uart_interrupt(state, smp_processor_id(),
+ get_irq_regs(),
+ current_thread_info());
+ if (not_done)
+ debug_handle_irq_context(state);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * If FIQs are used, not everything can happen in fiq context.
+ * FIQ handler does what it can and then signals this interrupt to finish the
+ * job in irq context.
+ */
+static irqreturn_t debug_signal_irq(int irq, void *dev)
+{
+ struct fiq_debugger_state *state = dev;
+
+ if (state->pdata->force_irq_ack)
+ state->pdata->force_irq_ack(state->pdev, state->signal_irq);
+
+ debug_handle_irq_context(state);
+
+ return IRQ_HANDLED;
+}
+
+static void debug_resume(struct fiq_glue_handler *h)
+{
+ struct fiq_debugger_state *state =
+ container_of(h, struct fiq_debugger_state, handler);
+ if (state->pdata->uart_resume)
+ state->pdata->uart_resume(state->pdev);
+}
+
+#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
+struct tty_driver *debug_console_device(struct console *co, int *index)
+{
+ *index = co->index;
+ return fiq_tty_driver;
+}
+
+static void debug_console_write(struct console *co,
+ const char *s, unsigned int count)
+{
+ struct fiq_debugger_state *state;
+ unsigned long flags;
+
+ state = container_of(co, struct fiq_debugger_state, console);
+
+ if (!state->console_enable && !state->syslog_dumping)
+ return;
+
+ debug_uart_enable(state);
+ spin_lock_irqsave(&state->console_lock, flags);
+ while (count--) {
+ if (*s == '\n')
+ debug_putc(state, '\r');
+ debug_putc(state, *s++);
+ }
+ debug_uart_flush(state);
+ spin_unlock_irqrestore(&state->console_lock, flags);
+ debug_uart_disable(state);
+}
+
+static struct console fiq_debugger_console = {
+ .name = "ttyFIQ",
+ .device = debug_console_device,
+ .write = debug_console_write,
+ .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED,
+};
+
+int fiq_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ int line = tty->index;
+ struct fiq_debugger_state **states = tty->driver->driver_state;
+ struct fiq_debugger_state *state = states[line];
+
+ return tty_port_open(&state->tty_port, tty, filp);
+}
+
+void fiq_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ tty_port_close(tty->port, tty, filp);
+}
+
+int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ int i;
+ int line = tty->index;
+ struct fiq_debugger_state **states = tty->driver->driver_state;
+ struct fiq_debugger_state *state = states[line];
+
+ if (!state->console_enable)
+ return count;
+
+ debug_uart_enable(state);
+ spin_lock_irq(&state->console_lock);
+ for (i = 0; i < count; i++)
+ debug_putc(state, *buf++);
+ spin_unlock_irq(&state->console_lock);
+ debug_uart_disable(state);
+
+ return count;
+}
+
+int fiq_tty_write_room(struct tty_struct *tty)
+{
+ return 16;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int fiq_tty_poll_init(struct tty_driver *driver, int line, char *options)
+{
+ return 0;
+}
+
+static int fiq_tty_poll_get_char(struct tty_driver *driver, int line)
+{
+ struct fiq_debugger_state **states = driver->driver_state;
+ struct fiq_debugger_state *state = states[line];
+ int c = NO_POLL_CHAR;
+
+ debug_uart_enable(state);
+ if (debug_have_fiq(state)) {
+ int count = fiq_debugger_ringbuf_level(state->tty_rbuf);
+ if (count > 0) {
+ c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0);
+ fiq_debugger_ringbuf_consume(state->tty_rbuf, 1);
+ }
+ } else {
+ c = debug_getc(state);
+ if (c == FIQ_DEBUGGER_NO_CHAR)
+ c = NO_POLL_CHAR;
+ }
+ debug_uart_disable(state);
+
+ return c;
+}
+
+static void fiq_tty_poll_put_char(struct tty_driver *driver, int line, char ch)
+{
+ struct fiq_debugger_state **states = driver->driver_state;
+ struct fiq_debugger_state *state = states[line];
+ debug_uart_enable(state);
+ debug_putc(state, ch);
+ debug_uart_disable(state);
+}
+#endif
+
+static const struct tty_port_operations fiq_tty_port_ops;
+
+static const struct tty_operations fiq_tty_driver_ops = {
+ .write = fiq_tty_write,
+ .write_room = fiq_tty_write_room,
+ .open = fiq_tty_open,
+ .close = fiq_tty_close,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_init = fiq_tty_poll_init,
+ .poll_get_char = fiq_tty_poll_get_char,
+ .poll_put_char = fiq_tty_poll_put_char,
+#endif
+};
+
+static int fiq_debugger_tty_init(void)
+{
+ int ret;
+ struct fiq_debugger_state **states = NULL;
+
+ states = kzalloc(sizeof(*states) * MAX_FIQ_DEBUGGER_PORTS, GFP_KERNEL);
+ if (!states) {
+ pr_err("Failed to allocate fiq debugger state structres\n");
+ return -ENOMEM;
+ }
+
+ fiq_tty_driver = alloc_tty_driver(MAX_FIQ_DEBUGGER_PORTS);
+ if (!fiq_tty_driver) {
+ pr_err("Failed to allocate fiq debugger tty\n");
+ ret = -ENOMEM;
+ goto err_free_state;
+ }
+
+ fiq_tty_driver->owner = THIS_MODULE;
+ fiq_tty_driver->driver_name = "fiq-debugger";
+ fiq_tty_driver->name = "ttyFIQ";
+ fiq_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ fiq_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ fiq_tty_driver->init_termios = tty_std_termios;
+ fiq_tty_driver->flags = TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV;
+ fiq_tty_driver->driver_state = states;
+
+ fiq_tty_driver->init_termios.c_cflag =
+ B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+ fiq_tty_driver->init_termios.c_ispeed = 115200;
+ fiq_tty_driver->init_termios.c_ospeed = 115200;
+
+ tty_set_operations(fiq_tty_driver, &fiq_tty_driver_ops);
+
+ ret = tty_register_driver(fiq_tty_driver);
+ if (ret) {
+ pr_err("Failed to register fiq tty: %d\n", ret);
+ goto err_free_tty;
+ }
+
+ pr_info("Registered FIQ tty driver\n");
+ return 0;
+
+err_free_tty:
+ put_tty_driver(fiq_tty_driver);
+ fiq_tty_driver = NULL;
+err_free_state:
+ kfree(states);
+ return ret;
+}
+
+static int fiq_debugger_tty_init_one(struct fiq_debugger_state *state)
+{
+ int ret;
+ struct device *tty_dev;
+ struct fiq_debugger_state **states = fiq_tty_driver->driver_state;
+
+ states[state->pdev->id] = state;
+
+ state->tty_rbuf = fiq_debugger_ringbuf_alloc(1024);
+ if (!state->tty_rbuf) {
+ pr_err("Failed to allocate fiq debugger ringbuf\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ tty_port_init(&state->tty_port);
+ state->tty_port.ops = &fiq_tty_port_ops;
+
+ tty_dev = tty_port_register_device(&state->tty_port, fiq_tty_driver,
+ state->pdev->id, &state->pdev->dev);
+ if (IS_ERR(tty_dev)) {
+ pr_err("Failed to register fiq debugger tty device\n");
+ ret = PTR_ERR(tty_dev);
+ goto err;
+ }
+
+ device_set_wakeup_capable(tty_dev, 1);
+
+ pr_info("Registered fiq debugger ttyFIQ%d\n", state->pdev->id);
+
+ return 0;
+
+err:
+ fiq_debugger_ringbuf_free(state->tty_rbuf);
+ state->tty_rbuf = NULL;
+ return ret;
+}
+#endif
+
+static int fiq_debugger_dev_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fiq_debugger_state *state = platform_get_drvdata(pdev);
+
+ if (state->pdata->uart_dev_suspend)
+ return state->pdata->uart_dev_suspend(pdev);
+ return 0;
+}
+
+static int fiq_debugger_dev_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fiq_debugger_state *state = platform_get_drvdata(pdev);
+
+ if (state->pdata->uart_dev_resume)
+ return state->pdata->uart_dev_resume(pdev);
+ return 0;
+}
+
+static int fiq_debugger_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct fiq_debugger_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct fiq_debugger_state *state;
+ int fiq;
+ int uart_irq;
+
+ if (pdev->id >= MAX_FIQ_DEBUGGER_PORTS)
+ return -EINVAL;
+
+ if (!pdata->uart_getc || !pdata->uart_putc)
+ return -EINVAL;
+ if ((pdata->uart_enable && !pdata->uart_disable) ||
+ (!pdata->uart_enable && pdata->uart_disable))
+ return -EINVAL;
+
+ fiq = platform_get_irq_byname(pdev, "fiq");
+ uart_irq = platform_get_irq_byname(pdev, "uart_irq");
+
+ /* uart_irq mode and fiq mode are mutually exclusive, but one of them
+ * is required */
+ if ((uart_irq < 0 && fiq < 0) || (uart_irq >= 0 && fiq >= 0))
+ return -EINVAL;
+ if (fiq >= 0 && !pdata->fiq_enable)
+ return -EINVAL;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ setup_timer(&state->sleep_timer, sleep_timer_expired,
+ (unsigned long)state);
+ state->pdata = pdata;
+ state->pdev = pdev;
+ state->no_sleep = initial_no_sleep;
+ state->debug_enable = initial_debug_enable;
+ state->console_enable = initial_console_enable;
+
+ state->fiq = fiq;
+ state->uart_irq = uart_irq;
+ state->signal_irq = platform_get_irq_byname(pdev, "signal");
+ state->wakeup_irq = platform_get_irq_byname(pdev, "wakeup");
+
+ INIT_WORK(&state->work, debug_work);
+ spin_lock_init(&state->work_lock);
+
+ platform_set_drvdata(pdev, state);
+
+ spin_lock_init(&state->sleep_timer_lock);
+
+ if (state->wakeup_irq < 0 && debug_have_fiq(state))
+ state->no_sleep = true;
+ state->ignore_next_wakeup_irq = !state->no_sleep;
+
+ wake_lock_init(&state->debugger_wake_lock,
+ WAKE_LOCK_SUSPEND, "serial-debug");
+
+ state->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(state->clk))
+ state->clk = NULL;
+
+ /* do not call pdata->uart_enable here since uart_init may still
+ * need to do some initialization before uart_enable can work.
+ * So, only try to manage the clock during init.
+ */
+ if (state->clk)
+ clk_enable(state->clk);
+
+ if (pdata->uart_init) {
+ ret = pdata->uart_init(pdev);
+ if (ret)
+ goto err_uart_init;
+ }
+
+ debug_printf_nfiq(state, "<hit enter %sto activate fiq debugger>\n",
+ state->no_sleep ? "" : "twice ");
+
+ if (debug_have_fiq(state)) {
+ state->handler.fiq = debug_fiq;
+ state->handler.resume = debug_resume;
+ ret = fiq_glue_register_handler(&state->handler);
+ if (ret) {
+ pr_err("%s: could not install fiq handler\n", __func__);
+ goto err_register_fiq;
+ }
+
+ pdata->fiq_enable(pdev, state->fiq, 1);
+ } else {
+ ret = request_irq(state->uart_irq, debug_uart_irq,
+ IRQF_NO_SUSPEND, "debug", state);
+ if (ret) {
+ pr_err("%s: could not install irq handler\n", __func__);
+ goto err_register_irq;
+ }
+
+ /* for irq-only mode, we want this irq to wake us up, if it
+ * can.
+ */
+ enable_irq_wake(state->uart_irq);
+ }
+
+ if (state->clk)
+ clk_disable(state->clk);
+
+ if (state->signal_irq >= 0) {
+ ret = request_irq(state->signal_irq, debug_signal_irq,
+ IRQF_TRIGGER_RISING, "debug-signal", state);
+ if (ret)
+ pr_err("serial_debugger: could not install signal_irq");
+ }
+
+ if (state->wakeup_irq >= 0) {
+ ret = request_irq(state->wakeup_irq, wakeup_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+ "debug-wakeup", state);
+ if (ret) {
+ pr_err("serial_debugger: "
+ "could not install wakeup irq\n");
+ state->wakeup_irq = -1;
+ } else {
+ ret = enable_irq_wake(state->wakeup_irq);
+ if (ret) {
+ pr_err("serial_debugger: "
+ "could not enable wakeup\n");
+ state->wakeup_irq_no_set_wake = true;
+ }
+ }
+ }
+ if (state->no_sleep)
+ handle_wakeup(state);
+
+#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
+ spin_lock_init(&state->console_lock);
+ state->console = fiq_debugger_console;
+ state->console.index = pdev->id;
+ if (!console_set_on_cmdline)
+ add_preferred_console(state->console.name,
+ state->console.index, NULL);
+ register_console(&state->console);
+ fiq_debugger_tty_init_one(state);
+#endif
+ return 0;
+
+err_register_irq:
+err_register_fiq:
+ if (pdata->uart_free)
+ pdata->uart_free(pdev);
+err_uart_init:
+ if (state->clk)
+ clk_disable(state->clk);
+ if (state->clk)
+ clk_put(state->clk);
+ wake_lock_destroy(&state->debugger_wake_lock);
+ platform_set_drvdata(pdev, NULL);
+ kfree(state);
+ return ret;
+}
+
+static const struct dev_pm_ops fiq_debugger_dev_pm_ops = {
+ .suspend = fiq_debugger_dev_suspend,
+ .resume = fiq_debugger_dev_resume,
+};
+
+static struct platform_driver fiq_debugger_driver = {
+ .probe = fiq_debugger_probe,
+ .driver = {
+ .name = "fiq_debugger",
+ .pm = &fiq_debugger_dev_pm_ops,
+ },
+};
+
+static int __init fiq_debugger_init(void)
+{
+#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
+ fiq_debugger_tty_init();
+#endif
+ return platform_driver_register(&fiq_debugger_driver);
+}
+
+postcore_initcall(fiq_debugger_init);
diff --git a/arch/arm/common/fiq_debugger_ringbuf.h b/arch/arm/common/fiq_debugger_ringbuf.h
new file mode 100644
index 000000000000..2649b5581088
--- /dev/null
+++ b/arch/arm/common/fiq_debugger_ringbuf.h
@@ -0,0 +1,94 @@
+/*
+ * arch/arm/common/fiq_debugger_ringbuf.c
+ *
+ * simple lockless ringbuffer
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/slab.h>
+
+struct fiq_debugger_ringbuf {
+ int len;
+ int head;
+ int tail;
+ u8 buf[];
+};
+
+
+static inline struct fiq_debugger_ringbuf *fiq_debugger_ringbuf_alloc(int len)
+{
+ struct fiq_debugger_ringbuf *rbuf;
+
+ rbuf = kzalloc(sizeof(*rbuf) + len, GFP_KERNEL);
+ if (rbuf == NULL)
+ return NULL;
+
+ rbuf->len = len;
+ rbuf->head = 0;
+ rbuf->tail = 0;
+ smp_mb();
+
+ return rbuf;
+}
+
+static inline void fiq_debugger_ringbuf_free(struct fiq_debugger_ringbuf *rbuf)
+{
+ kfree(rbuf);
+}
+
+static inline int fiq_debugger_ringbuf_level(struct fiq_debugger_ringbuf *rbuf)
+{
+ int level = rbuf->head - rbuf->tail;
+
+ if (level < 0)
+ level = rbuf->len + level;
+
+ return level;
+}
+
+static inline int fiq_debugger_ringbuf_room(struct fiq_debugger_ringbuf *rbuf)
+{
+ return rbuf->len - fiq_debugger_ringbuf_level(rbuf) - 1;
+}
+
+static inline u8
+fiq_debugger_ringbuf_peek(struct fiq_debugger_ringbuf *rbuf, int i)
+{
+ return rbuf->buf[(rbuf->tail + i) % rbuf->len];
+}
+
+static inline int
+fiq_debugger_ringbuf_consume(struct fiq_debugger_ringbuf *rbuf, int count)
+{
+ count = min(count, fiq_debugger_ringbuf_level(rbuf));
+
+ rbuf->tail = (rbuf->tail + count) % rbuf->len;
+ smp_mb();
+
+ return count;
+}
+
+static inline int
+fiq_debugger_ringbuf_push(struct fiq_debugger_ringbuf *rbuf, u8 datum)
+{
+ if (fiq_debugger_ringbuf_room(rbuf) == 0)
+ return 0;
+
+ rbuf->buf[rbuf->head] = datum;
+ smp_mb();
+ rbuf->head = (rbuf->head + 1) % rbuf->len;
+ smp_mb();
+
+ return 1;
+}
diff --git a/arch/arm/common/fiq_glue.S b/arch/arm/common/fiq_glue.S
new file mode 100644
index 000000000000..24b42cec4813
--- /dev/null
+++ b/arch/arm/common/fiq_glue.S
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/linkage.h>
+#include <asm/assembler.h>
+
+ .text
+
+ .global fiq_glue_end
+
+ /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */
+
+ENTRY(fiq_glue)
+ /* store pc, cpsr from previous mode, reserve space for spsr */
+ mrs r12, spsr
+ sub lr, lr, #4
+ subs r10, #1
+ bne nested_fiq
+
+ str r12, [sp, #-8]!
+ str lr, [sp, #-4]!
+
+ /* store r8-r14 from previous mode */
+ sub sp, sp, #(7 * 4)
+ stmia sp, {r8-r14}^
+ nop
+
+ /* store r0-r7 from previous mode */
+ stmfd sp!, {r0-r7}
+
+ /* setup func(data,regs) arguments */
+ mov r0, r9
+ mov r1, sp
+ mov r3, r8
+
+ mov r7, sp
+
+ /* Get sp and lr from non-user modes */
+ and r4, r12, #MODE_MASK
+ cmp r4, #USR_MODE
+ beq fiq_from_usr_mode
+
+ mov r7, sp
+ orr r4, r4, #(PSR_I_BIT | PSR_F_BIT)
+ msr cpsr_c, r4
+ str sp, [r7, #(4 * 13)]
+ str lr, [r7, #(4 * 14)]
+ mrs r5, spsr
+ str r5, [r7, #(4 * 17)]
+
+ cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT)
+ /* use fiq stack if we reenter this mode */
+ subne sp, r7, #(4 * 3)
+
+fiq_from_usr_mode:
+ msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT)
+ mov r2, sp
+ sub sp, r7, #12
+ stmfd sp!, {r2, ip, lr}
+ /* call func(data,regs) */
+ blx r3
+ ldmfd sp, {r2, ip, lr}
+ mov sp, r2
+
+ /* restore/discard saved state */
+ cmp r4, #USR_MODE
+ beq fiq_from_usr_mode_exit
+
+ msr cpsr_c, r4
+ ldr sp, [r7, #(4 * 13)]
+ ldr lr, [r7, #(4 * 14)]
+ msr spsr_cxsf, r5
+
+fiq_from_usr_mode_exit:
+ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
+
+ ldmfd sp!, {r0-r7}
+ ldr lr, [sp, #(4 * 7)]
+ ldr r12, [sp, #(4 * 8)]
+ add sp, sp, #(10 * 4)
+exit_fiq:
+ msr spsr_cxsf, r12
+ add r10, #1
+ cmp r11, #0
+ moveqs pc, lr
+ bx r11 /* jump to custom fiq return function */
+
+nested_fiq:
+ orr r12, r12, #(PSR_F_BIT)
+ b exit_fiq
+
+fiq_glue_end:
+
+ENTRY(fiq_glue_setup) /* func, data, sp, smc call number */
+ stmfd sp!, {r4}
+ mrs r4, cpsr
+ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
+ movs r8, r0
+ mov r9, r1
+ mov sp, r2
+ mov r11, r3
+ moveq r10, #0
+ movne r10, #1
+ msr cpsr_c, r4
+ ldmfd sp!, {r4}
+ bx lr
+
diff --git a/arch/arm/common/fiq_glue_setup.c b/arch/arm/common/fiq_glue_setup.c
new file mode 100644
index 000000000000..8cb1b611c6d5
--- /dev/null
+++ b/arch/arm/common/fiq_glue_setup.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/percpu.h>
+#include <linux/slab.h>
+#include <asm/fiq.h>
+#include <asm/fiq_glue.h>
+
+extern unsigned char fiq_glue, fiq_glue_end;
+extern void fiq_glue_setup(void *func, void *data, void *sp,
+ fiq_return_handler_t fiq_return_handler);
+
+static struct fiq_handler fiq_debbuger_fiq_handler = {
+ .name = "fiq_glue",
+};
+DEFINE_PER_CPU(void *, fiq_stack);
+static struct fiq_glue_handler *current_handler;
+static fiq_return_handler_t fiq_return_handler;
+static DEFINE_MUTEX(fiq_glue_lock);
+
+static void fiq_glue_setup_helper(void *info)
+{
+ struct fiq_glue_handler *handler = info;
+ fiq_glue_setup(handler->fiq, handler,
+ __get_cpu_var(fiq_stack) + THREAD_START_SP,
+ fiq_return_handler);
+}
+
+int fiq_glue_register_handler(struct fiq_glue_handler *handler)
+{
+ int ret;
+ int cpu;
+
+ if (!handler || !handler->fiq)
+ return -EINVAL;
+
+ mutex_lock(&fiq_glue_lock);
+ if (fiq_stack) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ for_each_possible_cpu(cpu) {
+ void *stack;
+ stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
+ if (WARN_ON(!stack)) {
+ ret = -ENOMEM;
+ goto err_alloc_fiq_stack;
+ }
+ per_cpu(fiq_stack, cpu) = stack;
+ }
+
+ ret = claim_fiq(&fiq_debbuger_fiq_handler);
+ if (WARN_ON(ret))
+ goto err_claim_fiq;
+
+ current_handler = handler;
+ on_each_cpu(fiq_glue_setup_helper, handler, true);
+ set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue);
+
+ mutex_unlock(&fiq_glue_lock);
+ return 0;
+
+err_claim_fiq:
+err_alloc_fiq_stack:
+ for_each_possible_cpu(cpu) {
+ __free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER);
+ per_cpu(fiq_stack, cpu) = NULL;
+ }
+err_busy:
+ mutex_unlock(&fiq_glue_lock);
+ return ret;
+}
+
+static void fiq_glue_update_return_handler(void (*fiq_return)(void))
+{
+ fiq_return_handler = fiq_return;
+ if (current_handler)
+ on_each_cpu(fiq_glue_setup_helper, current_handler, true);
+}
+
+int fiq_glue_set_return_handler(void (*fiq_return)(void))
+{
+ int ret;
+
+ mutex_lock(&fiq_glue_lock);
+ if (fiq_return_handler) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+ fiq_glue_update_return_handler(fiq_return);
+ ret = 0;
+err_busy:
+ mutex_unlock(&fiq_glue_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(fiq_glue_set_return_handler);
+
+int fiq_glue_clear_return_handler(void (*fiq_return)(void))
+{
+ int ret;
+
+ mutex_lock(&fiq_glue_lock);
+ if (WARN_ON(fiq_return_handler != fiq_return)) {
+ ret = -EINVAL;
+ goto err_inval;
+ }
+ fiq_glue_update_return_handler(NULL);
+ ret = 0;
+err_inval:
+ mutex_unlock(&fiq_glue_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(fiq_glue_clear_return_handler);
+
+/**
+ * fiq_glue_resume - Restore fiqs after suspend or low power idle states
+ *
+ * This must be called before calling local_fiq_enable after returning from a
+ * power state where the fiq mode registers were lost. If a driver provided
+ * a resume hook when it registered the handler it will be called.
+ */
+
+void fiq_glue_resume(void)
+{
+ if (!current_handler)
+ return;
+ fiq_glue_setup(current_handler->fiq, current_handler,
+ __get_cpu_var(fiq_stack) + THREAD_START_SP,
+ fiq_return_handler);
+ if (current_handler->resume)
+ current_handler->resume(current_handler);
+}
+
diff --git a/arch/arm/configs/ep93xx_defconfig b/arch/arm/configs/ep93xx_defconfig
index 1b650c85bdd0..72233b9c9d07 100644
--- a/arch/arm/configs/ep93xx_defconfig
+++ b/arch/arm/configs/ep93xx_defconfig
@@ -107,5 +107,6 @@ CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_LL_UART_PL01X=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/hi3xxx_defconfig b/arch/arm/configs/hisi_defconfig
index 9630687e7d07..8a97670b0d46 100644
--- a/arch/arm/configs/hi3xxx_defconfig
+++ b/arch/arm/configs/hisi_defconfig
@@ -5,12 +5,23 @@ CONFIG_BLK_DEV_INITRD=y
CONFIG_RD_LZMA=y
CONFIG_ARCH_HISI=y
CONFIG_ARCH_HI3xxx=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_CMDLINE_PARTITION=y
CONFIG_ARCH_HIX5HD2=y
+CONFIG_ARCH_HIP04=y
CONFIG_SMP=y
+CONFIG_NR_CPUS=16
CONFIG_PREEMPT=y
CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y
+CONFIG_PM_RUNTIME=y
CONFIG_NET=y
+CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_PNP=y
@@ -21,6 +32,14 @@ CONFIG_BLK_DEV_SD=y
CONFIG_ATA=y
CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_NETDEVICES=y
+CONFIG_HIX5HD2_GMAC=y
+CONFIG_HIP04_ETH=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
@@ -28,8 +47,12 @@ CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_SPI=y
CONFIG_SPI_PL022=y
CONFIG_PINCTRL_SINGLE=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIOLIB=y
CONFIG_GPIO_GENERIC_PLATFORM=y
CONFIG_REGULATOR_GPIO=y
+CONFIG_MFD_SYSCON=y
CONFIG_DRM=y
CONFIG_FB_SIMPLE=y
CONFIG_USB=y
@@ -37,15 +60,21 @@ CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_MXC=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_MMC=y
CONFIG_RTC_CLASS=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_IDMAC=y
+CONFIG_MMC_DW_PLTFM=y
CONFIG_RTC_DRV_PL031=y
CONFIG_DMADEVICES=y
CONFIG_DW_DMAC=y
CONFIG_PL330_DMA=y
CONFIG_PWM=y
+CONFIG_PHY_HIX5HD2_SATA=y
CONFIG_EXT4_FS=y
CONFIG_TMPFS=y
CONFIG_NFS_FS=y
@@ -54,5 +83,9 @@ CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
CONFIG_DEBUG_KERNEL=y
CONFIG_LOCKUP_DETECTOR=y
+CONFIG_VFP=y
+CONFIG_VFPv3=y
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 5fb95fb758d9..ed162f0a8e27 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -28,6 +28,7 @@ CONFIG_ARCH_HIGHBANK=y
CONFIG_ARCH_HISI=y
CONFIG_ARCH_HI3xxx=y
CONFIG_ARCH_HIX5HD2=y
+CONFIG_ARCH_HIP04=y
CONFIG_ARCH_KEYSTONE=y
CONFIG_ARCH_MXC=y
CONFIG_SOC_IMX51=y
@@ -107,8 +108,9 @@ CONFIG_CAN_RAW=y
CONFIG_CAN_BCM=y
CONFIG_CAN_DEV=y
CONFIG_CAN_MCP251X=y
-CONFIG_CFG80211=m
-CONFIG_MAC80211=m
+CONFIG_CFG80211=y
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=y
CONFIG_RFKILL=y
CONFIG_RFKILL_INPUT=y
CONFIG_RFKILL_GPIO=y
@@ -131,11 +133,13 @@ CONFIG_SCSI_MULTI_LUN=y
CONFIG_ATA=y
CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_AHCI_ST=y
+CONFIG_AHCI_QCOM=y
CONFIG_AHCI_SUNXI=y
CONFIG_SATA_HIGHBANK=y
CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y
CONFIG_SUN4I_EMAC=y
+CONFIG_ATL1C=y
CONFIG_MACB=y
CONFIG_NET_CALXEDA_XGMAC=y
CONFIG_IGB=y
@@ -153,6 +157,10 @@ CONFIG_USB_PEGASUS=y
CONFIG_USB_USBNET=y
CONFIG_USB_NET_SMSC75XX=y
CONFIG_USB_NET_SMSC95XX=y
+CONFIG_ATH_CARDS=y
+CONFIG_ATH_DEBUG=y
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
CONFIG_BRCMFMAC=m
CONFIG_RT2X00=m
CONFIG_RT2800USB=m
@@ -215,6 +223,9 @@ CONFIG_SPI_TEGRA114=y
CONFIG_SPI_TEGRA20_SFLASH=y
CONFIG_SPI_TEGRA20_SLINK=y
CONFIG_PINCTRL_AS3722=y
+CONFIG_PINCTRL_APQ8064=y
+CONFIG_PINCTRL_SPMI_PMIC=y
+CONFIG_PINCTRL_SSBI_PMIC=y
CONFIG_PINCTRL_PALMAS=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_GENERIC_PLATFORM=y
@@ -244,6 +255,8 @@ CONFIG_MFD_BCM590XX=y
CONFIG_MFD_CROS_EC=y
CONFIG_MFD_CROS_EC_SPI=y
CONFIG_MFD_MAX8907=y
+CONFIG_MFD_PM8921_CORE=y
+CONFIG_MFD_QCOM_RPM=y
CONFIG_MFD_SEC_CORE=y
CONFIG_MFD_STMPE=y
CONFIG_MFD_PALMAS=y
@@ -257,6 +270,7 @@ CONFIG_REGULATOR_BCM590XX=y
CONFIG_REGULATOR_GPIO=y
CONFIG_REGULATOR_MAX8907=y
CONFIG_REGULATOR_PALMAS=y
+CONFIG_REGULATOR_QCOM_RPM=y
CONFIG_REGULATOR_S2MPS11=y
CONFIG_REGULATOR_S5M8767=y
CONFIG_REGULATOR_TPS51632=y
@@ -295,6 +309,7 @@ CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_MVEBU=y
CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_TEGRA=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_ISP1760_HCD=y
@@ -309,6 +324,7 @@ CONFIG_SAMSUNG_USB2PHY=y
CONFIG_SAMSUNG_USB3PHY=y
CONFIG_USB_GPIO_VBUS=y
CONFIG_USB_ISP1301=y
+CONFIG_USB_MSM_OTG=y
CONFIG_USB_MXS_PHY=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=16
@@ -364,6 +380,7 @@ CONFIG_IMX_SDMA=y
CONFIG_IMX_DMA=y
CONFIG_MXS_DMA=y
CONFIG_DMA_OMAP=y
+CONFIG_QCOM_BAM_DMA=y
CONFIG_STAGING=y
CONFIG_SENSORS_ISL29018=y
CONFIG_SENSORS_ISL29028=y
@@ -373,6 +390,8 @@ CONFIG_SERIO_NVEC_PS2=y
CONFIG_NVEC_POWER=y
CONFIG_QCOM_GSBI=y
CONFIG_COMMON_CLK_QCOM=y
+CONFIG_APQ_GCC_8084=y
+CONFIG_APQ_MMCC_8084=y
CONFIG_MSM_GCC_8660=y
CONFIG_MSM_MMCC_8960=y
CONFIG_MSM_MMCC_8974=y
@@ -388,6 +407,7 @@ CONFIG_OMAP_USB2=y
CONFIG_TI_PIPE3=y
CONFIG_PHY_MIPHY365X=y
CONFIG_PHY_SUN4I_USB=y
+CONFIG_PHY_QCOM_APQ8064_SATA=y
CONFIG_EXT4_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index fd43f7f55b70..79ecb4f34ffb 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -472,7 +472,6 @@ static inline void __sync_cache_range_r(volatile void *p, size_t size)
"mcr p15, 0, r0, c1, c0, 0 @ set SCTLR \n\t" \
"isb \n\t" \
"bl v7_flush_dcache_"__stringify(level)" \n\t" \
- "clrex \n\t" \
"mrc p15, 0, r0, c1, c0, 1 @ get ACTLR \n\t" \
"bic r0, r0, #(1 << 6) @ disable local coherency \n\t" \
"mcr p15, 0, r0, c1, c0, 1 @ set ACTLR \n\t" \
diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h
index 963a2515906d..819777d0e91f 100644
--- a/arch/arm/include/asm/cputype.h
+++ b/arch/arm/include/asm/cputype.h
@@ -74,6 +74,7 @@
#define ARM_CPU_PART_CORTEX_A12 0x4100c0d0
#define ARM_CPU_PART_CORTEX_A17 0x4100c0e0
#define ARM_CPU_PART_CORTEX_A15 0x4100c0f0
+#define ARM_CPU_PART_MASK 0xff00fff0
#define ARM_CPU_XSCALE_ARCH_MASK 0xe000
#define ARM_CPU_XSCALE_ARCH_V1 0x2000
@@ -179,7 +180,7 @@ static inline unsigned int __attribute_const__ read_cpuid_implementor(void)
*/
static inline unsigned int __attribute_const__ read_cpuid_part(void)
{
- return read_cpuid_id() & 0xff00fff0;
+ return read_cpuid_id() & ARM_CPU_PART_MASK;
}
static inline unsigned int __attribute_const__ __deprecated read_cpuid_part_number(void)
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h
index f4b46d39b9cf..afb9cafd3786 100644
--- a/arch/arm/include/asm/elf.h
+++ b/arch/arm/include/asm/elf.h
@@ -50,6 +50,7 @@ typedef struct user_fp elf_fpregset_t;
#define R_ARM_ABS32 2
#define R_ARM_CALL 28
#define R_ARM_JUMP24 29
+#define R_ARM_TARGET1 38
#define R_ARM_V4BX 40
#define R_ARM_PREL31 42
#define R_ARM_MOVW_ABS_NC 43
diff --git a/arch/arm/include/asm/fiq_debugger.h b/arch/arm/include/asm/fiq_debugger.h
new file mode 100644
index 000000000000..4d274883ba6a
--- /dev/null
+++ b/arch/arm/include/asm/fiq_debugger.h
@@ -0,0 +1,64 @@
+/*
+ * arch/arm/include/asm/fiq_debugger.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Colin Cross <ccross@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_
+#define _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_
+
+#include <linux/serial_core.h>
+
+#define FIQ_DEBUGGER_NO_CHAR NO_POLL_CHAR
+#define FIQ_DEBUGGER_BREAK 0x00ff0100
+
+#define FIQ_DEBUGGER_FIQ_IRQ_NAME "fiq"
+#define FIQ_DEBUGGER_SIGNAL_IRQ_NAME "signal"
+#define FIQ_DEBUGGER_WAKEUP_IRQ_NAME "wakeup"
+
+/**
+ * struct fiq_debugger_pdata - fiq debugger platform data
+ * @uart_resume: used to restore uart state right before enabling
+ * the fiq.
+ * @uart_enable: Do the work necessary to communicate with the uart
+ * hw (enable clocks, etc.). This must be ref-counted.
+ * @uart_disable: Do the work necessary to disable the uart hw
+ * (disable clocks, etc.). This must be ref-counted.
+ * @uart_dev_suspend: called during PM suspend, generally not needed
+ * for real fiq mode debugger.
+ * @uart_dev_resume: called during PM resume, generally not needed
+ * for real fiq mode debugger.
+ */
+struct fiq_debugger_pdata {
+ int (*uart_init)(struct platform_device *pdev);
+ void (*uart_free)(struct platform_device *pdev);
+ int (*uart_resume)(struct platform_device *pdev);
+ int (*uart_getc)(struct platform_device *pdev);
+ void (*uart_putc)(struct platform_device *pdev, unsigned int c);
+ void (*uart_flush)(struct platform_device *pdev);
+ void (*uart_enable)(struct platform_device *pdev);
+ void (*uart_disable)(struct platform_device *pdev);
+
+ int (*uart_dev_suspend)(struct platform_device *pdev);
+ int (*uart_dev_resume)(struct platform_device *pdev);
+
+ void (*fiq_enable)(struct platform_device *pdev, unsigned int fiq,
+ bool enable);
+ void (*fiq_ack)(struct platform_device *pdev, unsigned int fiq);
+
+ void (*force_irq)(struct platform_device *pdev, unsigned int irq);
+ void (*force_irq_ack)(struct platform_device *pdev, unsigned int irq);
+};
+
+#endif
diff --git a/arch/arm/include/asm/fiq_glue.h b/arch/arm/include/asm/fiq_glue.h
new file mode 100644
index 000000000000..a9e244f9f197
--- /dev/null
+++ b/arch/arm/include/asm/fiq_glue.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __ASM_FIQ_GLUE_H
+#define __ASM_FIQ_GLUE_H
+
+struct fiq_glue_handler {
+ void (*fiq)(struct fiq_glue_handler *h, void *regs, void *svc_sp);
+ void (*resume)(struct fiq_glue_handler *h);
+};
+typedef void (*fiq_return_handler_t)(void);
+
+int fiq_glue_register_handler(struct fiq_glue_handler *handler);
+int fiq_glue_set_return_handler(fiq_return_handler_t fiq_return);
+int fiq_glue_clear_return_handler(fiq_return_handler_t fiq_return);
+
+#ifdef CONFIG_FIQ_GLUE
+void fiq_glue_resume(void);
+#else
+static inline void fiq_glue_resume(void) {}
+#endif
+
+#endif
diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h
index fe3ea776dc34..5df33e30ae1b 100644
--- a/arch/arm/include/asm/hardirq.h
+++ b/arch/arm/include/asm/hardirq.h
@@ -5,7 +5,7 @@
#include <linux/threads.h>
#include <asm/irq.h>
-#define NR_IPI 8
+#define NR_IPI 9
typedef struct {
unsigned int __softirq_pending;
diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h
index 3a5ec1c25659..2887342297e6 100644
--- a/arch/arm/include/asm/hardware/cache-l2x0.h
+++ b/arch/arm/include/asm/hardware/cache-l2x0.h
@@ -66,6 +66,7 @@
#define L310_STNDBY_MODE_EN (1 << 0)
/* Registers shifts and masks */
+#define L2X0_CACHE_ID_REV_MASK (0x3f)
#define L2X0_CACHE_ID_PART_MASK (0xf << 6)
#define L2X0_CACHE_ID_PART_L210 (1 << 6)
#define L2X0_CACHE_ID_PART_L220 (2 << 6)
@@ -146,6 +147,8 @@
#define L2X0_WAY_SIZE_SHIFT 3
+#define REV_PL310_R2P0 4
+
#ifndef __ASSEMBLY__
extern void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask);
#if defined(CONFIG_CACHE_L2X0) && defined(CONFIG_OF)
diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h
index ad774f37c47c..b3507650b41c 100644
--- a/arch/arm/include/asm/hardware/coresight.h
+++ b/arch/arm/include/asm/hardware/coresight.h
@@ -17,15 +17,23 @@
#define TRACER_ACCESSED_BIT 0
#define TRACER_RUNNING_BIT 1
#define TRACER_CYCLE_ACC_BIT 2
+#define TRACER_TRACE_DATA_BIT 3
+#define TRACER_TIMESTAMP_BIT 4
+#define TRACER_BRANCHOUTPUT_BIT 5
+#define TRACER_RETURN_STACK_BIT 6
#define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT)
#define TRACER_RUNNING BIT(TRACER_RUNNING_BIT)
#define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT)
+#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT)
+#define TRACER_TIMESTAMP BIT(TRACER_TIMESTAMP_BIT)
+#define TRACER_BRANCHOUTPUT BIT(TRACER_BRANCHOUTPUT_BIT)
+#define TRACER_RETURN_STACK BIT(TRACER_RETURN_STACK_BIT)
#define TRACER_TIMEOUT 10000
-#define etm_writel(t, v, x) \
- (writel_relaxed((v), (t)->etm_regs + (x)))
-#define etm_readl(t, x) (readl_relaxed((t)->etm_regs + (x)))
+#define etm_writel(t, id, v, x) \
+ (writel_relaxed((v), (t)->etm_regs[(id)] + (x)))
+#define etm_readl(t, id, x) (readl_relaxed((t)->etm_regs[(id)] + (x)))
/* CoreSight Management Registers */
#define CSMR_LOCKACCESS 0xfb0
@@ -43,7 +51,7 @@
#define ETMCTRL_POWERDOWN 1
#define ETMCTRL_PROGRAM (1 << 10)
#define ETMCTRL_PORTSEL (1 << 11)
-#define ETMCTRL_DO_CONTEXTID (3 << 14)
+#define ETMCTRL_CONTEXTIDSIZE(x) (((x) & 3) << 14)
#define ETMCTRL_PORTMASK1 (7 << 4)
#define ETMCTRL_PORTMASK2 (1 << 21)
#define ETMCTRL_PORTMASK (ETMCTRL_PORTMASK1 | ETMCTRL_PORTMASK2)
@@ -55,9 +63,12 @@
#define ETMCTRL_DATA_DO_BOTH (ETMCTRL_DATA_DO_DATA | ETMCTRL_DATA_DO_ADDR)
#define ETMCTRL_BRANCH_OUTPUT (1 << 8)
#define ETMCTRL_CYCLEACCURATE (1 << 12)
+#define ETMCTRL_TIMESTAMP_EN (1 << 28)
+#define ETMCTRL_RETURN_STACK_EN (1 << 29)
/* ETM configuration code register */
#define ETMR_CONFCODE (0x04)
+#define ETMCCR_ETMIDR_PRESENT BIT(31)
/* ETM trace start/stop resource control register */
#define ETMR_TRACESSCTRL (0x18)
@@ -113,10 +124,25 @@
#define ETMR_TRACEENCTRL 0x24
#define ETMTE_INCLEXCL BIT(24)
#define ETMR_TRACEENEVT 0x20
-#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \
- ETMCTRL_DATA_DO_ADDR | \
- ETMCTRL_BRANCH_OUTPUT | \
- ETMCTRL_DO_CONTEXTID)
+
+#define ETMR_VIEWDATAEVT 0x30
+#define ETMR_VIEWDATACTRL1 0x34
+#define ETMR_VIEWDATACTRL2 0x38
+#define ETMR_VIEWDATACTRL3 0x3c
+#define ETMVDC3_EXCLONLY BIT(16)
+
+#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT)
+
+#define ETMR_ID 0x1e4
+#define ETMIDR_VERSION(x) (((x) >> 4) & 0xff)
+#define ETMIDR_VERSION_3_1 0x21
+#define ETMIDR_VERSION_PFT_1_0 0x30
+
+#define ETMR_CCE 0x1e8
+#define ETMCCER_RETURN_STACK_IMPLEMENTED BIT(23)
+#define ETMCCER_TIMESTAMPING_IMPLEMENTED BIT(22)
+
+#define ETMR_TRACEIDR 0x200
/* ETM management registers, "ETM Architecture", 3.5.24 */
#define ETMMR_OSLAR 0x300
@@ -140,14 +166,16 @@
#define ETBFF_TRIGIN BIT(8)
#define ETBFF_TRIGEVT BIT(9)
#define ETBFF_TRIGFL BIT(10)
+#define ETBFF_STOPFL BIT(12)
#define etb_writel(t, v, x) \
(writel_relaxed((v), (t)->etb_regs + (x)))
#define etb_readl(t, x) (readl_relaxed((t)->etb_regs + (x)))
-#define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0)
-#define etm_unlock(t) \
- do { etm_writel((t), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0)
+#define etm_lock(t, id) \
+ do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0)
+#define etm_unlock(t, id) \
+ do { etm_writel((t), (id), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0)
#define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0)
#define etb_unlock(t) \
diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h
index 53c15dec7af6..b995e6a18a3b 100644
--- a/arch/arm/include/asm/irq.h
+++ b/arch/arm/include/asm/irq.h
@@ -35,6 +35,9 @@ extern void (*handle_arch_irq)(struct pt_regs *);
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
#endif
+void arch_trigger_all_cpu_backtrace(bool);
+#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace
+
#endif
#endif
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
new file mode 100644
index 000000000000..bca864ac945f
--- /dev/null
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm/include/asm/mach/mmc.h
+ */
+#ifndef ASMARM_MACH_MMC_H
+#define ASMARM_MACH_MMC_H
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+
+struct embedded_sdio_data {
+ struct sdio_cis cis;
+ struct sdio_cccr cccr;
+ struct sdio_embedded_func *funcs;
+ int num_funcs;
+};
+
+struct mmc_platform_data {
+ unsigned int ocr_mask; /* available voltages */
+ int built_in; /* built-in device flag */
+ int card_present; /* card detect state */
+ u32 (*translate_vdd)(struct device *, unsigned int);
+ unsigned int (*status)(struct device *);
+ struct embedded_sdio_data *embedded_sdio;
+ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
+};
+
+#endif
diff --git a/arch/arm/include/asm/mcpm.h b/arch/arm/include/asm/mcpm.h
index 57ff7f2a3084..d428e386c88e 100644
--- a/arch/arm/include/asm/mcpm.h
+++ b/arch/arm/include/asm/mcpm.h
@@ -20,7 +20,12 @@
* to consider dynamic allocation.
*/
#define MAX_CPUS_PER_CLUSTER 4
+
+#ifdef CONFIG_MCPM_QUAD_CLUSTER
+#define MAX_NR_CLUSTERS 4
+#else
#define MAX_NR_CLUSTERS 2
+#endif
#ifndef __ASSEMBLY__
diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
index 0b648c541293..bd810f34aba4 100644
--- a/arch/arm/include/asm/pmu.h
+++ b/arch/arm/include/asm/pmu.h
@@ -81,9 +81,19 @@ struct pmu_hw_events {
raw_spinlock_t pmu_lock;
};
+struct cpupmu_regs {
+ u32 pmc;
+ u32 pmcntenset;
+ u32 pmuseren;
+ u32 pmintenset;
+ u32 pmxevttype[8];
+ u32 pmxevtcnt[8];
+};
+
struct arm_pmu {
struct pmu pmu;
cpumask_t active_irqs;
+ cpumask_t valid_cpus;
char *name;
irqreturn_t (*handle_irq)(int irq_num, void *dev);
void (*enable)(struct perf_event *event);
@@ -102,6 +112,8 @@ struct arm_pmu {
int (*request_irq)(struct arm_pmu *, irq_handler_t handler);
void (*free_irq)(struct arm_pmu *);
int (*map_event)(struct perf_event *event);
+ void (*save_regs)(struct arm_pmu *, struct cpupmu_regs *);
+ void (*restore_regs)(struct arm_pmu *, struct cpupmu_regs *);
int num_events;
atomic_t active_events;
struct mutex reserve_mutex;
diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
index 2ec765c39ab4..99b60fc42c3e 100644
--- a/arch/arm/include/asm/smp.h
+++ b/arch/arm/include/asm/smp.h
@@ -87,6 +87,8 @@ extern void arch_send_wakeup_ipi_mask(const struct cpumask *mask);
extern int register_ipi_completion(struct completion *completion, int cpu);
+extern void smp_send_all_cpu_backtrace(bool);
+
struct smp_operations {
#ifdef CONFIG_SMP
/*
diff --git a/arch/arm/include/asm/smp_plat.h b/arch/arm/include/asm/smp_plat.h
index a252c0bfacf5..0ad7d490ee6f 100644
--- a/arch/arm/include/asm/smp_plat.h
+++ b/arch/arm/include/asm/smp_plat.h
@@ -8,6 +8,7 @@
#include <linux/cpumask.h>
#include <linux/err.h>
+#include <asm/cpu.h>
#include <asm/cputype.h>
/*
@@ -25,6 +26,20 @@ static inline bool is_smp(void)
#endif
}
+/**
+ * smp_cpuid_part() - return part id for a given cpu
+ * @cpu: logical cpu id.
+ *
+ * Return: part id of logical cpu passed as argument.
+ */
+static inline unsigned int smp_cpuid_part(int cpu)
+{
+ struct cpuinfo_arm *cpu_info = &per_cpu(cpu_data, cpu);
+
+ return is_smp() ? cpu_info->cpuid & ARM_CPU_PART_MASK :
+ read_cpuid_part();
+}
+
/* all SMP configurations have the extended CPUID registers */
#ifndef CONFIG_MMU
#define tlb_ops_need_broadcast() 0
diff --git a/arch/arm/include/asm/topology.h b/arch/arm/include/asm/topology.h
index 2fe85fff5cca..4fa2856974e9 100644
--- a/arch/arm/include/asm/topology.h
+++ b/arch/arm/include/asm/topology.h
@@ -23,11 +23,14 @@ extern struct cputopo_arm cpu_topology[NR_CPUS];
void init_cpu_topology(void);
void store_cpu_topology(unsigned int cpuid);
const struct cpumask *cpu_coregroup_mask(int cpu);
+int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask);
#else
static inline void init_cpu_topology(void) { }
static inline void store_cpu_topology(unsigned int cpuid) { }
+static inline int cluster_to_logical_mask(unsigned int socket_id,
+ cpumask_t *cluster_mask) { return -EINVAL; }
#endif
diff --git a/arch/arm/include/debug/samsung.S b/arch/arm/include/debug/samsung.S
index 8d8d922e5e44..dc62d4ae04d0 100644
--- a/arch/arm/include/debug/samsung.S
+++ b/arch/arm/include/debug/samsung.S
@@ -9,17 +9,20 @@
* published by the Free Software Foundation.
*/
+#include <asm/assembler.h>
#include <linux/serial_s3c.h>
/* The S5PV210/S5PC110 implementations are as belows. */
.macro fifo_level_s5pv210 rd, rx
ldr \rd, [\rx, # S3C2410_UFSTAT]
+ ARM_BE8(rev \rd, \rd)
and \rd, \rd, #S5PV210_UFSTAT_TXMASK
.endm
.macro fifo_full_s5pv210 rd, rx
ldr \rd, [\rx, # S3C2410_UFSTAT]
+ ARM_BE8(rev \rd, \rd)
tst \rd, #S5PV210_UFSTAT_TXFULL
.endm
@@ -28,6 +31,7 @@
.macro fifo_level_s3c2440 rd, rx
ldr \rd, [\rx, # S3C2410_UFSTAT]
+ ARM_BE8(rev \rd, \rd)
and \rd, \rd, #S3C2440_UFSTAT_TXMASK
.endm
@@ -37,6 +41,7 @@
.macro fifo_full_s3c2440 rd, rx
ldr \rd, [\rx, # S3C2410_UFSTAT]
+ ARM_BE8(rev \rd, \rd)
tst \rd, #S3C2440_UFSTAT_TXFULL
.endm
@@ -50,6 +55,7 @@
.macro busyuart, rd, rx
ldr \rd, [\rx, # S3C2410_UFCON]
+ ARM_BE8(rev \rd, \rd)
tst \rd, #S3C2410_UFCON_FIFOMODE @ fifo enabled?
beq 1001f @
@ FIFO enabled...
@@ -61,6 +67,7 @@
1001:
@ busy waiting for non fifo
ldr \rd, [\rx, # S3C2410_UTRSTAT]
+ ARM_BE8(rev \rd, \rd)
tst \rd, #S3C2410_UTRSTAT_TXFE
beq 1001b
@@ -69,6 +76,7 @@
.macro waituart,rd,rx
ldr \rd, [\rx, # S3C2410_UFCON]
+ ARM_BE8(rev \rd, \rd)
tst \rd, #S3C2410_UFCON_FIFOMODE @ fifo enabled?
beq 1001f @
@ FIFO enabled...
@@ -80,6 +88,7 @@
1001:
@ idle waiting for non fifo
ldr \rd, [\rx, # S3C2410_UTRSTAT]
+ ARM_BE8(rev \rd, \rd)
tst \rd, #S3C2410_UTRSTAT_TXFE
beq 1001b
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
index 8db307d0954b..2fdf8679b46e 100644
--- a/arch/arm/kernel/entry-header.S
+++ b/arch/arm/kernel/entry-header.S
@@ -208,26 +208,21 @@
#endif
.endif
msr spsr_cxsf, \rpsr
-#if defined(CONFIG_CPU_V6)
- ldr r0, [sp]
- strex r1, r2, [sp] @ clear the exclusive monitor
- ldmib sp, {r1 - pc}^ @ load r1 - pc, cpsr
-#elif defined(CONFIG_CPU_32v6K)
- clrex @ clear the exclusive monitor
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
-#else
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_32v6K)
+ @ We must avoid clrex due to Cortex-A15 erratum #830321
+ sub r0, sp, #4 @ uninhabited address
+ strex r1, r2, [r0] @ clear the exclusive monitor
#endif
+ ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
.endm
.macro restore_user_regs, fast = 0, offset = 0
ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr
ldr lr, [sp, #\offset + S_PC]! @ get pc
msr spsr_cxsf, r1 @ save in spsr_svc
-#if defined(CONFIG_CPU_V6)
+#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_32v6K)
+ @ We must avoid clrex due to Cortex-A15 erratum #830321
strex r1, r2, [sp] @ clear the exclusive monitor
-#elif defined(CONFIG_CPU_32v6K)
- clrex @ clear the exclusive monitor
#endif
.if \fast
ldmdb sp, {r1 - lr}^ @ get calling r1 - lr
@@ -261,7 +256,10 @@
.endif
ldr lr, [sp, #S_SP] @ top of the stack
ldrd r0, r1, [sp, #S_LR] @ calling lr and pc
- clrex @ clear the exclusive monitor
+
+ @ We must avoid clrex due to Cortex-A15 erratum #830321
+ strex r2, r1, [sp, #S_LR] @ clear the exclusive monitor
+
stmdb lr!, {r0, r1, \rpsr} @ calling lr and rfe context
ldmia sp, {r0 - r12}
mov sp, lr
@@ -282,13 +280,16 @@
.endm
#else /* ifdef CONFIG_CPU_V7M */
.macro restore_user_regs, fast = 0, offset = 0
- clrex @ clear the exclusive monitor
mov r2, sp
load_user_sp_lr r2, r3, \offset + S_SP @ calling sp, lr
ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr
ldr lr, [sp, #\offset + S_PC] @ get pc
add sp, sp, #\offset + S_SP
msr spsr_cxsf, r1 @ save in spsr_svc
+
+ @ We must avoid clrex due to Cortex-A15 erratum #830321
+ strex r1, r2, [sp] @ clear the exclusive monitor
+
.if \fast
ldmdb sp, {r1 - r12} @ get calling r1 - r12
.else
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 131a6ab5f355..a634e0ef6d1f 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/clk.h>
@@ -37,26 +38,37 @@ MODULE_AUTHOR("Alexander Shishkin");
struct tracectx {
unsigned int etb_bufsz;
void __iomem *etb_regs;
- void __iomem *etm_regs;
+ void __iomem **etm_regs;
+ int etm_regs_count;
unsigned long flags;
int ncmppairs;
int etm_portsz;
+ int etm_contextid_size;
+ u32 etb_fc;
+ unsigned long range_start;
+ unsigned long range_end;
+ unsigned long data_range_start;
+ unsigned long data_range_end;
+ bool dump_initial_etb;
struct device *dev;
struct clk *emu_clk;
struct mutex mutex;
};
-static struct tracectx tracer;
+static struct tracectx tracer = {
+ .range_start = (unsigned long)_stext,
+ .range_end = (unsigned long)_etext,
+};
static inline bool trace_isrunning(struct tracectx *t)
{
return !!(t->flags & TRACER_RUNNING);
}
-static int etm_setup_address_range(struct tracectx *t, int n,
+static int etm_setup_address_range(struct tracectx *t, int id, int n,
unsigned long start, unsigned long end, int exclude, int data)
{
- u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \
+ u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY |
ETMAAT_NOVALCMP;
if (n < 1 || n > t->ncmppairs)
@@ -72,95 +84,185 @@ static int etm_setup_address_range(struct tracectx *t, int n,
flags |= ETMAAT_IEXEC;
/* first comparator for the range */
- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2));
- etm_writel(t, start, ETMR_COMP_VAL(n * 2));
+ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2));
+ etm_writel(t, id, start, ETMR_COMP_VAL(n * 2));
/* second comparator is right next to it */
- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
- etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1));
-
- flags = exclude ? ETMTE_INCLEXCL : 0;
- etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL);
+ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
+ etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1));
+
+ if (data) {
+ flags = exclude ? ETMVDC3_EXCLONLY : 0;
+ if (exclude)
+ n += 8;
+ etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3);
+ } else {
+ flags = exclude ? ETMTE_INCLEXCL : 0;
+ etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL);
+ }
return 0;
}
-static int trace_start(struct tracectx *t)
+static int trace_start_etm(struct tracectx *t, int id)
{
u32 v;
unsigned long timeout = TRACER_TIMEOUT;
- etb_unlock(t);
-
- etb_writel(t, 0, ETBR_FORMATTERCTRL);
- etb_writel(t, 1, ETBR_CTRL);
-
- etb_lock(t);
-
- /* configure etm */
v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz);
+ v |= ETMCTRL_CONTEXTIDSIZE(t->etm_contextid_size);
if (t->flags & TRACER_CYCLE_ACC)
v |= ETMCTRL_CYCLEACCURATE;
- etm_unlock(t);
+ if (t->flags & TRACER_BRANCHOUTPUT)
+ v |= ETMCTRL_BRANCH_OUTPUT;
+
+ if (t->flags & TRACER_TRACE_DATA)
+ v |= ETMCTRL_DATA_DO_ADDR;
+
+ if (t->flags & TRACER_TIMESTAMP)
+ v |= ETMCTRL_TIMESTAMP_EN;
+
+ if (t->flags & TRACER_RETURN_STACK)
+ v |= ETMCTRL_RETURN_STACK_EN;
- etm_writel(t, v, ETMR_CTRL);
+ etm_unlock(t, id);
- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
+ etm_writel(t, id, v, ETMR_CTRL);
+
+ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
- etm_lock(t);
+ etm_lock(t, id);
return -EFAULT;
}
- etm_setup_address_range(t, 1, (unsigned long)_stext,
- (unsigned long)_etext, 0, 0);
- etm_writel(t, 0, ETMR_TRACEENCTRL2);
- etm_writel(t, 0, ETMR_TRACESSCTRL);
- etm_writel(t, 0x6f, ETMR_TRACEENEVT);
+ if (t->range_start || t->range_end)
+ etm_setup_address_range(t, id, 1,
+ t->range_start, t->range_end, 0, 0);
+ else
+ etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
+
+ etm_writel(t, id, 0, ETMR_TRACEENCTRL2);
+ etm_writel(t, id, 0, ETMR_TRACESSCTRL);
+ etm_writel(t, id, 0x6f, ETMR_TRACEENEVT);
+
+ etm_writel(t, id, 0, ETMR_VIEWDATACTRL1);
+ etm_writel(t, id, 0, ETMR_VIEWDATACTRL2);
+
+ if (t->data_range_start || t->data_range_end)
+ etm_setup_address_range(t, id, 2, t->data_range_start,
+ t->data_range_end, 0, 1);
+ else
+ etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3);
+
+ etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT);
v &= ~ETMCTRL_PROGRAM;
v |= ETMCTRL_PORTSEL;
- etm_writel(t, v, ETMR_CTRL);
+ etm_writel(t, id, v, ETMR_CTRL);
timeout = TRACER_TIMEOUT;
- while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout)
+ while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n");
- etm_lock(t);
+ etm_lock(t, id);
return -EFAULT;
}
- etm_lock(t);
+ etm_lock(t, id);
+ return 0;
+}
+
+static int trace_start(struct tracectx *t)
+{
+ int ret;
+ int id;
+ u32 etb_fc = t->etb_fc;
+
+ etb_unlock(t);
+
+ t->dump_initial_etb = false;
+ etb_writel(t, 0, ETBR_WRITEADDR);
+ etb_writel(t, etb_fc, ETBR_FORMATTERCTRL);
+ etb_writel(t, 1, ETBR_CTRL);
+
+ etb_lock(t);
+
+ /* configure etm(s) */
+ for (id = 0; id < t->etm_regs_count; id++) {
+ ret = trace_start_etm(t, id);
+ if (ret)
+ return ret;
+ }
t->flags |= TRACER_RUNNING;
return 0;
}
-static int trace_stop(struct tracectx *t)
+static int trace_stop_etm(struct tracectx *t, int id)
{
unsigned long timeout = TRACER_TIMEOUT;
- etm_unlock(t);
+ etm_unlock(t, id);
- etm_writel(t, 0x440, ETMR_CTRL);
- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
+ etm_writel(t, id, 0x440, ETMR_CTRL);
+ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
- dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
- etm_lock(t);
+ dev_err(t->dev,
+ "etm%d: Waiting for progbit to assert timed out\n",
+ id);
+ etm_lock(t, id);
return -EFAULT;
}
- etm_lock(t);
+ etm_lock(t, id);
+ return 0;
+}
+
+static int trace_power_down_etm(struct tracectx *t, int id)
+{
+ unsigned long timeout = TRACER_TIMEOUT;
+ etm_unlock(t, id);
+ while (!(etm_readl(t, id, ETMR_STATUS) & ETMST_PROGBIT) && --timeout)
+ ;
+ if (!timeout) {
+ dev_err(t->dev, "etm%d: Waiting for status progbit to assert timed out\n",
+ id);
+ etm_lock(t, id);
+ return -EFAULT;
+ }
+
+ etm_writel(t, id, 0x441, ETMR_CTRL);
+
+ etm_lock(t, id);
+ return 0;
+}
+
+static int trace_stop(struct tracectx *t)
+{
+ int id;
+ unsigned long timeout = TRACER_TIMEOUT;
+ u32 etb_fc = t->etb_fc;
+
+ for (id = 0; id < t->etm_regs_count; id++)
+ trace_stop_etm(t, id);
+
+ for (id = 0; id < t->etm_regs_count; id++)
+ trace_power_down_etm(t, id);
etb_unlock(t);
- etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL);
+ if (etb_fc) {
+ etb_fc |= ETBFF_STOPFL;
+ etb_writel(t, t->etb_fc, ETBR_FORMATTERCTRL);
+ }
+ etb_writel(t, etb_fc | ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL);
timeout = TRACER_TIMEOUT;
while (etb_readl(t, ETBR_FORMATTERCTRL) &
@@ -185,24 +287,15 @@ static int trace_stop(struct tracectx *t)
static int etb_getdatalen(struct tracectx *t)
{
u32 v;
- int rp, wp;
+ int wp;
v = etb_readl(t, ETBR_STATUS);
if (v & 1)
return t->etb_bufsz;
- rp = etb_readl(t, ETBR_READADDR);
wp = etb_readl(t, ETBR_WRITEADDR);
-
- if (rp > wp) {
- etb_writel(t, 0, ETBR_READADDR);
- etb_writel(t, 0, ETBR_WRITEADDR);
-
- return 0;
- }
-
- return wp - rp;
+ return wp;
}
/* sysrq+v will always stop the running trace and leave it at that */
@@ -235,21 +328,18 @@ static void etm_dump(void)
printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM)));
printk(KERN_INFO "\n--- ETB buffer end ---\n");
- /* deassert the overflow bit */
- etb_writel(t, 1, ETBR_CTRL);
- etb_writel(t, 0, ETBR_CTRL);
-
- etb_writel(t, 0, ETBR_TRIGGERCOUNT);
- etb_writel(t, 0, ETBR_READADDR);
- etb_writel(t, 0, ETBR_WRITEADDR);
-
etb_lock(t);
}
static void sysrq_etm_dump(int key)
{
+ if (!mutex_trylock(&tracer.mutex)) {
+ printk(KERN_INFO "Tracing hardware busy\n");
+ return;
+ }
dev_dbg(tracer.dev, "Dumping ETB buffer\n");
etm_dump();
+ mutex_unlock(&tracer.mutex);
}
static struct sysrq_key_op sysrq_etm_op = {
@@ -276,6 +366,10 @@ static ssize_t etb_read(struct file *file, char __user *data,
struct tracectx *t = file->private_data;
u32 first = 0;
u32 *buf;
+ int wpos;
+ int skip;
+ long wlength;
+ loff_t pos = *ppos;
mutex_lock(&t->mutex);
@@ -287,31 +381,39 @@ static ssize_t etb_read(struct file *file, char __user *data,
etb_unlock(t);
total = etb_getdatalen(t);
+ if (total == 0 && t->dump_initial_etb)
+ total = t->etb_bufsz;
if (total == t->etb_bufsz)
first = etb_readl(t, ETBR_WRITEADDR);
+ if (pos > total * 4) {
+ skip = 0;
+ wpos = total;
+ } else {
+ skip = (int)pos % 4;
+ wpos = (int)pos / 4;
+ }
+ total -= wpos;
+ first = (first + wpos) % t->etb_bufsz;
+
etb_writel(t, first, ETBR_READADDR);
- length = min(total * 4, (int)len);
- buf = vmalloc(length);
+ wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4));
+ length = min(total * 4 - skip, (int)len);
+ buf = vmalloc(wlength * 4);
- dev_dbg(t->dev, "ETB buffer length: %d\n", total);
+ dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n",
+ length, pos, wlength, first);
+ dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos);
dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS));
- for (i = 0; i < length / 4; i++)
+ for (i = 0; i < wlength; i++)
buf[i] = etb_readl(t, ETBR_READMEM);
- /* the only way to deassert overflow bit in ETB status is this */
- etb_writel(t, 1, ETBR_CTRL);
- etb_writel(t, 0, ETBR_CTRL);
-
- etb_writel(t, 0, ETBR_WRITEADDR);
- etb_writel(t, 0, ETBR_READADDR);
- etb_writel(t, 0, ETBR_TRIGGERCOUNT);
-
etb_lock(t);
- length -= copy_to_user(data, buf, length);
+ length -= copy_to_user(data, (u8 *)buf + skip, length);
vfree(buf);
+ *ppos = pos + length;
out:
mutex_unlock(&t->mutex);
@@ -348,28 +450,17 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id)
if (ret)
goto out;
+ mutex_lock(&t->mutex);
t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res));
if (!t->etb_regs) {
ret = -ENOMEM;
goto out_release;
}
+ t->dev = &dev->dev;
+ t->dump_initial_etb = true;
amba_set_drvdata(dev, t);
- etb_miscdev.parent = &dev->dev;
-
- ret = misc_register(&etb_miscdev);
- if (ret)
- goto out_unmap;
-
- t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
- if (IS_ERR(t->emu_clk)) {
- dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
- return -EFAULT;
- }
-
- clk_enable(t->emu_clk);
-
etb_unlock(t);
t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz);
@@ -378,6 +469,20 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id)
etb_writel(t, 0, ETBR_CTRL);
etb_writel(t, 0x1000, ETBR_FORMATTERCTRL);
etb_lock(t);
+ mutex_unlock(&t->mutex);
+
+ etb_miscdev.parent = &dev->dev;
+
+ ret = misc_register(&etb_miscdev);
+ if (ret)
+ goto out_unmap;
+
+ /* Get optional clock. Currently used to select clock source on omap3 */
+ t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
+ if (IS_ERR(t->emu_clk))
+ dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
+ else
+ clk_enable(t->emu_clk);
dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n");
@@ -385,9 +490,12 @@ out:
return ret;
out_unmap:
+ mutex_lock(&t->mutex);
iounmap(t->etb_regs);
+ t->etb_regs = NULL;
out_release:
+ mutex_unlock(&t->mutex);
amba_release_regions(dev);
return ret;
@@ -400,8 +508,10 @@ static int etb_remove(struct amba_device *dev)
iounmap(t->etb_regs);
t->etb_regs = NULL;
- clk_disable(t->emu_clk);
- clk_put(t->emu_clk);
+ if (!IS_ERR(t->emu_clk)) {
+ clk_disable(t->emu_clk);
+ clk_put(t->emu_clk);
+ }
amba_release_regions(dev);
@@ -445,7 +555,10 @@ static ssize_t trace_running_store(struct kobject *kobj,
return -EINVAL;
mutex_lock(&tracer.mutex);
- ret = value ? trace_start(&tracer) : trace_stop(&tracer);
+ if (!tracer.etb_regs)
+ ret = -ENODEV;
+ else
+ ret = value ? trace_start(&tracer) : trace_stop(&tracer);
mutex_unlock(&tracer.mutex);
return ret ? : n;
@@ -460,36 +573,50 @@ static ssize_t trace_info_show(struct kobject *kobj,
{
u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st;
int datalen;
+ int id;
+ int ret;
- etb_unlock(&tracer);
- datalen = etb_getdatalen(&tracer);
- etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
- etb_ra = etb_readl(&tracer, ETBR_READADDR);
- etb_st = etb_readl(&tracer, ETBR_STATUS);
- etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
- etb_lock(&tracer);
-
- etm_unlock(&tracer);
- etm_ctrl = etm_readl(&tracer, ETMR_CTRL);
- etm_st = etm_readl(&tracer, ETMR_STATUS);
- etm_lock(&tracer);
+ mutex_lock(&tracer.mutex);
+ if (tracer.etb_regs) {
+ etb_unlock(&tracer);
+ datalen = etb_getdatalen(&tracer);
+ etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
+ etb_ra = etb_readl(&tracer, ETBR_READADDR);
+ etb_st = etb_readl(&tracer, ETBR_STATUS);
+ etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
+ etb_lock(&tracer);
+ } else {
+ etb_wa = etb_ra = etb_st = etb_fc = ~0;
+ datalen = -1;
+ }
- return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
+ ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
"ETBR_WRITEADDR:\t%08x\n"
"ETBR_READADDR:\t%08x\n"
"ETBR_STATUS:\t%08x\n"
- "ETBR_FORMATTERCTRL:\t%08x\n"
- "ETMR_CTRL:\t%08x\n"
- "ETMR_STATUS:\t%08x\n",
+ "ETBR_FORMATTERCTRL:\t%08x\n",
datalen,
tracer.ncmppairs,
etb_wa,
etb_ra,
etb_st,
- etb_fc,
+ etb_fc
+ );
+
+ for (id = 0; id < tracer.etm_regs_count; id++) {
+ etm_unlock(&tracer, id);
+ etm_ctrl = etm_readl(&tracer, id, ETMR_CTRL);
+ etm_st = etm_readl(&tracer, id, ETMR_STATUS);
+ etm_lock(&tracer, id);
+ ret += sprintf(buf + ret, "ETMR_CTRL:\t%08x\n"
+ "ETMR_STATUS:\t%08x\n",
etm_ctrl,
etm_st
);
+ }
+ mutex_unlock(&tracer.mutex);
+
+ return ret;
}
static struct kobj_attribute trace_info_attr =
@@ -528,42 +655,260 @@ static ssize_t trace_mode_store(struct kobject *kobj,
static struct kobj_attribute trace_mode_attr =
__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
+static ssize_t trace_contextid_size_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ /* 0: No context id tracing, 1: One byte, 2: Two bytes, 3: Four bytes */
+ return sprintf(buf, "%d\n", (1 << tracer.etm_contextid_size) >> 1);
+}
+
+static ssize_t trace_contextid_size_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned int contextid_size;
+
+ if (sscanf(buf, "%u", &contextid_size) != 1)
+ return -EINVAL;
+
+ if (contextid_size == 3 || contextid_size > 4)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ tracer.etm_contextid_size = fls(contextid_size);
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+static struct kobj_attribute trace_contextid_size_attr =
+ __ATTR(trace_contextid_size, 0644,
+ trace_contextid_size_show, trace_contextid_size_store);
+
+static ssize_t trace_branch_output_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_BRANCHOUTPUT));
+}
+
+static ssize_t trace_branch_output_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned int branch_output;
+
+ if (sscanf(buf, "%u", &branch_output) != 1)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ if (branch_output) {
+ tracer.flags |= TRACER_BRANCHOUTPUT;
+ /* Branch broadcasting is incompatible with the return stack */
+ tracer.flags &= ~TRACER_RETURN_STACK;
+ } else {
+ tracer.flags &= ~TRACER_BRANCHOUTPUT;
+ }
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+static struct kobj_attribute trace_branch_output_attr =
+ __ATTR(trace_branch_output, 0644,
+ trace_branch_output_show, trace_branch_output_store);
+
+static ssize_t trace_return_stack_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_RETURN_STACK));
+}
+
+static ssize_t trace_return_stack_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned int return_stack;
+
+ if (sscanf(buf, "%u", &return_stack) != 1)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ if (return_stack) {
+ tracer.flags |= TRACER_RETURN_STACK;
+ /* Return stack is incompatible with branch broadcasting */
+ tracer.flags &= ~TRACER_BRANCHOUTPUT;
+ } else {
+ tracer.flags &= ~TRACER_RETURN_STACK;
+ }
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+static struct kobj_attribute trace_return_stack_attr =
+ __ATTR(trace_return_stack, 0644,
+ trace_return_stack_show, trace_return_stack_store);
+
+static ssize_t trace_timestamp_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_TIMESTAMP));
+}
+
+static ssize_t trace_timestamp_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned int timestamp;
+
+ if (sscanf(buf, "%u", &timestamp) != 1)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ if (timestamp)
+ tracer.flags |= TRACER_TIMESTAMP;
+ else
+ tracer.flags &= ~TRACER_TIMESTAMP;
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+static struct kobj_attribute trace_timestamp_attr =
+ __ATTR(trace_timestamp, 0644,
+ trace_timestamp_show, trace_timestamp_store);
+
+static ssize_t trace_range_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%08lx %08lx\n",
+ tracer.range_start, tracer.range_end);
+}
+
+static ssize_t trace_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long range_start, range_end;
+
+ if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ tracer.range_start = range_start;
+ tracer.range_end = range_end;
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+
+static struct kobj_attribute trace_range_attr =
+ __ATTR(trace_range, 0644, trace_range_show, trace_range_store);
+
+static ssize_t trace_data_range_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ unsigned long range_start;
+ u64 range_end;
+ mutex_lock(&tracer.mutex);
+ range_start = tracer.data_range_start;
+ range_end = tracer.data_range_end;
+ if (!range_end && (tracer.flags & TRACER_TRACE_DATA))
+ range_end = 0x100000000ULL;
+ mutex_unlock(&tracer.mutex);
+ return sprintf(buf, "%08lx %08llx\n", range_start, range_end);
+}
+
+static ssize_t trace_data_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long range_start;
+ u64 range_end;
+
+ if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ tracer.data_range_start = range_start;
+ tracer.data_range_end = (unsigned long)range_end;
+ if (range_end)
+ tracer.flags |= TRACER_TRACE_DATA;
+ else
+ tracer.flags &= ~TRACER_TRACE_DATA;
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+
+static struct kobj_attribute trace_data_range_attr =
+ __ATTR(trace_data_range, 0644,
+ trace_data_range_show, trace_data_range_store);
+
static int etm_probe(struct amba_device *dev, const struct amba_id *id)
{
struct tracectx *t = &tracer;
int ret = 0;
+ void __iomem **new_regs;
+ int new_count;
+ u32 etmccr;
+ u32 etmidr;
+ u32 etmccer = 0;
+ u8 etm_version = 0;
+
+ mutex_lock(&t->mutex);
+ new_count = t->etm_regs_count + 1;
+ new_regs = krealloc(t->etm_regs,
+ sizeof(t->etm_regs[0]) * new_count, GFP_KERNEL);
- if (t->etm_regs) {
- dev_dbg(&dev->dev, "ETM already initialized\n");
- ret = -EBUSY;
+ if (!new_regs) {
+ dev_dbg(&dev->dev, "Failed to allocate ETM register array\n");
+ ret = -ENOMEM;
goto out;
}
+ t->etm_regs = new_regs;
ret = amba_request_regions(dev, NULL);
if (ret)
goto out;
- t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res));
- if (!t->etm_regs) {
+ t->etm_regs[t->etm_regs_count] =
+ ioremap_nocache(dev->res.start, resource_size(&dev->res));
+ if (!t->etm_regs[t->etm_regs_count]) {
ret = -ENOMEM;
goto out_release;
}
- amba_set_drvdata(dev, t);
+ amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]);
- mutex_init(&t->mutex);
- t->dev = &dev->dev;
- t->flags = TRACER_CYCLE_ACC;
+ t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA | TRACER_BRANCHOUTPUT;
t->etm_portsz = 1;
+ t->etm_contextid_size = 3;
- etm_unlock(t);
- (void)etm_readl(t, ETMMR_PDSR);
+ etm_unlock(t, t->etm_regs_count);
+ (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR);
/* dummy first read */
- (void)etm_readl(&tracer, ETMMR_OSSRR);
-
- t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf;
- etm_writel(t, 0x440, ETMR_CTRL);
- etm_lock(t);
+ (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR);
+
+ etmccr = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE);
+ t->ncmppairs = etmccr & 0xf;
+ if (etmccr & ETMCCR_ETMIDR_PRESENT) {
+ etmidr = etm_readl(t, t->etm_regs_count, ETMR_ID);
+ etm_version = ETMIDR_VERSION(etmidr);
+ if (etm_version >= ETMIDR_VERSION_3_1)
+ etmccer = etm_readl(t, t->etm_regs_count, ETMR_CCE);
+ }
+ etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL);
+ etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR);
+ etm_lock(t, t->etm_regs_count);
ret = sysfs_create_file(&dev->dev.kobj,
&trace_running_attr.attr);
@@ -579,32 +924,97 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id)
if (ret)
dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n");
- dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
+ ret = sysfs_create_file(&dev->dev.kobj,
+ &trace_contextid_size_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev,
+ "Failed to create trace_contextid_size in sysfs\n");
+
+ ret = sysfs_create_file(&dev->dev.kobj,
+ &trace_branch_output_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev,
+ "Failed to create trace_branch_output in sysfs\n");
+
+ if (etmccer & ETMCCER_RETURN_STACK_IMPLEMENTED) {
+ ret = sysfs_create_file(&dev->dev.kobj,
+ &trace_return_stack_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev,
+ "Failed to create trace_return_stack in sysfs\n");
+ }
+
+ if (etmccer & ETMCCER_TIMESTAMPING_IMPLEMENTED) {
+ ret = sysfs_create_file(&dev->dev.kobj,
+ &trace_timestamp_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev,
+ "Failed to create trace_timestamp in sysfs\n");
+ }
+
+ ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
+
+ if (etm_version < ETMIDR_VERSION_PFT_1_0) {
+ ret = sysfs_create_file(&dev->dev.kobj,
+ &trace_data_range_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev,
+ "Failed to create trace_data_range in sysfs\n");
+ } else {
+ tracer.flags &= ~TRACER_TRACE_DATA;
+ }
+
+ dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n");
+
+ /* Enable formatter if there are multiple trace sources */
+ if (new_count > 1)
+ t->etb_fc = ETBFF_ENFCONT | ETBFF_ENFTC;
+
+ t->etm_regs_count = new_count;
out:
+ mutex_unlock(&t->mutex);
return ret;
out_unmap:
- iounmap(t->etm_regs);
+ iounmap(t->etm_regs[t->etm_regs_count]);
out_release:
amba_release_regions(dev);
+ mutex_unlock(&t->mutex);
return ret;
}
static int etm_remove(struct amba_device *dev)
{
- struct tracectx *t = amba_get_drvdata(dev);
-
- iounmap(t->etm_regs);
- t->etm_regs = NULL;
-
- amba_release_regions(dev);
+ int i;
+ struct tracectx *t = &tracer;
+ void __iomem *etm_regs = amba_get_drvdata(dev);
sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr);
+ sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr);
+ sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr);
+
+ mutex_lock(&t->mutex);
+ for (i = 0; i < t->etm_regs_count; i++)
+ if (t->etm_regs[i] == etm_regs)
+ break;
+ for (; i < t->etm_regs_count - 1; i++)
+ t->etm_regs[i] = t->etm_regs[i + 1];
+ t->etm_regs_count--;
+ if (!t->etm_regs_count) {
+ kfree(t->etm_regs);
+ t->etm_regs = NULL;
+ }
+ mutex_unlock(&t->mutex);
+
+ iounmap(etm_regs);
+ amba_release_regions(dev);
return 0;
}
@@ -614,6 +1024,10 @@ static struct amba_id etm_ids[] = {
.id = 0x0003b921,
.mask = 0x0007ffff,
},
+ {
+ .id = 0x0003b950,
+ .mask = 0x0007ffff,
+ },
{ 0, 0 },
};
@@ -631,6 +1045,8 @@ static int __init etm_init(void)
{
int retval;
+ mutex_init(&tracer.mutex);
+
retval = amba_driver_register(&etb_driver);
if (retval) {
printk(KERN_ERR "Failed to register etb\n");
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 2c4257604513..86ecca27bae4 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -167,11 +167,15 @@ static bool migrate_one_irq(struct irq_desc *desc)
if (irqd_is_per_cpu(d) || !cpumask_test_cpu(smp_processor_id(), affinity))
return false;
- if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
- affinity = cpu_online_mask;
+ if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids)
ret = true;
- }
+ /*
+ * when using forced irq_set_affinity we must ensure that the cpu
+ * being offlined is not present in the affinity mask, it may be
+ * selected as the target CPU otherwise
+ */
+ affinity = cpu_online_mask;
c = irq_data_get_irq_chip(d);
if (!c->irq_set_affinity)
pr_debug("IRQ%u: unable to set affinity\n", d->irq);
diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c
index a74b53c1b7df..241255835abb 100644
--- a/arch/arm/kernel/kgdb.c
+++ b/arch/arm/kernel/kgdb.c
@@ -144,6 +144,8 @@ int kgdb_arch_handle_exception(int exception_vector, int signo,
static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr)
{
+ if (user_mode(regs))
+ return -1;
kgdb_handle_exception(1, SIGTRAP, 0, regs);
return 0;
@@ -151,6 +153,8 @@ static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr)
static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)
{
+ if (user_mode(regs))
+ return -1;
compiled_break = 1;
kgdb_handle_exception(1, SIGTRAP, 0, regs);
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
index 45e478157278..6a4dffefd357 100644
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -91,6 +91,7 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
break;
case R_ARM_ABS32:
+ case R_ARM_TARGET1:
*(u32 *)loc += sym->st_value;
break;
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 266cba46db3e..62df634cc0db 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -12,6 +12,7 @@
*/
#define pr_fmt(fmt) "hw perfevents: " fmt
+#include <linux/cpumask.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -88,6 +89,9 @@ armpmu_map_event(struct perf_event *event,
return armpmu_map_cache_event(cache_map, config);
case PERF_TYPE_RAW:
return armpmu_map_raw_event(raw_event_mask, config);
+ default:
+ if (event->attr.type >= PERF_TYPE_MAX)
+ return armpmu_map_raw_event(raw_event_mask, config);
}
return -ENOENT;
@@ -161,6 +165,8 @@ armpmu_stop(struct perf_event *event, int flags)
struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
+ if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus))
+ return;
/*
* ARM pmu always has to update the counter, so ignore
* PERF_EF_UPDATE, see comments in armpmu_start().
@@ -177,6 +183,8 @@ static void armpmu_start(struct perf_event *event, int flags)
struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
+ if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus))
+ return;
/*
* ARM pmu always has to reprogram the period, so ignore
* PERF_EF_RELOAD, see the comment below.
@@ -204,6 +212,9 @@ armpmu_del(struct perf_event *event, int flags)
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;
+ if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus))
+ return;
+
armpmu_stop(event, PERF_EF_UPDATE);
hw_events->events[idx] = NULL;
clear_bit(idx, hw_events->used_mask);
@@ -222,6 +233,10 @@ armpmu_add(struct perf_event *event, int flags)
int idx;
int err = 0;
+ /* An event following a process won't be stopped earlier */
+ if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus))
+ return 0;
+
perf_pmu_disable(event->pmu);
/* If we don't have a space for the counter then finish early. */
@@ -436,6 +451,10 @@ static int armpmu_event_init(struct perf_event *event)
int err = 0;
atomic_t *active_events = &armpmu->active_events;
+ if (event->cpu != -1 &&
+ !cpumask_test_cpu(event->cpu, &armpmu->valid_cpus))
+ return -ENOENT;
+
/* does not support taken branch sampling */
if (has_branch_stack(event))
return -EOPNOTSUPP;
diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c
index e6a6edbec613..9d5a468f1c48 100644
--- a/arch/arm/kernel/perf_event_cpu.c
+++ b/arch/arm/kernel/perf_event_cpu.c
@@ -19,6 +19,7 @@
#define pr_fmt(fmt) "CPU PMU: " fmt
#include <linux/bitmap.h>
+#include <linux/cpu_pm.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/of.h>
@@ -33,34 +34,37 @@
#include <asm/pmu.h>
/* Set at runtime when we know what CPU type we are. */
-static struct arm_pmu *cpu_pmu;
+static DEFINE_PER_CPU(struct arm_pmu *, cpu_pmu);
static DEFINE_PER_CPU(struct arm_pmu *, percpu_pmu);
static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events);
static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask);
static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events);
+static DEFINE_PER_CPU(struct cpupmu_regs, cpu_pmu_regs);
+
/*
* Despite the names, these two functions are CPU-specific and are used
* by the OProfile/perf code.
*/
const char *perf_pmu_name(void)
{
- if (!cpu_pmu)
+ struct arm_pmu *pmu = per_cpu(cpu_pmu, 0);
+ if (!pmu)
return NULL;
- return cpu_pmu->name;
+ return pmu->name;
}
EXPORT_SYMBOL_GPL(perf_pmu_name);
int perf_num_counters(void)
{
- int max_events = 0;
+ struct arm_pmu *pmu = per_cpu(cpu_pmu, 0);
- if (cpu_pmu != NULL)
- max_events = cpu_pmu->num_events;
+ if (!pmu)
+ return 0;
- return max_events;
+ return pmu->num_events;
}
EXPORT_SYMBOL_GPL(perf_num_counters);
@@ -98,6 +102,7 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
{
int i, irq, irqs;
struct platform_device *pmu_device = cpu_pmu->plat_device;
+ int cpu = -1;
irqs = min(pmu_device->num_resources, num_possible_cpus());
@@ -107,7 +112,8 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
free_percpu_irq(irq, &percpu_pmu);
} else {
for (i = 0; i < irqs; ++i) {
- if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs))
+ cpu = cpumask_next(cpu, &cpu_pmu->valid_cpus);
+ if (!cpumask_test_and_clear_cpu(cpu, &cpu_pmu->active_irqs))
continue;
irq = platform_get_irq(pmu_device, i);
if (irq >= 0)
@@ -120,6 +126,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
{
int i, err, irq, irqs;
struct platform_device *pmu_device = cpu_pmu->plat_device;
+ int cpu = -1;
if (!pmu_device)
return -ENODEV;
@@ -142,6 +149,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
} else {
for (i = 0; i < irqs; ++i) {
err = 0;
+ cpu = cpumask_next(cpu, &cpu_pmu->valid_cpus);
irq = platform_get_irq(pmu_device, i);
if (irq < 0)
continue;
@@ -151,7 +159,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
* assume that we're running on a uniprocessor machine and
* continue. Otherwise, continue without this interrupt.
*/
- if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) {
+ if (irq_set_affinity(irq, cpumask_of(cpu)) && irqs > 1) {
pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n",
irq, i);
continue;
@@ -166,7 +174,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
return err;
}
- cpumask_set_cpu(i, &cpu_pmu->active_irqs);
+ cpumask_set_cpu(cpu, &cpu_pmu->active_irqs);
}
}
@@ -176,7 +184,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
static void cpu_pmu_init(struct arm_pmu *cpu_pmu)
{
int cpu;
- for_each_possible_cpu(cpu) {
+ for_each_cpu_mask(cpu, cpu_pmu->valid_cpus) {
struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu);
events->events = per_cpu(hw_events, cpu);
events->used_mask = per_cpu(used_mask, cpu);
@@ -190,7 +198,7 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu)
/* Ensure the PMU has sane values out of reset. */
if (cpu_pmu->reset)
- on_each_cpu(cpu_pmu->reset, cpu_pmu, 1);
+ on_each_cpu_mask(&cpu_pmu->valid_cpus, cpu_pmu->reset, cpu_pmu, 1);
/* If no interrupts available, set the corresponding capability flag */
if (!platform_get_irq(cpu_pmu->plat_device, 0))
@@ -206,21 +214,46 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu)
static int cpu_pmu_notify(struct notifier_block *b, unsigned long action,
void *hcpu)
{
+ struct arm_pmu *pmu = per_cpu(cpu_pmu, (long)hcpu);
+
if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING)
return NOTIFY_DONE;
- if (cpu_pmu && cpu_pmu->reset)
- cpu_pmu->reset(cpu_pmu);
+ if (pmu && pmu->reset)
+ pmu->reset(pmu);
else
return NOTIFY_DONE;
return NOTIFY_OK;
}
+static int cpu_pmu_pm_notify(struct notifier_block *b,
+ unsigned long action, void *hcpu)
+{
+ int cpu = smp_processor_id();
+ struct arm_pmu *pmu = per_cpu(cpu_pmu, cpu);
+ struct cpupmu_regs *pmuregs = &per_cpu(cpu_pmu_regs, cpu);
+
+ if (!pmu)
+ return NOTIFY_DONE;
+
+ if (action == CPU_PM_ENTER && pmu->save_regs) {
+ pmu->save_regs(pmu, pmuregs);
+ } else if (action == CPU_PM_EXIT && pmu->restore_regs) {
+ pmu->restore_regs(pmu, pmuregs);
+ }
+
+ return NOTIFY_OK;
+}
+
static struct notifier_block cpu_pmu_hotplug_notifier = {
.notifier_call = cpu_pmu_notify,
};
+static struct notifier_block cpu_pmu_pm_notifier = {
+ .notifier_call = cpu_pmu_pm_notify,
+};
+
/*
* PMU platform driver and devicetree bindings.
*/
@@ -292,6 +325,9 @@ static int probe_current_pmu(struct arm_pmu *pmu)
break;
}
+ /* assume PMU support all the CPUs in this case */
+ cpumask_setall(&pmu->valid_cpus);
+
put_cpu();
return ret;
}
@@ -299,15 +335,10 @@ static int probe_current_pmu(struct arm_pmu *pmu)
static int cpu_pmu_device_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id;
- const int (*init_fn)(struct arm_pmu *);
struct device_node *node = pdev->dev.of_node;
struct arm_pmu *pmu;
- int ret = -ENODEV;
-
- if (cpu_pmu) {
- pr_info("attempt to register multiple PMU devices!");
- return -ENOSPC;
- }
+ int ret = 0;
+ int cpu;
pmu = kzalloc(sizeof(struct arm_pmu), GFP_KERNEL);
if (!pmu) {
@@ -315,12 +346,33 @@ static int cpu_pmu_device_probe(struct platform_device *pdev)
return -ENOMEM;
}
- cpu_pmu = pmu;
- cpu_pmu->plat_device = pdev;
+ for_each_possible_cpu(cpu)
+ per_cpu(cpu_pmu, cpu) = pmu;
+ pmu->plat_device = pdev;
if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) {
- init_fn = of_id->data;
- ret = init_fn(pmu);
+ smp_call_func_t init_fn = (smp_call_func_t)of_id->data;
+ struct device_node *ncluster;
+ int cluster = -1;
+ cpumask_t sibling_mask;
+
+ ncluster = of_parse_phandle(node, "cluster", 0);
+ if (ncluster) {
+ int len;
+ const u32 *hwid;
+ hwid = of_get_property(ncluster, "reg", &len);
+ if (hwid && len == 4)
+ cluster = be32_to_cpup(hwid);
+ }
+ /* set sibling mask to all cpu mask if socket is not specified */
+ if (cluster == -1 ||
+ cluster_to_logical_mask(cluster, &sibling_mask))
+ cpumask_setall(&sibling_mask);
+
+ smp_call_function_any(&sibling_mask, init_fn, pmu, 1);
+
+ /* now set the valid_cpus after init */
+ cpumask_copy(&pmu->valid_cpus, &sibling_mask);
} else {
ret = probe_current_pmu(pmu);
}
@@ -330,8 +382,8 @@ static int cpu_pmu_device_probe(struct platform_device *pdev)
goto out_free;
}
- cpu_pmu_init(cpu_pmu);
- ret = armpmu_register(cpu_pmu, PERF_TYPE_RAW);
+ cpu_pmu_init(pmu);
+ ret = armpmu_register(pmu, -1);
if (!ret)
return 0;
@@ -360,9 +412,17 @@ static int __init register_pmu_driver(void)
if (err)
return err;
+ err = cpu_pm_register_notifier(&cpu_pmu_pm_notifier);
+ if (err) {
+ unregister_cpu_notifier(&cpu_pmu_hotplug_notifier);
+ return err;
+ }
+
err = platform_driver_register(&cpu_pmu_driver);
- if (err)
+ if (err) {
+ cpu_pm_unregister_notifier(&cpu_pmu_pm_notifier);
unregister_cpu_notifier(&cpu_pmu_hotplug_notifier);
+ }
return err;
}
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 116758b77f93..aff14ebf84a7 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -700,6 +700,51 @@ static void armv7_pmnc_dump_regs(struct arm_pmu *cpu_pmu)
}
#endif
+static void armv7pmu_save_regs(struct arm_pmu *cpu_pmu,
+ struct cpupmu_regs *regs)
+{
+ unsigned int cnt;
+ asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (regs->pmc));
+ if (!(regs->pmc & ARMV7_PMNC_E))
+ return;
+
+ asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (regs->pmcntenset));
+ asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r" (regs->pmuseren));
+ asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (regs->pmintenset));
+ asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (regs->pmxevtcnt[0]));
+ for (cnt = ARMV7_IDX_COUNTER0;
+ cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) {
+ armv7_pmnc_select_counter(cnt);
+ asm volatile("mrc p15, 0, %0, c9, c13, 1"
+ : "=r"(regs->pmxevttype[cnt]));
+ asm volatile("mrc p15, 0, %0, c9, c13, 2"
+ : "=r"(regs->pmxevtcnt[cnt]));
+ }
+ return;
+}
+
+static void armv7pmu_restore_regs(struct arm_pmu *cpu_pmu,
+ struct cpupmu_regs *regs)
+{
+ unsigned int cnt;
+ if (!(regs->pmc & ARMV7_PMNC_E))
+ return;
+
+ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (regs->pmcntenset));
+ asm volatile("mcr p15, 0, %0, c9, c14, 0" : : "r" (regs->pmuseren));
+ asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (regs->pmintenset));
+ asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (regs->pmxevtcnt[0]));
+ for (cnt = ARMV7_IDX_COUNTER0;
+ cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) {
+ armv7_pmnc_select_counter(cnt);
+ asm volatile("mcr p15, 0, %0, c9, c13, 1"
+ : : "r"(regs->pmxevttype[cnt]));
+ asm volatile("mcr p15, 0, %0, c9, c13, 2"
+ : : "r"(regs->pmxevtcnt[cnt]));
+ }
+ asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (regs->pmc));
+}
+
static void armv7pmu_enable_event(struct perf_event *event)
{
unsigned long flags;
@@ -991,6 +1036,8 @@ static void armv7pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->start = armv7pmu_start;
cpu_pmu->stop = armv7pmu_stop;
cpu_pmu->reset = armv7pmu_reset;
+ cpu_pmu->save_regs = armv7pmu_save_regs;
+ cpu_pmu->restore_regs = armv7pmu_restore_regs;
cpu_pmu->max_period = (1LLU << 32) - 1;
};
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 81ef686a91ca..3668e25bb292 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -32,6 +32,7 @@
#include <linux/hw_breakpoint.h>
#include <linux/leds.h>
#include <linux/reboot.h>
+#include <linux/console.h>
#include <asm/cacheflush.h>
#include <asm/idmap.h>
@@ -59,9 +60,46 @@ static const char *isa_modes[] __maybe_unused = {
"ARM" , "Thumb" , "Jazelle", "ThumbEE"
};
+#ifdef CONFIG_SMP
+void arch_trigger_all_cpu_backtrace(bool include_self)
+{
+ smp_send_all_cpu_backtrace(include_self);
+}
+#else
+void arch_trigger_all_cpu_backtrace(void)
+{
+ dump_stack();
+}
+#endif
+
extern void call_with_stack(void (*fn)(void *), void *arg, void *sp);
typedef void (*phys_reset_t)(unsigned long);
+#ifdef CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART
+void arm_machine_flush_console(void)
+{
+ printk("\n");
+ pr_emerg("Restarting %s\n", linux_banner);
+ if (console_trylock()) {
+ console_unlock();
+ return;
+ }
+
+ mdelay(50);
+
+ local_irq_disable();
+ if (!console_trylock())
+ pr_emerg("arm_restart: Console was locked! Busting\n");
+ else
+ pr_emerg("arm_restart: Console was locked!\n");
+ console_unlock();
+}
+#else
+void arm_machine_flush_console(void)
+{
+}
+#endif
+
/*
* A temporary stack to use for CPU reset. This is static so that we
* don't clobber it with the identity mapping. When running with this
@@ -153,6 +191,7 @@ void arch_cpu_idle_prepare(void)
void arch_cpu_idle_enter(void)
{
+ idle_notifier_call_chain(IDLE_START);
ledtrig_cpu(CPU_LED_IDLE_START);
#ifdef CONFIG_PL310_ERRATA_769419
wmb();
@@ -162,6 +201,7 @@ void arch_cpu_idle_enter(void)
void arch_cpu_idle_exit(void)
{
ledtrig_cpu(CPU_LED_IDLE_END);
+ idle_notifier_call_chain(IDLE_END);
}
#ifdef CONFIG_HOTPLUG_CPU
@@ -182,6 +222,16 @@ void arch_cpu_idle_dead(void)
*/
void machine_shutdown(void)
{
+#ifdef CONFIG_SMP
+ /*
+ * Disable preemption so we're guaranteed to
+ * run to power off or reboot and prevent
+ * the possibility of switching to another
+ * thread that might wind up blocking on
+ * one of the stopped CPUs.
+ */
+ preempt_disable();
+#endif
disable_nonboot_cpus();
}
@@ -230,6 +280,10 @@ void machine_restart(char *cmd)
local_irq_disable();
smp_send_stop();
+ /* Flush the console to make sure all the relevant messages make it
+ * out to the console drivers */
+ arm_machine_flush_console();
+
arm_pm_restart(reboot_mode, cmd);
/* Give a grace period for failure to restart of 1s */
@@ -241,6 +295,77 @@ void machine_restart(char *cmd)
while (1);
}
+/*
+ * dump a block of kernel memory from around the given address
+ */
+static void show_data(unsigned long addr, int nbytes, const char *name)
+{
+ int i, j;
+ int nlines;
+ u32 *p;
+
+ /*
+ * don't attempt to dump non-kernel addresses or
+ * values that are probably just small negative numbers
+ */
+ if (addr < PAGE_OFFSET || addr > -256UL)
+ return;
+
+ printk("\n%s: %#lx:\n", name, addr);
+
+ /*
+ * round address down to a 32 bit boundary
+ * and always dump a multiple of 32 bytes
+ */
+ p = (u32 *)(addr & ~(sizeof(u32) - 1));
+ nbytes += (addr & (sizeof(u32) - 1));
+ nlines = (nbytes + 31) / 32;
+
+
+ for (i = 0; i < nlines; i++) {
+ /*
+ * just display low 16 bits of address to keep
+ * each line of the dump < 80 characters
+ */
+ printk("%04lx ", (unsigned long)p & 0xffff);
+ for (j = 0; j < 8; j++) {
+ u32 data;
+ if (probe_kernel_address(p, data)) {
+ printk(" ********");
+ } else {
+ printk(" %08x", data);
+ }
+ ++p;
+ }
+ printk("\n");
+ }
+}
+
+static void show_extra_register_data(struct pt_regs *regs, int nbytes)
+{
+ mm_segment_t fs;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ show_data(regs->ARM_pc - nbytes, nbytes * 2, "PC");
+ show_data(regs->ARM_lr - nbytes, nbytes * 2, "LR");
+ show_data(regs->ARM_sp - nbytes, nbytes * 2, "SP");
+ show_data(regs->ARM_ip - nbytes, nbytes * 2, "IP");
+ show_data(regs->ARM_fp - nbytes, nbytes * 2, "FP");
+ show_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0");
+ show_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1");
+ show_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2");
+ show_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3");
+ show_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4");
+ show_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5");
+ show_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6");
+ show_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7");
+ show_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8");
+ show_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9");
+ show_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10");
+ set_fs(fs);
+}
+
void __show_regs(struct pt_regs *regs)
{
unsigned long flags;
@@ -302,6 +427,8 @@ void __show_regs(struct pt_regs *regs)
printk("Control: %08x%s\n", ctrl, buf);
}
#endif
+
+ show_extra_register_data(regs, 128);
}
void show_regs(struct pt_regs * regs)
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 84db893dedc2..f827b5632c5f 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -277,6 +277,19 @@ static int cpu_has_aliasing_icache(unsigned int arch)
int aliasing_icache;
unsigned int id_reg, num_sets, line_size;
+#ifdef CONFIG_BIG_LITTLE
+ /*
+ * We expect a combination of Cortex-A15 and Cortex-A7 cores.
+ * A7 = VIPT aliasing I-cache
+ * A15 = PIPT (non-aliasing) I-cache
+ * To cater for this discrepancy, let's assume aliasing I-cache
+ * all the time. This means unneeded extra work on the A15 but
+ * only ptrace is affected which is not performance critical.
+ */
+ if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc0f0)
+ return 1;
+#endif
+
/* PIPT caches never alias. */
if (icache_is_pipt())
return 0;
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 9388a3d479e1..6b7f33bb5236 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -72,6 +72,7 @@ enum ipi_msg_type {
IPI_CPU_STOP,
IPI_IRQ_WORK,
IPI_COMPLETION,
+ IPI_CPU_BACKTRACE,
};
static DECLARE_COMPLETION(cpu_running);
@@ -451,6 +452,7 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
S(IPI_CPU_STOP, "CPU stop interrupts"),
S(IPI_IRQ_WORK, "IRQ work interrupts"),
S(IPI_COMPLETION, "completion interrupts"),
+ S(IPI_CPU_BACKTRACE, "CPU backtrace"),
};
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
@@ -552,6 +554,58 @@ static void ipi_complete(unsigned int cpu)
complete(per_cpu(cpu_completion, cpu));
}
+static cpumask_t backtrace_mask;
+static DEFINE_RAW_SPINLOCK(backtrace_lock);
+
+/* "in progress" flag of arch_trigger_all_cpu_backtrace */
+static unsigned long backtrace_flag;
+
+void smp_send_all_cpu_backtrace(bool include_self)
+{
+ unsigned int this_cpu = smp_processor_id();
+ int i;
+
+ if (test_and_set_bit(0, &backtrace_flag))
+ /*
+ * If there is already a trigger_all_cpu_backtrace() in progress
+ * (backtrace_flag == 1), don't output double cpu dump infos.
+ */
+ return;
+
+ cpumask_copy(&backtrace_mask, cpu_online_mask);
+ cpu_clear(this_cpu, backtrace_mask);
+
+ pr_info("Backtrace for cpu %d (current):\n", this_cpu);
+ dump_stack();
+
+ pr_info("\nsending IPI to all other CPUs:\n");
+ smp_cross_call(&backtrace_mask, IPI_CPU_BACKTRACE);
+
+ /* Wait for up to 10 seconds for all other CPUs to do the backtrace */
+ for (i = 0; i < 10 * 1000; i++) {
+ if (cpumask_empty(&backtrace_mask))
+ break;
+ mdelay(1);
+ }
+
+ clear_bit(0, &backtrace_flag);
+ smp_mb__after_clear_bit();
+}
+
+/*
+ * ipi_cpu_backtrace - handle IPI from smp_send_all_cpu_backtrace()
+ */
+static void ipi_cpu_backtrace(unsigned int cpu, struct pt_regs *regs)
+{
+ if (cpu_isset(cpu, backtrace_mask)) {
+ raw_spin_lock(&backtrace_lock);
+ pr_warning("IPI backtrace for cpu %d\n", cpu);
+ show_regs(regs);
+ raw_spin_unlock(&backtrace_lock);
+ cpu_clear(cpu, backtrace_mask);
+ }
+}
+
/*
* Main handler for inter-processor interrupts
*/
@@ -618,6 +672,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
irq_exit();
break;
+ case IPI_CPU_BACKTRACE:
+ ipi_cpu_backtrace(cpu, regs);
+ break;
+
default:
printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n",
cpu, ipinr);
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index e35d880f9773..dfd36ff416dd 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -290,6 +290,33 @@ static struct sched_domain_topology_level arm_topology[] = {
};
/*
+ * cluster_to_logical_mask - return cpu logical mask of CPUs in a cluster
+ * @socket_id: cluster HW identifier
+ * @cluster_mask: the cpumask location to be initialized, modified by the
+ * function only if return value == 0
+ *
+ * Return:
+ *
+ * 0 on success
+ * -EINVAL if cluster_mask is NULL or there is no record matching socket_id
+ */
+int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask)
+{
+ int cpu;
+
+ if (!cluster_mask)
+ return -EINVAL;
+
+ for_each_online_cpu(cpu)
+ if (socket_id == topology_physical_package_id(cpu)) {
+ cpumask_copy(cluster_mask, topology_core_cpumask(cpu));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/*
* init_cpu_topology is called at boot when only one cpu is running
* which prevent simultaneous write access to cpu_topology array
*/
diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile
index 67c492aabf4d..b19a39652545 100644
--- a/arch/arm/mach-bcm/Makefile
+++ b/arch/arm/mach-bcm/Makefile
@@ -36,5 +36,4 @@ obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o
ifeq ($(CONFIG_ARCH_BRCMSTB),y)
obj-y += brcmstb.o
-obj-$(CONFIG_SMP) += headsmp-brcmstb.o platsmp-brcmstb.o
endif
diff --git a/arch/arm/mach-bcm/bcm_kona_smc.c b/arch/arm/mach-bcm/bcm_kona_smc.c
index a55a7ecf146a..3937bd5d452f 100644
--- a/arch/arm/mach-bcm/bcm_kona_smc.c
+++ b/arch/arm/mach-bcm/bcm_kona_smc.c
@@ -106,9 +106,14 @@ int __init bcm_kona_smc_init(void)
* request result appropriately. This result value is found in r0
* when the "smc" request completes.
*/
+#ifdef __clang__
+#define R12 "r12"
+#else /* !__clang__ */
+#define R12 "ip" /* gcc calls r12 "ip" */
+#endif /* !__clang__ */
static int bcm_kona_do_smc(u32 service_id, u32 buffer_phys)
{
- register u32 ip asm("ip"); /* Also called r12 */
+ register u32 ip asm(R12); /* Also called r12 */
register u32 r0 asm("r0");
register u32 r4 asm("r4");
register u32 r5 asm("r5");
@@ -120,7 +125,7 @@ static int bcm_kona_do_smc(u32 service_id, u32 buffer_phys)
asm volatile (
/* Make sure we got the registers we want */
- __asmeq("%0", "ip")
+ __asmeq("%0", R12)
__asmeq("%1", "r0")
__asmeq("%2", "r4")
__asmeq("%3", "r5")
diff --git a/arch/arm/mach-bcm/brcmstb.h b/arch/arm/mach-bcm/brcmstb.h
deleted file mode 100644
index ec0c3d112b36..000000000000
--- a/arch/arm/mach-bcm/brcmstb.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2013-2014 Broadcom Corporation
- *
- * 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.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __BRCMSTB_H__
-#define __BRCMSTB_H__
-
-void brcmstb_secondary_startup(void);
-
-#endif /* __BRCMSTB_H__ */
diff --git a/arch/arm/mach-bcm/headsmp-brcmstb.S b/arch/arm/mach-bcm/headsmp-brcmstb.S
deleted file mode 100644
index 199c1ea58248..000000000000
--- a/arch/arm/mach-bcm/headsmp-brcmstb.S
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * SMP boot code for secondary CPUs
- * Based on arch/arm/mach-tegra/headsmp.S
- *
- * Copyright (C) 2010 NVIDIA, Inc.
- * Copyright (C) 2013-2014 Broadcom Corporation
- *
- * 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.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <asm/assembler.h>
-#include <linux/linkage.h>
-#include <linux/init.h>
-
- .section ".text.head", "ax"
-
-ENTRY(brcmstb_secondary_startup)
- /*
- * Ensure CPU is in a sane state by disabling all IRQs and switching
- * into SVC mode.
- */
- setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r0
-
- bl v7_invalidate_l1
- b secondary_startup
-ENDPROC(brcmstb_secondary_startup)
diff --git a/arch/arm/mach-bcm/platsmp-brcmstb.c b/arch/arm/mach-bcm/platsmp-brcmstb.c
deleted file mode 100644
index af780e9c23a6..000000000000
--- a/arch/arm/mach-bcm/platsmp-brcmstb.c
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Broadcom STB CPU SMP and hotplug support for ARM
- *
- * Copyright (C) 2013-2014 Broadcom Corporation
- *
- * 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.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/printk.h>
-#include <linux/regmap.h>
-#include <linux/smp.h>
-#include <linux/mfd/syscon.h>
-#include <linux/spinlock.h>
-
-#include <asm/cacheflush.h>
-#include <asm/cp15.h>
-#include <asm/mach-types.h>
-#include <asm/smp_plat.h>
-
-#include "brcmstb.h"
-
-enum {
- ZONE_MAN_CLKEN_MASK = BIT(0),
- ZONE_MAN_RESET_CNTL_MASK = BIT(1),
- ZONE_MAN_MEM_PWR_MASK = BIT(4),
- ZONE_RESERVED_1_MASK = BIT(5),
- ZONE_MAN_ISO_CNTL_MASK = BIT(6),
- ZONE_MANUAL_CONTROL_MASK = BIT(7),
- ZONE_PWR_DN_REQ_MASK = BIT(9),
- ZONE_PWR_UP_REQ_MASK = BIT(10),
- ZONE_BLK_RST_ASSERT_MASK = BIT(12),
- ZONE_PWR_OFF_STATE_MASK = BIT(25),
- ZONE_PWR_ON_STATE_MASK = BIT(26),
- ZONE_DPG_PWR_STATE_MASK = BIT(28),
- ZONE_MEM_PWR_STATE_MASK = BIT(29),
- ZONE_RESET_STATE_MASK = BIT(31),
- CPU0_PWR_ZONE_CTRL_REG = 1,
- CPU_RESET_CONFIG_REG = 2,
-};
-
-static void __iomem *cpubiuctrl_block;
-static void __iomem *hif_cont_block;
-static u32 cpu0_pwr_zone_ctrl_reg;
-static u32 cpu_rst_cfg_reg;
-static u32 hif_cont_reg;
-
-#ifdef CONFIG_HOTPLUG_CPU
-static DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state);
-
-static int per_cpu_sw_state_rd(u32 cpu)
-{
- sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
- return per_cpu(per_cpu_sw_state, cpu);
-}
-
-static void per_cpu_sw_state_wr(u32 cpu, int val)
-{
- per_cpu(per_cpu_sw_state, cpu) = val;
- dmb();
- sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
- dsb_sev();
-}
-#else
-static inline void per_cpu_sw_state_wr(u32 cpu, int val) { }
-#endif
-
-static void __iomem *pwr_ctrl_get_base(u32 cpu)
-{
- void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg;
- base += (cpu_logical_map(cpu) * 4);
- return base;
-}
-
-static u32 pwr_ctrl_rd(u32 cpu)
-{
- void __iomem *base = pwr_ctrl_get_base(cpu);
- return readl_relaxed(base);
-}
-
-static void pwr_ctrl_wr(u32 cpu, u32 val)
-{
- void __iomem *base = pwr_ctrl_get_base(cpu);
- writel(val, base);
-}
-
-static void cpu_rst_cfg_set(u32 cpu, int set)
-{
- u32 val;
- val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg);
- if (set)
- val |= BIT(cpu_logical_map(cpu));
- else
- val &= ~BIT(cpu_logical_map(cpu));
- writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg);
-}
-
-static void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr)
-{
- const int reg_ofs = cpu_logical_map(cpu) * 8;
- writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs);
- writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs);
-}
-
-static void brcmstb_cpu_boot(u32 cpu)
-{
- pr_info("SMP: Booting CPU%d...\n", cpu);
-
- /*
- * set the reset vector to point to the secondary_startup
- * routine
- */
- cpu_set_boot_addr(cpu, virt_to_phys(brcmstb_secondary_startup));
-
- /* unhalt the cpu */
- cpu_rst_cfg_set(cpu, 0);
-}
-
-static void brcmstb_cpu_power_on(u32 cpu)
-{
- /*
- * The secondary cores power was cut, so we must go through
- * power-on initialization.
- */
- u32 tmp;
-
- pr_info("SMP: Powering up CPU%d...\n", cpu);
-
- /* Request zone power up */
- pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK);
-
- /* Wait for the power up FSM to complete */
- do {
- tmp = pwr_ctrl_rd(cpu);
- } while (!(tmp & ZONE_PWR_ON_STATE_MASK));
-
- per_cpu_sw_state_wr(cpu, 1);
-}
-
-static int brcmstb_cpu_get_power_state(u32 cpu)
-{
- int tmp = pwr_ctrl_rd(cpu);
- return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1;
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-
-static void brcmstb_cpu_die(u32 cpu)
-{
- v7_exit_coherency_flush(all);
-
- /* Prevent all interrupts from reaching this CPU. */
- arch_local_irq_disable();
-
- /*
- * Final full barrier to ensure everything before this instruction has
- * quiesced.
- */
- isb();
- dsb();
-
- per_cpu_sw_state_wr(cpu, 0);
-
- /* Sit and wait to die */
- wfi();
-
- /* We should never get here... */
- panic("Spurious interrupt on CPU %d received!\n", cpu);
-}
-
-static int brcmstb_cpu_kill(u32 cpu)
-{
- u32 tmp;
-
- pr_info("SMP: Powering down CPU%d...\n", cpu);
-
- while (per_cpu_sw_state_rd(cpu))
- ;
-
- /* Program zone reset */
- pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK |
- ZONE_PWR_DN_REQ_MASK);
-
- /* Verify zone reset */
- tmp = pwr_ctrl_rd(cpu);
- if (!(tmp & ZONE_RESET_STATE_MASK))
- pr_err("%s: Zone reset bit for CPU %d not asserted!\n",
- __func__, cpu);
-
- /* Wait for power down */
- do {
- tmp = pwr_ctrl_rd(cpu);
- } while (!(tmp & ZONE_PWR_OFF_STATE_MASK));
-
- /* Settle-time from Broadcom-internal DVT reference code */
- udelay(7);
-
- /* Assert reset on the CPU */
- cpu_rst_cfg_set(cpu, 1);
-
- return 1;
-}
-
-#endif /* CONFIG_HOTPLUG_CPU */
-
-static int __init setup_hifcpubiuctrl_regs(struct device_node *np)
-{
- int rc = 0;
- char *name;
- struct device_node *syscon_np = NULL;
-
- name = "syscon-cpu";
-
- syscon_np = of_parse_phandle(np, name, 0);
- if (!syscon_np) {
- pr_err("can't find phandle %s\n", name);
- rc = -EINVAL;
- goto cleanup;
- }
-
- cpubiuctrl_block = of_iomap(syscon_np, 0);
- if (!cpubiuctrl_block) {
- pr_err("iomap failed for cpubiuctrl_block\n");
- rc = -EINVAL;
- goto cleanup;
- }
-
- rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG,
- &cpu0_pwr_zone_ctrl_reg);
- if (rc) {
- pr_err("failed to read 1st entry from %s property (%d)\n", name,
- rc);
- rc = -EINVAL;
- goto cleanup;
- }
-
- rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG,
- &cpu_rst_cfg_reg);
- if (rc) {
- pr_err("failed to read 2nd entry from %s property (%d)\n", name,
- rc);
- rc = -EINVAL;
- goto cleanup;
- }
-
-cleanup:
- if (syscon_np)
- of_node_put(syscon_np);
-
- return rc;
-}
-
-static int __init setup_hifcont_regs(struct device_node *np)
-{
- int rc = 0;
- char *name;
- struct device_node *syscon_np = NULL;
-
- name = "syscon-cont";
-
- syscon_np = of_parse_phandle(np, name, 0);
- if (!syscon_np) {
- pr_err("can't find phandle %s\n", name);
- rc = -EINVAL;
- goto cleanup;
- }
-
- hif_cont_block = of_iomap(syscon_np, 0);
- if (!hif_cont_block) {
- pr_err("iomap failed for hif_cont_block\n");
- rc = -EINVAL;
- goto cleanup;
- }
-
- /* offset is at top of hif_cont_block */
- hif_cont_reg = 0;
-
-cleanup:
- if (syscon_np)
- of_node_put(syscon_np);
-
- return rc;
-}
-
-static void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus)
-{
- int rc;
- struct device_node *np;
- char *name;
-
- name = "brcm,brcmstb-smpboot";
- np = of_find_compatible_node(NULL, NULL, name);
- if (!np) {
- pr_err("can't find compatible node %s\n", name);
- return;
- }
-
- rc = setup_hifcpubiuctrl_regs(np);
- if (rc)
- return;
-
- rc = setup_hifcont_regs(np);
- if (rc)
- return;
-}
-
-static DEFINE_SPINLOCK(boot_lock);
-
-static void brcmstb_secondary_init(unsigned int cpu)
-{
- /*
- * Synchronise with the boot thread.
- */
- spin_lock(&boot_lock);
- spin_unlock(&boot_lock);
-}
-
-static int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle)
-{
- /*
- * set synchronisation state between this boot processor
- * and the secondary one
- */
- spin_lock(&boot_lock);
-
- /* Bring up power to the core if necessary */
- if (brcmstb_cpu_get_power_state(cpu) == 0)
- brcmstb_cpu_power_on(cpu);
-
- brcmstb_cpu_boot(cpu);
-
- /*
- * now the secondary core is starting up let it run its
- * calibrations, then wait for it to finish
- */
- spin_unlock(&boot_lock);
-
- return 0;
-}
-
-static struct smp_operations brcmstb_smp_ops __initdata = {
- .smp_prepare_cpus = brcmstb_cpu_ctrl_setup,
- .smp_secondary_init = brcmstb_secondary_init,
- .smp_boot_secondary = brcmstb_boot_secondary,
-#ifdef CONFIG_HOTPLUG_CPU
- .cpu_kill = brcmstb_cpu_kill,
- .cpu_die = brcmstb_cpu_die,
-#endif
-};
-
-CPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops);
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 2d0240f241b8..5aced6434134 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -50,6 +50,7 @@ config ARCH_EXYNOS4
config ARCH_EXYNOS5
bool "SAMSUNG EXYNOS5"
default y
+ select ARCH_SUPPORTS_BIG_ENDIAN
help
Samsung EXYNOS5 (Cortex-A15/A7) SoC based systems
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index 47b904b3b973..cb680e90251a 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -162,12 +162,12 @@ extern unsigned int samsung_rev(void);
static inline void pmu_raw_writel(u32 val, u32 offset)
{
- __raw_writel(val, pmu_base_addr + offset);
+ writel_relaxed(val, pmu_base_addr + offset);
}
static inline u32 pmu_raw_readl(u32 offset)
{
- return __raw_readl(pmu_base_addr + offset);
+ return readl_relaxed(pmu_base_addr + offset);
}
#endif /* __ARCH_ARM_MACH_EXYNOS_COMMON_H */
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index 6a24e111d6e1..4d4581aeb877 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -148,15 +148,15 @@ static void exynos_restart(enum reboot_mode mode, const char *cmd)
np = of_find_compatible_node(NULL, NULL, "samsung,exynos5440-clock");
addr = of_iomap(np, 0) + 0xbc;
- status = __raw_readl(addr);
+ status = readl_relaxed(addr);
addr = of_iomap(np, 0) + 0xcc;
- val = __raw_readl(addr);
+ val = readl_relaxed(addr);
val = (val & 0xffff0000) | (status & 0xffff);
}
- __raw_writel(val, addr);
+ writel_relaxed(val, addr);
}
static struct platform_device exynos_cpuidle = {
diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c
index e8797bb78871..b994fe846e02 100644
--- a/arch/arm/mach-exynos/firmware.c
+++ b/arch/arm/mach-exynos/firmware.c
@@ -65,7 +65,7 @@ static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
if (soc_is_exynos4412())
boot_reg += 4 * cpu;
- __raw_writel(boot_addr, boot_reg);
+ writel_relaxed(boot_addr, boot_reg);
return 0;
}
diff --git a/arch/arm/mach-exynos/headsmp.S b/arch/arm/mach-exynos/headsmp.S
index b54f9701e421..45b3006e4e58 100644
--- a/arch/arm/mach-exynos/headsmp.S
+++ b/arch/arm/mach-exynos/headsmp.S
@@ -11,6 +11,7 @@
*/
#include <linux/linkage.h>
#include <linux/init.h>
+#include <asm/assembler.h>
/*
* exynos4 specific entry point for secondary CPUs. This provides
@@ -18,6 +19,11 @@
* ready for them to initialise.
*/
ENTRY(exynos4_secondary_startup)
+ /*
+ * ROM code operates in little endian mode, when we get control we
+ * need to switch it to big endian mode.
+ */
+ARM_BE8(setend be)
mrc p15, 0, r0, c0, c0, 5
and r0, r0, #15
adr r4, 1f
diff --git a/arch/arm/mach-exynos/mcpm-exynos.c b/arch/arm/mach-exynos/mcpm-exynos.c
index b2f8b60cf0e9..dc9a764a7c37 100644
--- a/arch/arm/mach-exynos/mcpm-exynos.c
+++ b/arch/arm/mach-exynos/mcpm-exynos.c
@@ -43,7 +43,6 @@
"mcr p15, 0, r0, c1, c0, 0 @ set SCTLR\n\t" \
"isb\n\t"\
"bl v7_flush_dcache_"__stringify(level)"\n\t" \
- "clrex\n\t"\
"mrc p15, 0, r0, c1, c0, 1 @ get ACTLR\n\t" \
"bic r0, r0, #(1 << 6) @ disable local coherency\n\t" \
/* Dummy Load of a device register to avoid Erratum 799270 */ \
diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
index a9f1cf759949..870ee1086930 100644
--- a/arch/arm/mach-exynos/platsmp.c
+++ b/arch/arm/mach-exynos/platsmp.c
@@ -224,7 +224,7 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
ret = PTR_ERR(boot_reg);
goto fail;
}
- __raw_writel(boot_addr, cpu_boot_reg(core_id));
+ writel_relaxed(boot_addr, cpu_boot_reg(core_id));
}
call_firmware_op(cpu_boot, core_id);
@@ -313,7 +313,7 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
if (IS_ERR(boot_reg))
break;
- __raw_writel(boot_addr, cpu_boot_reg(core_id));
+ writel_relaxed(boot_addr, cpu_boot_reg(core_id));
}
}
}
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index abefacb45976..0ca06dee44c4 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -194,8 +194,8 @@ static void exynos_set_wakeupmask(long mask)
static void exynos_cpu_set_boot_vector(long flags)
{
- __raw_writel(virt_to_phys(exynos_cpu_resume), EXYNOS_BOOT_VECTOR_ADDR);
- __raw_writel(flags, EXYNOS_BOOT_VECTOR_FLAG);
+ writel_relaxed(virt_to_phys(exynos_cpu_resume), EXYNOS_BOOT_VECTOR_ADDR);
+ writel_relaxed(flags, EXYNOS_BOOT_VECTOR_FLAG);
}
static int exynos_aftr_finisher(unsigned long flags)
diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c
index fd76e1b5a471..dec02c4ed711 100644
--- a/arch/arm/mach-exynos/pm_domains.c
+++ b/arch/arm/mach-exynos/pm_domains.c
@@ -63,12 +63,12 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
}
pwr = power_on ? INT_LOCAL_PWR_EN : 0;
- __raw_writel(pwr, base);
+ writel_relaxed(pwr, base);
/* Wait max 1ms */
timeout = 10;
- while ((__raw_readl(base + 0x4) & INT_LOCAL_PWR_EN) != pwr) {
+ while ((readl_relaxed(base + 0x4) & INT_LOCAL_PWR_EN) != pwr) {
if (!timeout) {
op = (power_on) ? "enable" : "disable";
pr_err("Power domain %s %s failed\n", domain->name, op);
@@ -230,7 +230,7 @@ static __init int exynos4_pm_init_power_domain(void)
no_clk:
platform_set_drvdata(pdev, pd);
- on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN;
+ on = readl_relaxed(pd->base + 0x4) & INT_LOCAL_PWR_EN;
pm_genpd_init(&pd->pd, NULL, !on);
}
diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c
index 8c35ae4ff176..07a09570175d 100644
--- a/arch/arm/mach-highbank/highbank.c
+++ b/arch/arm/mach-highbank/highbank.c
@@ -20,7 +20,7 @@
#include <linux/input.h>
#include <linux/io.h>
#include <linux/irqchip.h>
-#include <linux/mailbox.h>
+#include <linux/pl320-ipc.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
diff --git a/arch/arm/mach-hisi/Kconfig b/arch/arm/mach-hisi/Kconfig
index 984882943f77..075772b34e80 100644
--- a/arch/arm/mach-hisi/Kconfig
+++ b/arch/arm/mach-hisi/Kconfig
@@ -22,6 +22,15 @@ config ARCH_HI3xxx
help
Support for Hisilicon Hi36xx SoC family
+config ARCH_HIP04
+ bool "Hisilicon HiP04 Cortex A15 family" if ARCH_MULTI_V7
+ select ARM_ERRATA_798181 if SMP
+ select HAVE_ARM_ARCH_TIMER
+ select MCPM if SMP
+ select MCPM_QUAD_CLUSTER if SMP
+ help
+ Support for Hisilicon HiP04 SoC family
+
config ARCH_HIX5HD2
bool "Hisilicon X5HD2 family" if ARCH_MULTI_V7
select CACHE_L2X0
diff --git a/arch/arm/mach-hisi/Makefile b/arch/arm/mach-hisi/Makefile
index ee2506b9cde3..d64831e6a782 100644
--- a/arch/arm/mach-hisi/Makefile
+++ b/arch/arm/mach-hisi/Makefile
@@ -3,4 +3,5 @@
#
obj-y += hisilicon.o
+obj-$(CONFIG_MCPM) += platmcpm.o
obj-$(CONFIG_SMP) += platsmp.o hotplug.o headsmp.o
diff --git a/arch/arm/mach-hisi/hisilicon.c b/arch/arm/mach-hisi/hisilicon.c
index 7cda6dda3cd0..7744c351bbfd 100644
--- a/arch/arm/mach-hisi/hisilicon.c
+++ b/arch/arm/mach-hisi/hisilicon.c
@@ -63,3 +63,12 @@ static const char *hix5hd2_compat[] __initconst = {
DT_MACHINE_START(HIX5HD2_DT, "Hisilicon HIX5HD2 (Flattened Device Tree)")
.dt_compat = hix5hd2_compat,
MACHINE_END
+
+static const char *hip04_compat[] __initconst = {
+ "hisilicon,hip04-d01",
+ NULL,
+};
+
+DT_MACHINE_START(HIP04, "Hisilicon HiP04 (Flattened Device Tree)")
+ .dt_compat = hip04_compat,
+MACHINE_END
diff --git a/arch/arm/mach-hisi/platmcpm.c b/arch/arm/mach-hisi/platmcpm.c
new file mode 100644
index 000000000000..280f3f14f77c
--- /dev/null
+++ b/arch/arm/mach-hisi/platmcpm.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2013-2014 Linaro Ltd.
+ * Copyright (c) 2013-2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/memblock.h>
+#include <linux/of_address.h>
+
+#include <asm/cputype.h>
+#include <asm/cp15.h>
+#include <asm/mcpm.h>
+
+#include "core.h"
+
+/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x]
+ * 1 -- unreset; 0 -- reset
+ */
+#define CORE_RESET_BIT(x) (1 << x)
+#define NEON_RESET_BIT(x) (1 << (x + 4))
+#define CORE_DEBUG_RESET_BIT(x) (1 << (x + 9))
+#define CLUSTER_L2_RESET_BIT (1 << 8)
+#define CLUSTER_DEBUG_RESET_BIT (1 << 13)
+
+/*
+ * bits definition in SC_CPU_RESET_STATUS[x]
+ * 1 -- reset status; 0 -- unreset status
+ */
+#define CORE_RESET_STATUS(x) (1 << x)
+#define NEON_RESET_STATUS(x) (1 << (x + 4))
+#define CORE_DEBUG_RESET_STATUS(x) (1 << (x + 9))
+#define CLUSTER_L2_RESET_STATUS (1 << 8)
+#define CLUSTER_DEBUG_RESET_STATUS (1 << 13)
+#define CORE_WFI_STATUS(x) (1 << (x + 16))
+#define CORE_WFE_STATUS(x) (1 << (x + 20))
+#define CORE_DEBUG_ACK(x) (1 << (x + 24))
+
+#define SC_CPU_RESET_REQ(x) (0x520 + (x << 3)) /* reset */
+#define SC_CPU_RESET_DREQ(x) (0x524 + (x << 3)) /* unreset */
+#define SC_CPU_RESET_STATUS(x) (0x1520 + (x << 3))
+
+#define FAB_SF_MODE 0x0c
+#define FAB_SF_INVLD 0x10
+
+/* bits definition in FB_SF_INVLD */
+#define FB_SF_INVLD_START (1 << 8)
+
+#define HIP04_MAX_CLUSTERS 4
+#define HIP04_MAX_CPUS_PER_CLUSTER 4
+
+#define POLL_MSEC 10
+#define TIMEOUT_MSEC 1000
+
+static void __iomem *sysctrl, *fabric;
+static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER];
+static DEFINE_SPINLOCK(boot_lock);
+static u32 fabric_phys_addr;
+/*
+ * [0]: bootwrapper physical address
+ * [1]: bootwrapper size
+ * [2]: relocation address
+ * [3]: relocation size
+ */
+static u32 hip04_boot_method[4];
+
+static bool hip04_cluster_is_down(unsigned int cluster)
+{
+ int i;
+
+ for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++)
+ if (hip04_cpu_table[cluster][i])
+ return false;
+ return true;
+}
+
+static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on)
+{
+ unsigned long data;
+
+ if (!fabric)
+ BUG();
+ data = readl_relaxed(fabric + FAB_SF_MODE);
+ if (on)
+ data |= 1 << cluster;
+ else
+ data &= ~(1 << cluster);
+ writel_relaxed(data, fabric + FAB_SF_MODE);
+ do {
+ cpu_relax();
+ } while (data != readl_relaxed(fabric + FAB_SF_MODE));
+}
+
+static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
+{
+ unsigned long data;
+ void __iomem *sys_dreq, *sys_status;
+
+ if (!sysctrl)
+ return -ENODEV;
+ if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER)
+ return -EINVAL;
+
+ spin_lock_irq(&boot_lock);
+
+ if (hip04_cpu_table[cluster][cpu])
+ goto out;
+
+ sys_dreq = sysctrl + SC_CPU_RESET_DREQ(cluster);
+ sys_status = sysctrl + SC_CPU_RESET_STATUS(cluster);
+ if (hip04_cluster_is_down(cluster)) {
+ data = CLUSTER_DEBUG_RESET_BIT;
+ writel_relaxed(data, sys_dreq);
+ do {
+ cpu_relax();
+ data = readl_relaxed(sys_status);
+ } while (data & CLUSTER_DEBUG_RESET_STATUS);
+ }
+
+ data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \
+ CORE_DEBUG_RESET_BIT(cpu);
+ writel_relaxed(data, sys_dreq);
+ do {
+ cpu_relax();
+ } while (data == readl_relaxed(sys_status));
+ /*
+ * We may fail to power up core again without this delay.
+ * It's not mentioned in document. It's found by test.
+ */
+ udelay(20);
+out:
+ hip04_cpu_table[cluster][cpu]++;
+ spin_unlock_irq(&boot_lock);
+
+ return 0;
+}
+
+static void hip04_mcpm_power_down(void)
+{
+ unsigned int mpidr, cpu, cluster;
+ bool skip_wfi = false, last_man = false;
+
+ mpidr = read_cpuid_mpidr();
+ cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+ cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+ __mcpm_cpu_going_down(cpu, cluster);
+
+ spin_lock(&boot_lock);
+ BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
+ hip04_cpu_table[cluster][cpu]--;
+ if (hip04_cpu_table[cluster][cpu] == 1) {
+ /* A power_up request went ahead of us. */
+ skip_wfi = true;
+ } else if (hip04_cpu_table[cluster][cpu] > 1) {
+ pr_err("Cluster %d CPU%d boots multiple times\n", cluster, cpu);
+ BUG();
+ }
+
+ last_man = hip04_cluster_is_down(cluster);
+ if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
+ spin_unlock(&boot_lock);
+ /* Since it's Cortex A15, disable L2 prefetching. */
+ asm volatile(
+ "mcr p15, 1, %0, c15, c0, 3 \n\t"
+ "isb \n\t"
+ "dsb "
+ : : "r" (0x400) );
+ v7_exit_coherency_flush(all);
+ hip04_set_snoop_filter(cluster, 0);
+ __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
+ } else {
+ spin_unlock(&boot_lock);
+ v7_exit_coherency_flush(louis);
+ }
+
+ __mcpm_cpu_down(cpu, cluster);
+
+ if (!skip_wfi)
+ wfi();
+}
+
+static int hip04_mcpm_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
+{
+ unsigned int data, tries, count;
+ int ret = -ETIMEDOUT;
+
+ BUG_ON(cluster >= HIP04_MAX_CLUSTERS ||
+ cpu >= HIP04_MAX_CPUS_PER_CLUSTER);
+
+ count = TIMEOUT_MSEC / POLL_MSEC;
+ spin_lock_irq(&boot_lock);
+ for (tries = 0; tries < count; tries++) {
+ if (hip04_cpu_table[cluster][cpu]) {
+ ret = -EBUSY;
+ goto err;
+ }
+ cpu_relax();
+ data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster));
+ if (data & CORE_WFI_STATUS(cpu))
+ break;
+ spin_unlock_irq(&boot_lock);
+ /* Wait for clean L2 when the whole cluster is down. */
+ msleep(POLL_MSEC);
+ spin_lock_irq(&boot_lock);
+ }
+ if (tries >= count)
+ goto err;
+ data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \
+ CORE_DEBUG_RESET_BIT(cpu);
+ writel_relaxed(data, sysctrl + SC_CPU_RESET_REQ(cluster));
+ for (tries = 0; tries < count; tries++) {
+ cpu_relax();
+ data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster));
+ if (data & CORE_RESET_STATUS(cpu))
+ break;
+ }
+ if (tries >= count)
+ goto err;
+ spin_unlock_irq(&boot_lock);
+ return 0;
+err:
+ spin_unlock_irq(&boot_lock);
+ return ret;
+}
+
+static void hip04_mcpm_powered_up(void)
+{
+ unsigned int mpidr, cpu, cluster;
+
+ mpidr = read_cpuid_mpidr();
+ cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+ cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+ spin_lock(&boot_lock);
+ if (!hip04_cpu_table[cluster][cpu])
+ hip04_cpu_table[cluster][cpu] = 1;
+ spin_unlock(&boot_lock);
+}
+
+static void __naked hip04_mcpm_power_up_setup(unsigned int affinity_level)
+{
+ asm volatile (" \n"
+" cmp r0, #0 \n"
+" bxeq lr \n"
+ /* calculate fabric phys address */
+" adr r2, 2f \n"
+" ldmia r2, {r1, r3} \n"
+" sub r0, r2, r1 \n"
+" ldr r2, [r0, r3] \n"
+ /* get cluster id from MPIDR */
+" mrc p15, 0, r0, c0, c0, 5 \n"
+" ubfx r1, r0, #8, #8 \n"
+ /* 1 << cluster id */
+" mov r0, #1 \n"
+" mov r3, r0, lsl r1 \n"
+" ldr r0, [r2, #"__stringify(FAB_SF_MODE)"] \n"
+" tst r0, r3 \n"
+" bxne lr \n"
+" orr r1, r0, r3 \n"
+" str r1, [r2, #"__stringify(FAB_SF_MODE)"] \n"
+"1: ldr r0, [r2, #"__stringify(FAB_SF_MODE)"] \n"
+" tst r0, r3 \n"
+" beq 1b \n"
+" bx lr \n"
+
+" .align 2 \n"
+"2: .word . \n"
+" .word fabric_phys_addr \n"
+ );
+}
+
+static const struct mcpm_platform_ops hip04_mcpm_ops = {
+ .power_up = hip04_mcpm_power_up,
+ .power_down = hip04_mcpm_power_down,
+ .wait_for_powerdown = hip04_mcpm_wait_for_powerdown,
+ .powered_up = hip04_mcpm_powered_up,
+};
+
+static bool __init hip04_cpu_table_init(void)
+{
+ unsigned int mpidr, cpu, cluster;
+
+ mpidr = read_cpuid_mpidr();
+ cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+ cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+ if (cluster >= HIP04_MAX_CLUSTERS ||
+ cpu >= HIP04_MAX_CPUS_PER_CLUSTER) {
+ pr_err("%s: boot CPU is out of bound!\n", __func__);
+ return false;
+ }
+ hip04_set_snoop_filter(cluster, 1);
+ hip04_cpu_table[cluster][cpu] = 1;
+ return true;
+}
+
+static int __init hip04_mcpm_init(void)
+{
+ struct device_node *np, *np_sctl, *np_fab;
+ struct resource fab_res;
+ void __iomem *relocation;
+ int ret = -ENODEV;
+
+ np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-bootwrapper");
+ if (!np)
+ goto err;
+ ret = of_property_read_u32_array(np, "boot-method",
+ &hip04_boot_method[0], 4);
+ if (ret)
+ goto err;
+ np_sctl = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
+ if (!np_sctl)
+ goto err;
+ np_fab = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-fabric");
+ if (!np_fab)
+ goto err;
+
+ ret = memblock_reserve(hip04_boot_method[0], hip04_boot_method[1]);
+ if (ret)
+ goto err;
+
+ relocation = ioremap(hip04_boot_method[2], hip04_boot_method[3]);
+ if (!relocation) {
+ pr_err("failed to map relocation space\n");
+ ret = -ENOMEM;
+ goto err_reloc;
+ }
+ sysctrl = of_iomap(np_sctl, 0);
+ if (!sysctrl) {
+ pr_err("failed to get sysctrl base\n");
+ ret = -ENOMEM;
+ goto err_sysctrl;
+ }
+ ret = of_address_to_resource(np_fab, 0, &fab_res);
+ if (ret) {
+ pr_err("failed to get fabric base phys\n");
+ goto err_fabric;
+ }
+ fabric_phys_addr = fab_res.start;
+ sync_cache_w(&fabric_phys_addr);
+ fabric = of_iomap(np_fab, 0);
+ if (!fabric) {
+ pr_err("failed to get fabric base\n");
+ ret = -ENOMEM;
+ goto err_fabric;
+ }
+
+ if (!hip04_cpu_table_init()) {
+ ret = -EINVAL;
+ goto err_table;
+ }
+ ret = mcpm_platform_register(&hip04_mcpm_ops);
+ if (ret) {
+ goto err_table;
+ }
+
+ /*
+ * Fill the instruction address that is used after secondary core
+ * out of reset.
+ */
+ writel_relaxed(hip04_boot_method[0], relocation);
+ writel_relaxed(0xa5a5a5a5, relocation + 4); /* magic number */
+ writel_relaxed(virt_to_phys(mcpm_entry_point), relocation + 8);
+ writel_relaxed(0, relocation + 12);
+ iounmap(relocation);
+
+ mcpm_sync_init(hip04_mcpm_power_up_setup);
+ mcpm_smp_set_ops();
+ pr_info("HiP04 MCPM initialized\n");
+ return ret;
+err_table:
+ iounmap(fabric);
+err_fabric:
+ iounmap(sysctrl);
+err_sysctrl:
+ iounmap(relocation);
+err_reloc:
+ memblock_free(hip04_boot_method[0], hip04_boot_method[1]);
+err:
+ return ret;
+}
+early_initcall(hip04_mcpm_init);
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index e7189dcc9309..06d6b633bf74 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -39,6 +39,7 @@ config ARCH_OMAP4
select PM_RUNTIME if CPU_IDLE
select ARM_ERRATA_754322
select ARM_ERRATA_775420
+ select ARCH_SUPPORTS_BIG_ENDIAN
config SOC_OMAP5
bool "TI OMAP5"
diff --git a/arch/arm/mach-omap2/board-flash.c b/arch/arm/mach-omap2/board-flash.c
index e87f2a83d6bf..2d245c2e641c 100644
--- a/arch/arm/mach-omap2/board-flash.c
+++ b/arch/arm/mach-omap2/board-flash.c
@@ -142,7 +142,7 @@ __init board_nand_init(struct mtd_partition *nand_parts, u8 nr_parts, u8 cs,
board_nand_data.nr_parts = nr_parts;
board_nand_data.devsize = nand_type;
- board_nand_data.ecc_opt = OMAP_ECC_HAM1_CODE_HW;
+ board_nand_data.ecc_opt = OMAP_ECC_HAM1_CODE_SW;
gpmc_nand_init(&board_nand_data, gpmc_t);
}
#endif /* CONFIG_MTD_NAND_OMAP2 || CONFIG_MTD_NAND_OMAP2_MODULE */
diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c
index 8897ad7035fd..cb7764314f17 100644
--- a/arch/arm/mach-omap2/gpmc-nand.c
+++ b/arch/arm/mach-omap2/gpmc-nand.c
@@ -49,7 +49,8 @@ static bool gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt)
return 0;
/* legacy platforms support only HAM1 (1-bit Hamming) ECC scheme */
- if (ecc_opt == OMAP_ECC_HAM1_CODE_HW)
+ if (ecc_opt == OMAP_ECC_HAM1_CODE_HW ||
+ ecc_opt == OMAP_ECC_HAM1_CODE_SW)
return 1;
else
return 0;
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
index 8bc13380f0a0..9f42d5437fcc 100644
--- a/arch/arm/mach-omap2/gpmc.c
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -1403,8 +1403,11 @@ static int gpmc_probe_nand_child(struct platform_device *pdev,
pr_err("%s: ti,nand-ecc-opt not found\n", __func__);
return -ENODEV;
}
- if (!strcmp(s, "ham1") || !strcmp(s, "sw") ||
- !strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
+
+ if (!strcmp(s, "sw"))
+ gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
+ else if (!strcmp(s, "ham1") ||
+ !strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
gpmc_nand_data->ecc_opt =
OMAP_ECC_HAM1_CODE_HW;
else if (!strcmp(s, "bch4"))
diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c
index d42022f2a71e..53841dea80ea 100644
--- a/arch/arm/mach-omap2/id.c
+++ b/arch/arm/mach-omap2/id.c
@@ -663,7 +663,7 @@ void __init dra7xxx_check_revision(void)
default:
/* Unknown default to latest silicon rev as default*/
- pr_warn("%s: unknown idcode=0x%08x (hawkeye=0x%08x,rev=0x%d)\n",
+ pr_warn("%s: unknown idcode=0x%08x (hawkeye=0x%08x,rev=0x%x)\n",
__func__, idcode, hawkeye, rev);
omap_revision = DRA752_REV_ES1_1;
}
diff --git a/arch/arm/mach-omap2/omap-headsmp.S b/arch/arm/mach-omap2/omap-headsmp.S
index 4993d4bfe9b2..998190a7b0f9 100644
--- a/arch/arm/mach-omap2/omap-headsmp.S
+++ b/arch/arm/mach-omap2/omap-headsmp.S
@@ -17,6 +17,7 @@
#include <linux/linkage.h>
#include <linux/init.h>
+#include <asm/assembler.h>
#include "omap44xx.h"
@@ -58,6 +59,12 @@ hold: ldr r12,=0x103
bne hold
/*
+ * ROM code operates in little endian mode, when we get control we
+ * need to switch it back to big endian mode.
+ */
+ARM_BE8(setend be)
+
+ /*
* we've been released from the wait loop,secondary_stack
* should now contain the SVC stack for this core
*/
@@ -75,6 +82,12 @@ hold_2: ldr r12,=0x103
bne hold_2
/*
+ * ROM code operates in little endian mode, when we get control we
+ * need to switch it back to big endian mode.
+ */
+ARM_BE8(setend be)
+
+ /*
* GIC distributor control register has changed between
* CortexA9 r1pX and r2pX. The Control Register secure
* banked version is now composed of 2 bits:
diff --git a/arch/arm/mach-omap2/omap_device.c b/arch/arm/mach-omap2/omap_device.c
index 01ef59def44b..d22c30d3ccfa 100644
--- a/arch/arm/mach-omap2/omap_device.c
+++ b/arch/arm/mach-omap2/omap_device.c
@@ -56,7 +56,7 @@ static void _add_clkdev(struct omap_device *od, const char *clk_alias,
r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias);
if (!IS_ERR(r)) {
- dev_warn(&od->pdev->dev,
+ dev_dbg(&od->pdev->dev,
"alias %s already exists\n", clk_alias);
clk_put(r);
return;
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 6c074f37cdd2..8fd87a3055bf 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -2185,6 +2185,8 @@ static int _enable(struct omap_hwmod *oh)
oh->mux->pads_dynamic))) {
omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
_reconfigure_io_chain();
+ } else if (oh->flags & HWMOD_FORCE_MSTANDBY) {
+ _reconfigure_io_chain();
}
_add_initiator_dep(oh, mpu_oh);
@@ -2291,6 +2293,8 @@ static int _idle(struct omap_hwmod *oh)
if (oh->mux && oh->mux->pads_dynamic) {
omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE);
_reconfigure_io_chain();
+ } else if (oh->flags & HWMOD_FORCE_MSTANDBY) {
+ _reconfigure_io_chain();
}
oh->_state = _HWMOD_STATE_IDLE;
@@ -3345,6 +3349,9 @@ int __init omap_hwmod_register_links(struct omap_hwmod_ocp_if **ois)
if (!ois)
return 0;
+ if (ois[0] == NULL) /* Empty list */
+ return 0;
+
if (!linkspace) {
if (_alloc_linkspace(ois)) {
pr_err("omap_hwmod: could not allocate link space\n");
diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c
index 2757abf87fbc..5684f112654b 100644
--- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c
@@ -35,6 +35,7 @@
#include "i2c.h"
#include "mmc.h"
#include "wd_timer.h"
+#include "soc.h"
/* Base offset for all DRA7XX interrupts external to MPUSS */
#define DRA7XX_IRQ_GIC_START 32
@@ -3261,7 +3262,6 @@ static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
&dra7xx_l4_per3__usb_otg_ss1,
&dra7xx_l4_per3__usb_otg_ss2,
&dra7xx_l4_per3__usb_otg_ss3,
- &dra7xx_l4_per3__usb_otg_ss4,
&dra7xx_l3_main_1__vcp1,
&dra7xx_l4_per2__vcp1,
&dra7xx_l3_main_1__vcp2,
@@ -3270,8 +3270,26 @@ static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
NULL,
};
+static struct omap_hwmod_ocp_if *dra74x_hwmod_ocp_ifs[] __initdata = {
+ &dra7xx_l4_per3__usb_otg_ss4,
+ NULL,
+};
+
+static struct omap_hwmod_ocp_if *dra72x_hwmod_ocp_ifs[] __initdata = {
+ NULL,
+};
+
int __init dra7xx_hwmod_init(void)
{
+ int ret;
+
omap_hwmod_init();
- return omap_hwmod_register_links(dra7xx_hwmod_ocp_ifs);
+ ret = omap_hwmod_register_links(dra7xx_hwmod_ocp_ifs);
+
+ if (!ret && soc_is_dra74x())
+ return omap_hwmod_register_links(dra74x_hwmod_ocp_ifs);
+ else if (!ret && soc_is_dra72x())
+ return omap_hwmod_register_links(dra72x_hwmod_ocp_ifs);
+
+ return ret;
}
diff --git a/arch/arm/mach-omap2/sleep44xx.S b/arch/arm/mach-omap2/sleep44xx.S
index b84a0122d823..317599665bf6 100644
--- a/arch/arm/mach-omap2/sleep44xx.S
+++ b/arch/arm/mach-omap2/sleep44xx.S
@@ -13,6 +13,7 @@
#include <asm/assembler.h>
#include <asm/smp_scu.h>
#include <asm/memory.h>
+#include <asm/assembler.h>
#include <asm/hardware/cache-l2x0.h>
#include "omap-secure.h"
@@ -75,6 +76,7 @@ ENTRY(omap4_finish_suspend)
*/
bl omap4_get_sar_ram_base
ldr r9, [r0, #OMAP_TYPE_OFFSET]
+ARM_BE8(rev r9, r9)
cmp r9, #0x1 @ Check for HS device
bne skip_secure_l1_clean
mov r0, #SCU_PM_NORMAL
@@ -114,12 +116,14 @@ skip_secure_l1_clean:
bl omap4_get_sar_ram_base
mov r8, r0
ldr r9, [r8, #OMAP_TYPE_OFFSET]
+ARM_BE8(rev r9, r9)
cmp r9, #0x1 @ Check for HS device
bne scu_gp_set
mrc p15, 0, r0, c0, c0, 5 @ Read MPIDR
ands r0, r0, #0x0f
ldreq r0, [r8, #SCU_OFFSET0]
ldrne r0, [r8, #SCU_OFFSET1]
+ARM_BE8(rev r0, r0)
mov r1, #0x00
stmfd r13!, {r4-r12, r14}
ldr r12, =OMAP4_MON_SCU_PWR_INDEX
@@ -131,6 +135,7 @@ scu_gp_set:
ands r0, r0, #0x0f
ldreq r1, [r8, #SCU_OFFSET0]
ldrne r1, [r8, #SCU_OFFSET1]
+ARM_BE8(rev r1, r1)
bl omap4_get_scu_base
bl scu_power_mode
skip_scu_gp_set:
@@ -158,6 +163,7 @@ skip_scu_gp_set:
ands r5, r5, #0x0f
ldreq r0, [r8, #L2X0_SAVE_OFFSET0] @ Retrieve L2 state from SAR
ldrne r0, [r8, #L2X0_SAVE_OFFSET1] @ memory.
+ARM_BE8(rev r0, r0)
cmp r0, #3
bne do_WFI
#ifdef CONFIG_PL310_ERRATA_727915
@@ -168,9 +174,11 @@ skip_scu_gp_set:
bl omap4_get_l2cache_base
mov r2, r0
ldr r0, =0xffff
+ARM_BE8(rev r0, r0)
str r0, [r2, #L2X0_CLEAN_INV_WAY]
wait:
ldr r0, [r2, #L2X0_CLEAN_INV_WAY]
+ARM_BE8(rev r0, r0)
ldr r1, =0xffff
ands r0, r0, r1
bne wait
@@ -183,9 +191,11 @@ l2x_sync:
bl omap4_get_l2cache_base
mov r2, r0
mov r0, #0x0
+ARM_BE8(rev r0, r0)
str r0, [r2, #L2X0_CACHE_SYNC]
sync:
ldr r0, [r2, #L2X0_CACHE_SYNC]
+ARM_BE8(rev r0, r0)
ands r0, r0, #0x1
bne sync
#endif
@@ -217,6 +227,7 @@ do_WFI:
bl omap4_get_sar_ram_base
mov r8, r0
ldr r9, [r8, #OMAP_TYPE_OFFSET]
+ARM_BE8(rev r9, r9)
cmp r9, #0x1 @ Check for HS device
bne scu_gp_clear
mov r0, #SCU_PM_NORMAL
@@ -250,6 +261,12 @@ ENDPROC(omap4_finish_suspend)
*/
ENTRY(omap4_cpu_resume)
/*
+ * ROM code operates in little endian mode, when we get control we
+ * need to switch it back to big endian mode.
+ */
+ARM_BE8(setend be)
+
+ /*
* Configure ACTRL and enable NS SMP bit access on CPU1 on HS device.
* OMAP44XX EMU/HS devices - CPU0 SMP bit access is enabled in PPA
* init and for CPU1, a secure PPA API provided. CPU0 must be ON
@@ -259,6 +276,7 @@ ENTRY(omap4_cpu_resume)
*/
ldr r8, =OMAP44XX_SAR_RAM_BASE
ldr r9, [r8, #OMAP_TYPE_OFFSET]
+ARM_BE8(rev r9, r9)
cmp r9, #0x1 @ Skip if GP device
bne skip_ns_smp_enable
mrc p15, 0, r0, c0, c0, 5
@@ -293,16 +311,19 @@ skip_ns_smp_enable:
*/
ldr r2, =OMAP44XX_L2CACHE_BASE
ldr r0, [r2, #L2X0_CTRL]
+ARM_BE8(rev r0, r0)
and r0, #0x0f
cmp r0, #1
beq skip_l2en @ Skip if already enabled
ldr r3, =OMAP44XX_SAR_RAM_BASE
ldr r1, [r3, #OMAP_TYPE_OFFSET]
+ARM_BE8(rev r1, r1)
cmp r1, #0x1 @ Check for HS device
bne set_gp_por
ldr r0, =OMAP4_PPA_L2_POR_INDEX
ldr r1, =OMAP44XX_SAR_RAM_BASE
ldr r4, [r1, #L2X0_PREFETCH_CTRL_OFFSET]
+ARM_BE8(rev r4, r4)
adr r3, ppa_por_params
str r4, [r3, #0x04]
mov r1, #0x0 @ Process ID
@@ -314,11 +335,13 @@ skip_ns_smp_enable:
set_gp_por:
ldr r1, =OMAP44XX_SAR_RAM_BASE
ldr r0, [r1, #L2X0_PREFETCH_CTRL_OFFSET]
+ARM_BE8(rev r0, r0)
ldr r12, =OMAP4_MON_L2X0_PREFETCH_INDEX @ Setup L2 PREFETCH
DO_SMC
set_aux_ctrl:
ldr r1, =OMAP44XX_SAR_RAM_BASE
ldr r0, [r1, #L2X0_AUXCTRL_OFFSET]
+ARM_BE8(rev r0, r0)
ldr r12, =OMAP4_MON_L2X0_AUXCTRL_INDEX @ Setup L2 AUXCTRL
DO_SMC
mov r0, #0x1
diff --git a/arch/arm/mach-omap2/soc.h b/arch/arm/mach-omap2/soc.h
index 01ca8086fb6c..4376f59626d1 100644
--- a/arch/arm/mach-omap2/soc.h
+++ b/arch/arm/mach-omap2/soc.h
@@ -245,6 +245,8 @@ IS_AM_SUBCLASS(437x, 0x437)
#define soc_is_omap54xx() 0
#define soc_is_omap543x() 0
#define soc_is_dra7xx() 0
+#define soc_is_dra74x() 0
+#define soc_is_dra72x() 0
#if defined(MULTI_OMAP2)
# if defined(CONFIG_ARCH_OMAP2)
@@ -393,7 +395,11 @@ IS_OMAP_TYPE(3430, 0x3430)
#if defined(CONFIG_SOC_DRA7XX)
#undef soc_is_dra7xx
+#undef soc_is_dra74x
+#undef soc_is_dra72x
#define soc_is_dra7xx() (of_machine_is_compatible("ti,dra7"))
+#define soc_is_dra74x() (of_machine_is_compatible("ti,dra74"))
+#define soc_is_dra72x() (of_machine_is_compatible("ti,dra72"))
#endif
/* Various silicon revisions for omap2 */
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index ee5697ba05bc..3b5539cc907b 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -5,6 +5,8 @@ menuconfig ARCH_QCOM
select ARM_AMBA
select CLKSRC_OF
select PINCTRL
+ select MIGHT_HAVE_PCI
+ select PCI_DOMAINS if PCI
select QCOM_SCM if SMP
help
Support for Qualcomm's devicetree based systems.
diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c
index c437a9941726..a0e9b477fabf 100644
--- a/arch/arm/mach-qcom/board.c
+++ b/arch/arm/mach-qcom/board.c
@@ -11,6 +11,10 @@
*/
#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
#include <asm/mach/arch.h>
@@ -23,6 +27,31 @@ static const char * const qcom_dt_match[] __initconst = {
NULL
};
+static void __init qcom_late_init(void)
+{
+ struct device_node *node;
+ int reset_gpio, ret;
+
+ for_each_compatible_node(node, NULL, "atheros,ath6kl") {
+ of_node_put(node);
+
+ reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
+ if (reset_gpio < 0)
+ return;
+
+ ret = gpio_request_one(reset_gpio,
+ GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "reset");
+ if (ret)
+ return;
+
+ udelay(100);
+ gpio_set_value(reset_gpio, 0);
+ udelay(100);
+ gpio_set_value(reset_gpio, 1);
+ }
+}
+
DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)")
- .dt_compat = qcom_dt_match,
+ .init_late = qcom_late_init,
+ .dt_compat = qcom_dt_match,
MACHINE_END
diff --git a/arch/arm/mach-shmobile/clock-r8a7790.c b/arch/arm/mach-shmobile/clock-r8a7790.c
index 17435c1aa2fe..126ddafad526 100644
--- a/arch/arm/mach-shmobile/clock-r8a7790.c
+++ b/arch/arm/mach-shmobile/clock-r8a7790.c
@@ -183,8 +183,8 @@ enum {
static struct clk div4_clks[DIV4_NR] = {
[DIV4_SDH] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 8, 0x0dff, CLK_ENABLE_ON_INIT),
- [DIV4_SD0] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 4, 0x1de0, CLK_ENABLE_ON_INIT),
- [DIV4_SD1] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 0, 0x1de0, CLK_ENABLE_ON_INIT),
+ [DIV4_SD0] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 4, 0x1df0, CLK_ENABLE_ON_INIT),
+ [DIV4_SD1] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 0, 0x1df0, CLK_ENABLE_ON_INIT),
};
/* DIV6 clocks */
diff --git a/arch/arm/mach-shmobile/clock-r8a7791.c b/arch/arm/mach-shmobile/clock-r8a7791.c
index 10e193d707f5..453b23129cfa 100644
--- a/arch/arm/mach-shmobile/clock-r8a7791.c
+++ b/arch/arm/mach-shmobile/clock-r8a7791.c
@@ -152,7 +152,7 @@ enum {
static struct clk div4_clks[DIV4_NR] = {
[DIV4_SDH] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 8, 0x0dff, CLK_ENABLE_ON_INIT),
- [DIV4_SD0] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 4, 0x1de0, CLK_ENABLE_ON_INIT),
+ [DIV4_SD0] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 4, 0x1df0, CLK_ENABLE_ON_INIT),
};
/* DIV6 clocks */
diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c
index d8c4048b9e33..02a6f45a0b9e 100644
--- a/arch/arm/mach-shmobile/clock-sh73a0.c
+++ b/arch/arm/mach-shmobile/clock-sh73a0.c
@@ -644,7 +644,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP207]), /* SCIFA5 */
CLKDEV_DEV_ID("e6cb0000.serial", &mstp_clks[MSTP207]), /* SCIFA5 */
CLKDEV_DEV_ID("sh-sci.8", &mstp_clks[MSTP206]), /* SCIFB */
- CLKDEV_DEV_ID("0xe6c3000.serial", &mstp_clks[MSTP206]), /* SCIFB */
+ CLKDEV_DEV_ID("e6c3000.serial", &mstp_clks[MSTP206]), /* SCIFB */
CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]), /* SCIFA0 */
CLKDEV_DEV_ID("e6c40000.serial", &mstp_clks[MSTP204]), /* SCIFA0 */
CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]), /* SCIFA1 */
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig
index b2cfba16c4e8..1c41572cc95e 100644
--- a/arch/arm/mach-vexpress/Kconfig
+++ b/arch/arm/mach-vexpress/Kconfig
@@ -51,6 +51,7 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA
config ARCH_VEXPRESS_CA9X4
bool "Versatile Express Cortex-A9x4 tile"
+ select ARM_ERRATA_643719
config ARCH_VEXPRESS_DCSCB
bool "Dual Cluster System Control Block (DCSCB) support"
diff --git a/arch/arm/mach-vexpress/spc.c b/arch/arm/mach-vexpress/spc.c
index 2c2754e79cb3..f61158c6ce71 100644
--- a/arch/arm/mach-vexpress/spc.c
+++ b/arch/arm/mach-vexpress/spc.c
@@ -426,9 +426,15 @@ static int ve_spc_populate_opps(uint32_t cluster)
static int ve_init_opp_table(struct device *cpu_dev)
{
- int cluster = topology_physical_package_id(cpu_dev->id);
- int idx, ret = 0, max_opp = info->num_opps[cluster];
- struct ve_spc_opp *opps = info->opps[cluster];
+ int cluster;
+ int idx, ret = 0, max_opp;
+ struct ve_spc_opp *opps;
+
+ cluster = topology_physical_package_id(cpu_dev->id);
+ cluster = cluster < 0 ? 0 : cluster;
+
+ max_opp = info->num_opps[cluster];
+ opps = info->opps[cluster];
for (idx = 0; idx < max_opp; idx++, opps++) {
ret = dev_pm_opp_add(cpu_dev, opps->freq * 1000, opps->u_volt);
@@ -537,6 +543,8 @@ static struct clk *ve_spc_clk_register(struct device *cpu_dev)
spc->hw.init = &init;
spc->cluster = topology_physical_package_id(cpu_dev->id);
+ spc->cluster = spc->cluster < 0 ? 0 : spc->cluster;
+
init.name = dev_name(cpu_dev);
init.ops = &clk_spc_ops;
init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c
index 6ff681a24ba7..62632735a085 100644
--- a/arch/arm/mach-vexpress/v2m.c
+++ b/arch/arm/mach-vexpress/v2m.c
@@ -7,6 +7,7 @@
#include <linux/io.h>
#include <linux/smp.h>
#include <linux/init.h>
+#include <linux/memblock.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
@@ -370,6 +371,36 @@ MACHINE_START(VEXPRESS, "ARM-Versatile Express")
.init_machine = v2m_init,
MACHINE_END
+static void __init v2m_dt_hdlcd_init(void)
+{
+ struct device_node *node;
+ int len, na, ns;
+ const __be32 *prop;
+ phys_addr_t fb_base, fb_size;
+
+ node = of_find_compatible_node(NULL, NULL, "arm,hdlcd");
+ if (!node)
+ return;
+
+ na = of_n_addr_cells(node);
+ ns = of_n_size_cells(node);
+
+ prop = of_get_property(node, "framebuffer", &len);
+ if (WARN_ON(!prop || len < (na + ns) * sizeof(*prop)))
+ return;
+
+ fb_base = of_read_number(prop, na);
+ fb_size = of_read_number(prop + na, ns);
+
+ if (WARN_ON(memblock_remove(fb_base, fb_size)))
+ return;
+};
+
+void __init v2m_dt_init_early(void)
+{
+ v2m_dt_hdlcd_init();
+}
+
static void __init v2m_dt_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
@@ -386,5 +417,6 @@ DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
.l2c_aux_mask = 0xfe0fffff,
.smp = smp_ops(vexpress_smp_dt_ops),
.smp_init = smp_init_ops(vexpress_smp_init_ops),
+ .init_early = v2m_dt_init_early,
.init_machine = v2m_dt_init,
MACHINE_END
diff --git a/arch/arm/mm/abort-ev6.S b/arch/arm/mm/abort-ev6.S
index 3815a8262af0..8c48c5c22a33 100644
--- a/arch/arm/mm/abort-ev6.S
+++ b/arch/arm/mm/abort-ev6.S
@@ -17,12 +17,6 @@
*/
.align 5
ENTRY(v6_early_abort)
-#ifdef CONFIG_CPU_V6
- sub r1, sp, #4 @ Get unused stack location
- strex r0, r1, [r1] @ Clear the exclusive monitor
-#elif defined(CONFIG_CPU_32v6K)
- clrex
-#endif
mrc p15, 0, r1, c5, c0, 0 @ get FSR
mrc p15, 0, r0, c6, c0, 0 @ get FAR
/*
diff --git a/arch/arm/mm/abort-ev7.S b/arch/arm/mm/abort-ev7.S
index 703375277ba6..4812ad054214 100644
--- a/arch/arm/mm/abort-ev7.S
+++ b/arch/arm/mm/abort-ev7.S
@@ -13,12 +13,6 @@
*/
.align 5
ENTRY(v7_early_abort)
- /*
- * The effect of data aborts on on the exclusive access monitor are
- * UNPREDICTABLE. Do a CLREX to clear the state
- */
- clrex
-
mrc p15, 0, r1, c5, c0, 0 @ get FSR
mrc p15, 0, r0, c6, c0, 0 @ get FAR
diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S
index 24659952c278..11da0f50a1fe 100644
--- a/arch/arm/mm/cache-v6.S
+++ b/arch/arm/mm/cache-v6.S
@@ -270,6 +270,11 @@ v6_dma_clean_range:
* - end - virtual end address of region
*/
ENTRY(v6_dma_flush_range)
+#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT
+ sub r2, r1, r0
+ cmp r2, #CONFIG_CACHE_FLUSH_RANGE_LIMIT
+ bhi v6_dma_flush_dcache_all
+#endif
#ifdef CONFIG_DMA_CACHE_RWFO
ldrb r2, [r0] @ read for ownership
strb r2, [r0] @ write for ownership
@@ -292,6 +297,18 @@ ENTRY(v6_dma_flush_range)
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
ret lr
+#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT
+v6_dma_flush_dcache_all:
+ mov r0, #0
+#ifdef HARVARD_CACHE
+ mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate
+#else
+ mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate
+#endif
+ mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
+ mov pc, lr
+#endif
+
/*
* dma_map_area(start, size, dir)
* - start - kernel virtual start address
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index eb8830a4c5ed..b784579545e8 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -274,10 +274,10 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
local_irq_enable();
/*
- * If we're in an interrupt or have no user
+ * If we're in an interrupt, or have no irqs, or have no user
* context, we must not take the fault..
*/
- if (in_atomic() || !mm)
+ if (in_atomic() || irqs_disabled() || !mm)
goto no_context;
if (user_mode(regs))
diff --git a/arch/arm/plat-samsung/cpu.c b/arch/arm/plat-samsung/cpu.c
index 360618ee39e5..4dcbd1932520 100644
--- a/arch/arm/plat-samsung/cpu.c
+++ b/arch/arm/plat-samsung/cpu.c
@@ -29,14 +29,14 @@ EXPORT_SYMBOL(samsung_rev);
void __init s3c64xx_init_cpu(void)
{
- samsung_cpu_id = __raw_readl(S3C_VA_SYS + 0x118);
+ samsung_cpu_id = readl_relaxed(S3C_VA_SYS + 0x118);
if (!samsung_cpu_id) {
/*
* S3C6400 has the ID register in a different place,
* and needs a write before it can be read.
*/
- __raw_writel(0x0, S3C_VA_SYS + 0xA1C);
- samsung_cpu_id = __raw_readl(S3C_VA_SYS + 0xA1C);
+ writel_relaxed(0x0, S3C_VA_SYS + 0xA1C);
+ samsung_cpu_id = readl_relaxed(S3C_VA_SYS + 0xA1C);
}
samsung_cpu_rev = 0;
@@ -44,6 +44,6 @@ void __init s3c64xx_init_cpu(void)
void __init s5p_init_cpu(void __iomem *cpuid_addr)
{
- samsung_cpu_id = __raw_readl(cpuid_addr);
+ samsung_cpu_id = readl_relaxed(cpuid_addr);
samsung_cpu_rev = samsung_cpu_id & 0xFF;
}
diff --git a/arch/arm/plat-samsung/pm-common.c b/arch/arm/plat-samsung/pm-common.c
index 515cd53372bd..6534c3ff9fe2 100644
--- a/arch/arm/plat-samsung/pm-common.c
+++ b/arch/arm/plat-samsung/pm-common.c
@@ -31,7 +31,7 @@
void s3c_pm_do_save(struct sleep_save *ptr, int count)
{
for (; count > 0; count--, ptr++) {
- ptr->val = __raw_readl(ptr->reg);
+ ptr->val = readl_relaxed(ptr->reg);
S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
}
}
@@ -51,9 +51,9 @@ void s3c_pm_do_restore(const struct sleep_save *ptr, int count)
{
for (; count > 0; count--, ptr++) {
pr_debug("restore %p (restore %08lx, was %08x)\n",
- ptr->reg, ptr->val, __raw_readl(ptr->reg));
+ ptr->reg, ptr->val, readl_relaxed(ptr->reg));
- __raw_writel(ptr->val, ptr->reg);
+ writel_relaxed(ptr->val, ptr->reg);
}
}
@@ -71,5 +71,5 @@ void s3c_pm_do_restore(const struct sleep_save *ptr, int count)
void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count)
{
for (; count > 0; count--, ptr++)
- __raw_writel(ptr->val, ptr->reg);
+ writel_relaxed(ptr->val, ptr->reg);
}
diff --git a/arch/arm/plat-samsung/pm-gpio.c b/arch/arm/plat-samsung/pm-gpio.c
index f9a09262f2fa..75a3d386b4d9 100644
--- a/arch/arm/plat-samsung/pm-gpio.c
+++ b/arch/arm/plat-samsung/pm-gpio.c
@@ -32,15 +32,15 @@
static void samsung_gpio_pm_1bit_save(struct samsung_gpio_chip *chip)
{
- chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
- chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
+ chip->pm_save[0] = readl_relaxed(chip->base + OFFS_CON);
+ chip->pm_save[1] = readl_relaxed(chip->base + OFFS_DAT);
}
static void samsung_gpio_pm_1bit_resume(struct samsung_gpio_chip *chip)
{
void __iomem *base = chip->base;
- u32 old_gpcon = __raw_readl(base + OFFS_CON);
- u32 old_gpdat = __raw_readl(base + OFFS_DAT);
+ u32 old_gpcon = readl_relaxed(base + OFFS_CON);
+ u32 old_gpdat = readl_relaxed(base + OFFS_DAT);
u32 gps_gpcon = chip->pm_save[0];
u32 gps_gpdat = chip->pm_save[1];
u32 gpcon;
@@ -51,12 +51,12 @@ static void samsung_gpio_pm_1bit_resume(struct samsung_gpio_chip *chip)
/* first set all SFN bits to SFN */
gpcon = old_gpcon | gps_gpcon;
- __raw_writel(gpcon, base + OFFS_CON);
+ writel_relaxed(gpcon, base + OFFS_CON);
/* now set all the other bits */
- __raw_writel(gps_gpdat, base + OFFS_DAT);
- __raw_writel(gps_gpcon, base + OFFS_CON);
+ writel_relaxed(gps_gpdat, base + OFFS_DAT);
+ writel_relaxed(gps_gpcon, base + OFFS_CON);
S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
@@ -69,9 +69,9 @@ struct samsung_gpio_pm samsung_gpio_pm_1bit = {
static void samsung_gpio_pm_2bit_save(struct samsung_gpio_chip *chip)
{
- chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
- chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
- chip->pm_save[2] = __raw_readl(chip->base + OFFS_UP);
+ chip->pm_save[0] = readl_relaxed(chip->base + OFFS_CON);
+ chip->pm_save[1] = readl_relaxed(chip->base + OFFS_DAT);
+ chip->pm_save[2] = readl_relaxed(chip->base + OFFS_UP);
}
/* Test whether the given masked+shifted bits of an GPIO configuration
@@ -126,8 +126,8 @@ static inline int is_out(unsigned long con)
static void samsung_gpio_pm_2bit_resume(struct samsung_gpio_chip *chip)
{
void __iomem *base = chip->base;
- u32 old_gpcon = __raw_readl(base + OFFS_CON);
- u32 old_gpdat = __raw_readl(base + OFFS_DAT);
+ u32 old_gpcon = readl_relaxed(base + OFFS_CON);
+ u32 old_gpdat = readl_relaxed(base + OFFS_DAT);
u32 gps_gpcon = chip->pm_save[0];
u32 gps_gpdat = chip->pm_save[1];
u32 gpcon, old, new, mask;
@@ -135,7 +135,7 @@ static void samsung_gpio_pm_2bit_resume(struct samsung_gpio_chip *chip)
int nr;
/* restore GPIO pull-up settings */
- __raw_writel(chip->pm_save[2], base + OFFS_UP);
+ writel_relaxed(chip->pm_save[2], base + OFFS_UP);
/* Create a change_mask of all the items that need to have
* their CON value changed before their DAT value, so that
@@ -178,12 +178,12 @@ static void samsung_gpio_pm_2bit_resume(struct samsung_gpio_chip *chip)
gpcon = old_gpcon & ~change_mask;
gpcon |= gps_gpcon & change_mask;
- __raw_writel(gpcon, base + OFFS_CON);
+ writel_relaxed(gpcon, base + OFFS_CON);
/* Now change any items that require DAT,CON */
- __raw_writel(gps_gpdat, base + OFFS_DAT);
- __raw_writel(gps_gpcon, base + OFFS_CON);
+ writel_relaxed(gps_gpdat, base + OFFS_DAT);
+ writel_relaxed(gps_gpcon, base + OFFS_CON);
S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
@@ -197,12 +197,12 @@ struct samsung_gpio_pm samsung_gpio_pm_2bit = {
#if defined(CONFIG_ARCH_S3C64XX)
static void samsung_gpio_pm_4bit_save(struct samsung_gpio_chip *chip)
{
- chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON);
- chip->pm_save[2] = __raw_readl(chip->base + OFFS_DAT);
- chip->pm_save[3] = __raw_readl(chip->base + OFFS_UP);
+ chip->pm_save[1] = readl_relaxed(chip->base + OFFS_CON);
+ chip->pm_save[2] = readl_relaxed(chip->base + OFFS_DAT);
+ chip->pm_save[3] = readl_relaxed(chip->base + OFFS_UP);
if (chip->chip.ngpio > 8)
- chip->pm_save[0] = __raw_readl(chip->base - 4);
+ chip->pm_save[0] = readl_relaxed(chip->base - 4);
}
static u32 samsung_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon)
@@ -247,7 +247,7 @@ static u32 samsung_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon)
static void samsung_gpio_pm_4bit_con(struct samsung_gpio_chip *chip, int index)
{
void __iomem *con = chip->base + (index * 4);
- u32 old_gpcon = __raw_readl(con);
+ u32 old_gpcon = readl_relaxed(con);
u32 gps_gpcon = chip->pm_save[index + 1];
u32 gpcon, mask;
@@ -256,47 +256,47 @@ static void samsung_gpio_pm_4bit_con(struct samsung_gpio_chip *chip, int index)
gpcon = old_gpcon & ~mask;
gpcon |= gps_gpcon & mask;
- __raw_writel(gpcon, con);
+ writel_relaxed(gpcon, con);
}
static void samsung_gpio_pm_4bit_resume(struct samsung_gpio_chip *chip)
{
void __iomem *base = chip->base;
u32 old_gpcon[2];
- u32 old_gpdat = __raw_readl(base + OFFS_DAT);
+ u32 old_gpdat = readl_relaxed(base + OFFS_DAT);
u32 gps_gpdat = chip->pm_save[2];
/* First, modify the CON settings */
old_gpcon[0] = 0;
- old_gpcon[1] = __raw_readl(base + OFFS_CON);
+ old_gpcon[1] = readl_relaxed(base + OFFS_CON);
samsung_gpio_pm_4bit_con(chip, 0);
if (chip->chip.ngpio > 8) {
- old_gpcon[0] = __raw_readl(base - 4);
+ old_gpcon[0] = readl_relaxed(base - 4);
samsung_gpio_pm_4bit_con(chip, -1);
}
/* Now change the configurations that require DAT,CON */
- __raw_writel(chip->pm_save[2], base + OFFS_DAT);
- __raw_writel(chip->pm_save[1], base + OFFS_CON);
+ writel_relaxed(chip->pm_save[2], base + OFFS_DAT);
+ writel_relaxed(chip->pm_save[1], base + OFFS_CON);
if (chip->chip.ngpio > 8)
- __raw_writel(chip->pm_save[0], base - 4);
+ writel_relaxed(chip->pm_save[0], base - 4);
- __raw_writel(chip->pm_save[2], base + OFFS_DAT);
- __raw_writel(chip->pm_save[3], base + OFFS_UP);
+ writel_relaxed(chip->pm_save[2], base + OFFS_DAT);
+ writel_relaxed(chip->pm_save[3], base + OFFS_UP);
if (chip->chip.ngpio > 8) {
S3C_PMDBG("%s: CON4 %08x,%08x => %08x,%08x, DAT %08x => %08x\n",
chip->chip.label, old_gpcon[0], old_gpcon[1],
- __raw_readl(base - 4),
- __raw_readl(base + OFFS_CON),
+ readl_relaxed(base - 4),
+ readl_relaxed(base + OFFS_CON),
old_gpdat, gps_gpdat);
} else
S3C_PMDBG("%s: CON4 %08x => %08x, DAT %08x => %08x\n",
chip->chip.label, old_gpcon[1],
- __raw_readl(base + OFFS_CON),
+ readl_relaxed(base + OFFS_CON),
old_gpdat, gps_gpdat);
}
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index fd4e81a4e1ce..c6080a322663 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -17,7 +17,7 @@ config ARM64
select CLONE_BACKWARDS
select COMMON_CLK
select CPU_PM if (SUSPEND || CPU_IDLE)
- select DCACHE_WORD_ACCESS
+ select DCACHE_WORD_ACCESS if !CPU_BIG_ENDIAN
select GENERIC_CLOCKEVENTS
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select GENERIC_CPU_AUTOPROBE
diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile
index c52bdb051f66..f88dc106d862 100644
--- a/arch/arm64/boot/dts/Makefile
+++ b/arch/arm64/boot/dts/Makefile
@@ -1,4 +1,5 @@
-dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb foundation-v8.dtb
+dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb foundation-v8.dtb fvp-base-gicv2legacy-psci.dtb fvp-base-gicv2-psci.dtb fvp-base-gicv3-psci.dtb fvp-foundation-gicv2legacy-psci.dtb fvp-foundation-gicv2-psci.dtb fvp-foundation-gicv3-psci.dtb
+dtb-$(CONFIG_ARCH_VEXPRESS) += juno.dtb
dtb-$(CONFIG_ARCH_XGENE) += apm-mustang.dtb
targets += dtbs
diff --git a/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts b/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts
new file mode 100644
index 000000000000..5843bfd92c75
--- /dev/null
+++ b/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2013, ARM Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+/dts-v1/;
+
+/memreserve/ 0x80000000 0x00010000;
+
+/ {
+};
+
+/ {
+ model = "FVP Base";
+ compatible = "arm,vfp-base", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ psci {
+ compatible = "arm,psci";
+ method = "smc";
+ cpu_suspend = <0xc4000001>;
+ cpu_off = <0x84000002>;
+ cpu_on = <0xc4000003>;
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ };
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x2>;
+ enable-method = "psci";
+ };
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x3>;
+ enable-method = "psci";
+ };
+ cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ };
+ cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ };
+ cpu@102 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x102>;
+ enable-method = "psci";
+ };
+ cpu@103 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x103>;
+ enable-method = "psci";
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x00000000 0x80000000 0 0x80000000>;
+ /*
+ <0x00000008 0x80000000 0 0x80000000>;
+ */
+ };
+
+ gic: interrupt-controller@2f000000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0x0 0x2f000000 0 0x10000>,
+ <0x0 0x2c000000 0 0x2000>,
+ <0x0 0x2c010000 0 0x2000>,
+ <0x0 0x2c02F000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <1 13 0xff01>,
+ <1 14 0xff01>,
+ <1 11 0xff01>,
+ <1 10 0xff01>;
+ clock-frequency = <100000000>;
+ };
+
+ timer@2a810000 {
+ compatible = "arm,armv7-timer-mem";
+ reg = <0x0 0x2a810000 0x0 0x10000>;
+ clock-frequency = <100000000>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ frame@2a820000 {
+ frame-number = <0>;
+ interrupts = <0 25 4>;
+ reg = <0x0 0x2a820000 0x0 0x10000>;
+ };
+ };
+
+ pmu {
+ compatible = "arm,armv8-pmuv3";
+ interrupts = <0 60 4>,
+ <0 61 4>,
+ <0 62 4>,
+ <0 63 4>;
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
diff --git a/arch/arm64/boot/dts/fvp-base-gicv2legacy-psci.dts b/arch/arm64/boot/dts/fvp-base-gicv2legacy-psci.dts
new file mode 100644
index 000000000000..3e92a92b324b
--- /dev/null
+++ b/arch/arm64/boot/dts/fvp-base-gicv2legacy-psci.dts
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2013, ARM Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+/dts-v1/;
+
+/memreserve/ 0x80000000 0x00010000;
+
+/ {
+};
+
+/ {
+ model = "FVP Base";
+ compatible = "arm,vfp-base", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ psci {
+ compatible = "arm,psci";
+ method = "smc";
+ cpu_suspend = <0xc4000001>;
+ cpu_off = <0x84000002>;
+ cpu_on = <0xc4000003>;
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ };
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x2>;
+ enable-method = "psci";
+ };
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x3>;
+ enable-method = "psci";
+ };
+ cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ };
+ cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ };
+ cpu@102 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x102>;
+ enable-method = "psci";
+ };
+ cpu@103 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x103>;
+ enable-method = "psci";
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x00000000 0x80000000 0 0x80000000>;
+ /*
+ <0x00000008 0x80000000 0 0x80000000>;
+ */
+ };
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0x0 0x2c001000 0 0x1000>,
+ <0x0 0x2c002000 0 0x1000>,
+ <0x0 0x2c004000 0 0x2000>,
+ <0x0 0x2c006000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <1 13 0xff01>,
+ <1 14 0xff01>,
+ <1 11 0xff01>,
+ <1 10 0xff01>;
+ clock-frequency = <100000000>;
+ };
+
+ timer@2a810000 {
+ compatible = "arm,armv7-timer-mem";
+ reg = <0x0 0x2a810000 0x0 0x10000>;
+ clock-frequency = <100000000>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ frame@2a820000 {
+ frame-number = <0>;
+ interrupts = <0 25 4>;
+ reg = <0x0 0x2a820000 0x0 0x10000>;
+ };
+ };
+
+ pmu {
+ compatible = "arm,armv8-pmuv3";
+ interrupts = <0 60 4>,
+ <0 61 4>,
+ <0 62 4>,
+ <0 63 4>;
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
diff --git a/arch/arm64/boot/dts/fvp-base-gicv3-psci.dts b/arch/arm64/boot/dts/fvp-base-gicv3-psci.dts
new file mode 100644
index 000000000000..ebbbbd331466
--- /dev/null
+++ b/arch/arm64/boot/dts/fvp-base-gicv3-psci.dts
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2013, ARM Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+/dts-v1/;
+
+/memreserve/ 0x80000000 0x00010000;
+
+/ {
+};
+
+/ {
+ model = "FVP Base";
+ compatible = "arm,vfp-base", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ psci {
+ compatible = "arm,psci";
+ method = "smc";
+ cpu_suspend = <0xc4000001>;
+ cpu_off = <0x84000002>;
+ cpu_on = <0xc4000003>;
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ };
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x2>;
+ enable-method = "psci";
+ };
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x3>;
+ enable-method = "psci";
+ };
+ cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ };
+ cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ };
+ cpu@102 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x102>;
+ enable-method = "psci";
+ };
+ cpu@103 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x103>;
+ enable-method = "psci";
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x00000000 0x80000000 0 0x80000000>;
+ /*
+ <0x00000008 0x80000000 0 0x80000000>;
+ */
+ };
+
+ gic: interrupt-controller@2f000000 {
+ compatible = "arm,gic-v3";
+ #interrupt-cells = <3>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ interrupt-controller;
+ reg = <0x0 0x2f000000 0 0x10000>, // GICD
+ <0x0 0x2f100000 0 0x200000>, // GICR
+ <0x0 0x2c000000 0 0x2000>, // GICC
+ <0x0 0x2c010000 0 0x2000>, // GICH
+ <0x0 0x2c02f000 0 0x2000>; // GICV
+ interrupts = <1 9 4>;
+
+ its: its@2f020000 {
+ compatible = "arm,gic-v3-its";
+ msi-controller;
+ reg = <0x0 0x2f020000 0x0 0x20000>; // GITS
+ };
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <1 13 0xff01>,
+ <1 14 0xff01>,
+ <1 11 0xff01>,
+ <1 10 0xff01>;
+ clock-frequency = <100000000>;
+ };
+
+ timer@2a810000 {
+ compatible = "arm,armv7-timer-mem";
+ reg = <0x0 0x2a810000 0x0 0x10000>;
+ clock-frequency = <100000000>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ frame@2a820000 {
+ frame-number = <0>;
+ interrupts = <0 25 4>;
+ reg = <0x0 0x2a820000 0x0 0x10000>;
+ };
+ };
+
+ pmu {
+ compatible = "arm,armv8-pmuv3";
+ interrupts = <0 60 4>,
+ <0 61 4>,
+ <0 62 4>,
+ <0 63 4>;
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 0 0 4>,
+ <0 0 1 &gic 0 0 0 1 4>,
+ <0 0 2 &gic 0 0 0 2 4>,
+ <0 0 3 &gic 0 0 0 3 4>,
+ <0 0 4 &gic 0 0 0 4 4>,
+ <0 0 5 &gic 0 0 0 5 4>,
+ <0 0 6 &gic 0 0 0 6 4>,
+ <0 0 7 &gic 0 0 0 7 4>,
+ <0 0 8 &gic 0 0 0 8 4>,
+ <0 0 9 &gic 0 0 0 9 4>,
+ <0 0 10 &gic 0 0 0 10 4>,
+ <0 0 11 &gic 0 0 0 11 4>,
+ <0 0 12 &gic 0 0 0 12 4>,
+ <0 0 13 &gic 0 0 0 13 4>,
+ <0 0 14 &gic 0 0 0 14 4>,
+ <0 0 15 &gic 0 0 0 15 4>,
+ <0 0 16 &gic 0 0 0 16 4>,
+ <0 0 17 &gic 0 0 0 17 4>,
+ <0 0 18 &gic 0 0 0 18 4>,
+ <0 0 19 &gic 0 0 0 19 4>,
+ <0 0 20 &gic 0 0 0 20 4>,
+ <0 0 21 &gic 0 0 0 21 4>,
+ <0 0 22 &gic 0 0 0 22 4>,
+ <0 0 23 &gic 0 0 0 23 4>,
+ <0 0 24 &gic 0 0 0 24 4>,
+ <0 0 25 &gic 0 0 0 25 4>,
+ <0 0 26 &gic 0 0 0 26 4>,
+ <0 0 27 &gic 0 0 0 27 4>,
+ <0 0 28 &gic 0 0 0 28 4>,
+ <0 0 29 &gic 0 0 0 29 4>,
+ <0 0 30 &gic 0 0 0 30 4>,
+ <0 0 31 &gic 0 0 0 31 4>,
+ <0 0 32 &gic 0 0 0 32 4>,
+ <0 0 33 &gic 0 0 0 33 4>,
+ <0 0 34 &gic 0 0 0 34 4>,
+ <0 0 35 &gic 0 0 0 35 4>,
+ <0 0 36 &gic 0 0 0 36 4>,
+ <0 0 37 &gic 0 0 0 37 4>,
+ <0 0 38 &gic 0 0 0 38 4>,
+ <0 0 39 &gic 0 0 0 39 4>,
+ <0 0 40 &gic 0 0 0 40 4>,
+ <0 0 41 &gic 0 0 0 41 4>,
+ <0 0 42 &gic 0 0 0 42 4>;
+
+ /include/ "rtsm_ve-motherboard.dtsi"
+ };
+};
diff --git a/arch/arm64/boot/dts/fvp-foundation-gicv2-psci.dts b/arch/arm64/boot/dts/fvp-foundation-gicv2-psci.dts
new file mode 100644
index 000000000000..7924ff2124da
--- /dev/null
+++ b/arch/arm64/boot/dts/fvp-foundation-gicv2-psci.dts
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2013, ARM Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+/dts-v1/;
+
+/memreserve/ 0x80000000 0x00010000;
+
+/ {
+};
+
+/ {
+ model = "FVP Base";
+ compatible = "arm,fvp-base", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ psci {
+ compatible = "arm,psci";
+ method = "smc";
+ cpu_suspend = <0xc4000001>;
+ cpu_off = <0x84000002>;
+ cpu_on = <0xc4000003>;
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ };
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x2>;
+ enable-method = "psci";
+ };
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x3>;
+ enable-method = "psci";
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x00000000 0x80000000 0 0x80000000>,
+ <0x00000008 0x80000000 0 0x80000000>;
+ };
+
+ gic: interrupt-controller@2f000000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0x0 0x2f000000 0 0x10000>,
+ <0x0 0x2c000000 0 0x2000>,
+ <0x0 0x2c010000 0 0x2000>,
+ <0x0 0x2c02F000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <1 13 0xff01>,
+ <1 14 0xff01>,
+ <1 11 0xff01>,
+ <1 10 0xff01>;
+ clock-frequency = <100000000>;
+ };
+
+ timer@2a810000 {
+ compatible = "arm,armv7-timer-mem";
+ reg = <0x0 0x2a810000 0x0 0x10000>;
+ clock-frequency = <100000000>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ frame@2a820000 {
+ frame-number = <0>;
+ interrupts = <0 25 4>;
+ reg = <0x0 0x2a820000 0x0 0x10000>;
+ };
+ };
+
+ pmu {
+ compatible = "arm,armv8-pmuv3";
+ interrupts = <0 60 4>,
+ <0 61 4>,
+ <0 62 4>,
+ <0 63 4>;
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "fvp-foundation-motherboard.dtsi"
+ };
+};
diff --git a/arch/arm64/boot/dts/fvp-foundation-gicv2legacy-psci.dts b/arch/arm64/boot/dts/fvp-foundation-gicv2legacy-psci.dts
new file mode 100644
index 000000000000..656802eaa9b9
--- /dev/null
+++ b/arch/arm64/boot/dts/fvp-foundation-gicv2legacy-psci.dts
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2013, ARM Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+/dts-v1/;
+
+/memreserve/ 0x80000000 0x00010000;
+
+/ {
+};
+
+/ {
+ model = "FVP Base";
+ compatible = "arm,fvp-base", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ psci {
+ compatible = "arm,psci";
+ method = "smc";
+ cpu_suspend = <0xc4000001>;
+ cpu_off = <0x84000002>;
+ cpu_on = <0xc4000003>;
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ };
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x2>;
+ enable-method = "psci";
+ };
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x3>;
+ enable-method = "psci";
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x00000000 0x80000000 0 0x80000000>,
+ <0x00000008 0x80000000 0 0x80000000>;
+ };
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0x0 0x2c001000 0 0x1000>,
+ <0x0 0x2c002000 0 0x1000>,
+ <0x0 0x2c004000 0 0x2000>,
+ <0x0 0x2c006000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <1 13 0xff01>,
+ <1 14 0xff01>,
+ <1 11 0xff01>,
+ <1 10 0xff01>;
+ clock-frequency = <100000000>;
+ };
+
+ timer@2a810000 {
+ compatible = "arm,armv7-timer-mem";
+ reg = <0x0 0x2a810000 0x0 0x10000>;
+ clock-frequency = <100000000>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ frame@2a820000 {
+ frame-number = <0>;
+ interrupts = <0 25 4>;
+ reg = <0x0 0x2a820000 0x0 0x10000>;
+ };
+ };
+
+ pmu {
+ compatible = "arm,armv8-pmuv3";
+ interrupts = <0 60 4>,
+ <0 61 4>,
+ <0 62 4>,
+ <0 63 4>;
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 4>,
+ <0 0 1 &gic 0 1 4>,
+ <0 0 2 &gic 0 2 4>,
+ <0 0 3 &gic 0 3 4>,
+ <0 0 4 &gic 0 4 4>,
+ <0 0 5 &gic 0 5 4>,
+ <0 0 6 &gic 0 6 4>,
+ <0 0 7 &gic 0 7 4>,
+ <0 0 8 &gic 0 8 4>,
+ <0 0 9 &gic 0 9 4>,
+ <0 0 10 &gic 0 10 4>,
+ <0 0 11 &gic 0 11 4>,
+ <0 0 12 &gic 0 12 4>,
+ <0 0 13 &gic 0 13 4>,
+ <0 0 14 &gic 0 14 4>,
+ <0 0 15 &gic 0 15 4>,
+ <0 0 16 &gic 0 16 4>,
+ <0 0 17 &gic 0 17 4>,
+ <0 0 18 &gic 0 18 4>,
+ <0 0 19 &gic 0 19 4>,
+ <0 0 20 &gic 0 20 4>,
+ <0 0 21 &gic 0 21 4>,
+ <0 0 22 &gic 0 22 4>,
+ <0 0 23 &gic 0 23 4>,
+ <0 0 24 &gic 0 24 4>,
+ <0 0 25 &gic 0 25 4>,
+ <0 0 26 &gic 0 26 4>,
+ <0 0 27 &gic 0 27 4>,
+ <0 0 28 &gic 0 28 4>,
+ <0 0 29 &gic 0 29 4>,
+ <0 0 30 &gic 0 30 4>,
+ <0 0 31 &gic 0 31 4>,
+ <0 0 32 &gic 0 32 4>,
+ <0 0 33 &gic 0 33 4>,
+ <0 0 34 &gic 0 34 4>,
+ <0 0 35 &gic 0 35 4>,
+ <0 0 36 &gic 0 36 4>,
+ <0 0 37 &gic 0 37 4>,
+ <0 0 38 &gic 0 38 4>,
+ <0 0 39 &gic 0 39 4>,
+ <0 0 40 &gic 0 40 4>,
+ <0 0 41 &gic 0 41 4>,
+ <0 0 42 &gic 0 42 4>;
+
+ /include/ "fvp-foundation-motherboard.dtsi"
+ };
+};
diff --git a/arch/arm64/boot/dts/fvp-foundation-gicv3-psci.dts b/arch/arm64/boot/dts/fvp-foundation-gicv3-psci.dts
new file mode 100644
index 000000000000..87ee3c01619f
--- /dev/null
+++ b/arch/arm64/boot/dts/fvp-foundation-gicv3-psci.dts
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2013, ARM Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+/dts-v1/;
+
+/memreserve/ 0x80000000 0x00010000;
+
+/ {
+};
+
+/ {
+ model = "FVP Base";
+ compatible = "arm,fvp-base", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ chosen { };
+
+ aliases {
+ serial0 = &v2m_serial0;
+ serial1 = &v2m_serial1;
+ serial2 = &v2m_serial2;
+ serial3 = &v2m_serial3;
+ };
+
+ psci {
+ compatible = "arm,psci";
+ method = "smc";
+ cpu_suspend = <0xc4000001>;
+ cpu_off = <0x84000002>;
+ cpu_on = <0xc4000003>;
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ };
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x2>;
+ enable-method = "psci";
+ };
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x3>;
+ enable-method = "psci";
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x00000000 0x80000000 0 0x80000000>,
+ <0x00000008 0x80000000 0 0x80000000>;
+ };
+
+ gic: interrupt-controller@2f000000 {
+ compatible = "arm,gic-v3";
+ #interrupt-cells = <3>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ interrupt-controller;
+ reg = <0x0 0x2f000000 0 0x10000>, // GICD
+ <0x0 0x2f100000 0 0x200000>, // GICR
+ <0x0 0x2c000000 0 0x2000>, // GICC
+ <0x0 0x2c010000 0 0x2000>, // GICH
+ <0x0 0x2c02f000 0 0x2000>; // GICV
+ interrupts = <1 9 4>;
+
+ its: its@2f020000 {
+ compatible = "arm,gic-v3-its";
+ msi-controller;
+ reg = <0x0 0x2f020000 0x0 0x20000>; // GITS
+ };
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <1 13 0xff01>,
+ <1 14 0xff01>,
+ <1 11 0xff01>,
+ <1 10 0xff01>;
+ clock-frequency = <100000000>;
+ };
+
+ timer@2a810000 {
+ compatible = "arm,armv7-timer-mem";
+ reg = <0x0 0x2a810000 0x0 0x10000>;
+ clock-frequency = <100000000>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ frame@2a820000 {
+ frame-number = <0>;
+ interrupts = <0 25 4>;
+ reg = <0x0 0x2a820000 0x0 0x10000>;
+ };
+ };
+
+ pmu {
+ compatible = "arm,armv8-pmuv3";
+ interrupts = <0 60 4>,
+ <0 61 4>,
+ <0 62 4>,
+ <0 63 4>;
+ };
+
+ smb {
+ compatible = "simple-bus";
+
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 63>;
+ interrupt-map = <0 0 0 &gic 0 0 0 0 4>,
+ <0 0 1 &gic 0 0 0 1 4>,
+ <0 0 2 &gic 0 0 0 2 4>,
+ <0 0 3 &gic 0 0 0 3 4>,
+ <0 0 4 &gic 0 0 0 4 4>,
+ <0 0 5 &gic 0 0 0 5 4>,
+ <0 0 6 &gic 0 0 0 6 4>,
+ <0 0 7 &gic 0 0 0 7 4>,
+ <0 0 8 &gic 0 0 0 8 4>,
+ <0 0 9 &gic 0 0 0 9 4>,
+ <0 0 10 &gic 0 0 0 10 4>,
+ <0 0 11 &gic 0 0 0 11 4>,
+ <0 0 12 &gic 0 0 0 12 4>,
+ <0 0 13 &gic 0 0 0 13 4>,
+ <0 0 14 &gic 0 0 0 14 4>,
+ <0 0 15 &gic 0 0 0 15 4>,
+ <0 0 16 &gic 0 0 0 16 4>,
+ <0 0 17 &gic 0 0 0 17 4>,
+ <0 0 18 &gic 0 0 0 18 4>,
+ <0 0 19 &gic 0 0 0 19 4>,
+ <0 0 20 &gic 0 0 0 20 4>,
+ <0 0 21 &gic 0 0 0 21 4>,
+ <0 0 22 &gic 0 0 0 22 4>,
+ <0 0 23 &gic 0 0 0 23 4>,
+ <0 0 24 &gic 0 0 0 24 4>,
+ <0 0 25 &gic 0 0 0 25 4>,
+ <0 0 26 &gic 0 0 0 26 4>,
+ <0 0 27 &gic 0 0 0 27 4>,
+ <0 0 28 &gic 0 0 0 28 4>,
+ <0 0 29 &gic 0 0 0 29 4>,
+ <0 0 30 &gic 0 0 0 30 4>,
+ <0 0 31 &gic 0 0 0 31 4>,
+ <0 0 32 &gic 0 0 0 32 4>,
+ <0 0 33 &gic 0 0 0 33 4>,
+ <0 0 34 &gic 0 0 0 34 4>,
+ <0 0 35 &gic 0 0 0 35 4>,
+ <0 0 36 &gic 0 0 0 36 4>,
+ <0 0 37 &gic 0 0 0 37 4>,
+ <0 0 38 &gic 0 0 0 38 4>,
+ <0 0 39 &gic 0 0 0 39 4>,
+ <0 0 40 &gic 0 0 0 40 4>,
+ <0 0 41 &gic 0 0 0 41 4>,
+ <0 0 42 &gic 0 0 0 42 4>;
+
+ /include/ "fvp-foundation-motherboard.dtsi"
+ };
+};
diff --git a/arch/arm64/boot/dts/fvp-foundation-motherboard.dtsi b/arch/arm64/boot/dts/fvp-foundation-motherboard.dtsi
new file mode 100644
index 000000000000..76cae31f4f85
--- /dev/null
+++ b/arch/arm64/boot/dts/fvp-foundation-motherboard.dtsi
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2013, ARM Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+ motherboard {
+ arm,v2m-memory-map = "rs1";
+ compatible = "arm,vexpress,v2m-p1", "simple-bus";
+ #address-cells = <2>; /* SMB chipselect number and offset */
+ #size-cells = <1>;
+ #interrupt-cells = <1>;
+ ranges;
+
+ ethernet@2,02000000 {
+ compatible = "smsc,lan91c111";
+ reg = <2 0x02000000 0x10000>;
+ interrupts = <15>;
+ };
+
+ v2m_clk24mhz: clk24mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "v2m:clk24mhz";
+ };
+
+ v2m_refclk1mhz: refclk1mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <1000000>;
+ clock-output-names = "v2m:refclk1mhz";
+ };
+
+ v2m_refclk32khz: refclk32khz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <32768>;
+ clock-output-names = "v2m:refclk32khz";
+ };
+
+ iofpga@3,00000000 {
+ compatible = "arm,amba-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 3 0 0x200000>;
+
+ v2m_sysreg: sysreg@010000 {
+ compatible = "arm,vexpress-sysreg";
+ reg = <0x010000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ v2m_sysctl: sysctl@020000 {
+ compatible = "arm,sp810", "arm,primecell";
+ reg = <0x020000 0x1000>;
+ clocks = <&v2m_refclk32khz>, <&v2m_refclk1mhz>, <&v2m_clk24mhz>;
+ clock-names = "refclk", "timclk", "apb_pclk";
+ #clock-cells = <1>;
+ clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3";
+ };
+
+ v2m_serial0: uart@090000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x090000 0x1000>;
+ interrupts = <5>;
+ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
+ clock-names = "uartclk", "apb_pclk";
+ };
+
+ v2m_serial1: uart@0a0000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0a0000 0x1000>;
+ interrupts = <6>;
+ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
+ clock-names = "uartclk", "apb_pclk";
+ };
+
+ v2m_serial2: uart@0b0000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0b0000 0x1000>;
+ interrupts = <7>;
+ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
+ clock-names = "uartclk", "apb_pclk";
+ };
+
+ v2m_serial3: uart@0c0000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0c0000 0x1000>;
+ interrupts = <8>;
+ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
+ clock-names = "uartclk", "apb_pclk";
+ };
+
+ wdt@0f0000 {
+ compatible = "arm,sp805", "arm,primecell";
+ reg = <0x0f0000 0x1000>;
+ interrupts = <0>;
+ clocks = <&v2m_refclk32khz>, <&v2m_clk24mhz>;
+ clock-names = "wdogclk", "apb_pclk";
+ };
+
+ v2m_timer01: timer@110000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0x110000 0x1000>;
+ interrupts = <2>;
+ clocks = <&v2m_sysctl 0>, <&v2m_sysctl 1>, <&v2m_clk24mhz>;
+ clock-names = "timclken1", "timclken2", "apb_pclk";
+ };
+
+ v2m_timer23: timer@120000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0x120000 0x1000>;
+ interrupts = <3>;
+ clocks = <&v2m_sysctl 2>, <&v2m_sysctl 3>, <&v2m_clk24mhz>;
+ clock-names = "timclken1", "timclken2", "apb_pclk";
+ };
+
+ rtc@170000 {
+ compatible = "arm,pl031", "arm,primecell";
+ reg = <0x170000 0x1000>;
+ interrupts = <4>;
+ clocks = <&v2m_clk24mhz>;
+ clock-names = "apb_pclk";
+ };
+
+ virtio_block@0130000 {
+ compatible = "virtio,mmio";
+ reg = <0x130000 0x1000>;
+ interrupts = <0x2a>;
+ };
+ };
+
+ v2m_fixed_3v3: fixedregulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+
+ mcc {
+ compatible = "arm,vexpress,config-bus", "simple-bus";
+ arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+ reset@0 {
+ compatible = "arm,vexpress-reset";
+ arm,vexpress-sysreg,func = <5 0>;
+ };
+
+ muxfpga@0 {
+ compatible = "arm,vexpress-muxfpga";
+ arm,vexpress-sysreg,func = <7 0>;
+ };
+
+ shutdown@0 {
+ compatible = "arm,vexpress-shutdown";
+ arm,vexpress-sysreg,func = <8 0>;
+ };
+
+ reboot@0 {
+ compatible = "arm,vexpress-reboot";
+ arm,vexpress-sysreg,func = <9 0>;
+ };
+
+ dvimode@0 {
+ compatible = "arm,vexpress-dvimode";
+ arm,vexpress-sysreg,func = <11 0>;
+ };
+ };
+ };
diff --git a/arch/arm64/boot/dts/juno.dts b/arch/arm64/boot/dts/juno.dts
new file mode 100644
index 000000000000..969a26884266
--- /dev/null
+++ b/arch/arm64/boot/dts/juno.dts
@@ -0,0 +1,486 @@
+/*
+ * ARM Ltd. Juno Plaform
+ *
+ * Fast Models FVP v2 support
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+ model = "Juno";
+ compatible = "arm,juno", "arm,vexpress";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ aliases {
+ serial0 = &soc_uart0;
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu-map {
+ cluster1 {
+ core0 {
+ cpu = <&CPU0>;
+ };
+ core1 {
+ cpu = <&CPU1>;
+ };
+ core2 {
+ cpu = <&CPU2>;
+ };
+ core3 {
+ cpu = <&CPU3>;
+ };
+ };
+
+ cluster0 {
+ core0 {
+ cpu = <&CPU4>;
+ };
+ core1 {
+ cpu = <&CPU5>;
+ };
+ };
+ };
+
+ CPU0:cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53","arm,armv8";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ clocks = <&scpi_dvfs 1>;
+ clock-names = "vlittle";
+ };
+
+ CPU1:cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53","arm,armv8";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ clocks = <&scpi_dvfs 1>;
+ clock-names = "vlittle";
+ };
+
+ CPU2:cpu@102 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53","arm,armv8";
+ reg = <0x0 0x102>;
+ enable-method = "psci";
+ clocks = <&scpi_dvfs 1>;
+ clock-names = "vlittle";
+ };
+
+ CPU3:cpu@103 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53","arm,armv8";
+ reg = <0x0 0x103>;
+ enable-method = "psci";
+ clocks = <&scpi_dvfs 1>;
+ clock-names = "vlittle";
+ };
+
+ CPU4:cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57","arm,armv8";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ clocks = <&scpi_dvfs 0>;
+ clock-names = "vbig";
+ };
+
+ CPU5:cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a57","arm,armv8";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ clocks = <&scpi_dvfs 0>;
+ clock-names = "vbig";
+ };
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x00000000 0x80000000 0x0 0x7f000000>,
+ <0x00000008 0x80000000 0x1 0x80000000>;
+ };
+
+ /* memory@14000000 {
+ device_type = "memory";
+ reg = <0x00000000 0x14000000 0x0 0x02000000>;
+ }; */
+
+ gic: interrupt-controller@2c001000 {
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0x0 0x2c010000 0 0x1000>,
+ <0x0 0x2c02f000 0 0x1000>,
+ <0x0 0x2c04f000 0 0x2000>,
+ <0x0 0x2c06f000 0 0x2000>;
+ interrupts = <GIC_PPI 9 0xf04>;
+ };
+
+ msi0: msi@2c1c0000 {
+ compatible = "arm,gic-msi";
+ reg = <0x0 0x2c1c0000 0 0x10000
+ 0x0 0x2c1d0000 0 0x10000
+ 0x0 0x2c1e0000 0 0x10000
+ 0x0 0x2c1f0000 0 0x10000>;
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <GIC_PPI 13 0xff01>,
+ <GIC_PPI 14 0xff01>,
+ <GIC_PPI 11 0xff01>,
+ <GIC_PPI 10 0xff01>;
+ };
+
+ pmu {
+ compatible = "arm,armv8-pmuv3";
+ interrupts = <GIC_SPI 18 4>,
+ <GIC_SPI 22 4>,
+ <GIC_SPI 26 4>,
+ <GIC_SPI 30 4>,
+ <GIC_SPI 02 4>,
+ <GIC_SPI 06 4>;
+ };
+
+ psci {
+ compatible = "arm,psci";
+ method = "smc";
+ cpu_suspend = <0xC4000001>;
+ cpu_off = <0x84000002>;
+ cpu_on = <0xC4000003>;
+ migrate = <0xC4000005>;
+ };
+
+ pci0: pci@30000000 {
+ compatible = "arm,pcie-xr3";
+ device_type = "pci";
+ reg = <0 0x7ff30000 0 0x1000
+ 0 0x7ff20000 0 0x10000
+ 0 0x40000000 0 0x10000000>;
+ bus-range = <0 255>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges = <0x01000000 0x0 0x00000000 0x00 0x5ff00000 0x0 0x00100000
+ 0x02000000 0x0 0x00000000 0x40 0x00000000 0x0 0x80000000
+ 0x42000000 0x0 0x80000000 0x40 0x80000000 0x0 0x80000000>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &gic 0 136 4
+ 0 0 0 2 &gic 0 137 4
+ 0 0 0 3 &gic 0 138 4
+ 0 0 0 4 &gic 0 139 4>;
+ };
+
+
+ mailbox: mhu@2b1f0000 {
+ compatible = "arm,mhu";
+ reg = <0x0 0x2b1f0000 0x0 0x10000>, /* MHU registers */
+ <0x0 0x2e000000 0x0 0x10000>; /* Payload area */
+ interrupts = <0 36 4>, /* low priority interrupt */
+ <0 35 4>; /* high priority interrupt */
+ #mbox-cells = <1>;
+ mbox-names = "cpu_to_scp_low", "cpu_to_scp_high";
+ mboxes = <&mailbox 0 &mailbox 1>;
+ };
+
+ clocks {
+ compatible = "arm,scpi-clks";
+
+ scpi_dvfs: scpi_clocks@0 {
+ compatible = "arm,scpi-clk-indexed";
+ #clock-cells = <1>;
+ clock-indices = <0>, <1>, <2>;
+ clock-output-names = "vbig", "vlittle", "vgpu";
+ };
+
+ scpi_clk: scpi_clocks@3 {
+ compatible = "arm,scpi-clk-range";
+ #clock-cells = <1>;
+ clock-indices = <3>, <4>;
+ frequency-range = <23000000 210000000>;
+ clock-output-names = "pxlclk0", "pxlclk1";
+ };
+
+ };
+
+ cpufreq {
+ compatible = "arm,scpi-cpufreq";
+ };
+
+ soc_uartclk: refclk72738khz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <7273800>;
+ clock-output-names = "juno:uartclk";
+ };
+
+ soc_refclk24mhz: clk24mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "juno:clk24mhz";
+ };
+
+ mb_eth25mhz: clk25mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "ethclk25mhz";
+ };
+
+ soc_usb48mhz: clk48mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <48000000>;
+ clock-output-names = "clk48mhz";
+ };
+
+ soc_smc50mhz: clk50mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <50000000>;
+ clock-output-names = "smc_clk";
+ };
+
+ soc_refclk100mhz: refclk100mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <100000000>;
+ clock-output-names = "apb_pclk";
+ };
+
+ soc_faxiclk: refclk533mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <533000000>;
+ clock-output-names = "faxi_clk";
+ };
+
+ soc_fixed_3v3: fixedregulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ memory-controller@7ffd0000 {
+ compatible = "arm,pl354", "arm,primecell";
+ reg = <0 0x7ffd0000 0 0x1000>;
+ interrupts = <0 86 4>,
+ <0 87 4>;
+ clocks = <&soc_smc50mhz>;
+ clock-names = "apb_pclk";
+ chip5-memwidth = <16>;
+ };
+
+ dma0: dma@0x7ff00000 {
+ compatible = "arm,pl330", "arm,primecell";
+ reg = <0x0 0x7ff00000 0 0x1000>;
+ interrupts = <0 95 4>,
+ <0 88 4>,
+ <0 89 4>,
+ <0 90 4>,
+ <0 91 4>,
+ <0 108 4>,
+ <0 109 4>,
+ <0 110 4>,
+ <0 111 4>;
+ #dma-cells = <1>;
+ #dma-channels = <8>;
+ #dma-requests = <32>;
+ clocks = <&soc_faxiclk>;
+ clock-names = "apb_pclk";
+ };
+
+ soc_uart0: uart@7ff80000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0 0x7ff80000 0x0 0x1000>;
+ interrupts = <0 83 4>;
+ clocks = <&soc_uartclk>, <&soc_refclk100mhz>;
+ clock-names = "uartclk", "apb_pclk";
+ dmas = <&dma0 1
+ &dma0 2>;
+ dma-names = "rx", "tx";
+ };
+
+ /* this UART is reserved for secure software.
+ soc_uart1: uart@7ff70000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0 0x7ff70000 0x0 0x1000>;
+ interrupts = <0 84 4>;
+ clocks = <&soc_uartclk>, <&soc_refclk100mhz>;
+ clock-names = "uartclk", "apb_pclk";
+ }; */
+
+ ulpi_phy: phy@0 {
+ compatible = "phy-ulpi-generic";
+ reg = <0x0 0x94 0x0 0x4>;
+ phy-id = <0>;
+ };
+
+ ehci@7ffc0000 {
+ compatible = "snps,ehci-h20ahb";
+ /* compatible = "arm,h20ahb-ehci"; */
+ reg = <0x0 0x7ffc0000 0x0 0x10000>;
+ interrupts = <0 117 4>;
+ clocks = <&soc_usb48mhz>;
+ clock-names = "otg";
+ phys = <&ulpi_phy>;
+ };
+
+ ohci@0x7ffb0000 {
+ compatible = "generic-ohci";
+ reg = <0x0 0x7ffb0000 0x0 0x10000>;
+ interrupts = <0 116 4>;
+ clocks = <&soc_usb48mhz>;
+ clock-names = "otg";
+ };
+
+ i2c@0x7ffa0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "snps,designware-i2c";
+ reg = <0x0 0x7ffa0000 0x0 0x1000>;
+ interrupts = <0 104 4>;
+ clock-frequency = <400000>;
+ i2c-sda-hold-time-ns = <500>;
+ clocks = <&soc_smc50mhz>;
+
+ dvi0: dvi-transmitter@70 {
+ compatible = "nxp,tda998x";
+ reg = <0x70>;
+ };
+
+ dvi1: dvi-transmitter@71 {
+ compatible = "nxp,tda998x";
+ reg = <0x71>;
+ };
+ };
+
+ smb {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0x08000000 0x04000000>,
+ <1 0 0 0x14000000 0x04000000>,
+ <2 0 0 0x18000000 0x04000000>,
+ <3 0 0 0x1c000000 0x04000000>,
+ <4 0 0 0x0c000000 0x04000000>,
+ <5 0 0 0x10000000 0x04000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 15>;
+ interrupt-map = <0 0 0 &gic 0 68 4>,
+ <0 0 1 &gic 0 69 4>,
+ <0 0 2 &gic 0 70 4>,
+ <0 0 3 &gic 0 160 4>,
+ <0 0 4 &gic 0 161 4>,
+ <0 0 5 &gic 0 162 4>,
+ <0 0 6 &gic 0 163 4>,
+ <0 0 7 &gic 0 164 4>,
+ <0 0 8 &gic 0 165 4>,
+ <0 0 9 &gic 0 166 4>,
+ <0 0 10 &gic 0 167 4>,
+ <0 0 11 &gic 0 168 4>,
+ <0 0 12 &gic 0 169 4>;
+
+ motherboard {
+ model = "V2M-Juno";
+ arm,hbi = <0x252>;
+ arm,vexpress,site = <0>;
+ arm,v2m-memory-map = "rs1";
+ compatible = "arm,vexpress,v2p-p1", "simple-bus";
+ #address-cells = <2>; /* SMB chipselect number and offset */
+ #size-cells = <1>;
+ #interrupt-cells = <1>;
+ ranges;
+
+ usb@5,00000000 {
+ compatible = "nxp,usb-isp1763";
+ reg = <5 0x00000000 0x20000>;
+ bus-width = <16>;
+ interrupts = <4>;
+ };
+
+ ethernet@2,00000000 {
+ compatible = "smsc,lan9118", "smsc,lan9115";
+ reg = <2 0x00000000 0x10000>;
+ interrupts = <3>;
+ phy-mode = "mii";
+ reg-io-width = <4>;
+ smsc,irq-active-high;
+ smsc,irq-push-pull;
+ clocks = <&mb_eth25mhz>;
+ vdd33a-supply = <&soc_fixed_3v3>; /* change this */
+ vddvario-supply = <&soc_fixed_3v3>; /* and this */
+ };
+
+ iofpga@3,00000000 {
+ compatible = "arm,amba-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 3 0 0x200000>;
+
+ kmi@060000 {
+ compatible = "arm,pl050", "arm,primecell";
+ reg = <0x060000 0x1000>;
+ interrupts = <8>;
+ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>;
+ clock-names = "KMIREFCLK", "apb_pclk";
+ };
+
+ kmi@070000 {
+ compatible = "arm,pl050", "arm,primecell";
+ reg = <0x070000 0x1000>;
+ interrupts = <8>;
+ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>;
+ clock-names = "KMIREFCLK", "apb_pclk";
+ };
+
+ wdt@0f0000 {
+ compatible = "arm,sp805", "arm,primecell";
+ reg = <0x0f0000 0x10000>;
+ interrupts = <7>;
+ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>;
+ clock-names = "wdogclk", "apb_pclk";
+ };
+
+ v2m_timer01: timer@110000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0x110000 0x10000>;
+ interrupts = <9>;
+ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>;
+ clock-names = "timclken1", "apb_pclk";
+ };
+
+ v2m_timer23: timer@120000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0x120000 0x10000>;
+ interrupts = <9>;
+ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>;
+ clock-names = "timclken1", "apb_pclk";
+ };
+
+ rtc@170000 {
+ compatible = "arm,pl031", "arm,primecell";
+ reg = <0x170000 0x10000>;
+ interrupts = <0>;
+ clocks = <&soc_smc50mhz>;
+ clock-names = "apb_pclk";
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi b/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi
index ac2cb2418025..aea335325ca8 100644
--- a/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi
+++ b/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi
@@ -22,7 +22,7 @@
bank-width = <4>;
};
- vram@2,00000000 {
+ v2m_video_ram: vram@2,00000000 {
compatible = "arm,vexpress-vram";
reg = <2 0x00000000 0x00800000>;
};
@@ -179,9 +179,41 @@
clcd@1f0000 {
compatible = "arm,pl111", "arm,primecell";
reg = <0x1f0000 0x1000>;
+ interrupt-names = "combined";
interrupts = <14>;
clocks = <&v2m_oscclk1>, <&v2m_clk24mhz>;
clock-names = "clcdclk", "apb_pclk";
+ memory-region = <&v2m_video_ram>;
+ max-memory-bandwidth = <36864000>; /* Bps, 640x480@60 16bpp */
+
+ port {
+ v2m_clcd_pads: endpoint {
+ remote-endpoint = <&v2m_clcd_panel>;
+ arm,pl11x,tft-r0g0b0-pads = <0 8 16>;
+ };
+ };
+
+ panel {
+ compatible = "panel-dpi";
+
+ port {
+ v2m_clcd_panel: endpoint {
+ remote-endpoint = <&v2m_clcd_pads>;
+ };
+ };
+
+ panel-timing {
+ clock-frequency = <25175000>;
+ hactive = <640>;
+ hback-porch = <40>;
+ hfront-porch = <24>;
+ hsync-len = <96>;
+ vactive = <480>;
+ vback-porch = <32>;
+ vfront-porch = <11>;
+ vsync-len = <2>;
+ };
+ };
};
virtio_block@0130000 {
diff --git a/arch/arm64/include/asm/bL_switcher.h b/arch/arm64/include/asm/bL_switcher.h
new file mode 100644
index 000000000000..2bee500b7f54
--- /dev/null
+++ b/arch/arm64/include/asm/bL_switcher.h
@@ -0,0 +1,54 @@
+/*
+ * Based on the stubs for the ARM implementation which is:
+ *
+ * Created by: Nicolas Pitre, April 2012
+ * Copyright: (C) 2012-2013 Linaro Limited
+ *
+ * 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_BL_SWITCHER_H
+#define ASM_BL_SWITCHER_H
+
+#include <linux/notifier.h>
+#include <linux/types.h>
+
+typedef void (*bL_switch_completion_handler)(void *cookie);
+
+static inline int bL_switch_request(unsigned int cpu,
+ unsigned int new_cluster_id)
+{
+ return -ENOTSUPP;
+}
+
+/*
+ * Register here to be notified about runtime enabling/disabling of
+ * the switcher.
+ *
+ * The notifier chain is called with the switcher activation lock held:
+ * the switcher will not be enabled or disabled during callbacks.
+ * Callbacks must not call bL_switcher_{get,put}_enabled().
+ */
+#define BL_NOTIFY_PRE_ENABLE 0
+#define BL_NOTIFY_POST_ENABLE 1
+#define BL_NOTIFY_PRE_DISABLE 2
+#define BL_NOTIFY_POST_DISABLE 3
+
+static inline int bL_switcher_register_notifier(struct notifier_block *nb)
+{
+ return 0;
+}
+
+static inline int bL_switcher_unregister_notifier(struct notifier_block *nb)
+{
+ return 0;
+}
+
+static inline bool bL_switcher_get_enabled(void) { return false; }
+static inline void bL_switcher_put_enabled(void) { }
+static inline int bL_switcher_trace_trigger(void) { return 0; }
+static inline int bL_switcher_get_logical_index(u32 mpidr) { return -EUNATCH; }
+
+#endif
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index b6ee26b0939a..4af25776eb95 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -19,10 +19,34 @@
#include <linux/nodemask.h>
#include <linux/of.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <asm/cputype.h>
#include <asm/topology.h>
+/*
+ * cpu power table
+ * This per cpu data structure describes the relative capacity of each core.
+ * On a heteregenous system, cores don't have the same computation capacity
+ * and we reflect that difference in the cpu_power field so the scheduler can
+ * take this difference into account during load balance. A per cpu structure
+ * is preferred because each CPU updates its own cpu_power field during the
+ * load balance except for idle cores. One idle core is selected to run the
+ * rebalance_domains for all idle cores and the cpu_power can be updated
+ * during this sequence.
+ */
+static DEFINE_PER_CPU(unsigned long, cpu_scale);
+
+unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
+{
+ return per_cpu(cpu_scale, cpu);
+}
+
+static void set_power_scale(unsigned int cpu, unsigned long power)
+{
+ per_cpu(cpu_scale, cpu) = power;
+}
+
static int __init get_cpu_for_node(struct device_node *node)
{
struct device_node *cpu_node;
@@ -161,6 +185,40 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
return 0;
}
+struct cpu_efficiency {
+ const char *compatible;
+ unsigned long efficiency;
+};
+
+/*
+ * Table of relative efficiency of each processors
+ * The efficiency value must fit in 20bit and the final
+ * cpu_scale value must be in the range
+ * 0 < cpu_scale < 3*SCHED_POWER_SCALE/2
+ * in order to return at most 1 when DIV_ROUND_CLOSEST
+ * is used to compute the capacity of a CPU.
+ * Processors that are not defined in the table,
+ * use the default SCHED_POWER_SCALE value for cpu_scale.
+ */
+static const struct cpu_efficiency table_efficiency[] = {
+ { "arm,cortex-a57", 3891 },
+ { "arm,cortex-a53", 2048 },
+ { NULL, },
+};
+
+static unsigned long *__cpu_capacity;
+#define cpu_capacity(cpu) __cpu_capacity[cpu]
+
+static unsigned long middle_capacity = 1;
+
+/*
+ * Iterate all CPUs' descriptor in DT and compute the efficiency
+ * (as per table_efficiency). Also calculate a middle efficiency
+ * as close as possible to (max{eff_i} - min{eff_i}) / 2
+ * This is later used to scale the cpu_power field such that an
+ * 'average' CPU is of middle power. Also see the comments near
+ * table_efficiency[] and update_cpu_power().
+ */
static int __init parse_dt_topology(void)
{
struct device_node *cn, *map;
@@ -200,6 +258,91 @@ out:
return ret;
}
+static void __init parse_dt_cpu_power(void)
+{
+ const struct cpu_efficiency *cpu_eff;
+ struct device_node *cn;
+ unsigned long min_capacity = ULONG_MAX;
+ unsigned long max_capacity = 0;
+ unsigned long capacity = 0;
+ int cpu;
+
+ __cpu_capacity = kcalloc(nr_cpu_ids, sizeof(*__cpu_capacity),
+ GFP_NOWAIT);
+
+ for_each_possible_cpu(cpu) {
+ const u32 *rate;
+ int len;
+
+ /* Too early to use cpu->of_node */
+ cn = of_get_cpu_node(cpu, NULL);
+ if (!cn) {
+ pr_err("Missing device node for CPU %d\n", cpu);
+ continue;
+ }
+
+ for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++)
+ if (of_device_is_compatible(cn, cpu_eff->compatible))
+ break;
+
+ if (cpu_eff->compatible == NULL) {
+ pr_warn("%s: Unknown CPU type\n", cn->full_name);
+ continue;
+ }
+
+ rate = of_get_property(cn, "clock-frequency", &len);
+ if (!rate || len != 4) {
+ pr_err("%s: Missing clock-frequency property\n",
+ cn->full_name);
+ continue;
+ }
+
+ capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency;
+
+ /* Save min capacity of the system */
+ if (capacity < min_capacity)
+ min_capacity = capacity;
+
+ /* Save max capacity of the system */
+ if (capacity > max_capacity)
+ max_capacity = capacity;
+
+ cpu_capacity(cpu) = capacity;
+ }
+
+ /* If min and max capacities are equal we bypass the update of the
+ * cpu_scale because all CPUs have the same capacity. Otherwise, we
+ * compute a middle_capacity factor that will ensure that the capacity
+ * of an 'average' CPU of the system will be as close as possible to
+ * SCHED_POWER_SCALE, which is the default value, but with the
+ * constraint explained near table_efficiency[].
+ */
+ if (min_capacity == max_capacity)
+ return;
+ else if (4 * max_capacity < (3 * (max_capacity + min_capacity)))
+ middle_capacity = (min_capacity + max_capacity)
+ >> (SCHED_CAPACITY_SHIFT+1);
+ else
+ middle_capacity = ((max_capacity / 3)
+ >> (SCHED_CAPACITY_SHIFT-1)) + 1;
+}
+
+/*
+ * Look for a customed capacity of a CPU in the cpu_topo_data table during the
+ * boot. The update of all CPUs is in O(n^2) for heteregeneous system but the
+ * function returns directly for SMP system.
+ */
+static void update_cpu_power(unsigned int cpu)
+{
+ if (!cpu_capacity(cpu))
+ return;
+
+ set_power_scale(cpu, cpu_capacity(cpu) / middle_capacity);
+
+ pr_info("CPU%u: update cpu_power %lu\n",
+ cpu, arch_scale_freq_power(NULL, cpu));
+}
+
/*
* cpu topology table
*/
@@ -269,6 +412,7 @@ void store_cpu_topology(unsigned int cpuid)
topology_populated:
update_siblings_masks(cpuid);
+ update_cpu_power(cpuid);
}
static void __init reset_cpu_topology(void)
@@ -289,6 +433,14 @@ static void __init reset_cpu_topology(void)
}
}
+static void __init reset_cpu_power(void)
+{
+ unsigned int cpu;
+
+ for_each_possible_cpu(cpu)
+ set_power_scale(cpu, SCHED_CAPACITY_SCALE);
+}
+
void __init init_cpu_topology(void)
{
reset_cpu_topology();
@@ -299,4 +451,7 @@ void __init init_cpu_topology(void)
*/
if (parse_dt_topology())
reset_cpu_topology();
+
+ reset_cpu_power();
+ parse_dt_cpu_power();
}
diff --git a/arch/unicore32/kernel/signal.c b/arch/unicore32/kernel/signal.c
index 780d77388dec..7c8fb7018dc6 100644
--- a/arch/unicore32/kernel/signal.c
+++ b/arch/unicore32/kernel/signal.c
@@ -254,7 +254,8 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,
err |= setup_sigframe(frame, regs, set);
if (err == 0)
- err |= setup_return(regs, &ksig->ka, frame->retcode, frame, usig);
+ err |= setup_return(regs, &ksig->ka, frame->retcode, frame,
+ ksig->sig);
return err;
}
@@ -276,7 +277,8 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
err |= __save_altstack(&frame->sig.uc.uc_stack, regs->UCreg_sp);
err |= setup_sigframe(&frame->sig, regs, set);
if (err == 0)
- err |= setup_return(regs, &ksig->ka, frame->sig.retcode, frame, usig);
+ err |= setup_return(regs, &ksig->ka, frame->sig.retcode, frame,
+ ksig->sig);
if (err == 0) {
/*
@@ -303,7 +305,6 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs,
int syscall)
{
struct thread_info *thread = current_thread_info();
- struct task_struct *tsk = current;
sigset_t *oldset = sigmask_to_save();
int usig = ksig->sig;
int ret;
@@ -373,7 +374,7 @@ static void do_signal(struct pt_regs *regs, int syscall)
if (!user_mode(regs))
return;
- if (get_signsl(&ksig)) {
+ if (get_signal(&ksig)) {
handle_signal(&ksig, regs, syscall);
return;
}
diff --git a/arch/x86/include/asm/idle.h b/arch/x86/include/asm/idle.h
index c5d1785373ed..02bab09707f2 100644
--- a/arch/x86/include/asm/idle.h
+++ b/arch/x86/include/asm/idle.h
@@ -1,13 +1,6 @@
#ifndef _ASM_X86_IDLE_H
#define _ASM_X86_IDLE_H
-#define IDLE_START 1
-#define IDLE_END 2
-
-struct notifier_block;
-void idle_notifier_register(struct notifier_block *n);
-void idle_notifier_unregister(struct notifier_block *n);
-
#ifdef CONFIG_X86_64
void enter_idle(void);
void exit_idle(void);
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index f804dc935d2a..3d7847b34d08 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -40,19 +40,6 @@ __visible DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, init_tss) = INIT_TSS;
#ifdef CONFIG_X86_64
static DEFINE_PER_CPU(unsigned char, is_idle);
-static ATOMIC_NOTIFIER_HEAD(idle_notifier);
-
-void idle_notifier_register(struct notifier_block *n)
-{
- atomic_notifier_chain_register(&idle_notifier, n);
-}
-EXPORT_SYMBOL_GPL(idle_notifier_register);
-
-void idle_notifier_unregister(struct notifier_block *n)
-{
- atomic_notifier_chain_unregister(&idle_notifier, n);
-}
-EXPORT_SYMBOL_GPL(idle_notifier_unregister);
#endif
struct kmem_cache *task_xstate_cachep;
@@ -258,14 +245,14 @@ static inline void play_dead(void)
void enter_idle(void)
{
this_cpu_write(is_idle, 1);
- atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL);
+ idle_notifier_call_chain(IDLE_START);
}
static void __exit_idle(void)
{
if (x86_test_and_clear_bit_percpu(0, is_idle) == 0)
return;
- atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL);
+ idle_notifier_call_chain(IDLE_END);
}
/* Called from interrupts to signify idle end */
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index 3a617af60d46..49c6c3d94449 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -4,24 +4,23 @@ config ZONE_DMA
config XTENSA
def_bool y
select ARCH_WANT_FRAME_POINTERS
- select HAVE_IDE
- select GENERIC_ATOMIC64
- select GENERIC_CLOCKEVENTS
- select VIRT_TO_BUS
- select GENERIC_IRQ_SHOW
- select GENERIC_SCHED_CLOCK
- select MODULES_USE_ELF_RELA
- select GENERIC_PCI_IOMAP
select ARCH_WANT_IPC_PARSE_VERSION
select ARCH_WANT_OPTIONAL_GPIOLIB
select BUILDTIME_EXTABLE_SORT
select CLONE_BACKWARDS
- select IRQ_DOMAIN
- select HAVE_OPROFILE
+ select COMMON_CLK
+ select GENERIC_ATOMIC64
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_IRQ_SHOW
+ select GENERIC_PCI_IOMAP
+ select GENERIC_SCHED_CLOCK
select HAVE_FUNCTION_TRACER
select HAVE_IRQ_TIME_ACCOUNTING
+ select HAVE_OPROFILE
select HAVE_PERF_EVENTS
- select COMMON_CLK
+ select IRQ_DOMAIN
+ select MODULES_USE_ELF_RELA
+ select VIRT_TO_BUS
help
Xtensa processors are 32-bit RISC machines designed by Tensilica
primarily for embedded systems. These processors are both
@@ -62,7 +61,9 @@ config TRACE_IRQFLAGS_SUPPORT
def_bool y
config MMU
- def_bool n
+ bool
+ default n if !XTENSA_VARIANT_CUSTOM
+ default XTENSA_VARIANT_MMU if XTENSA_VARIANT_CUSTOM
config VARIANT_IRQ_SWITCH
def_bool n
@@ -102,8 +103,40 @@ config XTENSA_VARIANT_S6000
select VARIANT_IRQ_SWITCH
select ARCH_REQUIRE_GPIOLIB
select XTENSA_CALIBRATE_CCOUNT
+
+config XTENSA_VARIANT_CUSTOM
+ bool "Custom Xtensa processor configuration"
+ select MAY_HAVE_SMP
+ select HAVE_XTENSA_GPIO32
+ help
+ Select this variant to use a custom Xtensa processor configuration.
+ You will be prompted for a processor variant CORENAME.
endchoice
+config XTENSA_VARIANT_CUSTOM_NAME
+ string "Xtensa Processor Custom Core Variant Name"
+ depends on XTENSA_VARIANT_CUSTOM
+ help
+ Provide the name of a custom Xtensa processor variant.
+ This CORENAME selects arch/xtensa/variant/CORENAME.
+ Dont forget you have to select MMU if you have one.
+
+config XTENSA_VARIANT_NAME
+ string
+ default "dc232b" if XTENSA_VARIANT_DC232B
+ default "dc233c" if XTENSA_VARIANT_DC233C
+ default "fsf" if XTENSA_VARIANT_FSF
+ default "s6000" if XTENSA_VARIANT_S6000
+ default XTENSA_VARIANT_CUSTOM_NAME if XTENSA_VARIANT_CUSTOM
+
+config XTENSA_VARIANT_MMU
+ bool "Core variant has a Full MMU (TLB, Pages, Protection, etc)"
+ depends on XTENSA_VARIANT_CUSTOM
+ default y
+ help
+ Build a Conventional Kernel with full MMU support,
+ ie: it supports a TLB with auto-loading, page protection.
+
config XTENSA_UNALIGNED_USER
bool "Unaligned memory access in use space"
help
@@ -156,13 +189,9 @@ config HOTPLUG_CPU
Say N if you want to disable CPU hotplug.
-config MATH_EMULATION
- bool "Math emulation"
- help
- Can we use information of configuration file?
-
config INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX
bool "Initialize Xtensa MMU inside the Linux kernel code"
+ depends on MMU
default y
help
Earlier version initialized the MMU in the exception vector
@@ -192,6 +221,7 @@ config INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX
config HIGHMEM
bool "High Memory Support"
+ depends on MMU
help
Linux can use the full amount of RAM in the system by
default. However, the default MMUv2 setup only maps the
@@ -208,6 +238,32 @@ config HIGHMEM
If unsure, say Y.
+config FAST_SYSCALL_XTENSA
+ bool "Enable fast atomic syscalls"
+ default n
+ help
+ fast_syscall_xtensa is a syscall that can make atomic operations
+ on UP kernel when processor has no s32c1i support.
+
+ This syscall is deprecated. It may have issues when called with
+ invalid arguments. It is provided only for backwards compatibility.
+ Only enable it if your userspace software requires it.
+
+ If unsure, say N.
+
+config FAST_SYSCALL_SPILL_REGISTERS
+ bool "Enable spill registers syscall"
+ default n
+ help
+ fast_syscall_spill_registers is a syscall that spills all active
+ register windows of a calling userspace task onto its stack.
+
+ This syscall is deprecated. It may have issues when called with
+ invalid arguments. It is provided only for backwards compatibility.
+ Only enable it if your userspace software requires it.
+
+ If unsure, say N.
+
endmenu
config XTENSA_CALIBRATE_CCOUNT
@@ -250,12 +306,14 @@ config XTENSA_PLATFORM_ISS
config XTENSA_PLATFORM_XT2000
bool "XT2000"
+ select HAVE_IDE
help
XT2000 is the name of Tensilica's feature-rich emulation platform.
This hardware is capable of running a full Linux distribution.
config XTENSA_PLATFORM_S6105
bool "S6105"
+ select HAVE_IDE
select SERIAL_CONSOLE
select NO_IOPORT_MAP
diff --git a/arch/xtensa/Makefile b/arch/xtensa/Makefile
index 81250ece3062..472533064b46 100644
--- a/arch/xtensa/Makefile
+++ b/arch/xtensa/Makefile
@@ -4,6 +4,7 @@
# for more details.
#
# Copyright (C) 2001 - 2005 Tensilica Inc.
+# Copyright (C) 2014 Cadence Design Systems Inc.
#
# This file is included by the global makefile so that you can add your own
# architecture-specific flags and dependencies. Remember to do have actions
@@ -13,11 +14,7 @@
# Core configuration.
# (Use VAR=<xtensa_config> to use another default compiler.)
-variant-$(CONFIG_XTENSA_VARIANT_FSF) := fsf
-variant-$(CONFIG_XTENSA_VARIANT_DC232B) := dc232b
-variant-$(CONFIG_XTENSA_VARIANT_DC233C) := dc233c
-variant-$(CONFIG_XTENSA_VARIANT_S6000) := s6000
-variant-$(CONFIG_XTENSA_VARIANT_LINUX_CUSTOM) := custom
+variant-y := $(patsubst "%",%,$(CONFIG_XTENSA_VARIANT_NAME))
VARIANT = $(variant-y)
export VARIANT
diff --git a/arch/xtensa/boot/dts/kc705.dts b/arch/xtensa/boot/dts/kc705.dts
index 742a347be67a..c4d17a34ab86 100644
--- a/arch/xtensa/boot/dts/kc705.dts
+++ b/arch/xtensa/boot/dts/kc705.dts
@@ -4,8 +4,11 @@
/ {
compatible = "cdns,xtensa-kc705";
+ chosen {
+ bootargs = "earlycon=uart8250,mmio32,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug memmap=0x38000000";
+ };
memory@0 {
device_type = "memory";
- reg = <0x00000000 0x08000000>;
+ reg = <0x00000000 0x38000000>;
};
};
diff --git a/arch/xtensa/configs/common_defconfig b/arch/xtensa/configs/common_defconfig
index f6000fe05119..721df1214bc3 100644
--- a/arch/xtensa/configs/common_defconfig
+++ b/arch/xtensa/configs/common_defconfig
@@ -66,7 +66,6 @@ CONFIG_XTENSA_ARCH_LINUX_BE=y
CONFIG_MMU=y
# CONFIG_XTENSA_UNALIGNED_USER is not set
# CONFIG_PREEMPT is not set
-# CONFIG_MATH_EMULATION is not set
# CONFIG_HIGHMEM is not set
#
diff --git a/arch/xtensa/configs/iss_defconfig b/arch/xtensa/configs/iss_defconfig
index 1493c68352d1..b966baf82cae 100644
--- a/arch/xtensa/configs/iss_defconfig
+++ b/arch/xtensa/configs/iss_defconfig
@@ -146,7 +146,6 @@ CONFIG_XTENSA_VARIANT_FSF=y
# CONFIG_XTENSA_VARIANT_S6000 is not set
# CONFIG_XTENSA_UNALIGNED_USER is not set
# CONFIG_PREEMPT is not set
-# CONFIG_MATH_EMULATION is not set
CONFIG_XTENSA_CALIBRATE_CCOUNT=y
CONFIG_SERIAL_CONSOLE=y
CONFIG_XTENSA_ISS_NETWORK=y
@@ -308,7 +307,7 @@ CONFIG_MISC_DEVICES=y
# EEPROM support
#
# CONFIG_EEPROM_93CX6 is not set
-CONFIG_HAVE_IDE=y
+# CONFIG_HAVE_IDE is not set
# CONFIG_IDE is not set
#
diff --git a/arch/xtensa/configs/s6105_defconfig b/arch/xtensa/configs/s6105_defconfig
index 12a492ab6d17..9471265b8ca6 100644
--- a/arch/xtensa/configs/s6105_defconfig
+++ b/arch/xtensa/configs/s6105_defconfig
@@ -109,7 +109,6 @@ CONFIG_VARIANT_IRQ_SWITCH=y
CONFIG_XTENSA_VARIANT_S6000=y
# CONFIG_XTENSA_UNALIGNED_USER is not set
CONFIG_PREEMPT=y
-# CONFIG_MATH_EMULATION is not set
# CONFIG_HIGHMEM is not set
CONFIG_XTENSA_CALIBRATE_CCOUNT=y
CONFIG_SERIAL_CONSOLE=y
diff --git a/arch/xtensa/include/asm/cacheflush.h b/arch/xtensa/include/asm/cacheflush.h
index 555a98a18453..e72aaca7a77f 100644
--- a/arch/xtensa/include/asm/cacheflush.h
+++ b/arch/xtensa/include/asm/cacheflush.h
@@ -37,6 +37,7 @@
* specials for cache aliasing:
*
* __flush_invalidate_dcache_page_alias(vaddr,paddr)
+ * __invalidate_dcache_page_alias(vaddr,paddr)
* __invalidate_icache_page_alias(vaddr,paddr)
*/
@@ -62,6 +63,7 @@ extern void __flush_invalidate_dcache_range(unsigned long, unsigned long);
#if defined(CONFIG_MMU) && (DCACHE_WAY_SIZE > PAGE_SIZE)
extern void __flush_invalidate_dcache_page_alias(unsigned long, unsigned long);
+extern void __invalidate_dcache_page_alias(unsigned long, unsigned long);
#else
static inline void __flush_invalidate_dcache_page_alias(unsigned long virt,
unsigned long phys) { }
diff --git a/arch/xtensa/include/asm/fixmap.h b/arch/xtensa/include/asm/fixmap.h
index 9f6c33d0428a..62b507deea9d 100644
--- a/arch/xtensa/include/asm/fixmap.h
+++ b/arch/xtensa/include/asm/fixmap.h
@@ -23,8 +23,8 @@
* Here we define all the compile-time 'special' virtual
* addresses. The point is to have a constant address at
* compile time, but to set the physical address only
- * in the boot process. We allocate these special addresses
- * from the end of the consistent memory region backwards.
+ * in the boot process. We allocate these special addresses
+ * from the start of the consistent memory region upwards.
* Also this lets us do fail-safe vmalloc(), we
* can guarantee that these special addresses and
* vmalloc()-ed addresses never overlap.
@@ -38,7 +38,8 @@ enum fixed_addresses {
#ifdef CONFIG_HIGHMEM
/* reserved pte's for temporary kernel mappings */
FIX_KMAP_BEGIN,
- FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1,
+ FIX_KMAP_END = FIX_KMAP_BEGIN +
+ (KM_TYPE_NR * NR_CPUS * DCACHE_N_COLORS) - 1,
#endif
__end_of_fixed_addresses
};
@@ -47,7 +48,28 @@ enum fixed_addresses {
#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_START ((FIXADDR_TOP - FIXADDR_SIZE) & PMD_MASK)
-#include <asm-generic/fixmap.h>
+#define __fix_to_virt(x) (FIXADDR_START + ((x) << PAGE_SHIFT))
+#define __virt_to_fix(x) (((x) - FIXADDR_START) >> PAGE_SHIFT)
+
+#ifndef __ASSEMBLY__
+/*
+ * 'index to address' translation. If anyone tries to use the idx
+ * directly without translation, we catch the bug with a NULL-deference
+ * kernel oops. Illegal ranges of incoming indices are caught too.
+ */
+static __always_inline unsigned long fix_to_virt(const unsigned int idx)
+{
+ BUILD_BUG_ON(idx >= __end_of_fixed_addresses);
+ return __fix_to_virt(idx);
+}
+
+static inline unsigned long virt_to_fix(const unsigned long vaddr)
+{
+ BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START);
+ return __virt_to_fix(vaddr);
+}
+
+#endif
#define kmap_get_fixmap_pte(vaddr) \
pte_offset_kernel( \
diff --git a/arch/xtensa/include/asm/highmem.h b/arch/xtensa/include/asm/highmem.h
index 2653ef5d55f1..2c7901edffaf 100644
--- a/arch/xtensa/include/asm/highmem.h
+++ b/arch/xtensa/include/asm/highmem.h
@@ -12,19 +12,55 @@
#ifndef _XTENSA_HIGHMEM_H
#define _XTENSA_HIGHMEM_H
+#include <linux/wait.h>
#include <asm/cacheflush.h>
#include <asm/fixmap.h>
#include <asm/kmap_types.h>
#include <asm/pgtable.h>
-#define PKMAP_BASE (FIXADDR_START - PMD_SIZE)
-#define LAST_PKMAP PTRS_PER_PTE
+#define PKMAP_BASE ((FIXADDR_START - \
+ (LAST_PKMAP + 1) * PAGE_SIZE) & PMD_MASK)
+#define LAST_PKMAP (PTRS_PER_PTE * DCACHE_N_COLORS)
#define LAST_PKMAP_MASK (LAST_PKMAP - 1)
#define PKMAP_NR(virt) (((virt) - PKMAP_BASE) >> PAGE_SHIFT)
#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT))
#define kmap_prot PAGE_KERNEL
+#if DCACHE_WAY_SIZE > PAGE_SIZE
+#define get_pkmap_color get_pkmap_color
+static inline int get_pkmap_color(struct page *page)
+{
+ return DCACHE_ALIAS(page_to_phys(page));
+}
+
+extern unsigned int last_pkmap_nr_arr[];
+
+static inline unsigned int get_next_pkmap_nr(unsigned int color)
+{
+ last_pkmap_nr_arr[color] =
+ (last_pkmap_nr_arr[color] + DCACHE_N_COLORS) & LAST_PKMAP_MASK;
+ return last_pkmap_nr_arr[color] + color;
+}
+
+static inline int no_more_pkmaps(unsigned int pkmap_nr, unsigned int color)
+{
+ return pkmap_nr < DCACHE_N_COLORS;
+}
+
+static inline int get_pkmap_entries_count(unsigned int color)
+{
+ return LAST_PKMAP / DCACHE_N_COLORS;
+}
+
+extern wait_queue_head_t pkmap_map_wait_arr[];
+
+static inline wait_queue_head_t *get_pkmap_wait_queue_head(unsigned int color)
+{
+ return pkmap_map_wait_arr + color;
+}
+#endif
+
extern pte_t *pkmap_page_table;
void *kmap_high(struct page *page);
diff --git a/arch/xtensa/include/asm/page.h b/arch/xtensa/include/asm/page.h
index 47f582333f6b..abe24c6f8b2f 100644
--- a/arch/xtensa/include/asm/page.h
+++ b/arch/xtensa/include/asm/page.h
@@ -78,7 +78,9 @@
# define DCACHE_ALIAS_EQ(a,b) ((((a) ^ (b)) & DCACHE_ALIAS_MASK) == 0)
#else
# define DCACHE_ALIAS_ORDER 0
+# define DCACHE_ALIAS(a) ((void)(a), 0)
#endif
+#define DCACHE_N_COLORS (1 << DCACHE_ALIAS_ORDER)
#if ICACHE_WAY_SIZE > PAGE_SIZE
# define ICACHE_ALIAS_ORDER (ICACHE_WAY_SHIFT - PAGE_SHIFT)
@@ -134,6 +136,7 @@ static inline __attribute_const__ int get_order(unsigned long size)
#endif
struct page;
+struct vm_area_struct;
extern void clear_page(void *page);
extern void copy_page(void *to, void *from);
@@ -143,8 +146,15 @@ extern void copy_page(void *to, void *from);
*/
#if DCACHE_WAY_SIZE > PAGE_SIZE
-extern void clear_user_page(void*, unsigned long, struct page*);
-extern void copy_user_page(void*, void*, unsigned long, struct page*);
+extern void clear_page_alias(void *vaddr, unsigned long paddr);
+extern void copy_page_alias(void *to, void *from,
+ unsigned long to_paddr, unsigned long from_paddr);
+
+#define clear_user_highpage clear_user_highpage
+void clear_user_highpage(struct page *page, unsigned long vaddr);
+#define __HAVE_ARCH_COPY_USER_HIGHPAGE
+void copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr, struct vm_area_struct *vma);
#else
# define clear_user_page(page, vaddr, pg) clear_page(page)
# define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
diff --git a/arch/xtensa/include/asm/pgtable.h b/arch/xtensa/include/asm/pgtable.h
index 4b0ca35a93b1..b2173e5da601 100644
--- a/arch/xtensa/include/asm/pgtable.h
+++ b/arch/xtensa/include/asm/pgtable.h
@@ -67,7 +67,12 @@
#define VMALLOC_START 0xC0000000
#define VMALLOC_END 0xC7FEFFFF
#define TLBTEMP_BASE_1 0xC7FF0000
-#define TLBTEMP_BASE_2 0xC7FF8000
+#define TLBTEMP_BASE_2 (TLBTEMP_BASE_1 + DCACHE_WAY_SIZE)
+#if 2 * DCACHE_WAY_SIZE > ICACHE_WAY_SIZE
+#define TLBTEMP_SIZE (2 * DCACHE_WAY_SIZE)
+#else
+#define TLBTEMP_SIZE ICACHE_WAY_SIZE
+#endif
/*
* For the Xtensa architecture, the PTE layout is as follows:
diff --git a/arch/xtensa/include/asm/uaccess.h b/arch/xtensa/include/asm/uaccess.h
index fd686dc45d1a..c7211e7e182d 100644
--- a/arch/xtensa/include/asm/uaccess.h
+++ b/arch/xtensa/include/asm/uaccess.h
@@ -52,7 +52,12 @@
*/
.macro get_fs ad, sp
GET_CURRENT(\ad,\sp)
+#if THREAD_CURRENT_DS > 1020
+ addi \ad, \ad, TASK_THREAD
+ l32i \ad, \ad, THREAD_CURRENT_DS - TASK_THREAD
+#else
l32i \ad, \ad, THREAD_CURRENT_DS
+#endif
.endm
/*
diff --git a/arch/xtensa/include/uapi/asm/ioctls.h b/arch/xtensa/include/uapi/asm/ioctls.h
index b4cb1100c0fb..a47909f0c34b 100644
--- a/arch/xtensa/include/uapi/asm/ioctls.h
+++ b/arch/xtensa/include/uapi/asm/ioctls.h
@@ -28,17 +28,17 @@
#define TCSETSW 0x5403
#define TCSETSF 0x5404
-#define TCGETA _IOR('t', 23, struct termio)
-#define TCSETA _IOW('t', 24, struct termio)
-#define TCSETAW _IOW('t', 25, struct termio)
-#define TCSETAF _IOW('t', 28, struct termio)
+#define TCGETA 0x80127417 /* _IOR('t', 23, struct termio) */
+#define TCSETA 0x40127418 /* _IOW('t', 24, struct termio) */
+#define TCSETAW 0x40127419 /* _IOW('t', 25, struct termio) */
+#define TCSETAF 0x4012741C /* _IOW('t', 28, struct termio) */
#define TCSBRK _IO('t', 29)
#define TCXONC _IO('t', 30)
#define TCFLSH _IO('t', 31)
-#define TIOCSWINSZ _IOW('t', 103, struct winsize)
-#define TIOCGWINSZ _IOR('t', 104, struct winsize)
+#define TIOCSWINSZ 0x40087467 /* _IOW('t', 103, struct winsize) */
+#define TIOCGWINSZ 0x80087468 /* _IOR('t', 104, struct winsize) */
#define TIOCSTART _IO('t', 110) /* start output, like ^Q */
#define TIOCSTOP _IO('t', 111) /* stop output, like ^S */
#define TIOCOUTQ _IOR('t', 115, int) /* output queue size */
@@ -88,7 +88,6 @@
#define TIOCSETD _IOW('T', 35, int)
#define TIOCGETD _IOR('T', 36, int)
#define TCSBRKP _IOW('T', 37, int) /* Needed for POSIX tcsendbreak()*/
-#define TIOCTTYGSTRUCT _IOR('T', 38, struct tty_struct) /* For debugging only*/
#define TIOCSBRK _IO('T', 39) /* BSD compatibility */
#define TIOCCBRK _IO('T', 40) /* BSD compatibility */
#define TIOCGSID _IOR('T', 41, pid_t) /* Return the session ID of FD*/
@@ -114,8 +113,10 @@
#define TIOCSERGETLSR _IOR('T', 89, unsigned int) /* Get line status reg. */
/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
# define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
-#define TIOCSERGETMULTI _IOR('T', 90, struct serial_multiport_struct) /* Get multiport config */
-#define TIOCSERSETMULTI _IOW('T', 91, struct serial_multiport_struct) /* Set multiport config */
+#define TIOCSERGETMULTI 0x80a8545a /* Get multiport config */
+ /* _IOR('T', 90, struct serial_multiport_struct) */
+#define TIOCSERSETMULTI 0x40a8545b /* Set multiport config */
+ /* _IOW('T', 91, struct serial_multiport_struct) */
#define TIOCMIWAIT _IO('T', 92) /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
diff --git a/arch/xtensa/include/uapi/asm/unistd.h b/arch/xtensa/include/uapi/asm/unistd.h
index b9395529f02d..8883fc877c5c 100644
--- a/arch/xtensa/include/uapi/asm/unistd.h
+++ b/arch/xtensa/include/uapi/asm/unistd.h
@@ -739,7 +739,10 @@ __SYSCALL(334, sys_sched_setattr, 2)
#define __NR_sched_getattr 335
__SYSCALL(335, sys_sched_getattr, 3)
-#define __NR_syscall_count 336
+#define __NR_renameat2 336
+__SYSCALL(336, sys_renameat2, 5)
+
+#define __NR_syscall_count 337
/*
* sysxtensa syscall handler
diff --git a/arch/xtensa/kernel/align.S b/arch/xtensa/kernel/align.S
index d4cef6039a5c..890004af03a9 100644
--- a/arch/xtensa/kernel/align.S
+++ b/arch/xtensa/kernel/align.S
@@ -8,6 +8,7 @@
* this archive for more details.
*
* Copyright (C) 2001 - 2005 Tensilica, Inc.
+ * Copyright (C) 2014 Cadence Design Systems Inc.
*
* Rewritten by Chris Zankel <chris@zankel.net>
*
@@ -174,6 +175,10 @@ ENTRY(fast_unaligned)
s32i a0, a2, PT_AREG2
s32i a3, a2, PT_AREG3
+ rsr a3, excsave1
+ movi a4, fast_unaligned_fixup
+ s32i a4, a3, EXC_TABLE_FIXUP
+
/* Keep value of SAR in a0 */
rsr a0, sar
@@ -225,10 +230,6 @@ ENTRY(fast_unaligned)
addx8 a5, a6, a5
jx a5 # jump into table
- /* Invalid instruction, CRITICAL! */
-.Linvalid_instruction_load:
- j .Linvalid_instruction
-
/* Load: Load memory address. */
.Lload: movi a3, ~3
@@ -272,18 +273,6 @@ ENTRY(fast_unaligned)
/* Set target register. */
1:
-
-#if XCHAL_HAVE_LOOPS
- rsr a5, lend # check if we reached LEND
- bne a7, a5, 1f
- rsr a5, lcount # and LCOUNT != 0
- beqz a5, 1f
- addi a5, a5, -1 # decrement LCOUNT and set
- rsr a7, lbeg # set PC to LBEGIN
- wsr a5, lcount
-#endif
-
-1: wsr a7, epc1 # skip load instruction
extui a4, a4, INSN_T, 4 # extract target register
movi a5, .Lload_table
addx8 a4, a4, a5
@@ -326,6 +315,35 @@ ENTRY(fast_unaligned)
mov a3, a14 ; _j 1f; .align 8
mov a3, a15 ; _j 1f; .align 8
+ /* We cannot handle this exception. */
+
+ .extern _kernel_exception
+.Linvalid_instruction_load:
+.Linvalid_instruction_store:
+
+ movi a4, 0
+ rsr a3, excsave1
+ s32i a4, a3, EXC_TABLE_FIXUP
+
+ /* Restore a4...a8 and SAR, set SP, and jump to default exception. */
+
+ l32i a8, a2, PT_AREG8
+ l32i a7, a2, PT_AREG7
+ l32i a6, a2, PT_AREG6
+ l32i a5, a2, PT_AREG5
+ l32i a4, a2, PT_AREG4
+ wsr a0, sar
+ mov a1, a2
+
+ rsr a0, ps
+ bbsi.l a0, PS_UM_BIT, 2f # jump if user mode
+
+ movi a0, _kernel_exception
+ jx a0
+
+2: movi a0, _user_exception
+ jx a0
+
1: # a7: instruction pointer, a4: instruction, a3: value
movi a6, 0 # mask: ffffffff:00000000
@@ -353,17 +371,6 @@ ENTRY(fast_unaligned)
/* Get memory address */
1:
-#if XCHAL_HAVE_LOOPS
- rsr a4, lend # check if we reached LEND
- bne a7, a4, 1f
- rsr a4, lcount # and LCOUNT != 0
- beqz a4, 1f
- addi a4, a4, -1 # decrement LCOUNT and set
- rsr a7, lbeg # set PC to LBEGIN
- wsr a4, lcount
-#endif
-
-1: wsr a7, epc1 # skip store instruction
movi a4, ~3
and a4, a4, a8 # align memory address
@@ -375,25 +382,25 @@ ENTRY(fast_unaligned)
#endif
__ssa8r a8
- __src_b a7, a5, a6 # lo-mask F..F0..0 (BE) 0..0F..F (LE)
+ __src_b a8, a5, a6 # lo-mask F..F0..0 (BE) 0..0F..F (LE)
__src_b a6, a6, a5 # hi-mask 0..0F..F (BE) F..F0..0 (LE)
#ifdef UNALIGNED_USER_EXCEPTION
l32e a5, a4, -8
#else
l32i a5, a4, 0 # load lower address word
#endif
- and a5, a5, a7 # mask
- __sh a7, a3 # shift value
- or a5, a5, a7 # or with original value
+ and a5, a5, a8 # mask
+ __sh a8, a3 # shift value
+ or a5, a5, a8 # or with original value
#ifdef UNALIGNED_USER_EXCEPTION
s32e a5, a4, -8
- l32e a7, a4, -4
+ l32e a8, a4, -4
#else
s32i a5, a4, 0 # store
- l32i a7, a4, 4 # same for upper address word
+ l32i a8, a4, 4 # same for upper address word
#endif
__sl a5, a3
- and a6, a7, a6
+ and a6, a8, a6
or a6, a6, a5
#ifdef UNALIGNED_USER_EXCEPTION
s32e a6, a4, -4
@@ -401,9 +408,27 @@ ENTRY(fast_unaligned)
s32i a6, a4, 4
#endif
- /* Done. restore stack and return */
-
.Lexit:
+#if XCHAL_HAVE_LOOPS
+ rsr a4, lend # check if we reached LEND
+ bne a7, a4, 1f
+ rsr a4, lcount # and LCOUNT != 0
+ beqz a4, 1f
+ addi a4, a4, -1 # decrement LCOUNT and set
+ rsr a7, lbeg # set PC to LBEGIN
+ wsr a4, lcount
+#endif
+
+1: wsr a7, epc1 # skip emulated instruction
+
+ /* Update icount if we're single-stepping in userspace. */
+ rsr a4, icountlevel
+ beqz a4, 1f
+ bgeui a4, LOCKLEVEL + 1, 1f
+ rsr a4, icount
+ addi a4, a4, 1
+ wsr a4, icount
+1:
movi a4, 0
rsr a3, excsave1
s32i a4, a3, EXC_TABLE_FIXUP
@@ -424,31 +449,40 @@ ENTRY(fast_unaligned)
l32i a2, a2, PT_AREG2
rfe
- /* We cannot handle this exception. */
+ENDPROC(fast_unaligned)
- .extern _kernel_exception
-.Linvalid_instruction_store:
-.Linvalid_instruction:
+ENTRY(fast_unaligned_fixup)
- /* Restore a4...a8 and SAR, set SP, and jump to default exception. */
+ l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
+ wsr a3, excsave1
l32i a8, a2, PT_AREG8
l32i a7, a2, PT_AREG7
l32i a6, a2, PT_AREG6
l32i a5, a2, PT_AREG5
l32i a4, a2, PT_AREG4
+ l32i a0, a2, PT_AREG2
+ xsr a0, depc # restore depc and a0
wsr a0, sar
- mov a1, a2
+
+ rsr a0, exccause
+ s32i a0, a2, PT_DEPC # mark as a regular exception
rsr a0, ps
- bbsi.l a2, PS_UM_BIT, 1f # jump if user mode
+ bbsi.l a0, PS_UM_BIT, 1f # jump if user mode
- movi a0, _kernel_exception
+ rsr a0, exccause
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_KERNEL # load handler
+ l32i a3, a2, PT_AREG3
jx a0
-
-1: movi a0, _user_exception
+1:
+ rsr a0, exccause
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_USER # load handler
+ l32i a3, a2, PT_AREG3
jx a0
-ENDPROC(fast_unaligned)
+ENDPROC(fast_unaligned_fixup)
#endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
index ef7f4990722b..82bbfa5a05b3 100644
--- a/arch/xtensa/kernel/entry.S
+++ b/arch/xtensa/kernel/entry.S
@@ -986,6 +986,8 @@ ENDPROC(fast_syscall_unrecoverable)
* j done
*/
+#ifdef CONFIG_FAST_SYSCALL_XTENSA
+
#define TRY \
.section __ex_table, "a"; \
.word 66f, 67f; \
@@ -1001,9 +1003,8 @@ ENTRY(fast_syscall_xtensa)
movi a7, 4 # sizeof(unsigned int)
access_ok a3, a7, a0, a2, .Leac # a0: scratch reg, a2: sp
- addi a6, a6, -1 # assuming SYS_XTENSA_ATOMIC_SET = 1
- _bgeui a6, SYS_XTENSA_COUNT - 1, .Lill
- _bnei a6, SYS_XTENSA_ATOMIC_CMP_SWP - 1, .Lnswp
+ _bgeui a6, SYS_XTENSA_COUNT, .Lill
+ _bnei a6, SYS_XTENSA_ATOMIC_CMP_SWP, .Lnswp
/* Fall through for ATOMIC_CMP_SWP. */
@@ -1015,27 +1016,26 @@ TRY s32i a5, a3, 0 # different, modify value
l32i a7, a2, PT_AREG7 # restore a7
l32i a0, a2, PT_AREG0 # restore a0
movi a2, 1 # and return 1
- addi a6, a6, 1 # restore a6 (really necessary?)
rfe
1: l32i a7, a2, PT_AREG7 # restore a7
l32i a0, a2, PT_AREG0 # restore a0
movi a2, 0 # return 0 (note that we cannot set
- addi a6, a6, 1 # restore a6 (really necessary?)
rfe
.Lnswp: /* Atomic set, add, and exg_add. */
TRY l32i a7, a3, 0 # orig
+ addi a6, a6, -SYS_XTENSA_ATOMIC_SET
add a0, a4, a7 # + arg
moveqz a0, a4, a6 # set
+ addi a6, a6, SYS_XTENSA_ATOMIC_SET
TRY s32i a0, a3, 0 # write new value
mov a0, a2
mov a2, a7
l32i a7, a0, PT_AREG7 # restore a7
l32i a0, a0, PT_AREG0 # restore a0
- addi a6, a6, 1 # restore a6 (really necessary?)
rfe
CATCH
@@ -1044,13 +1044,25 @@ CATCH
movi a2, -EFAULT
rfe
-.Lill: l32i a7, a2, PT_AREG0 # restore a7
+.Lill: l32i a7, a2, PT_AREG7 # restore a7
l32i a0, a2, PT_AREG0 # restore a0
movi a2, -EINVAL
rfe
ENDPROC(fast_syscall_xtensa)
+#else /* CONFIG_FAST_SYSCALL_XTENSA */
+
+ENTRY(fast_syscall_xtensa)
+
+ l32i a0, a2, PT_AREG0 # restore a0
+ movi a2, -ENOSYS
+ rfe
+
+ENDPROC(fast_syscall_xtensa)
+
+#endif /* CONFIG_FAST_SYSCALL_XTENSA */
+
/* fast_syscall_spill_registers.
*
@@ -1066,6 +1078,8 @@ ENDPROC(fast_syscall_xtensa)
* Note: We assume the stack pointer is EXC_TABLE_KSTK in the fixup handler.
*/
+#ifdef CONFIG_FAST_SYSCALL_SPILL_REGISTERS
+
ENTRY(fast_syscall_spill_registers)
/* Register a FIXUP handler (pass current wb as a parameter) */
@@ -1400,6 +1414,18 @@ ENTRY(fast_syscall_spill_registers_fixup_return)
ENDPROC(fast_syscall_spill_registers_fixup_return)
+#else /* CONFIG_FAST_SYSCALL_SPILL_REGISTERS */
+
+ENTRY(fast_syscall_spill_registers)
+
+ l32i a0, a2, PT_AREG0 # restore a0
+ movi a2, -ENOSYS
+ rfe
+
+ENDPROC(fast_syscall_spill_registers)
+
+#endif /* CONFIG_FAST_SYSCALL_SPILL_REGISTERS */
+
#ifdef CONFIG_MMU
/*
* We should never get here. Bail out!
@@ -1565,7 +1591,7 @@ ENTRY(fast_second_level_miss)
rsr a0, excvaddr
bltu a0, a3, 2f
- addi a1, a0, -(2 << (DCACHE_ALIAS_ORDER + PAGE_SHIFT))
+ addi a1, a0, -TLBTEMP_SIZE
bgeu a1, a3, 2f
/* Check if we have to restore an ITLB mapping. */
@@ -1820,7 +1846,6 @@ ENTRY(_switch_to)
entry a1, 16
- mov a10, a2 # preserve 'prev' (a2)
mov a11, a3 # and 'next' (a3)
l32i a4, a2, TASK_THREAD_INFO
@@ -1828,8 +1853,14 @@ ENTRY(_switch_to)
save_xtregs_user a4 a6 a8 a9 a12 a13 THREAD_XTREGS_USER
- s32i a0, a10, THREAD_RA # save return address
- s32i a1, a10, THREAD_SP # save stack pointer
+#if THREAD_RA > 1020 || THREAD_SP > 1020
+ addi a10, a2, TASK_THREAD
+ s32i a0, a10, THREAD_RA - TASK_THREAD # save return address
+ s32i a1, a10, THREAD_SP - TASK_THREAD # save stack pointer
+#else
+ s32i a0, a2, THREAD_RA # save return address
+ s32i a1, a2, THREAD_SP # save stack pointer
+#endif
/* Disable ints while we manipulate the stack pointer. */
@@ -1870,7 +1901,6 @@ ENTRY(_switch_to)
load_xtregs_user a5 a6 a8 a9 a12 a13 THREAD_XTREGS_USER
wsr a14, ps
- mov a2, a10 # return 'prev'
rsync
retw
diff --git a/arch/xtensa/kernel/pci-dma.c b/arch/xtensa/kernel/pci-dma.c
index 2d9cc6dbfd78..e8b76b8e4b29 100644
--- a/arch/xtensa/kernel/pci-dma.c
+++ b/arch/xtensa/kernel/pci-dma.c
@@ -49,9 +49,8 @@ dma_alloc_coherent(struct device *dev,size_t size,dma_addr_t *handle,gfp_t flag)
/* We currently don't support coherent memory outside KSEG */
- if (ret < XCHAL_KSEG_CACHED_VADDR
- || ret >= XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE)
- BUG();
+ BUG_ON(ret < XCHAL_KSEG_CACHED_VADDR ||
+ ret > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);
if (ret != 0) {
@@ -68,10 +67,11 @@ EXPORT_SYMBOL(dma_alloc_coherent);
void dma_free_coherent(struct device *hwdev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
- long addr=(long)vaddr+XCHAL_KSEG_CACHED_VADDR-XCHAL_KSEG_BYPASS_VADDR;
+ unsigned long addr = (unsigned long)vaddr +
+ XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
- if (addr < 0 || addr >= XCHAL_KSEG_SIZE)
- BUG();
+ BUG_ON(addr < XCHAL_KSEG_CACHED_VADDR ||
+ addr > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);
free_pages(addr, get_order(size));
}
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c
index eebbfd8c26fc..9d2f45f010ef 100644
--- a/arch/xtensa/kernel/traps.c
+++ b/arch/xtensa/kernel/traps.c
@@ -101,9 +101,8 @@ static dispatch_init_table_t __initdata dispatch_init_table[] = {
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
#ifdef CONFIG_XTENSA_UNALIGNED_USER
{ EXCCAUSE_UNALIGNED, USER, fast_unaligned },
-#else
-{ EXCCAUSE_UNALIGNED, 0, do_unaligned_user },
#endif
+{ EXCCAUSE_UNALIGNED, 0, do_unaligned_user },
{ EXCCAUSE_UNALIGNED, KRNL, fast_unaligned },
#endif
#ifdef CONFIG_MMU
@@ -264,7 +263,6 @@ do_illegal_instruction(struct pt_regs *regs)
*/
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
-#ifndef CONFIG_XTENSA_UNALIGNED_USER
void
do_unaligned_user (struct pt_regs *regs)
{
@@ -286,7 +284,6 @@ do_unaligned_user (struct pt_regs *regs)
}
#endif
-#endif
void
do_debug(struct pt_regs *regs)
diff --git a/arch/xtensa/kernel/vectors.S b/arch/xtensa/kernel/vectors.S
index 8453e6e39895..1b397a902292 100644
--- a/arch/xtensa/kernel/vectors.S
+++ b/arch/xtensa/kernel/vectors.S
@@ -454,8 +454,14 @@ _DoubleExceptionVector_WindowOverflow:
s32i a0, a2, PT_DEPC
_DoubleExceptionVector_handle_exception:
+ addi a0, a0, -EXCCAUSE_UNALIGNED
+ beqz a0, 2f
addx4 a0, a0, a3
- l32i a0, a0, EXC_TABLE_FAST_USER
+ l32i a0, a0, EXC_TABLE_FAST_USER + 4 * EXCCAUSE_UNALIGNED
+ xsr a3, excsave1
+ jx a0
+2:
+ movi a0, user_exception
xsr a3, excsave1
jx a0
diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S
index d16db6df86f8..fc1bc2ba8d5d 100644
--- a/arch/xtensa/kernel/vmlinux.lds.S
+++ b/arch/xtensa/kernel/vmlinux.lds.S
@@ -269,13 +269,13 @@ SECTIONS
.UserExceptionVector.literal)
SECTION_VECTOR (_DoubleExceptionVector_literal,
.DoubleExceptionVector.literal,
- DOUBLEEXC_VECTOR_VADDR - 40,
+ DOUBLEEXC_VECTOR_VADDR - 48,
SIZEOF(.UserExceptionVector.text),
.UserExceptionVector.text)
SECTION_VECTOR (_DoubleExceptionVector_text,
.DoubleExceptionVector.text,
DOUBLEEXC_VECTOR_VADDR,
- 40,
+ 48,
.DoubleExceptionVector.literal)
. = (LOADADDR( .DoubleExceptionVector.text ) + SIZEOF( .DoubleExceptionVector.text ) + 3) & ~ 3;
diff --git a/arch/xtensa/mm/cache.c b/arch/xtensa/mm/cache.c
index 63cbb867dadd..d75aa1476da7 100644
--- a/arch/xtensa/mm/cache.c
+++ b/arch/xtensa/mm/cache.c
@@ -59,9 +59,68 @@
*
*/
-#if (DCACHE_WAY_SIZE > PAGE_SIZE) && defined(CONFIG_HIGHMEM)
-#error "HIGHMEM is not supported on cores with aliasing cache."
-#endif
+#if (DCACHE_WAY_SIZE > PAGE_SIZE)
+static inline void kmap_invalidate_coherent(struct page *page,
+ unsigned long vaddr)
+{
+ if (!DCACHE_ALIAS_EQ(page_to_phys(page), vaddr)) {
+ unsigned long kvaddr;
+
+ if (!PageHighMem(page)) {
+ kvaddr = (unsigned long)page_to_virt(page);
+
+ __invalidate_dcache_page(kvaddr);
+ } else {
+ kvaddr = TLBTEMP_BASE_1 +
+ (page_to_phys(page) & DCACHE_ALIAS_MASK);
+
+ __invalidate_dcache_page_alias(kvaddr,
+ page_to_phys(page));
+ }
+ }
+}
+
+static inline void *coherent_kvaddr(struct page *page, unsigned long base,
+ unsigned long vaddr, unsigned long *paddr)
+{
+ if (PageHighMem(page) || !DCACHE_ALIAS_EQ(page_to_phys(page), vaddr)) {
+ *paddr = page_to_phys(page);
+ return (void *)(base + (vaddr & DCACHE_ALIAS_MASK));
+ } else {
+ *paddr = 0;
+ return page_to_virt(page);
+ }
+}
+
+void clear_user_highpage(struct page *page, unsigned long vaddr)
+{
+ unsigned long paddr;
+ void *kvaddr = coherent_kvaddr(page, TLBTEMP_BASE_1, vaddr, &paddr);
+
+ pagefault_disable();
+ kmap_invalidate_coherent(page, vaddr);
+ set_bit(PG_arch_1, &page->flags);
+ clear_page_alias(kvaddr, paddr);
+ pagefault_enable();
+}
+
+void copy_user_highpage(struct page *dst, struct page *src,
+ unsigned long vaddr, struct vm_area_struct *vma)
+{
+ unsigned long dst_paddr, src_paddr;
+ void *dst_vaddr = coherent_kvaddr(dst, TLBTEMP_BASE_1, vaddr,
+ &dst_paddr);
+ void *src_vaddr = coherent_kvaddr(src, TLBTEMP_BASE_2, vaddr,
+ &src_paddr);
+
+ pagefault_disable();
+ kmap_invalidate_coherent(dst, vaddr);
+ set_bit(PG_arch_1, &dst->flags);
+ copy_page_alias(dst_vaddr, src_vaddr, dst_paddr, src_paddr);
+ pagefault_enable();
+}
+
+#endif /* DCACHE_WAY_SIZE > PAGE_SIZE */
#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
@@ -103,7 +162,8 @@ void flush_dcache_page(struct page *page)
if (!alias && !mapping)
return;
- __flush_invalidate_dcache_page((long)page_address(page));
+ virt = TLBTEMP_BASE_1 + (phys & DCACHE_ALIAS_MASK);
+ __flush_invalidate_dcache_page_alias(virt, phys);
virt = TLBTEMP_BASE_1 + (temp & DCACHE_ALIAS_MASK);
@@ -168,13 +228,12 @@ update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t *ptep)
#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
if (!PageReserved(page) && test_bit(PG_arch_1, &page->flags)) {
-
- unsigned long paddr = (unsigned long) page_address(page);
unsigned long phys = page_to_phys(page);
- unsigned long tmp = TLBTEMP_BASE_1 + (addr & DCACHE_ALIAS_MASK);
-
- __flush_invalidate_dcache_page(paddr);
+ unsigned long tmp;
+ tmp = TLBTEMP_BASE_1 + (phys & DCACHE_ALIAS_MASK);
+ __flush_invalidate_dcache_page_alias(tmp, phys);
+ tmp = TLBTEMP_BASE_1 + (addr & DCACHE_ALIAS_MASK);
__flush_invalidate_dcache_page_alias(tmp, phys);
__invalidate_icache_page_alias(tmp, phys);
diff --git a/arch/xtensa/mm/highmem.c b/arch/xtensa/mm/highmem.c
index 17a8c0d6fd17..8cfb71ec0937 100644
--- a/arch/xtensa/mm/highmem.c
+++ b/arch/xtensa/mm/highmem.c
@@ -14,23 +14,45 @@
static pte_t *kmap_pte;
+#if DCACHE_WAY_SIZE > PAGE_SIZE
+unsigned int last_pkmap_nr_arr[DCACHE_N_COLORS];
+wait_queue_head_t pkmap_map_wait_arr[DCACHE_N_COLORS];
+
+static void __init kmap_waitqueues_init(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(pkmap_map_wait_arr); ++i)
+ init_waitqueue_head(pkmap_map_wait_arr + i);
+}
+#else
+static inline void kmap_waitqueues_init(void)
+{
+}
+#endif
+
+static inline enum fixed_addresses kmap_idx(int type, unsigned long color)
+{
+ return (type + KM_TYPE_NR * smp_processor_id()) * DCACHE_N_COLORS +
+ color;
+}
+
void *kmap_atomic(struct page *page)
{
enum fixed_addresses idx;
unsigned long vaddr;
- int type;
pagefault_disable();
if (!PageHighMem(page))
return page_address(page);
- type = kmap_atomic_idx_push();
- idx = type + KM_TYPE_NR * smp_processor_id();
+ idx = kmap_idx(kmap_atomic_idx_push(),
+ DCACHE_ALIAS(page_to_phys(page)));
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
#ifdef CONFIG_DEBUG_HIGHMEM
- BUG_ON(!pte_none(*(kmap_pte - idx)));
+ BUG_ON(!pte_none(*(kmap_pte + idx)));
#endif
- set_pte(kmap_pte - idx, mk_pte(page, PAGE_KERNEL_EXEC));
+ set_pte(kmap_pte + idx, mk_pte(page, PAGE_KERNEL_EXEC));
return (void *)vaddr;
}
@@ -38,12 +60,10 @@ EXPORT_SYMBOL(kmap_atomic);
void __kunmap_atomic(void *kvaddr)
{
- int idx, type;
-
if (kvaddr >= (void *)FIXADDR_START &&
kvaddr < (void *)FIXADDR_TOP) {
- type = kmap_atomic_idx();
- idx = type + KM_TYPE_NR * smp_processor_id();
+ int idx = kmap_idx(kmap_atomic_idx(),
+ DCACHE_ALIAS((unsigned long)kvaddr));
/*
* Force other mappings to Oops if they'll try to access this
@@ -51,7 +71,7 @@ void __kunmap_atomic(void *kvaddr)
* is a bad idea also, in case the page changes cacheability
* attributes or becomes a protected page in a hypervisor.
*/
- pte_clear(&init_mm, kvaddr, kmap_pte - idx);
+ pte_clear(&init_mm, kvaddr, kmap_pte + idx);
local_flush_tlb_kernel_range((unsigned long)kvaddr,
(unsigned long)kvaddr + PAGE_SIZE);
@@ -69,4 +89,5 @@ void __init kmap_init(void)
/* cache the first kmap pte */
kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
kmap_pte = kmap_get_fixmap_pte(kmap_vstart);
+ kmap_waitqueues_init();
}
diff --git a/arch/xtensa/mm/misc.S b/arch/xtensa/mm/misc.S
index 1f68558dbcc2..11a01c3e9cea 100644
--- a/arch/xtensa/mm/misc.S
+++ b/arch/xtensa/mm/misc.S
@@ -110,41 +110,24 @@ ENTRY(__tlbtemp_mapping_start)
#if (DCACHE_WAY_SIZE > PAGE_SIZE)
/*
- * clear_user_page (void *addr, unsigned long vaddr, struct page *page)
- * a2 a3 a4
+ * clear_page_alias(void *addr, unsigned long paddr)
+ * a2 a3
*/
-ENTRY(clear_user_page)
+ENTRY(clear_page_alias)
entry a1, 32
- /* Mark page dirty and determine alias. */
+ /* Skip setting up a temporary DTLB if not aliased low page. */
- movi a7, (1 << PG_ARCH_1)
- l32i a5, a4, PAGE_FLAGS
- xor a6, a2, a3
- extui a3, a3, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- extui a6, a6, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- or a5, a5, a7
- slli a3, a3, PAGE_SHIFT
- s32i a5, a4, PAGE_FLAGS
+ movi a5, PAGE_OFFSET
+ movi a6, 0
+ beqz a3, 1f
- /* Skip setting up a temporary DTLB if not aliased. */
-
- beqz a6, 1f
-
- /* Invalidate kernel page. */
-
- mov a10, a2
- call8 __invalidate_dcache_page
-
- /* Setup a temporary DTLB with the color of the VPN */
-
- movi a4, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff
- movi a5, TLBTEMP_BASE_1 # virt
- add a6, a2, a4 # ppn
- add a2, a5, a3 # add 'color'
+ /* Setup a temporary DTLB for the addr. */
+ addi a6, a3, (PAGE_KERNEL | _PAGE_HW_WRITE)
+ mov a4, a2
wdtlb a6, a2
dsync
@@ -165,62 +148,43 @@ ENTRY(clear_user_page)
/* We need to invalidate the temporary idtlb entry, if any. */
-1: addi a2, a2, -PAGE_SIZE
- idtlb a2
+1: idtlb a4
dsync
retw
-ENDPROC(clear_user_page)
+ENDPROC(clear_page_alias)
/*
- * copy_page_user (void *to, void *from, unsigned long vaddr, struct page *page)
- * a2 a3 a4 a5
+ * copy_page_alias(void *to, void *from,
+ * a2 a3
+ * unsigned long to_paddr, unsigned long from_paddr)
+ * a4 a5
*/
-ENTRY(copy_user_page)
+ENTRY(copy_page_alias)
entry a1, 32
- /* Mark page dirty and determine alias for destination. */
-
- movi a8, (1 << PG_ARCH_1)
- l32i a9, a5, PAGE_FLAGS
- xor a6, a2, a4
- xor a7, a3, a4
- extui a4, a4, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- extui a6, a6, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- extui a7, a7, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- or a9, a9, a8
- slli a4, a4, PAGE_SHIFT
- s32i a9, a5, PAGE_FLAGS
- movi a5, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff
-
- beqz a6, 1f
-
- /* Invalidate dcache */
-
- mov a10, a2
- call8 __invalidate_dcache_page
+ /* Skip setting up a temporary DTLB for destination if not aliased. */
- /* Setup a temporary DTLB with a matching color. */
+ movi a6, 0
+ movi a7, 0
+ beqz a4, 1f
- movi a8, TLBTEMP_BASE_1 # base
- add a6, a2, a5 # ppn
- add a2, a8, a4 # add 'color'
+ /* Setup a temporary DTLB for destination. */
+ addi a6, a4, (PAGE_KERNEL | _PAGE_HW_WRITE)
wdtlb a6, a2
dsync
- /* Skip setting up a temporary DTLB for destination if not aliased. */
+ /* Skip setting up a temporary DTLB for source if not aliased. */
-1: beqz a7, 1f
+1: beqz a5, 1f
- /* Setup a temporary DTLB with a matching color. */
+ /* Setup a temporary DTLB for source. */
- movi a8, TLBTEMP_BASE_2 # base
- add a7, a3, a5 # ppn
- add a3, a8, a4
+ addi a7, a5, PAGE_KERNEL
addi a8, a3, 1 # way1
wdtlb a7, a8
@@ -271,7 +235,7 @@ ENTRY(copy_user_page)
retw
-ENDPROC(copy_user_page)
+ENDPROC(copy_page_alias)
#endif
@@ -300,6 +264,30 @@ ENTRY(__flush_invalidate_dcache_page_alias)
retw
ENDPROC(__flush_invalidate_dcache_page_alias)
+
+/*
+ * void __invalidate_dcache_page_alias (addr, phys)
+ * a2 a3
+ */
+
+ENTRY(__invalidate_dcache_page_alias)
+
+ entry sp, 16
+
+ movi a7, 0 # required for exception handler
+ addi a6, a3, (PAGE_KERNEL | _PAGE_HW_WRITE)
+ mov a4, a2
+ wdtlb a6, a2
+ dsync
+
+ ___invalidate_dcache_page a2 a3
+
+ idtlb a4
+ dsync
+
+ retw
+
+ENDPROC(__invalidate_dcache_page_alias)
#endif
ENTRY(__tlbtemp_mapping_itlb)
diff --git a/arch/xtensa/mm/mmu.c b/arch/xtensa/mm/mmu.c
index 3429b483d9f8..abe4513eb0dd 100644
--- a/arch/xtensa/mm/mmu.c
+++ b/arch/xtensa/mm/mmu.c
@@ -18,32 +18,38 @@
#include <asm/io.h>
#if defined(CONFIG_HIGHMEM)
-static void * __init init_pmd(unsigned long vaddr)
+static void * __init init_pmd(unsigned long vaddr, unsigned long n_pages)
{
pgd_t *pgd = pgd_offset_k(vaddr);
pmd_t *pmd = pmd_offset(pgd, vaddr);
+ pte_t *pte;
+ unsigned long i;
- if (pmd_none(*pmd)) {
- unsigned i;
- pte_t *pte = alloc_bootmem_low_pages(PAGE_SIZE);
+ n_pages = ALIGN(n_pages, PTRS_PER_PTE);
- for (i = 0; i < 1024; i++)
- pte_clear(NULL, 0, pte + i);
+ pr_debug("%s: vaddr: 0x%08lx, n_pages: %ld\n",
+ __func__, vaddr, n_pages);
- set_pmd(pmd, __pmd(((unsigned long)pte) & PAGE_MASK));
- BUG_ON(pte != pte_offset_kernel(pmd, 0));
- pr_debug("%s: vaddr: 0x%08lx, pmd: 0x%p, pte: 0x%p\n",
- __func__, vaddr, pmd, pte);
- return pte;
- } else {
- return pte_offset_kernel(pmd, 0);
+ pte = alloc_bootmem_low_pages(n_pages * sizeof(pte_t));
+
+ for (i = 0; i < n_pages; ++i)
+ pte_clear(NULL, 0, pte + i);
+
+ for (i = 0; i < n_pages; i += PTRS_PER_PTE, ++pmd) {
+ pte_t *cur_pte = pte + i;
+
+ BUG_ON(!pmd_none(*pmd));
+ set_pmd(pmd, __pmd(((unsigned long)cur_pte) & PAGE_MASK));
+ BUG_ON(cur_pte != pte_offset_kernel(pmd, 0));
+ pr_debug("%s: pmd: 0x%p, pte: 0x%p\n",
+ __func__, pmd, cur_pte);
}
+ return pte;
}
static void __init fixedrange_init(void)
{
- BUILD_BUG_ON(FIXADDR_SIZE > PMD_SIZE);
- init_pmd(__fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK);
+ init_pmd(__fix_to_virt(0), __end_of_fixed_addresses);
}
#endif
@@ -52,7 +58,7 @@ void __init paging_init(void)
memset(swapper_pg_dir, 0, PAGE_SIZE);
#ifdef CONFIG_HIGHMEM
fixedrange_init();
- pkmap_page_table = init_pmd(PKMAP_BASE);
+ pkmap_page_table = init_pmd(PKMAP_BASE, LAST_PKMAP);
kmap_init();
#endif
}
diff --git a/block/genhd.c b/block/genhd.c
index 791f41943132..3bceeb40aec2 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1107,6 +1107,22 @@ static void disk_release(struct device *dev)
blk_put_queue(disk->queue);
kfree(disk);
}
+
+static int disk_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct gendisk *disk = dev_to_disk(dev);
+ struct disk_part_iter piter;
+ struct hd_struct *part;
+ int cnt = 0;
+
+ disk_part_iter_init(&piter, disk, 0);
+ while((part = disk_part_iter_next(&piter)))
+ cnt++;
+ disk_part_iter_exit(&piter);
+ add_uevent_var(env, "NPARTS=%u", cnt);
+ return 0;
+}
+
struct class block_class = {
.name = "block",
};
@@ -1126,6 +1142,7 @@ static struct device_type disk_type = {
.groups = disk_attr_groups,
.release = disk_release,
.devnode = block_devnode,
+ .uevent = disk_uevent,
};
#ifdef CONFIG_PROC_FS
diff --git a/block/partition-generic.c b/block/partition-generic.c
index 789cdea05893..c7942acf1379 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -216,10 +216,21 @@ static void part_release(struct device *dev)
kfree(p);
}
+static int part_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct hd_struct *part = dev_to_part(dev);
+
+ add_uevent_var(env, "PARTN=%u", part->partno);
+ if (part->info && part->info->volname[0])
+ add_uevent_var(env, "PARTNAME=%s", part->info->volname);
+ return 0;
+}
+
struct device_type part_type = {
.name = "partition",
.groups = part_attr_groups,
.release = part_release,
+ .uevent = part_uevent,
};
static void delete_partition_rcu_cb(struct rcu_head *head)
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 622fa266b29e..1817a76bad67 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -100,6 +100,8 @@ source "drivers/memstick/Kconfig"
source "drivers/leds/Kconfig"
+source "drivers/switch/Kconfig"
+
source "drivers/accessibility/Kconfig"
source "drivers/infiniband/Kconfig"
@@ -180,4 +182,6 @@ source "drivers/ras/Kconfig"
source "drivers/thunderbolt/Kconfig"
+source "drivers/gator/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index ebee55537a05..48a5395ba400 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -117,6 +117,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/
obj-y += mmc/
obj-$(CONFIG_MEMSTICK) += memstick/
obj-y += leds/
+obj-$(CONFIG_SWITCH) += switch/
obj-$(CONFIG_INFINIBAND) += infiniband/
obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
@@ -161,3 +162,5 @@ obj-$(CONFIG_POWERCAP) += powercap/
obj-$(CONFIG_MCB) += mcb/
obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
+
+obj-$(CONFIG_GATOR) += gator/
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index e1b92788c225..165d2faee9b3 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -133,6 +133,16 @@ config AHCI_MVEBU
If unsure, say N.
+config AHCI_QCOM
+ tristate "Qualcomm AHCI SATA support"
+ depends on ARCH_QCOM
+ help
+ This option enables support for AHCI SATA controller
+ integrated into Qualcomm ARM SoC chipsets. For more
+ information please refer to http://www.qualcomm.com/chipsets.
+
+ If unsure, say N.
+
config AHCI_SUNXI
tristate "Allwinner sunxi AHCI SATA support"
depends on ARCH_SUNXI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index ae41107afc1f..812435c59aec 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
obj-$(CONFIG_AHCI_DA850) += ahci_da850.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o libahci.o libahci_platform.o
+obj-$(CONFIG_AHCI_QCOM) += ahci_qcom.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_ST) += ahci_st.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_TEGRA) += ahci_tegra.o libahci.o libahci_platform.o
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 59ae0ee00149..36ae69a8e4e3 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -53,7 +53,7 @@
enum {
AHCI_MAX_PORTS = 32,
- AHCI_MAX_CLKS = 4,
+ AHCI_MAX_CLKS = 8,
AHCI_MAX_SG = 168, /* hardware max is 64K */
AHCI_DMA_BOUNDARY = 0xffffffff,
AHCI_MAX_CMDS = 32,
diff --git a/drivers/ata/ahci_qcom.c b/drivers/ata/ahci_qcom.c
new file mode 100644
index 000000000000..9cf144ab7a25
--- /dev/null
+++ b/drivers/ata/ahci_qcom.c
@@ -0,0 +1,89 @@
+/*
+ * Qualcomm ARM SoC AHCI SATA platform driver
+ *
+ * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
+ *
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/libata.h>
+#include <linux/ahci_platform.h>
+#include "ahci.h"
+
+static const struct ata_port_info qcom_ahci_port_info = {
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_platform_ops,
+};
+
+static int qcom_ahci_probe(struct platform_device *pdev)
+{
+ struct ahci_host_priv *hpriv;
+ struct clk *rxoob_clk;
+ int rc;
+
+ hpriv = ahci_platform_get_resources(pdev);
+ if (IS_ERR(hpriv))
+ return PTR_ERR(hpriv);
+
+ /* Try and set the rxoob clk to 100Mhz */
+ rxoob_clk = devm_clk_get(&pdev->dev, "rxoob");
+ if (IS_ERR(rxoob_clk))
+ return PTR_ERR(rxoob_clk);
+
+ rc = clk_set_rate(rxoob_clk, 100000000);
+ if (rc)
+ return rc;
+
+ devm_clk_put(&pdev->dev, rxoob_clk);
+
+ rc = ahci_platform_enable_resources(hpriv);
+ if (rc)
+ return rc;
+
+ rc = ahci_platform_init_host(pdev, hpriv, &qcom_ahci_port_info);
+ if (rc)
+ goto disable_resources;
+
+ return 0;
+disable_resources:
+ ahci_platform_disable_resources(hpriv);
+ return rc;
+}
+
+static const struct of_device_id qcom_ahci_of_match[] = {
+ { .compatible = "qcom,msm-ahci", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, qcom_ahci_of_match);
+
+static struct platform_driver qcom_ahci_driver = {
+ .probe = qcom_ahci_probe,
+ .remove = ata_platform_remove_one,
+ .driver = {
+ .name = "qcom_ahci_qcom",
+ .owner = THIS_MODULE,
+ .of_match_table = qcom_ahci_of_match,
+ },
+ /* suspend/resume not currently supported */
+};
+module_platform_driver(qcom_ahci_driver);
+
+MODULE_DESCRIPTION("Qualcomm AHCI SATA platform driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("ahci:qcom");
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index b67d9aef9fe4..8668aaf1b109 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -58,6 +58,12 @@ struct suspend_stats suspend_stats;
static DEFINE_MUTEX(dpm_list_mtx);
static pm_message_t pm_transition;
+static void dpm_drv_timeout(unsigned long data);
+struct dpm_drv_wd_data {
+ struct device *dev;
+ struct task_struct *tsk;
+};
+
static int async_error;
static char *pm_verb(int event)
@@ -828,6 +834,30 @@ static void async_resume(void *data, async_cookie_t cookie)
}
/**
+ * dpm_drv_timeout - Driver suspend / resume watchdog handler
+ * @data: struct device which timed out
+ *
+ * Called when a driver has timed out suspending or resuming.
+ * There's not much we can do here to recover so
+ * BUG() out for a crash-dump
+ *
+ */
+static void dpm_drv_timeout(unsigned long data)
+{
+ struct dpm_drv_wd_data *wd_data = (void *)data;
+ struct device *dev = wd_data->dev;
+ struct task_struct *tsk = wd_data->tsk;
+
+ printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev),
+ (dev->driver ? dev->driver->name : "no driver"));
+
+ printk(KERN_EMERG "dpm suspend stack:\n");
+ show_stack(tsk, NULL);
+
+ BUG();
+}
+
+/**
* dpm_resume - Execute "resume" callbacks for non-sysdev devices.
* @state: PM transition of the system being carried out.
*
@@ -1334,6 +1364,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
pm_callback_t callback = NULL;
char *info = NULL;
int error = 0;
+ struct timer_list timer;
+ struct dpm_drv_wd_data data;
DECLARE_DPM_WATCHDOG_ON_STACK(wd);
dpm_wait_for_children(dev, async);
@@ -1357,6 +1389,14 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
if (dev->power.syscore)
goto Complete;
+
+ data.dev = dev;
+ data.tsk = get_current();
+ init_timer_on_stack(&timer);
+ timer.expires = jiffies + HZ * 12;
+ timer.function = dpm_drv_timeout;
+ timer.data = (unsigned long)&data;
+ add_timer(&timer);
if (dev->power.direct_complete) {
if (pm_runtime_status_suspended(dev)) {
@@ -1437,6 +1477,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
device_unlock(dev);
dpm_watchdog_clear(&wd);
+ del_timer_sync(&timer);
+ destroy_timer_on_stack(&timer);
+
Complete:
complete_all(&dev->power.completion);
if (error)
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
index 7af78df241f2..12d0963086e4 100644
--- a/drivers/bus/arm-cci.c
+++ b/drivers/bus/arm-cci.c
@@ -547,6 +547,7 @@ static int cci_pmu_init(struct arm_pmu *cci_pmu, struct platform_device *pdev)
cci_pmu->plat_device = pdev;
cci_pmu->num_events = pmu_get_max_counters();
+ cpumask_setall(&cci_pmu->valid_cpus);
return armpmu_register(cci_pmu, -1);
}
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 6e9f74a5c095..26fb1720b2dd 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -6,6 +6,19 @@ menu "Character devices"
source "drivers/tty/Kconfig"
+config DEVMEM
+ bool "Memory device driver"
+ default y
+ help
+ The memory driver provides two character devices, mem and kmem, which
+ provide access to the system's memory. The mem device is a view of
+ physical memory, and each byte in the device corresponds to the
+ matching physical address. The kmem device is the same as mem, but
+ the addresses correspond to the kernel's virtual address space rather
+ than physical memory. These devices are standard parts of a Linux
+ system and most users should say Y here. You might say N if very
+ security conscience or memory is tight.
+
config DEVKMEM
bool "/dev/kmem virtual device support"
default y
@@ -579,6 +592,10 @@ config DEVPORT
depends on ISA || PCI
default y
+config DCC_TTY
+ tristate "DCC tty driver"
+ depends on ARM
+
source "drivers/s390/char/Kconfig"
config MSM_SMD_PKT
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index a324f9303e36..9e4c495d71e5 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
obj-$(CONFIG_TCG_TPM) += tpm/
+obj-$(CONFIG_DCC_TTY) += dcc_tty.o
obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c
new file mode 100644
index 000000000000..0a62d410286f
--- /dev/null
+++ b/drivers/char/dcc_tty.c
@@ -0,0 +1,326 @@
+/* drivers/char/dcc_tty.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/hrtimer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+MODULE_DESCRIPTION("DCC TTY Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+DEFINE_SPINLOCK(g_dcc_tty_lock);
+static struct hrtimer g_dcc_timer;
+static char g_dcc_buffer[16];
+static int g_dcc_buffer_head;
+static int g_dcc_buffer_count;
+static unsigned g_dcc_write_delay_usecs = 1;
+static struct tty_driver *g_dcc_tty_driver;
+static struct tty_struct *g_dcc_tty;
+static int g_dcc_tty_open_count;
+
+static void dcc_poll_locked(void)
+{
+ char ch;
+ int rch;
+ int written;
+
+ while (g_dcc_buffer_count) {
+ ch = g_dcc_buffer[g_dcc_buffer_head];
+ asm(
+ "mrc 14, 0, r15, c0, c1, 0\n"
+ "mcrcc 14, 0, %1, c0, c5, 0\n"
+ "movcc %0, #1\n"
+ "movcs %0, #0\n"
+ : "=r" (written)
+ : "r" (ch)
+ );
+ if (written) {
+ if (ch == '\n')
+ g_dcc_buffer[g_dcc_buffer_head] = '\r';
+ else {
+ g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer);
+ g_dcc_buffer_count--;
+ if (g_dcc_tty)
+ tty_wakeup(g_dcc_tty);
+ }
+ g_dcc_write_delay_usecs = 1;
+ } else {
+ if (g_dcc_write_delay_usecs > 0x100)
+ break;
+ g_dcc_write_delay_usecs <<= 1;
+ udelay(g_dcc_write_delay_usecs);
+ }
+ }
+
+ if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) {
+ asm(
+ "mrc 14, 0, %0, c0, c1, 0\n"
+ "tst %0, #(1 << 30)\n"
+ "moveq %0, #-1\n"
+ "mrcne 14, 0, %0, c0, c5, 0\n"
+ : "=r" (rch)
+ );
+ if (rch >= 0) {
+ ch = rch;
+ tty_insert_flip_string(g_dcc_tty->port, &ch, 1);
+ tty_flip_buffer_push(g_dcc_tty->port);
+ }
+ }
+
+
+ if (g_dcc_buffer_count)
+ hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL);
+ else
+ hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+}
+
+static int dcc_tty_open(struct tty_struct * tty, struct file * filp)
+{
+ int ret;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ if (g_dcc_tty == NULL || g_dcc_tty == tty) {
+ g_dcc_tty = tty;
+ g_dcc_tty_open_count++;
+ ret = 0;
+ } else
+ ret = -EBUSY;
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+
+ printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret);
+
+ return ret;
+}
+
+static void dcc_tty_close(struct tty_struct * tty, struct file * filp)
+{
+ printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags);
+ if (g_dcc_tty == tty) {
+ if (--g_dcc_tty_open_count == 0)
+ g_dcc_tty = NULL;
+ }
+}
+
+static int dcc_write(const unsigned char *buf_start, int count)
+{
+ const unsigned char *buf = buf_start;
+ unsigned long irq_flags;
+ int copy_len;
+ int space_left;
+ int tail;
+
+ if (count < 1)
+ return 0;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ do {
+ tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer);
+ copy_len = ARRAY_SIZE(g_dcc_buffer) - tail;
+ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
+ if (copy_len > space_left)
+ copy_len = space_left;
+ if (copy_len > count)
+ copy_len = count;
+ memcpy(&g_dcc_buffer[tail], buf, copy_len);
+ g_dcc_buffer_count += copy_len;
+ buf += copy_len;
+ count -= copy_len;
+ if (copy_len < count && copy_len < space_left) {
+ space_left -= copy_len;
+ copy_len = count;
+ if (copy_len > space_left) {
+ copy_len = space_left;
+ }
+ memcpy(g_dcc_buffer, buf, copy_len);
+ buf += copy_len;
+ count -= copy_len;
+ g_dcc_buffer_count += copy_len;
+ }
+ dcc_poll_locked();
+ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
+ } while(count && space_left);
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+ return buf - buf_start;
+}
+
+static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
+{
+ int ret;
+ /* printk("dcc_tty_write %p, %d\n", buf, count); */
+ ret = dcc_write(buf, count);
+ if (ret != count)
+ printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret);
+ return ret;
+}
+
+static int dcc_tty_write_room(struct tty_struct *tty)
+{
+ int space_left;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+ return space_left;
+}
+
+static int dcc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ int ret;
+ asm(
+ "mrc 14, 0, %0, c0, c1, 0\n"
+ "mov %0, %0, LSR #30\n"
+ "and %0, %0, #1\n"
+ : "=r" (ret)
+ );
+ return ret;
+}
+
+static void dcc_tty_unthrottle(struct tty_struct * tty)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ dcc_poll_locked();
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+}
+
+static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ dcc_poll_locked();
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+ return HRTIMER_NORESTART;
+}
+
+void dcc_console_write(struct console *co, const char *b, unsigned count)
+{
+#if 1
+ dcc_write(b, count);
+#else
+ /* blocking printk */
+ while (count > 0) {
+ int written;
+ written = dcc_write(b, count);
+ if (written) {
+ b += written;
+ count -= written;
+ }
+ }
+#endif
+}
+
+static struct tty_driver *dcc_console_device(struct console *c, int *index)
+{
+ *index = 0;
+ return g_dcc_tty_driver;
+}
+
+static int __init dcc_console_setup(struct console *co, char *options)
+{
+ if (co->index != 0)
+ return -ENODEV;
+ return 0;
+}
+
+
+static struct console dcc_console =
+{
+ .name = "ttyDCC",
+ .write = dcc_console_write,
+ .device = dcc_console_device,
+ .setup = dcc_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+static struct tty_operations dcc_tty_ops = {
+ .open = dcc_tty_open,
+ .close = dcc_tty_close,
+ .write = dcc_tty_write,
+ .write_room = dcc_tty_write_room,
+ .chars_in_buffer = dcc_tty_chars_in_buffer,
+ .unthrottle = dcc_tty_unthrottle,
+};
+
+static int __init dcc_tty_init(void)
+{
+ int ret;
+
+ hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ g_dcc_timer.function = dcc_tty_timer_func;
+
+ g_dcc_tty_driver = alloc_tty_driver(1);
+ if (!g_dcc_tty_driver) {
+ printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n");
+ ret = -ENOMEM;
+ goto err_alloc_tty_driver_failed;
+ }
+ g_dcc_tty_driver->owner = THIS_MODULE;
+ g_dcc_tty_driver->driver_name = "dcc";
+ g_dcc_tty_driver->name = "ttyDCC";
+ g_dcc_tty_driver->major = 0; // auto assign
+ g_dcc_tty_driver->minor_start = 0;
+ g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ g_dcc_tty_driver->init_termios = tty_std_termios;
+ g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops);
+ ret = tty_register_driver(g_dcc_tty_driver);
+ if (ret) {
+ printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret);
+ goto err_tty_register_driver_failed;
+ }
+ tty_register_device(g_dcc_tty_driver, 0, NULL);
+
+ register_console(&dcc_console);
+ hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+
+ return 0;
+
+err_tty_register_driver_failed:
+ put_tty_driver(g_dcc_tty_driver);
+ g_dcc_tty_driver = NULL;
+err_alloc_tty_driver_failed:
+ return ret;
+}
+
+static void __exit dcc_tty_exit(void)
+{
+ int ret;
+
+ tty_unregister_device(g_dcc_tty_driver, 0);
+ ret = tty_unregister_driver(g_dcc_tty_driver);
+ if (ret < 0) {
+ printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret);
+ } else {
+ put_tty_driver(g_dcc_tty_driver);
+ }
+ g_dcc_tty_driver = NULL;
+}
+
+module_init(dcc_tty_init);
+module_exit(dcc_tty_exit);
+
+
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index f66ea258382f..4c8290ad377c 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -131,13 +131,13 @@ struct omap_rng_dev {
static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg)
{
- return __raw_readl(priv->base + priv->pdata->regs[reg]);
+ return readl_relaxed(priv->base + priv->pdata->regs[reg]);
}
static inline void omap_rng_write(struct omap_rng_dev *priv, u16 reg,
u32 val)
{
- __raw_writel(val, priv->base + priv->pdata->regs[reg]);
+ writel_relaxed(val, priv->base + priv->pdata->regs[reg]);
}
static int omap_rng_data_present(struct hwrng *rng, int wait)
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 917403fe10da..83b22ad0e68b 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -58,6 +58,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
}
#endif
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
#ifdef CONFIG_STRICT_DEVMEM
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
@@ -83,7 +84,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
return 1;
}
#endif
+#endif
+#ifdef CONFIG_DEVMEM
void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr)
{
}
@@ -216,6 +219,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf,
*ppos += written;
return written;
}
+#endif /* CONFIG_DEVMEM */
+
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
int __weak phys_mem_access_prot_allowed(struct file *file,
unsigned long pfn, unsigned long size, pgprot_t *vma_prot)
@@ -337,6 +343,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
}
return 0;
}
+#endif /* CONFIG_DEVMEM */
#ifdef CONFIG_DEVKMEM
static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
@@ -697,6 +704,8 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig)
return file->f_pos = 0;
}
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
+
/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
@@ -730,10 +739,14 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
return ret;
}
+#endif
+
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
static int open_port(struct inode *inode, struct file *filp)
{
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
}
+#endif
#define zero_lseek null_lseek
#define full_lseek null_lseek
@@ -743,6 +756,7 @@ static int open_port(struct inode *inode, struct file *filp)
#define open_mem open_port
#define open_kmem open_mem
+#ifdef CONFIG_DEVMEM
static const struct file_operations mem_fops = {
.llseek = memory_lseek,
.read = read_mem,
@@ -751,6 +765,7 @@ static const struct file_operations mem_fops = {
.open = open_mem,
.get_unmapped_area = get_unmapped_area_mem,
};
+#endif
#ifdef CONFIG_DEVKMEM
static const struct file_operations kmem_fops = {
@@ -812,7 +827,9 @@ static const struct memdev {
const struct file_operations *fops;
struct backing_dev_info *dev_info;
} devlist[] = {
+#ifdef CONFIG_DEVMEM
[1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
+#endif
#ifdef CONFIG_DEVKMEM
[2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
#endif
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index cfd3af7b2cbd..900c33735001 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -32,6 +32,16 @@ config COMMON_CLK_WM831X
source "drivers/clk/versatile/Kconfig"
+config COMMON_CLK_SCPI
+ bool "Clock driver controlled via SCPI interface"
+ depends on ARM_SCPI_PROTOCOL
+ ---help---
+ This driver provides support for clocks that are controlled
+ by firmware that implements the SCPI interface.
+
+ This driver uses SCPI Message Protocol to interact with the
+ firware providing all the clock controls.
+
config COMMON_CLK_MAX77686
tristate "Clock driver for Maxim 77686 MFD"
depends on MFD_MAX77686
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f537a0b1f798..2df0d1717f97 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o
obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
+obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c
index 502a487d62c5..b937fc9886b6 100644
--- a/drivers/clk/bcm/clk-bcm281xx.c
+++ b/drivers/clk/bcm/clk-bcm281xx.c
@@ -309,8 +309,17 @@ static struct peri_clk_data pwm_data = {
.trig = TRIGGER(0x0afc, 15),
};
+static struct bus_clk_data bsc3_apb_data = {
+ .policy = POLICY(0x0048, 4),
+ .gate = HW_SW_GATE(0x0484, 16, 0, 1),
+};
+
static struct ccu_data slave_ccu_data = {
BCM281XX_CCU_COMMON(slave, SLAVE),
+ .policy = {
+ .enable = CCU_LVM_EN(0x0034, 0),
+ .control = CCU_POLICY_CTL(0x000c, 0, 1, 2),
+ },
.kona_clks = {
[BCM281XX_SLAVE_CCU_UARTB] =
KONA_CLK(slave, uartb, peri),
@@ -329,9 +338,11 @@ static struct ccu_data slave_ccu_data = {
[BCM281XX_SLAVE_CCU_BSC2] =
KONA_CLK(slave, bsc2, peri),
[BCM281XX_SLAVE_CCU_BSC3] =
- KONA_CLK(slave, bsc3, peri),
+ KONA_CLK_PREREQ(slave, bsc3, peri, bsc3_apb),
[BCM281XX_SLAVE_CCU_PWM] =
KONA_CLK(slave, pwm, peri),
+ [BCM281XX_SLAVE_CCU_BSC3_APB] =
+ KONA_CLK(slave, bsc3_apb, bus),
[BCM281XX_SLAVE_CCU_CLOCK_COUNT] = LAST_KONA_CLK,
},
};
diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c
index e5aededdd322..1dae5915cc7c 100644
--- a/drivers/clk/bcm/clk-kona-setup.c
+++ b/drivers/clk/bcm/clk-kona-setup.c
@@ -76,6 +76,56 @@ static bool clk_requires_trigger(struct kona_clk *bcm_clk)
return divider_exists(div) && !divider_is_fixed(div);
}
+static bool bus_clk_data_offsets_valid(struct kona_clk *bcm_clk)
+{
+ struct bus_clk_data *bus;
+ struct bcm_clk_policy *policy;
+ struct bcm_clk_gate *gate;
+ struct bcm_clk_hyst *hyst;
+ const char *name;
+ u32 limit;
+
+ BUG_ON(bcm_clk->type != bcm_clk_bus);
+ bus = bcm_clk->u.bus;
+ name = bcm_clk->init_data.name;
+
+ limit = bcm_clk->ccu->range - sizeof(u32);
+ limit = round_down(limit, sizeof(u32));
+
+ policy = &bus->policy;
+ if (policy_exists(policy)) {
+ if (policy->offset > limit) {
+ pr_err("%s: bad policy offset for %s (%u > %u)\n",
+ __func__, name, policy->offset, limit);
+ return false;
+ }
+ }
+
+ gate = &bus->gate;
+ hyst = &bus->hyst;
+ if (gate_exists(gate)) {
+ if (gate->offset > limit) {
+ pr_err("%s: bad gate offset for %s (%u > %u)\n",
+ __func__, name, gate->offset, limit);
+ return false;
+ }
+ if (hyst_exists(hyst)) {
+ if (hyst->offset > limit) {
+ pr_err("%s: bad hysteresis offset for %s "
+ "(%u > %u)\n", __func__,
+ name, hyst->offset, limit);
+ return false;
+ }
+ }
+ } else if (hyst_exists(hyst)) {
+ pr_err("%s: hysteresis but no gate for %s\n", __func__, name);
+ return false;
+ }
+
+
+ return true;
+}
+
static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
{
struct peri_clk_data *peri;
@@ -86,15 +136,13 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
struct bcm_clk_sel *sel;
struct bcm_clk_trig *trig;
const char *name;
- u32 range;
u32 limit;
BUG_ON(bcm_clk->type != bcm_clk_peri);
peri = bcm_clk->u.peri;
name = bcm_clk->init_data.name;
- range = bcm_clk->ccu->range;
- limit = range - sizeof(u32);
+ limit = bcm_clk->ccu->range - sizeof(u32);
limit = round_down(limit, sizeof(u32));
policy = &peri->policy;
@@ -397,6 +445,23 @@ static bool trig_valid(struct bcm_clk_trig *trig, const char *field_name,
return bit_posn_valid(trig->bit, field_name, clock_name);
}
+/* Determine whether the set of bus clock registers are valid. */
+static bool
+bus_clk_data_valid(struct kona_clk *bcm_clk)
+{
+ struct bcm_clk_gate *gate;
+
+ BUG_ON(bcm_clk->type != bcm_clk_bus);
+ if (!bus_clk_data_offsets_valid(bcm_clk))
+ return false;
+
+ gate = &bcm_clk->u.bus->gate;
+ if (!gate_exists(gate))
+ return true;
+
+ return gate_valid(gate, "gate", bcm_clk->init_data.name);
+}
+
/* Determine whether the set of peripheral clock registers are valid. */
static bool
peri_clk_data_valid(struct kona_clk *bcm_clk)
@@ -494,6 +559,10 @@ peri_clk_data_valid(struct kona_clk *bcm_clk)
static bool kona_clk_valid(struct kona_clk *bcm_clk)
{
switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ if (!bus_clk_data_valid(bcm_clk))
+ return false;
+ break;
case bcm_clk_peri:
if (!peri_clk_data_valid(bcm_clk))
return false;
@@ -664,6 +733,20 @@ static void clk_sel_teardown(struct bcm_clk_sel *sel,
init_data->parent_names = NULL;
}
+static void bus_clk_teardown(struct bus_clk_data *data,
+ struct clk_init_data *init_data)
+{
+ /* Nothing to do */
+}
+
+static int
+bus_clk_setup(struct bus_clk_data *data, struct clk_init_data *init_data)
+{
+ init_data->flags = CLK_IGNORE_UNUSED;
+
+ return 0;
+}
+
static void peri_clk_teardown(struct peri_clk_data *data,
struct clk_init_data *init_data)
{
@@ -687,6 +770,9 @@ peri_clk_setup(struct peri_clk_data *data, struct clk_init_data *init_data)
static void bcm_clk_teardown(struct kona_clk *bcm_clk)
{
switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ bus_clk_teardown(bcm_clk->u.data, &bcm_clk->init_data);
+ break;
case bcm_clk_peri:
peri_clk_teardown(bcm_clk->u.data, &bcm_clk->init_data);
break;
@@ -722,6 +808,10 @@ struct clk *kona_clk_setup(struct kona_clk *bcm_clk)
struct clk *clk = NULL;
switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ if (bus_clk_setup(bcm_clk->u.data, init_data))
+ return NULL;
+ break;
case bcm_clk_peri:
if (peri_clk_setup(bcm_clk->u.data, init_data))
return NULL;
@@ -867,9 +957,6 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu,
goto out_err;
}
- if (!kona_ccu_init(ccu))
- pr_err("Broadcom %s initialization had errors\n", node->name);
-
return;
out_err:
kona_ccu_teardown(ccu);
diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c
index 95af2e665dd3..a938b15919b2 100644
--- a/drivers/clk/bcm/clk-kona.c
+++ b/drivers/clk/bcm/clk-kona.c
@@ -27,6 +27,9 @@
#define CCU_ACCESS_PASSWORD 0xA5A500
#define CLK_GATE_DELAY_LOOP 2000
+#define clk_is_initialized(_clk) FLAG_TEST((_clk), KONA, INITIALIZED)
+#define clk_set_initialized(_clk) FLAG_SET((_clk), KONA, INITIALIZED)
+
/* Bitfield operations */
/* Produces a mask of set bits covering a range of a 32-bit value */
@@ -981,8 +984,226 @@ static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
return ret;
}
+/* Put a bus clock into its initial state */
+static bool __bus_clk_init(struct kona_clk *bcm_clk)
+{
+ struct ccu_data *ccu = bcm_clk->ccu;
+ struct bus_clk_data *bus = bcm_clk->u.bus;
+ const char *name = bcm_clk->init_data.name;
+
+ BUG_ON(bcm_clk->type != bcm_clk_bus);
+
+ if (!policy_init(ccu, &bus->policy)) {
+ pr_err("%s: error initializing policy for %s\n",
+ __func__, name);
+ return false;
+ }
+ if (!gate_init(ccu, &bus->gate)) {
+ pr_err("%s: error initializing gate for %s\n", __func__, name);
+ return false;
+ }
+ if (!hyst_init(ccu, &bus->hyst)) {
+ pr_err("%s: error initializing hyst for %s\n", __func__, name);
+ return false;
+ }
+ return true;
+}
+
+/* Put a peripheral clock into its initial state */
+static bool __peri_clk_init(struct kona_clk *bcm_clk)
+{
+ struct ccu_data *ccu = bcm_clk->ccu;
+ struct peri_clk_data *peri = bcm_clk->u.peri;
+ const char *name = bcm_clk->init_data.name;
+ struct bcm_clk_trig *trig;
+
+ BUG_ON(bcm_clk->type != bcm_clk_peri);
+
+ if (!policy_init(ccu, &peri->policy)) {
+ pr_err("%s: error initializing policy for %s\n",
+ __func__, name);
+ return false;
+ }
+ if (!gate_init(ccu, &peri->gate)) {
+ pr_err("%s: error initializing gate for %s\n", __func__, name);
+ return false;
+ }
+ if (!hyst_init(ccu, &peri->hyst)) {
+ pr_err("%s: error initializing hyst for %s\n", __func__, name);
+ return false;
+ }
+ if (!div_init(ccu, &peri->gate, &peri->div, &peri->trig)) {
+ pr_err("%s: error initializing divider for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ /*
+ * For the pre-divider and selector, the pre-trigger is used
+ * if it's present, otherwise we just use the regular trigger.
+ */
+ trig = trigger_exists(&peri->pre_trig) ? &peri->pre_trig
+ : &peri->trig;
+
+ if (!div_init(ccu, &peri->gate, &peri->pre_div, trig)) {
+ pr_err("%s: error initializing pre-divider for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ if (!sel_init(ccu, &peri->gate, &peri->sel, trig)) {
+ pr_err("%s: error initializing selector for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ return true;
+}
+
/* Clock operations */
+static int kona_prereq_prepare_enable(struct kona_clk *bcm_clk)
+{
+ const char *clk_name = bcm_clk->init_data.name;
+ const char *prereq_name = bcm_clk->prereq.name;
+ struct clk *prereq_clk = bcm_clk->prereq.clk;
+ int ret;
+
+ BUG_ON(!clk_name);
+
+ /* Look up the prerequisite clock if we haven't already */
+ if (!prereq_clk) {
+ prereq_clk = __clk_lookup(prereq_name);
+ if (WARN_ON_ONCE(!prereq_clk))
+ return -ENOENT;
+ bcm_clk->prereq.clk = prereq_clk;
+ }
+
+ /* Dependent clock already holds the prepare lock */
+ ret = __clk_prepare(prereq_clk);
+ if (ret) {
+ pr_err("%s: unable to prepare prereq clock %s for %s\n",
+ __func__, prereq_name, clk_name);
+ return ret;
+ }
+
+ ret = clk_enable(prereq_clk);
+ if (ret) {
+ __clk_unprepare(prereq_clk);
+ pr_err("%s: unable to enable prereq clock %s for %s\n",
+ __func__, prereq_name, clk_name);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int kona_clk_prepare(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct ccu_data *ccu;
+ unsigned long flags;
+ int ret = 0;
+
+ /* Prepare the prerequisite clock first */
+ if (bcm_clk->prereq.name) {
+ ret = kona_prereq_prepare_enable(bcm_clk);
+ if (ret)
+ goto out;
+ }
+
+ if (clk_is_initialized(bcm_clk))
+ return 0;
+
+ ccu = bcm_clk->ccu;
+ flags = ccu_lock(ccu);
+ __ccu_write_enable(ccu);
+
+ switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ if (!__bus_clk_init(bcm_clk))
+ ret = -EINVAL;
+ break;
+ case bcm_clk_peri:
+ if (!__peri_clk_init(bcm_clk))
+ ret = -EINVAL;
+ break;
+ default:
+ BUG();
+ }
+
+ __ccu_write_disable(ccu);
+ ccu_unlock(ccu, flags);
+out:
+ if (!ret)
+ clk_set_initialized(bcm_clk);
+
+ return ret;
+}
+
+/*
+ * Disable and unprepare a prerequisite clock, and drop our
+ * reference to it.
+ */
+static void kona_prereq_disable_unprepare(struct kona_clk *bcm_clk)
+{
+ struct clk *prereq_clk = bcm_clk->prereq.clk;
+
+ BUG_ON(!bcm_clk->prereq.name);
+ WARN_ON_ONCE(!prereq_clk);
+
+ clk_disable(prereq_clk);
+ __clk_unprepare(prereq_clk);
+}
+
+static void kona_clk_unprepare(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+
+ WARN_ON(!clk_is_initialized(bcm_clk));
+
+ /*
+ * We don't do anything to unprepare Kona clocks themselves,
+ * but if there's a prerequisite we'll need to unprepare it.
+ */
+ if (!bcm_clk->prereq.name)
+ return;
+
+ kona_prereq_disable_unprepare(bcm_clk);
+}
+
+static int kona_bus_clk_enable(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->u.bus->gate;
+
+ return clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, true);
+}
+
+static void kona_bus_clk_disable(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->u.bus->gate;
+
+ (void)clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, false);
+}
+
+static int kona_bus_clk_is_enabled(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->u.bus->gate;
+
+ return is_clk_gate_enabled(bcm_clk->ccu, gate) ? 1 : 0;
+}
+
+struct clk_ops kona_bus_clk_ops = {
+ .prepare = kona_clk_prepare,
+ .unprepare = kona_clk_unprepare,
+ .enable = kona_bus_clk_enable,
+ .disable = kona_bus_clk_disable,
+ .is_enabled = kona_bus_clk_is_enabled,
+};
+
static int kona_peri_clk_enable(struct clk_hw *hw)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
@@ -1183,6 +1404,8 @@ static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate,
}
struct clk_ops kona_peri_clk_ops = {
+ .prepare = kona_clk_prepare,
+ .unprepare = kona_clk_unprepare,
.enable = kona_peri_clk_enable,
.disable = kona_peri_clk_disable,
.is_enabled = kona_peri_clk_is_enabled,
@@ -1192,90 +1415,3 @@ struct clk_ops kona_peri_clk_ops = {
.get_parent = kona_peri_clk_get_parent,
.set_rate = kona_peri_clk_set_rate,
};
-
-/* Put a peripheral clock into its initial state */
-static bool __peri_clk_init(struct kona_clk *bcm_clk)
-{
- struct ccu_data *ccu = bcm_clk->ccu;
- struct peri_clk_data *peri = bcm_clk->u.peri;
- const char *name = bcm_clk->init_data.name;
- struct bcm_clk_trig *trig;
-
- BUG_ON(bcm_clk->type != bcm_clk_peri);
-
- if (!policy_init(ccu, &peri->policy)) {
- pr_err("%s: error initializing policy for %s\n",
- __func__, name);
- return false;
- }
- if (!gate_init(ccu, &peri->gate)) {
- pr_err("%s: error initializing gate for %s\n", __func__, name);
- return false;
- }
- if (!hyst_init(ccu, &peri->hyst)) {
- pr_err("%s: error initializing hyst for %s\n", __func__, name);
- return false;
- }
- if (!div_init(ccu, &peri->gate, &peri->div, &peri->trig)) {
- pr_err("%s: error initializing divider for %s\n", __func__,
- name);
- return false;
- }
-
- /*
- * For the pre-divider and selector, the pre-trigger is used
- * if it's present, otherwise we just use the regular trigger.
- */
- trig = trigger_exists(&peri->pre_trig) ? &peri->pre_trig
- : &peri->trig;
-
- if (!div_init(ccu, &peri->gate, &peri->pre_div, trig)) {
- pr_err("%s: error initializing pre-divider for %s\n", __func__,
- name);
- return false;
- }
-
- if (!sel_init(ccu, &peri->gate, &peri->sel, trig)) {
- pr_err("%s: error initializing selector for %s\n", __func__,
- name);
- return false;
- }
-
- return true;
-}
-
-static bool __kona_clk_init(struct kona_clk *bcm_clk)
-{
- switch (bcm_clk->type) {
- case bcm_clk_peri:
- return __peri_clk_init(bcm_clk);
- default:
- BUG();
- }
- return -EINVAL;
-}
-
-/* Set a CCU and all its clocks into their desired initial state */
-bool __init kona_ccu_init(struct ccu_data *ccu)
-{
- unsigned long flags;
- unsigned int which;
- struct clk **clks = ccu->clk_data.clks;
- bool success = true;
-
- flags = ccu_lock(ccu);
- __ccu_write_enable(ccu);
-
- for (which = 0; which < ccu->clk_data.clk_num; which++) {
- struct kona_clk *bcm_clk;
-
- if (!clks[which])
- continue;
- bcm_clk = to_kona_clk(__clk_get_hw(clks[which]));
- success &= __kona_clk_init(bcm_clk);
- }
-
- __ccu_write_disable(ccu);
- ccu_unlock(ccu, flags);
- return success;
-}
diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h
index 2537b3072910..46ea11f24c89 100644
--- a/drivers/clk/bcm/clk-kona.h
+++ b/drivers/clk/bcm/clk-kona.h
@@ -387,6 +387,12 @@ struct bcm_clk_trig {
.flags = FLAG(TRIG, EXISTS), \
}
+struct bus_clk_data {
+ struct bcm_clk_policy policy;
+ struct bcm_clk_gate gate;
+ struct bcm_clk_hyst hyst;
+};
+
struct peri_clk_data {
struct bcm_clk_policy policy;
struct bcm_clk_gate gate;
@@ -406,25 +412,47 @@ struct kona_clk {
struct clk_init_data init_data; /* includes name of this clock */
struct ccu_data *ccu; /* ccu this clock is associated with */
enum bcm_clk_type type;
+ u32 flags; /* BCM_CLK_KONA_FLAGS_* below */
+ struct {
+ const char *name;
+ struct clk *clk;
+ } prereq;
union {
void *data;
+ struct bus_clk_data *bus;
struct peri_clk_data *peri;
} u;
};
#define to_kona_clk(_hw) \
container_of(_hw, struct kona_clk, hw)
+/*
+ * Kona clock flags:
+ * INITIALIZED clock has been initialized already
+ */
+#define BCM_CLK_KONA_FLAGS_INITIALIZED ((u32)1 << 0) /* Clock initialized */
+
/* Initialization macro for an entry in a CCU's kona_clks[] array. */
-#define KONA_CLK(_ccu_name, _clk_name, _type) \
- { \
+#define ___KONA_CLK_COMMON(_ccu_name, _clk_name, _type) \
.init_data = { \
.name = #_clk_name, \
.ops = &kona_ ## _type ## _clk_ops, \
}, \
.ccu = &_ccu_name ## _ccu_data, \
.type = bcm_clk_ ## _type, \
- .u.data = &_clk_name ## _data, \
+ .u.data = &_clk_name ## _data
+
+#define KONA_CLK_PREREQ(_ccu_name, _clk_name, _type, _prereq) \
+ { \
+ .prereq.name = #_prereq, \
+ ___KONA_CLK_COMMON(_ccu_name, _clk_name, _type), \
+ }
+
+#define KONA_CLK(_ccu_name, _clk_name, _type) \
+ { \
+ ___KONA_CLK_COMMON(_ccu_name, _clk_name, _type), \
}
+
#define LAST_KONA_CLK { .type = bcm_clk_none }
/*
@@ -499,6 +527,7 @@ struct ccu_data {
/* Exported globals */
+extern struct clk_ops kona_bus_clk_ops;
extern struct clk_ops kona_peri_clk_ops;
/* Externally visible functions */
@@ -511,6 +540,5 @@ extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value,
extern struct clk *kona_clk_setup(struct kona_clk *bcm_clk);
extern void __init kona_dt_ccu_setup(struct ccu_data *ccu,
struct device_node *node);
-extern bool __init kona_ccu_init(struct ccu_data *ccu);
#endif /* _CLK_KONA_H */
diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c
new file mode 100644
index 000000000000..2d707663542f
--- /dev/null
+++ b/drivers/clk/clk-scpi.c
@@ -0,0 +1,309 @@
+/*
+ * System Control and Power Interface (SCPI) Protocol based clock driver
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scpi_protocol.h>
+
+struct scpi_clk {
+ u32 id;
+ const char *name;
+ struct clk_hw hw;
+ struct scpi_opp *opps;
+ unsigned long rate_min;
+ unsigned long rate_max;
+};
+
+#define to_scpi_clk(clk) container_of(clk, struct scpi_clk, hw)
+
+static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct scpi_clk *clk = to_scpi_clk(hw);
+ return scpi_clk_get_val(clk->id);
+}
+
+static long scpi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct scpi_clk *clk = to_scpi_clk(hw);
+ if (clk->rate_min && rate < clk->rate_min)
+ rate = clk->rate_min;
+ if (clk->rate_max && rate > clk->rate_max)
+ rate = clk->rate_max;
+
+ return rate;
+}
+
+static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct scpi_clk *clk = to_scpi_clk(hw);
+ return scpi_clk_set_val(clk->id, rate);
+}
+
+static struct clk_ops scpi_clk_ops = {
+ .recalc_rate = scpi_clk_recalc_rate,
+ .round_rate = scpi_clk_round_rate,
+ .set_rate = scpi_clk_set_rate,
+};
+
+/* find closest match to given frequency in OPP table */
+static int __scpi_dvfs_round_rate(struct scpi_clk *clk, unsigned long rate)
+{
+ int idx, max_opp = clk->opps->count;
+ u32 *freqs = clk->opps->freqs;
+ u32 fmin = 0, fmax = ~0, ftmp;
+
+ for (idx = 0; idx < max_opp; idx++, freqs++) {
+ ftmp = *freqs;
+ if (ftmp >= (u32)rate) {
+ if (ftmp <= fmax)
+ fmax = ftmp;
+ } else {
+ if (ftmp >= fmin)
+ fmin = ftmp;
+ }
+ }
+ if (fmax != ~0)
+ return fmax;
+ else
+ return fmin;
+}
+
+static unsigned long scpi_dvfs_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct scpi_clk *clk = to_scpi_clk(hw);
+ int idx = scpi_dvfs_get_idx(clk->id);
+ u32 *freqs = clk->opps->freqs;
+
+ if (idx < 0)
+ return 0;
+ else
+ return *(freqs + idx);
+}
+
+static long scpi_dvfs_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct scpi_clk *clk = to_scpi_clk(hw);
+ return __scpi_dvfs_round_rate(clk, rate);
+}
+
+static int __scpi_find_dvfs_index(struct scpi_clk *clk, unsigned long rate)
+{
+ int idx, max_opp = clk->opps->count;
+ u32 *freqs = clk->opps->freqs;
+
+ for (idx = 0; idx < max_opp; idx++, freqs++)
+ if (*freqs == (u32)rate)
+ break;
+ return (idx == max_opp) ? -EINVAL : idx;
+}
+
+static int scpi_dvfs_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct scpi_clk *clk = to_scpi_clk(hw);
+ int ret = __scpi_find_dvfs_index(clk, rate);
+
+ if (ret < 0)
+ return ret;
+ else
+ return scpi_dvfs_set_idx(clk->id, (u8)ret);
+}
+
+static struct clk_ops scpi_dvfs_ops = {
+ .recalc_rate = scpi_dvfs_recalc_rate,
+ .round_rate = scpi_dvfs_round_rate,
+ .set_rate = scpi_dvfs_set_rate,
+};
+
+static struct clk *
+scpi_dvfs_ops_init(struct device *dev, struct device_node *np,
+ struct scpi_clk *sclk)
+{
+ struct clk_init_data init;
+ struct scpi_opp *opp;
+
+ init.name = sclk->name;
+ init.flags = CLK_IS_ROOT;
+ init.num_parents = 0;
+ init.ops = &scpi_dvfs_ops;
+ sclk->hw.init = &init;
+
+ opp = scpi_dvfs_get_opps(sclk->id);
+ if (IS_ERR(opp))
+ return (struct clk *)opp;
+
+ sclk->opps = opp;
+
+ return devm_clk_register(dev, &sclk->hw);
+}
+
+static struct clk *
+scpi_clk_ops_init(struct device *dev, struct device_node *np,
+ struct scpi_clk *sclk)
+{
+ struct clk_init_data init;
+ u32 range[2];
+ int ret;
+
+ init.name = sclk->name;
+ init.flags = CLK_IS_ROOT;
+ init.num_parents = 0;
+ init.ops = &scpi_clk_ops;
+ sclk->hw.init = &init;
+
+ ret = of_property_read_u32_array(np, "frequency-range", range,
+ ARRAY_SIZE(range));
+ if (ret)
+ return ERR_PTR(ret);
+ sclk->rate_min = range[0];
+ sclk->rate_max = range[1];
+
+ return devm_clk_register(dev, &sclk->hw);
+}
+
+static int scpi_clk_setup(struct device *dev, struct device_node *np,
+ const void *data)
+{
+ struct clk *(*setup_ops)(struct device *, struct device_node *,
+ struct scpi_clk *) = data;
+ struct clk_onecell_data *clk_data;
+ struct clk **clks;
+ size_t count;
+ int idx;
+
+ count = of_property_count_strings(np, "clock-output-names");
+ if (count < 0) {
+ dev_err(dev, "%s: invalid clock output count\n", np->name);
+ return -EINVAL;
+ }
+
+ clk_data = devm_kmalloc(dev, sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data) {
+ dev_err(dev, "failed to allocate clock provider data\n");
+ return -ENOMEM;
+ }
+
+ clks = devm_kmalloc(dev, count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ dev_err(dev, "failed to allocate clock providers\n");
+ return -ENOMEM;
+ }
+
+ for (idx = 0; idx < count; idx++) {
+ struct scpi_clk *sclk;
+ u32 val;
+
+ sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
+ if (!sclk) {
+ dev_err(dev, "failed to allocate scpi clocks\n");
+ return -ENOMEM;
+ }
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ idx, &sclk->name)) {
+ dev_err(dev, "invalid clock name @ %s\n", np->name);
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32_index(np, "clock-indices",
+ idx, &val)) {
+ dev_err(dev, "invalid clock index @ %s\n", np->name);
+ return -EINVAL;
+ }
+
+ sclk->id = val;
+
+ clks[idx] = setup_ops(dev, np, sclk);
+ if (IS_ERR(clks[idx])) {
+ dev_err(dev, "failed to register clock '%s'\n",
+ sclk->name);
+ return PTR_ERR(clks[idx]);
+ }
+
+ dev_dbg(dev, "Registered clock '%s'\n", sclk->name);
+ }
+
+ clk_data->clks = clks;
+ clk_data->clk_num = count;
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+
+ return 0;
+}
+
+static const struct of_device_id clk_match[] = {
+ { .compatible = "arm,scpi-clk-indexed", .data = scpi_dvfs_ops_init, },
+ { .compatible = "arm,scpi-clk-range", .data = &scpi_clk_ops_init, },
+ {}
+};
+
+static int scpi_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node, *child;
+ const struct of_device_id *match;
+ int ret;
+
+ for_each_child_of_node(np, child) {
+ match = of_match_node(clk_match, child);
+ if (!match)
+ continue;
+ ret = scpi_clk_setup(dev, child, match->data);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static struct of_device_id scpi_clk_ids[] = {
+ { .compatible = "arm,scpi-clks", },
+ {}
+};
+
+static struct platform_driver scpi_clk_driver = {
+ .driver = {
+ .name = "scpi_clocks",
+ .of_match_table = scpi_clk_ids,
+ },
+ .probe = scpi_clk_probe,
+};
+
+static int __init scpi_clk_init(void)
+{
+ return platform_driver_register(&scpi_clk_driver);
+}
+postcore_initcall(scpi_clk_init);
+
+static void __exit scpi_clk_exit(void)
+{
+ platform_driver_unregister(&scpi_clk_driver);
+}
+module_exit(scpi_clk_exit);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCPI clock driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c
index e5fcfb4e32ef..75efccb3d516 100644
--- a/drivers/clk/hisilicon/clk-hix5hd2.c
+++ b/drivers/clk/hisilicon/clk-hix5hd2.c
@@ -9,6 +9,8 @@
#include <linux/of_address.h>
#include <dt-bindings/clock/hix5hd2-clock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
#include "clk.h"
static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = {
@@ -48,9 +50,9 @@ static const char *sfc_mux_p[] __initconst = {
"24m", "150m", "200m", "100m", "75m", };
static u32 sfc_mux_table[] = {0, 4, 5, 6, 7};
-static const char *sdio1_mux_p[] __initconst = {
+static const char *sdio_mux_p[] __initconst = {
"75m", "100m", "50m", "15m", };
-static u32 sdio1_mux_table[] = {0, 1, 2, 3};
+static u32 sdio_mux_table[] = {0, 1, 2, 3};
static const char *fephy_mux_p[] __initconst = { "25m", "125m"};
static u32 fephy_mux_table[] = {0, 1};
@@ -59,28 +61,247 @@ static u32 fephy_mux_table[] = {0, 1};
static struct hisi_mux_clock hix5hd2_mux_clks[] __initdata = {
{ HIX5HD2_SFC_MUX, "sfc_mux", sfc_mux_p, ARRAY_SIZE(sfc_mux_p),
CLK_SET_RATE_PARENT, 0x5c, 8, 3, 0, sfc_mux_table, },
- { HIX5HD2_MMC_MUX, "mmc_mux", sdio1_mux_p, ARRAY_SIZE(sdio1_mux_p),
- CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio1_mux_table, },
+ { HIX5HD2_MMC_MUX, "mmc_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p),
+ CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio_mux_table, },
+ { HIX5HD2_SD_MUX, "sd_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p),
+ CLK_SET_RATE_PARENT, 0x9c, 8, 2, 0, sdio_mux_table, },
{ HIX5HD2_FEPHY_MUX, "fephy_mux",
fephy_mux_p, ARRAY_SIZE(fephy_mux_p),
CLK_SET_RATE_PARENT, 0x120, 8, 2, 0, fephy_mux_table, },
};
static struct hisi_gate_clock hix5hd2_gate_clks[] __initdata = {
- /*sfc*/
+ /* sfc */
{ HIX5HD2_SFC_CLK, "clk_sfc", "sfc_mux",
CLK_SET_RATE_PARENT, 0x5c, 0, 0, },
{ HIX5HD2_SFC_RST, "rst_sfc", "clk_sfc",
CLK_SET_RATE_PARENT, 0x5c, 4, CLK_GATE_SET_TO_DISABLE, },
- /*sdio1*/
+ /* sdio0 */
+ { HIX5HD2_SD_BIU_CLK, "clk_sd_biu", "200m",
+ CLK_SET_RATE_PARENT, 0x9c, 0, 0, },
+ { HIX5HD2_SD_CIU_CLK, "clk_sd_ciu", "sd_mux",
+ CLK_SET_RATE_PARENT, 0x9c, 1, 0, },
+ { HIX5HD2_SD_CIU_RST, "rst_sd_ciu", "clk_sd_ciu",
+ CLK_SET_RATE_PARENT, 0x9c, 4, CLK_GATE_SET_TO_DISABLE, },
+ /* sdio1 */
{ HIX5HD2_MMC_BIU_CLK, "clk_mmc_biu", "200m",
CLK_SET_RATE_PARENT, 0xa0, 0, 0, },
{ HIX5HD2_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux",
CLK_SET_RATE_PARENT, 0xa0, 1, 0, },
{ HIX5HD2_MMC_CIU_RST, "rst_mmc_ciu", "clk_mmc_ciu",
CLK_SET_RATE_PARENT, 0xa0, 4, CLK_GATE_SET_TO_DISABLE, },
+ /* gsf */
+ { HIX5HD2_FWD_BUS_CLK, "clk_fwd_bus", NULL, 0, 0xcc, 0, 0, },
+ { HIX5HD2_FWD_SYS_CLK, "clk_fwd_sys", "clk_fwd_bus", 0, 0xcc, 5, 0, },
+ { HIX5HD2_MAC0_PHY_CLK, "clk_fephy", "clk_fwd_sys",
+ CLK_SET_RATE_PARENT, 0x120, 0, 0, },
+ /* wdg0 */
+ { HIX5HD2_WDG0_CLK, "clk_wdg0", "24m",
+ CLK_SET_RATE_PARENT, 0x178, 0, 0, },
+ { HIX5HD2_WDG0_RST, "rst_wdg0", "clk_wdg0",
+ CLK_SET_RATE_PARENT, 0x178, 4, CLK_GATE_SET_TO_DISABLE, },
+ /* I2C */
+ {HIX5HD2_I2C0_CLK, "clk_i2c0", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 4, 0, },
+ {HIX5HD2_I2C0_RST, "rst_i2c0", "clk_i2c0",
+ CLK_SET_RATE_PARENT, 0x06c, 5, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C1_CLK, "clk_i2c1", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 8, 0, },
+ {HIX5HD2_I2C1_RST, "rst_i2c1", "clk_i2c1",
+ CLK_SET_RATE_PARENT, 0x06c, 9, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C2_CLK, "clk_i2c2", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 12, 0, },
+ {HIX5HD2_I2C2_RST, "rst_i2c2", "clk_i2c2",
+ CLK_SET_RATE_PARENT, 0x06c, 13, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C3_CLK, "clk_i2c3", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 16, 0, },
+ {HIX5HD2_I2C3_RST, "rst_i2c3", "clk_i2c3",
+ CLK_SET_RATE_PARENT, 0x06c, 17, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C4_CLK, "clk_i2c4", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 20, 0, },
+ {HIX5HD2_I2C4_RST, "rst_i2c4", "clk_i2c4",
+ CLK_SET_RATE_PARENT, 0x06c, 21, CLK_GATE_SET_TO_DISABLE, },
+ {HIX5HD2_I2C5_CLK, "clk_i2c5", "100m",
+ CLK_SET_RATE_PARENT, 0x06c, 0, 0, },
+ {HIX5HD2_I2C5_RST, "rst_i2c5", "clk_i2c5",
+ CLK_SET_RATE_PARENT, 0x06c, 1, CLK_GATE_SET_TO_DISABLE, },
};
+enum hix5hd2_clk_type {
+ TYPE_COMPLEX,
+ TYPE_ETHER,
+};
+
+struct hix5hd2_complex_clock {
+ const char *name;
+ const char *parent_name;
+ u32 id;
+ u32 ctrl_reg;
+ u32 ctrl_clk_mask;
+ u32 ctrl_rst_mask;
+ u32 phy_reg;
+ u32 phy_clk_mask;
+ u32 phy_rst_mask;
+ enum hix5hd2_clk_type type;
+};
+
+struct hix5hd2_clk_complex {
+ struct clk_hw hw;
+ u32 id;
+ void __iomem *ctrl_reg;
+ u32 ctrl_clk_mask;
+ u32 ctrl_rst_mask;
+ void __iomem *phy_reg;
+ u32 phy_clk_mask;
+ u32 phy_rst_mask;
+};
+
+static struct hix5hd2_complex_clock hix5hd2_complex_clks[] __initdata = {
+ {"clk_mac0", "clk_fephy", HIX5HD2_MAC0_CLK,
+ 0xcc, 0xa, 0x500, 0x120, 0, 0x10, TYPE_ETHER},
+ {"clk_mac1", "clk_fwd_sys", HIX5HD2_MAC1_CLK,
+ 0xcc, 0x14, 0xa00, 0x168, 0x2, 0, TYPE_ETHER},
+ {"clk_sata", NULL, HIX5HD2_SATA_CLK,
+ 0xa8, 0x1f, 0x300, 0xac, 0x1, 0x0, TYPE_COMPLEX},
+ {"clk_usb", NULL, HIX5HD2_USB_CLK,
+ 0xb8, 0xff, 0x3f000, 0xbc, 0x7, 0x3f00, TYPE_COMPLEX},
+};
+
+#define to_complex_clk(_hw) container_of(_hw, struct hix5hd2_clk_complex, hw)
+
+static int clk_ether_enable(struct clk_hw *hw)
+{
+ struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+ u32 val;
+
+ val = readl_relaxed(clk->ctrl_reg);
+ val |= clk->ctrl_clk_mask | clk->ctrl_rst_mask;
+ writel_relaxed(val, clk->ctrl_reg);
+ val &= ~(clk->ctrl_rst_mask);
+ writel_relaxed(val, clk->ctrl_reg);
+
+ val = readl_relaxed(clk->phy_reg);
+ val |= clk->phy_clk_mask;
+ val &= ~(clk->phy_rst_mask);
+ writel_relaxed(val, clk->phy_reg);
+ mdelay(10);
+
+ val &= ~(clk->phy_clk_mask);
+ val |= clk->phy_rst_mask;
+ writel_relaxed(val, clk->phy_reg);
+ mdelay(10);
+
+ val |= clk->phy_clk_mask;
+ val &= ~(clk->phy_rst_mask);
+ writel_relaxed(val, clk->phy_reg);
+ mdelay(30);
+ return 0;
+}
+
+static void clk_ether_disable(struct clk_hw *hw)
+{
+ struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+ u32 val;
+
+ val = readl_relaxed(clk->ctrl_reg);
+ val &= ~(clk->ctrl_clk_mask);
+ writel_relaxed(val, clk->ctrl_reg);
+}
+
+static struct clk_ops clk_ether_ops = {
+ .enable = clk_ether_enable,
+ .disable = clk_ether_disable,
+};
+
+static int clk_complex_enable(struct clk_hw *hw)
+{
+ struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+ u32 val;
+
+ val = readl_relaxed(clk->ctrl_reg);
+ val |= clk->ctrl_clk_mask;
+ val &= ~(clk->ctrl_rst_mask);
+ writel_relaxed(val, clk->ctrl_reg);
+
+ val = readl_relaxed(clk->phy_reg);
+ val |= clk->phy_clk_mask;
+ val &= ~(clk->phy_rst_mask);
+ writel_relaxed(val, clk->phy_reg);
+
+ return 0;
+}
+
+static void clk_complex_disable(struct clk_hw *hw)
+{
+ struct hix5hd2_clk_complex *clk = to_complex_clk(hw);
+ u32 val;
+
+ val = readl_relaxed(clk->ctrl_reg);
+ val |= clk->ctrl_rst_mask;
+ val &= ~(clk->ctrl_clk_mask);
+ writel_relaxed(val, clk->ctrl_reg);
+
+ val = readl_relaxed(clk->phy_reg);
+ val |= clk->phy_rst_mask;
+ val &= ~(clk->phy_clk_mask);
+ writel_relaxed(val, clk->phy_reg);
+
+ return;
+}
+
+static struct clk_ops clk_complex_ops = {
+ .enable = clk_complex_enable,
+ .disable = clk_complex_disable,
+};
+
+void __init hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks,
+ int nums, struct hisi_clock_data *data)
+{
+ void __iomem *base = data->base;
+ int i;
+
+ for (i = 0; i < nums; i++) {
+ struct hix5hd2_clk_complex *p_clk;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL);
+ if (!p_clk) {
+ pr_err("%s: fail to allocate clk\n", __func__);
+ return;
+ }
+
+ init.name = clks[i].name;
+ if (clks[i].type == TYPE_ETHER)
+ init.ops = &clk_ether_ops;
+ else
+ init.ops = &clk_complex_ops;
+
+ init.flags = CLK_IS_BASIC;
+ init.parent_names =
+ (clks[i].parent_name ? &clks[i].parent_name : NULL);
+ init.num_parents = (clks[i].parent_name ? 1 : 0);
+
+ p_clk->ctrl_reg = base + clks[i].ctrl_reg;
+ p_clk->ctrl_clk_mask = clks[i].ctrl_clk_mask;
+ p_clk->ctrl_rst_mask = clks[i].ctrl_rst_mask;
+ p_clk->phy_reg = base + clks[i].phy_reg;
+ p_clk->phy_clk_mask = clks[i].phy_clk_mask;
+ p_clk->phy_rst_mask = clks[i].phy_rst_mask;
+ p_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &p_clk->hw);
+ if (IS_ERR(clk)) {
+ kfree(p_clk);
+ pr_err("%s: failed to register clock %s\n",
+ __func__, clks[i].name);
+ continue;
+ }
+
+ data->clk_data.clks[clks[i].id] = clk;
+ }
+}
+
static void __init hix5hd2_clk_init(struct device_node *np)
{
struct hisi_clock_data *clk_data;
@@ -96,6 +317,9 @@ static void __init hix5hd2_clk_init(struct device_node *np)
clk_data);
hisi_clk_register_gate(hix5hd2_gate_clks,
ARRAY_SIZE(hix5hd2_gate_clks), clk_data);
+ hix5hd2_clk_register_complex(hix5hd2_complex_clks,
+ ARRAY_SIZE(hix5hd2_complex_clks),
+ clk_data);
}
CLK_OF_DECLARE(hix5hd2_clk, "hisilicon,hix5hd2-clock", hix5hd2_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index 70ec3d2608a1..e76f7772fd54 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -806,7 +806,7 @@ static void __init exynos5250_clk_init(struct device_node *np)
PWR_CTRL1_DIV2_DOWN_EN | PWR_CTRL1_DIV1_DOWN_EN |
PWR_CTRL1_USE_CORE1_WFE | PWR_CTRL1_USE_CORE0_WFE |
PWR_CTRL1_USE_CORE1_WFI | PWR_CTRL1_USE_CORE0_WFI);
- __raw_writel(tmp, reg_base + PWR_CTRL1);
+ writel_relaxed(tmp, reg_base + PWR_CTRL1);
/*
* Enable arm clock up (on exiting idle). Set arm divider
@@ -816,7 +816,7 @@ static void __init exynos5250_clk_init(struct device_node *np)
tmp = (PWR_CTRL2_DIV2_UP_EN | PWR_CTRL2_DIV1_UP_EN |
PWR_CTRL2_DUR_STANDBY2_VAL | PWR_CTRL2_DUR_STANDBY1_VAL |
PWR_CTRL2_CORE2_UP_RATIO | PWR_CTRL2_CORE1_UP_RATIO);
- __raw_writel(tmp, reg_base + PWR_CTRL2);
+ writel_relaxed(tmp, reg_base + PWR_CTRL2);
exynos5250_clk_sleep_init();
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index b07fad2a9167..2de50ef56118 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -77,7 +77,7 @@ static unsigned long samsung_pll2126_recalc_rate(struct clk_hw *hw,
u32 pll_con, mdiv, pdiv, sdiv;
u64 fvco = parent_rate;
- pll_con = __raw_readl(pll->con_reg);
+ pll_con = readl_relaxed(pll->con_reg);
mdiv = (pll_con >> PLL2126_MDIV_SHIFT) & PLL2126_MDIV_MASK;
pdiv = (pll_con >> PLL2126_PDIV_SHIFT) & PLL2126_PDIV_MASK;
sdiv = (pll_con >> PLL2126_SDIV_SHIFT) & PLL2126_SDIV_MASK;
@@ -110,7 +110,7 @@ static unsigned long samsung_pll3000_recalc_rate(struct clk_hw *hw,
u32 pll_con, mdiv, pdiv, sdiv;
u64 fvco = parent_rate;
- pll_con = __raw_readl(pll->con_reg);
+ pll_con = readl_relaxed(pll->con_reg);
mdiv = (pll_con >> PLL3000_MDIV_SHIFT) & PLL3000_MDIV_MASK;
pdiv = (pll_con >> PLL3000_PDIV_SHIFT) & PLL3000_PDIV_MASK;
sdiv = (pll_con >> PLL3000_SDIV_SHIFT) & PLL3000_SDIV_MASK;
@@ -147,7 +147,7 @@ static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw,
u32 mdiv, pdiv, sdiv, pll_con;
u64 fvco = parent_rate;
- pll_con = __raw_readl(pll->con_reg);
+ pll_con = readl_relaxed(pll->con_reg);
mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK;
pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK;
sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK;
@@ -184,19 +184,19 @@ static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate,
return -EINVAL;
}
- tmp = __raw_readl(pll->con_reg);
+ tmp = readl_relaxed(pll->con_reg);
if (!(samsung_pll35xx_mp_change(rate, tmp))) {
/* If only s change, change just s value only*/
tmp &= ~(PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT);
tmp |= rate->sdiv << PLL35XX_SDIV_SHIFT;
- __raw_writel(tmp, pll->con_reg);
+ writel_relaxed(tmp, pll->con_reg);
return 0;
}
/* Set PLL lock time. */
- __raw_writel(rate->pdiv * PLL35XX_LOCK_FACTOR,
+ writel_relaxed(rate->pdiv * PLL35XX_LOCK_FACTOR,
pll->lock_reg);
/* Change PLL PMS values */
@@ -206,12 +206,12 @@ static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate,
tmp |= (rate->mdiv << PLL35XX_MDIV_SHIFT) |
(rate->pdiv << PLL35XX_PDIV_SHIFT) |
(rate->sdiv << PLL35XX_SDIV_SHIFT);
- __raw_writel(tmp, pll->con_reg);
+ writel_relaxed(tmp, pll->con_reg);
/* wait_lock_time */
do {
cpu_relax();
- tmp = __raw_readl(pll->con_reg);
+ tmp = readl_relaxed(pll->con_reg);
} while (!(tmp & (PLL35XX_LOCK_STAT_MASK
<< PLL35XX_LOCK_STAT_SHIFT)));
return 0;
@@ -251,8 +251,8 @@ static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw,
s16 kdiv;
u64 fvco = parent_rate;
- pll_con0 = __raw_readl(pll->con_reg);
- pll_con1 = __raw_readl(pll->con_reg + 4);
+ pll_con0 = readl_relaxed(pll->con_reg);
+ pll_con1 = readl_relaxed(pll->con_reg + 4);
mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK;
pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK;
sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK;
@@ -292,20 +292,20 @@ static int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate,
return -EINVAL;
}
- pll_con0 = __raw_readl(pll->con_reg);
- pll_con1 = __raw_readl(pll->con_reg + 4);
+ pll_con0 = readl_relaxed(pll->con_reg);
+ pll_con1 = readl_relaxed(pll->con_reg + 4);
if (!(samsung_pll36xx_mpk_change(rate, pll_con0, pll_con1))) {
/* If only s change, change just s value only*/
pll_con0 &= ~(PLL36XX_SDIV_MASK << PLL36XX_SDIV_SHIFT);
pll_con0 |= (rate->sdiv << PLL36XX_SDIV_SHIFT);
- __raw_writel(pll_con0, pll->con_reg);
+ writel_relaxed(pll_con0, pll->con_reg);
return 0;
}
/* Set PLL lock time. */
- __raw_writel(rate->pdiv * PLL36XX_LOCK_FACTOR, pll->lock_reg);
+ writel_relaxed(rate->pdiv * PLL36XX_LOCK_FACTOR, pll->lock_reg);
/* Change PLL PMS values */
pll_con0 &= ~((PLL36XX_MDIV_MASK << PLL36XX_MDIV_SHIFT) |
@@ -314,16 +314,16 @@ static int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate,
pll_con0 |= (rate->mdiv << PLL36XX_MDIV_SHIFT) |
(rate->pdiv << PLL36XX_PDIV_SHIFT) |
(rate->sdiv << PLL36XX_SDIV_SHIFT);
- __raw_writel(pll_con0, pll->con_reg);
+ writel_relaxed(pll_con0, pll->con_reg);
pll_con1 &= ~(PLL36XX_KDIV_MASK << PLL36XX_KDIV_SHIFT);
pll_con1 |= rate->kdiv << PLL36XX_KDIV_SHIFT;
- __raw_writel(pll_con1, pll->con_reg + 4);
+ writel_relaxed(pll_con1, pll->con_reg + 4);
/* wait_lock_time */
do {
cpu_relax();
- tmp = __raw_readl(pll->con_reg);
+ tmp = readl_relaxed(pll->con_reg);
} while (!(tmp & (1 << PLL36XX_LOCK_STAT_SHIFT)));
return 0;
@@ -364,7 +364,7 @@ static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw,
u32 mdiv, pdiv, sdiv, pll_con;
u64 fvco = parent_rate;
- pll_con = __raw_readl(pll->con_reg);
+ pll_con = readl_relaxed(pll->con_reg);
mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK;
pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK;
sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK;
@@ -407,14 +407,14 @@ static int samsung_pll45xx_set_rate(struct clk_hw *hw, unsigned long drate,
return -EINVAL;
}
- con0 = __raw_readl(pll->con_reg);
- con1 = __raw_readl(pll->con_reg + 0x4);
+ con0 = readl_relaxed(pll->con_reg);
+ con1 = readl_relaxed(pll->con_reg + 0x4);
if (!(samsung_pll45xx_mp_change(con0, con1, rate))) {
/* If only s change, change just s value only*/
con0 &= ~(PLL45XX_SDIV_MASK << PLL45XX_SDIV_SHIFT);
con0 |= rate->sdiv << PLL45XX_SDIV_SHIFT;
- __raw_writel(con0, pll->con_reg);
+ writel_relaxed(con0, pll->con_reg);
return 0;
}
@@ -428,29 +428,29 @@ static int samsung_pll45xx_set_rate(struct clk_hw *hw, unsigned long drate,
(rate->sdiv << PLL45XX_SDIV_SHIFT);
/* Set PLL AFC value. */
- con1 = __raw_readl(pll->con_reg + 0x4);
+ con1 = readl_relaxed(pll->con_reg + 0x4);
con1 &= ~(PLL45XX_AFC_MASK << PLL45XX_AFC_SHIFT);
con1 |= (rate->afc << PLL45XX_AFC_SHIFT);
/* Set PLL lock time. */
switch (pll->type) {
case pll_4502:
- __raw_writel(rate->pdiv * PLL4502_LOCK_FACTOR, pll->lock_reg);
+ writel_relaxed(rate->pdiv * PLL4502_LOCK_FACTOR, pll->lock_reg);
break;
case pll_4508:
- __raw_writel(rate->pdiv * PLL4508_LOCK_FACTOR, pll->lock_reg);
+ writel_relaxed(rate->pdiv * PLL4508_LOCK_FACTOR, pll->lock_reg);
break;
default:
break;
}
/* Set new configuration. */
- __raw_writel(con1, pll->con_reg + 0x4);
- __raw_writel(con0, pll->con_reg);
+ writel_relaxed(con1, pll->con_reg + 0x4);
+ writel_relaxed(con0, pll->con_reg);
/* Wait for locking. */
start = ktime_get();
- while (!(__raw_readl(pll->con_reg) & PLL45XX_LOCKED)) {
+ while (!(readl_relaxed(pll->con_reg) & PLL45XX_LOCKED)) {
ktime_t delta = ktime_sub(ktime_get(), start);
if (ktime_to_ms(delta) > PLL_TIMEOUT_MS) {
@@ -509,8 +509,8 @@ static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw,
u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift;
u64 fvco = parent_rate;
- pll_con0 = __raw_readl(pll->con_reg);
- pll_con1 = __raw_readl(pll->con_reg + 4);
+ pll_con0 = readl_relaxed(pll->con_reg);
+ pll_con1 = readl_relaxed(pll->con_reg + 4);
mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK;
@@ -554,14 +554,14 @@ static int samsung_pll46xx_set_rate(struct clk_hw *hw, unsigned long drate,
return -EINVAL;
}
- con0 = __raw_readl(pll->con_reg);
- con1 = __raw_readl(pll->con_reg + 0x4);
+ con0 = readl_relaxed(pll->con_reg);
+ con1 = readl_relaxed(pll->con_reg + 0x4);
if (!(samsung_pll46xx_mpk_change(con0, con1, rate))) {
/* If only s change, change just s value only*/
con0 &= ~(PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT);
con0 |= rate->sdiv << PLL46XX_SDIV_SHIFT;
- __raw_writel(con0, pll->con_reg);
+ writel_relaxed(con0, pll->con_reg);
return 0;
}
@@ -583,7 +583,7 @@ static int samsung_pll46xx_set_rate(struct clk_hw *hw, unsigned long drate,
(rate->vsel << PLL46XX_VSEL_SHIFT);
/* Set PLL K, MFR and MRR values. */
- con1 = __raw_readl(pll->con_reg + 0x4);
+ con1 = readl_relaxed(pll->con_reg + 0x4);
con1 &= ~((PLL46XX_KDIV_MASK << PLL46XX_KDIV_SHIFT) |
(PLL46XX_MFR_MASK << PLL46XX_MFR_SHIFT) |
(PLL46XX_MRR_MASK << PLL46XX_MRR_SHIFT));
@@ -592,13 +592,13 @@ static int samsung_pll46xx_set_rate(struct clk_hw *hw, unsigned long drate,
(rate->mrr << PLL46XX_MRR_SHIFT);
/* Write configuration to PLL */
- __raw_writel(lock, pll->lock_reg);
- __raw_writel(con0, pll->con_reg);
- __raw_writel(con1, pll->con_reg + 0x4);
+ writel_relaxed(lock, pll->lock_reg);
+ writel_relaxed(con0, pll->con_reg);
+ writel_relaxed(con1, pll->con_reg + 0x4);
/* Wait for locking. */
start = ktime_get();
- while (!(__raw_readl(pll->con_reg) & PLL46XX_LOCKED)) {
+ while (!(readl_relaxed(pll->con_reg) & PLL46XX_LOCKED)) {
ktime_t delta = ktime_sub(ktime_get(), start);
if (ktime_to_ms(delta) > PLL_TIMEOUT_MS) {
@@ -643,7 +643,7 @@ static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw,
u32 mdiv, pdiv, sdiv, pll_con;
u64 fvco = parent_rate;
- pll_con = __raw_readl(pll->con_reg);
+ pll_con = readl_relaxed(pll->con_reg);
if (pll->type == pll_6552_s3c2416) {
mdiv = (pll_con >> PLL6552_MDIV_SHIFT_2416) & PLL6552_MDIV_MASK;
pdiv = (pll_con >> PLL6552_PDIV_SHIFT_2416) & PLL6552_PDIV_MASK;
@@ -683,8 +683,8 @@ static unsigned long samsung_pll6553_recalc_rate(struct clk_hw *hw,
u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1;
u64 fvco = parent_rate;
- pll_con0 = __raw_readl(pll->con_reg);
- pll_con1 = __raw_readl(pll->con_reg + 0x4);
+ pll_con0 = readl_relaxed(pll->con_reg);
+ pll_con1 = readl_relaxed(pll->con_reg + 0x4);
mdiv = (pll_con0 >> PLL6553_MDIV_SHIFT) & PLL6553_MDIV_MASK;
pdiv = (pll_con0 >> PLL6553_PDIV_SHIFT) & PLL6553_PDIV_MASK;
sdiv = (pll_con0 >> PLL6553_SDIV_SHIFT) & PLL6553_SDIV_MASK;
@@ -721,7 +721,7 @@ static unsigned long samsung_s3c2410_pll_recalc_rate(struct clk_hw *hw,
u32 pll_con, mdiv, pdiv, sdiv;
u64 fvco = parent_rate;
- pll_con = __raw_readl(pll->con_reg);
+ pll_con = readl_relaxed(pll->con_reg);
mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK;
pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK;
sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK;
@@ -739,7 +739,7 @@ static unsigned long samsung_s3c2440_mpll_recalc_rate(struct clk_hw *hw,
u32 pll_con, mdiv, pdiv, sdiv;
u64 fvco = parent_rate;
- pll_con = __raw_readl(pll->con_reg);
+ pll_con = readl_relaxed(pll->con_reg);
mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK;
pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK;
sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK;
@@ -765,7 +765,7 @@ static int samsung_s3c2410_pll_set_rate(struct clk_hw *hw, unsigned long drate,
return -EINVAL;
}
- tmp = __raw_readl(pll->con_reg);
+ tmp = readl_relaxed(pll->con_reg);
/* Change PLL PMS values */
tmp &= ~((PLLS3C2410_MDIV_MASK << PLLS3C2410_MDIV_SHIFT) |
@@ -774,7 +774,7 @@ static int samsung_s3c2410_pll_set_rate(struct clk_hw *hw, unsigned long drate,
tmp |= (rate->mdiv << PLLS3C2410_MDIV_SHIFT) |
(rate->pdiv << PLLS3C2410_PDIV_SHIFT) |
(rate->sdiv << PLLS3C2410_SDIV_SHIFT);
- __raw_writel(tmp, pll->con_reg);
+ writel_relaxed(tmp, pll->con_reg);
/* Time to settle according to the manual */
udelay(300);
@@ -785,7 +785,7 @@ static int samsung_s3c2410_pll_set_rate(struct clk_hw *hw, unsigned long drate,
static int samsung_s3c2410_pll_enable(struct clk_hw *hw, int bit, bool enable)
{
struct samsung_clk_pll *pll = to_clk_pll(hw);
- u32 pll_en = __raw_readl(pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET);
+ u32 pll_en = readl_relaxed(pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET);
u32 pll_en_orig = pll_en;
if (enable)
@@ -793,7 +793,7 @@ static int samsung_s3c2410_pll_enable(struct clk_hw *hw, int bit, bool enable)
else
pll_en |= BIT(bit);
- __raw_writel(pll_en, pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET);
+ writel_relaxed(pll_en, pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET);
/* if we started the UPLL, then allow to settle */
if (enable && (pll_en_orig & BIT(bit)))
@@ -892,7 +892,7 @@ static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
u32 r, p, m, s, pll_stat;
u64 fvco = parent_rate;
- pll_stat = __raw_readl(pll->reg_base + pll->offset * 3);
+ pll_stat = readl_relaxed(pll->reg_base + pll->offset * 3);
r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
if (!r)
return 0;
@@ -970,7 +970,7 @@ static unsigned long samsung_pll2550xx_recalc_rate(struct clk_hw *hw,
u32 mdiv, pdiv, sdiv, pll_con;
u64 fvco = parent_rate;
- pll_con = __raw_readl(pll->con_reg);
+ pll_con = readl_relaxed(pll->con_reg);
mdiv = (pll_con >> PLL2550XX_M_SHIFT) & PLL2550XX_M_MASK;
pdiv = (pll_con >> PLL2550XX_P_SHIFT) & PLL2550XX_P_MASK;
sdiv = (pll_con >> PLL2550XX_S_SHIFT) & PLL2550XX_S_MASK;
@@ -1006,19 +1006,19 @@ static int samsung_pll2550xx_set_rate(struct clk_hw *hw, unsigned long drate,
return -EINVAL;
}
- tmp = __raw_readl(pll->con_reg);
+ tmp = readl_relaxed(pll->con_reg);
if (!(samsung_pll2550xx_mp_change(rate->mdiv, rate->pdiv, tmp))) {
/* If only s change, change just s value only*/
tmp &= ~(PLL2550XX_S_MASK << PLL2550XX_S_SHIFT);
tmp |= rate->sdiv << PLL2550XX_S_SHIFT;
- __raw_writel(tmp, pll->con_reg);
+ writel_relaxed(tmp, pll->con_reg);
return 0;
}
/* Set PLL lock time. */
- __raw_writel(rate->pdiv * PLL2550XX_LOCK_FACTOR, pll->lock_reg);
+ writel_relaxed(rate->pdiv * PLL2550XX_LOCK_FACTOR, pll->lock_reg);
/* Change PLL PMS values */
tmp &= ~((PLL2550XX_M_MASK << PLL2550XX_M_SHIFT) |
@@ -1027,12 +1027,12 @@ static int samsung_pll2550xx_set_rate(struct clk_hw *hw, unsigned long drate,
tmp |= (rate->mdiv << PLL2550XX_M_SHIFT) |
(rate->pdiv << PLL2550XX_P_SHIFT) |
(rate->sdiv << PLL2550XX_S_SHIFT);
- __raw_writel(tmp, pll->con_reg);
+ writel_relaxed(tmp, pll->con_reg);
/* wait_lock_time */
do {
cpu_relax();
- tmp = __raw_readl(pll->con_reg);
+ tmp = readl_relaxed(pll->con_reg);
} while (!(tmp & (PLL2550XX_LOCK_STAT_MASK
<< PLL2550XX_LOCK_STAT_SHIFT)));
@@ -1076,8 +1076,8 @@ static unsigned long samsung_pll2650xx_recalc_rate(struct clk_hw *hw,
s16 kdiv;
u64 fvco = parent_rate;
- pll_con0 = __raw_readl(pll->con_reg);
- pll_con2 = __raw_readl(pll->con_reg + 8);
+ pll_con0 = readl_relaxed(pll->con_reg);
+ pll_con2 = readl_relaxed(pll->con_reg + 8);
mdiv = (pll_con0 >> PLL2650XX_MDIV_SHIFT) & PLL2650XX_MDIV_MASK;
pdiv = (pll_con0 >> PLL2650XX_PDIV_SHIFT) & PLL2650XX_PDIV_MASK;
sdiv = (pll_con0 >> PLL2650XX_SDIV_SHIFT) & PLL2650XX_SDIV_MASK;
@@ -1104,8 +1104,8 @@ static int samsung_pll2650xx_set_rate(struct clk_hw *hw, unsigned long drate,
return -EINVAL;
}
- pll_con0 = __raw_readl(pll->con_reg);
- pll_con2 = __raw_readl(pll->con_reg + 8);
+ pll_con0 = readl_relaxed(pll->con_reg);
+ pll_con2 = readl_relaxed(pll->con_reg + 8);
/* Change PLL PMS values */
pll_con0 &= ~(PLL2650XX_MDIV_MASK << PLL2650XX_MDIV_SHIFT |
@@ -1122,13 +1122,13 @@ static int samsung_pll2650xx_set_rate(struct clk_hw *hw, unsigned long drate,
<< PLL2650XX_KDIV_SHIFT;
/* Set PLL lock time. */
- __raw_writel(PLL2650XX_LOCK_FACTOR * rate->pdiv, pll->lock_reg);
+ writel_relaxed(PLL2650XX_LOCK_FACTOR * rate->pdiv, pll->lock_reg);
- __raw_writel(pll_con0, pll->con_reg);
- __raw_writel(pll_con2, pll->con_reg + 8);
+ writel_relaxed(pll_con0, pll->con_reg);
+ writel_relaxed(pll_con2, pll->con_reg + 8);
do {
- tmp = __raw_readl(pll->con_reg);
+ tmp = readl_relaxed(pll->con_reg);
} while (!(tmp & (0x1 << PLL2650XX_PLL_LOCKTIME_SHIFT)));
return 0;
diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c
index 5645cfc90c41..19aaddd247d3 100644
--- a/drivers/clocksource/samsung_pwm_timer.c
+++ b/drivers/clocksource/samsung_pwm_timer.c
@@ -130,9 +130,9 @@ static void samsung_time_stop(unsigned int channel)
spin_lock_irqsave(&samsung_pwm_lock, flags);
- tcon = __raw_readl(pwm.base + REG_TCON);
+ tcon = readl_relaxed(pwm.base + REG_TCON);
tcon &= ~TCON_START(channel);
- __raw_writel(tcon, pwm.base + REG_TCON);
+ writel_relaxed(tcon, pwm.base + REG_TCON);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
@@ -148,14 +148,14 @@ static void samsung_time_setup(unsigned int channel, unsigned long tcnt)
spin_lock_irqsave(&samsung_pwm_lock, flags);
- tcon = __raw_readl(pwm.base + REG_TCON);
+ tcon = readl_relaxed(pwm.base + REG_TCON);
tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan));
tcon |= TCON_MANUALUPDATE(tcon_chan);
- __raw_writel(tcnt, pwm.base + REG_TCNTB(channel));
- __raw_writel(tcnt, pwm.base + REG_TCMPB(channel));
- __raw_writel(tcon, pwm.base + REG_TCON);
+ writel_relaxed(tcnt, pwm.base + REG_TCNTB(channel));
+ writel_relaxed(tcnt, pwm.base + REG_TCMPB(channel));
+ writel_relaxed(tcon, pwm.base + REG_TCON);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
@@ -170,7 +170,7 @@ static void samsung_time_start(unsigned int channel, bool periodic)
spin_lock_irqsave(&samsung_pwm_lock, flags);
- tcon = __raw_readl(pwm.base + REG_TCON);
+ tcon = readl_relaxed(pwm.base + REG_TCON);
tcon &= ~TCON_MANUALUPDATE(channel);
tcon |= TCON_START(channel);
@@ -180,7 +180,7 @@ static void samsung_time_start(unsigned int channel, bool periodic)
else
tcon &= ~TCON_AUTORELOAD(channel);
- __raw_writel(tcon, pwm.base + REG_TCON);
+ writel_relaxed(tcon, pwm.base + REG_TCON);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index ffe350f86bca..9113e8e2ef50 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -102,6 +102,16 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
Be aware that not all cpufreq drivers support the conservative
governor. If unsure have a look at the help section of the
driver. Fallback governor will be the performance governor.
+
+config CPU_FREQ_DEFAULT_GOV_INTERACTIVE
+ bool "interactive"
+ select CPU_FREQ_GOV_INTERACTIVE
+ help
+ Use the CPUFreq governor 'interactive' as default. This allows
+ you to get a full dynamic cpu frequency capable system by simply
+ loading your cpufreq low-level hardware driver, using the
+ 'interactive' governor for latency-sensitive workloads.
+
endchoice
config CPU_FREQ_GOV_PERFORMANCE
@@ -159,6 +169,24 @@ config CPU_FREQ_GOV_ONDEMAND
If in doubt, say N.
+config CPU_FREQ_GOV_INTERACTIVE
+ tristate "'interactive' cpufreq policy governor"
+ default n
+ help
+ 'interactive' - This driver adds a dynamic cpufreq policy governor
+ designed for latency-sensitive workloads.
+
+ This governor attempts to reduce the latency of clock
+ increases so that the system is more responsive to
+ interactive workloads.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cpufreq_interactive.
+
+ For details, take a look at linux/Documentation/cpu-freq.
+
+ If in doubt, say N.
+
config CPU_FREQ_GOV_CONSERVATIVE
tristate "'conservative' cpufreq governor"
depends on CPU_FREQ
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 7364a538e056..daf4e516f415 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -5,7 +5,8 @@
# big LITTLE core layer and glue drivers
config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver"
- depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK
+ depends on ARM_CPU_TOPOLOGY || (ARM64 && SMP)
+ depends on HAVE_CLK
select PM_OPP
help
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
@@ -24,6 +25,15 @@ config ARM_VEXPRESS_SPC_CPUFREQ
This add the CPUfreq driver support for Versatile Express
big.LITTLE platforms using SPC for power management.
+config ARM_SPCI_CPUFREQ
+ tristate "SPCI based CPUfreq driver"
+ depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL
+ help
+ This add the CPUfreq driver support for ARM big.LITTLE platforms
+ using SCPI interface for CPU power management.
+
+ This driver works only if firmware the supporting CPU DVFS adhere
+ to SCPI protocol.
config ARM_EXYNOS_CPUFREQ
bool
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index db6d9a2fea4d..38bd66fedfc9 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o
obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o
obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o
obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o
+obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o
obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o
@@ -77,6 +78,7 @@ obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
+obj-$(CONFIG_ARM_SPCI_CPUFREQ) += scpi-cpufreq.o
##################################################################################
# PowerPC platform drivers
diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c
index a46c223c2506..91bf1c829358 100644
--- a/drivers/cpufreq/arm_big_little.c
+++ b/drivers/cpufreq/arm_big_little.c
@@ -341,7 +341,9 @@ static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
}
name[12] = cluster + '0';
- clk[cluster] = clk_get(cpu_dev, name);
+ clk[cluster] = clk_get_sys(name, NULL);
+ if (IS_ERR(clk[cluster]))
+ clk[cluster] = clk_get(cpu_dev, NULL);
if (!IS_ERR(clk[cluster])) {
dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
__func__, clk[cluster], freq_table[cluster],
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
new file mode 100644
index 000000000000..ff77b300df88
--- /dev/null
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -0,0 +1,1343 @@
+/*
+ * drivers/cpufreq/cpufreq_interactive.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * Author: Mike Chan (mike@android.com)
+ *
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/sched/rt.h>
+#include <linux/tick.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/cpufreq_interactive.h>
+
+struct cpufreq_interactive_cpuinfo {
+ struct timer_list cpu_timer;
+ struct timer_list cpu_slack_timer;
+ spinlock_t load_lock; /* protects the next 4 fields */
+ u64 time_in_idle;
+ u64 time_in_idle_timestamp;
+ u64 cputime_speedadj;
+ u64 cputime_speedadj_timestamp;
+ struct cpufreq_policy *policy;
+ struct cpufreq_frequency_table *freq_table;
+ unsigned int target_freq;
+ unsigned int floor_freq;
+ u64 floor_validate_time;
+ u64 hispeed_validate_time;
+ struct rw_semaphore enable_sem;
+ int governor_enabled;
+};
+
+static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo);
+
+/* realtime thread handles frequency scaling */
+static struct task_struct *speedchange_task;
+static cpumask_t speedchange_cpumask;
+static spinlock_t speedchange_cpumask_lock;
+static struct mutex gov_lock;
+
+/* Target load. Lower values result in higher CPU speeds. */
+#define DEFAULT_TARGET_LOAD 90
+static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD};
+
+#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC)
+#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE
+static unsigned int default_above_hispeed_delay[] = {
+ DEFAULT_ABOVE_HISPEED_DELAY };
+
+struct cpufreq_interactive_tunables {
+ int usage_count;
+ /* Hi speed to bump to from lo speed when load burst (default max) */
+ unsigned int hispeed_freq;
+ /* Go to hi speed when CPU load at or above this value. */
+#define DEFAULT_GO_HISPEED_LOAD 99
+ unsigned long go_hispeed_load;
+ /* Target load. Lower values result in higher CPU speeds. */
+ spinlock_t target_loads_lock;
+ unsigned int *target_loads;
+ int ntarget_loads;
+ /*
+ * The minimum amount of time to spend at a frequency before we can ramp
+ * down.
+ */
+#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC)
+ unsigned long min_sample_time;
+ /*
+ * The sample rate of the timer used to increase frequency
+ */
+ unsigned long timer_rate;
+ /*
+ * Wait this long before raising speed above hispeed, by default a
+ * single timer interval.
+ */
+ spinlock_t above_hispeed_delay_lock;
+ unsigned int *above_hispeed_delay;
+ int nabove_hispeed_delay;
+ /* Non-zero means indefinite speed boost active */
+ int boost_val;
+ /* Duration of a boot pulse in usecs */
+ int boostpulse_duration_val;
+ /* End time of boost pulse in ktime converted to usecs */
+ u64 boostpulse_endtime;
+ /*
+ * Max additional time to wait in idle, beyond timer_rate, at speeds
+ * above minimum before wakeup to reduce speed, or -1 if unnecessary.
+ */
+#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE)
+ int timer_slack_val;
+ bool io_is_busy;
+};
+
+/* For cases where we have single governor instance for system */
+struct cpufreq_interactive_tunables *common_tunables;
+
+static struct attribute_group *get_sysfs_attr(void);
+
+static void cpufreq_interactive_timer_resched(
+ struct cpufreq_interactive_cpuinfo *pcpu)
+{
+ struct cpufreq_interactive_tunables *tunables =
+ pcpu->policy->governor_data;
+ unsigned long expires;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcpu->load_lock, flags);
+ pcpu->time_in_idle =
+ get_cpu_idle_time(smp_processor_id(),
+ &pcpu->time_in_idle_timestamp,
+ tunables->io_is_busy);
+ pcpu->cputime_speedadj = 0;
+ pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
+ expires = jiffies + usecs_to_jiffies(tunables->timer_rate);
+ mod_timer_pinned(&pcpu->cpu_timer, expires);
+
+ if (tunables->timer_slack_val >= 0 &&
+ pcpu->target_freq > pcpu->policy->min) {
+ expires += usecs_to_jiffies(tunables->timer_slack_val);
+ mod_timer_pinned(&pcpu->cpu_slack_timer, expires);
+ }
+
+ spin_unlock_irqrestore(&pcpu->load_lock, flags);
+}
+
+/* The caller shall take enable_sem write semaphore to avoid any timer race.
+ * The cpu_timer and cpu_slack_timer must be deactivated when calling this
+ * function.
+ */
+static void cpufreq_interactive_timer_start(
+ struct cpufreq_interactive_tunables *tunables, int cpu)
+{
+ struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
+ unsigned long expires = jiffies +
+ usecs_to_jiffies(tunables->timer_rate);
+ unsigned long flags;
+
+ pcpu->cpu_timer.expires = expires;
+ add_timer_on(&pcpu->cpu_timer, cpu);
+ if (tunables->timer_slack_val >= 0 &&
+ pcpu->target_freq > pcpu->policy->min) {
+ expires += usecs_to_jiffies(tunables->timer_slack_val);
+ pcpu->cpu_slack_timer.expires = expires;
+ add_timer_on(&pcpu->cpu_slack_timer, cpu);
+ }
+
+ spin_lock_irqsave(&pcpu->load_lock, flags);
+ pcpu->time_in_idle =
+ get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp,
+ tunables->io_is_busy);
+ pcpu->cputime_speedadj = 0;
+ pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
+ spin_unlock_irqrestore(&pcpu->load_lock, flags);
+}
+
+static unsigned int freq_to_above_hispeed_delay(
+ struct cpufreq_interactive_tunables *tunables,
+ unsigned int freq)
+{
+ int i;
+ unsigned int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags);
+
+ for (i = 0; i < tunables->nabove_hispeed_delay - 1 &&
+ freq >= tunables->above_hispeed_delay[i+1]; i += 2)
+ ;
+
+ ret = tunables->above_hispeed_delay[i];
+ spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags);
+ return ret;
+}
+
+static unsigned int freq_to_targetload(
+ struct cpufreq_interactive_tunables *tunables, unsigned int freq)
+{
+ int i;
+ unsigned int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tunables->target_loads_lock, flags);
+
+ for (i = 0; i < tunables->ntarget_loads - 1 &&
+ freq >= tunables->target_loads[i+1]; i += 2)
+ ;
+
+ ret = tunables->target_loads[i];
+ spin_unlock_irqrestore(&tunables->target_loads_lock, flags);
+ return ret;
+}
+
+/*
+ * If increasing frequencies never map to a lower target load then
+ * choose_freq() will find the minimum frequency that does not exceed its
+ * target load given the current load.
+ */
+static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu,
+ unsigned int loadadjfreq)
+{
+ unsigned int freq = pcpu->policy->cur;
+ unsigned int prevfreq, freqmin, freqmax;
+ unsigned int tl;
+ int index;
+
+ freqmin = 0;
+ freqmax = UINT_MAX;
+
+ do {
+ prevfreq = freq;
+ tl = freq_to_targetload(pcpu->policy->governor_data, freq);
+
+ /*
+ * Find the lowest frequency where the computed load is less
+ * than or equal to the target load.
+ */
+
+ if (cpufreq_frequency_table_target(
+ pcpu->policy, pcpu->freq_table, loadadjfreq / tl,
+ CPUFREQ_RELATION_L, &index))
+ break;
+ freq = pcpu->freq_table[index].frequency;
+
+ if (freq > prevfreq) {
+ /* The previous frequency is too low. */
+ freqmin = prevfreq;
+
+ if (freq >= freqmax) {
+ /*
+ * Find the highest frequency that is less
+ * than freqmax.
+ */
+ if (cpufreq_frequency_table_target(
+ pcpu->policy, pcpu->freq_table,
+ freqmax - 1, CPUFREQ_RELATION_H,
+ &index))
+ break;
+ freq = pcpu->freq_table[index].frequency;
+
+ if (freq == freqmin) {
+ /*
+ * The first frequency below freqmax
+ * has already been found to be too
+ * low. freqmax is the lowest speed
+ * we found that is fast enough.
+ */
+ freq = freqmax;
+ break;
+ }
+ }
+ } else if (freq < prevfreq) {
+ /* The previous frequency is high enough. */
+ freqmax = prevfreq;
+
+ if (freq <= freqmin) {
+ /*
+ * Find the lowest frequency that is higher
+ * than freqmin.
+ */
+ if (cpufreq_frequency_table_target(
+ pcpu->policy, pcpu->freq_table,
+ freqmin + 1, CPUFREQ_RELATION_L,
+ &index))
+ break;
+ freq = pcpu->freq_table[index].frequency;
+
+ /*
+ * If freqmax is the first frequency above
+ * freqmin then we have already found that
+ * this speed is fast enough.
+ */
+ if (freq == freqmax)
+ break;
+ }
+ }
+
+ /* If same frequency chosen as previous then done. */
+ } while (freq != prevfreq);
+
+ return freq;
+}
+
+static u64 update_load(int cpu)
+{
+ struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
+ struct cpufreq_interactive_tunables *tunables =
+ pcpu->policy->governor_data;
+ u64 now;
+ u64 now_idle;
+ unsigned int delta_idle;
+ unsigned int delta_time;
+ u64 active_time;
+
+ now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy);
+ delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle);
+ delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp);
+
+ if (delta_time <= delta_idle)
+ active_time = 0;
+ else
+ active_time = delta_time - delta_idle;
+
+ pcpu->cputime_speedadj += active_time * pcpu->policy->cur;
+
+ pcpu->time_in_idle = now_idle;
+ pcpu->time_in_idle_timestamp = now;
+ return now;
+}
+
+static void cpufreq_interactive_timer(unsigned long data)
+{
+ u64 now;
+ unsigned int delta_time;
+ u64 cputime_speedadj;
+ int cpu_load;
+ struct cpufreq_interactive_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, data);
+ struct cpufreq_interactive_tunables *tunables =
+ pcpu->policy->governor_data;
+ unsigned int new_freq;
+ unsigned int loadadjfreq;
+ unsigned int index;
+ unsigned long flags;
+ bool boosted;
+
+ if (!down_read_trylock(&pcpu->enable_sem))
+ return;
+ if (!pcpu->governor_enabled)
+ goto exit;
+
+ spin_lock_irqsave(&pcpu->load_lock, flags);
+ now = update_load(data);
+ delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp);
+ cputime_speedadj = pcpu->cputime_speedadj;
+ spin_unlock_irqrestore(&pcpu->load_lock, flags);
+
+ if (WARN_ON_ONCE(!delta_time))
+ goto rearm;
+
+ do_div(cputime_speedadj, delta_time);
+ loadadjfreq = (unsigned int)cputime_speedadj * 100;
+ cpu_load = loadadjfreq / pcpu->target_freq;
+ boosted = tunables->boost_val || now < tunables->boostpulse_endtime;
+
+ if (cpu_load >= tunables->go_hispeed_load || boosted) {
+ if (pcpu->target_freq < tunables->hispeed_freq) {
+ new_freq = tunables->hispeed_freq;
+ } else {
+ new_freq = choose_freq(pcpu, loadadjfreq);
+
+ if (new_freq < tunables->hispeed_freq)
+ new_freq = tunables->hispeed_freq;
+ }
+ } else {
+ new_freq = choose_freq(pcpu, loadadjfreq);
+ }
+
+ if (pcpu->target_freq >= tunables->hispeed_freq &&
+ new_freq > pcpu->target_freq &&
+ now - pcpu->hispeed_validate_time <
+ freq_to_above_hispeed_delay(tunables, pcpu->target_freq)) {
+ trace_cpufreq_interactive_notyet(
+ data, cpu_load, pcpu->target_freq,
+ pcpu->policy->cur, new_freq);
+ goto rearm;
+ }
+
+ pcpu->hispeed_validate_time = now;
+
+ if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,
+ new_freq, CPUFREQ_RELATION_L,
+ &index))
+ goto rearm;
+
+ new_freq = pcpu->freq_table[index].frequency;
+
+ /*
+ * Do not scale below floor_freq unless we have been at or above the
+ * floor frequency for the minimum sample time since last validated.
+ */
+ if (new_freq < pcpu->floor_freq) {
+ if (now - pcpu->floor_validate_time <
+ tunables->min_sample_time) {
+ trace_cpufreq_interactive_notyet(
+ data, cpu_load, pcpu->target_freq,
+ pcpu->policy->cur, new_freq);
+ goto rearm;
+ }
+ }
+
+ /*
+ * Update the timestamp for checking whether speed has been held at
+ * or above the selected frequency for a minimum of min_sample_time,
+ * if not boosted to hispeed_freq. If boosted to hispeed_freq then we
+ * allow the speed to drop as soon as the boostpulse duration expires
+ * (or the indefinite boost is turned off).
+ */
+
+ if (!boosted || new_freq > tunables->hispeed_freq) {
+ pcpu->floor_freq = new_freq;
+ pcpu->floor_validate_time = now;
+ }
+
+ if (pcpu->target_freq == new_freq) {
+ trace_cpufreq_interactive_already(
+ data, cpu_load, pcpu->target_freq,
+ pcpu->policy->cur, new_freq);
+ goto rearm_if_notmax;
+ }
+
+ trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq,
+ pcpu->policy->cur, new_freq);
+
+ pcpu->target_freq = new_freq;
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
+ cpumask_set_cpu(data, &speedchange_cpumask);
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
+ wake_up_process(speedchange_task);
+
+rearm_if_notmax:
+ /*
+ * Already set max speed and don't see a need to change that,
+ * wait until next idle to re-evaluate, don't need timer.
+ */
+ if (pcpu->target_freq == pcpu->policy->max)
+ goto exit;
+
+rearm:
+ if (!timer_pending(&pcpu->cpu_timer))
+ cpufreq_interactive_timer_resched(pcpu);
+
+exit:
+ up_read(&pcpu->enable_sem);
+ return;
+}
+
+static void cpufreq_interactive_idle_start(void)
+{
+ struct cpufreq_interactive_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, smp_processor_id());
+ int pending;
+
+ if (!down_read_trylock(&pcpu->enable_sem))
+ return;
+ if (!pcpu->governor_enabled) {
+ up_read(&pcpu->enable_sem);
+ return;
+ }
+
+ pending = timer_pending(&pcpu->cpu_timer);
+
+ if (pcpu->target_freq != pcpu->policy->min) {
+ /*
+ * Entering idle while not at lowest speed. On some
+ * platforms this can hold the other CPU(s) at that speed
+ * even though the CPU is idle. Set a timer to re-evaluate
+ * speed so this idle CPU doesn't hold the other CPUs above
+ * min indefinitely. This should probably be a quirk of
+ * the CPUFreq driver.
+ */
+ if (!pending)
+ cpufreq_interactive_timer_resched(pcpu);
+ }
+
+ up_read(&pcpu->enable_sem);
+}
+
+static void cpufreq_interactive_idle_end(void)
+{
+ struct cpufreq_interactive_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, smp_processor_id());
+
+ if (!down_read_trylock(&pcpu->enable_sem))
+ return;
+ if (!pcpu->governor_enabled) {
+ up_read(&pcpu->enable_sem);
+ return;
+ }
+
+ /* Arm the timer for 1-2 ticks later if not already. */
+ if (!timer_pending(&pcpu->cpu_timer)) {
+ cpufreq_interactive_timer_resched(pcpu);
+ } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) {
+ del_timer(&pcpu->cpu_timer);
+ del_timer(&pcpu->cpu_slack_timer);
+ cpufreq_interactive_timer(smp_processor_id());
+ }
+
+ up_read(&pcpu->enable_sem);
+}
+
+static int cpufreq_interactive_speedchange_task(void *data)
+{
+ unsigned int cpu;
+ cpumask_t tmp_mask;
+ unsigned long flags;
+ struct cpufreq_interactive_cpuinfo *pcpu;
+
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
+
+ if (cpumask_empty(&speedchange_cpumask)) {
+ spin_unlock_irqrestore(&speedchange_cpumask_lock,
+ flags);
+ schedule();
+
+ if (kthread_should_stop())
+ break;
+
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
+ }
+
+ set_current_state(TASK_RUNNING);
+ tmp_mask = speedchange_cpumask;
+ cpumask_clear(&speedchange_cpumask);
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
+
+ for_each_cpu(cpu, &tmp_mask) {
+ unsigned int j;
+ unsigned int max_freq = 0;
+
+ pcpu = &per_cpu(cpuinfo, cpu);
+ if (!down_read_trylock(&pcpu->enable_sem))
+ continue;
+ if (!pcpu->governor_enabled) {
+ up_read(&pcpu->enable_sem);
+ continue;
+ }
+
+ for_each_cpu(j, pcpu->policy->cpus) {
+ struct cpufreq_interactive_cpuinfo *pjcpu =
+ &per_cpu(cpuinfo, j);
+
+ if (pjcpu->target_freq > max_freq)
+ max_freq = pjcpu->target_freq;
+ }
+
+ if (max_freq != pcpu->policy->cur)
+ __cpufreq_driver_target(pcpu->policy,
+ max_freq,
+ CPUFREQ_RELATION_H);
+ trace_cpufreq_interactive_setspeed(cpu,
+ pcpu->target_freq,
+ pcpu->policy->cur);
+
+ up_read(&pcpu->enable_sem);
+ }
+ }
+
+ return 0;
+}
+
+static void cpufreq_interactive_boost(void)
+{
+ int i;
+ int anyboost = 0;
+ unsigned long flags;
+ struct cpufreq_interactive_cpuinfo *pcpu;
+ struct cpufreq_interactive_tunables *tunables;
+
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
+
+ for_each_online_cpu(i) {
+ pcpu = &per_cpu(cpuinfo, i);
+ tunables = pcpu->policy->governor_data;
+
+ if (pcpu->target_freq < tunables->hispeed_freq) {
+ pcpu->target_freq = tunables->hispeed_freq;
+ cpumask_set_cpu(i, &speedchange_cpumask);
+ pcpu->hispeed_validate_time =
+ ktime_to_us(ktime_get());
+ anyboost = 1;
+ }
+
+ /*
+ * Set floor freq and (re)start timer for when last
+ * validated.
+ */
+
+ pcpu->floor_freq = tunables->hispeed_freq;
+ pcpu->floor_validate_time = ktime_to_us(ktime_get());
+ }
+
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
+
+ if (anyboost)
+ wake_up_process(speedchange_task);
+}
+
+static int cpufreq_interactive_notifier(
+ struct notifier_block *nb, unsigned long val, void *data)
+{
+ struct cpufreq_freqs *freq = data;
+ struct cpufreq_interactive_cpuinfo *pcpu;
+ int cpu;
+ unsigned long flags;
+
+ if (val == CPUFREQ_POSTCHANGE) {
+ pcpu = &per_cpu(cpuinfo, freq->cpu);
+ if (!down_read_trylock(&pcpu->enable_sem))
+ return 0;
+ if (!pcpu->governor_enabled) {
+ up_read(&pcpu->enable_sem);
+ return 0;
+ }
+
+ for_each_cpu(cpu, pcpu->policy->cpus) {
+ struct cpufreq_interactive_cpuinfo *pjcpu =
+ &per_cpu(cpuinfo, cpu);
+ if (cpu != freq->cpu) {
+ if (!down_read_trylock(&pjcpu->enable_sem))
+ continue;
+ if (!pjcpu->governor_enabled) {
+ up_read(&pjcpu->enable_sem);
+ continue;
+ }
+ }
+ spin_lock_irqsave(&pjcpu->load_lock, flags);
+ update_load(cpu);
+ spin_unlock_irqrestore(&pjcpu->load_lock, flags);
+ if (cpu != freq->cpu)
+ up_read(&pjcpu->enable_sem);
+ }
+
+ up_read(&pcpu->enable_sem);
+ }
+ return 0;
+}
+
+static struct notifier_block cpufreq_notifier_block = {
+ .notifier_call = cpufreq_interactive_notifier,
+};
+
+static unsigned int *get_tokenized_data(const char *buf, int *num_tokens)
+{
+ const char *cp;
+ int i;
+ int ntokens = 1;
+ unsigned int *tokenized_data;
+ int err = -EINVAL;
+
+ cp = buf;
+ while ((cp = strpbrk(cp + 1, " :")))
+ ntokens++;
+
+ if (!(ntokens & 0x1))
+ goto err;
+
+ tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL);
+ if (!tokenized_data) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ cp = buf;
+ i = 0;
+ while (i < ntokens) {
+ if (sscanf(cp, "%u", &tokenized_data[i++]) != 1)
+ goto err_kfree;
+
+ cp = strpbrk(cp, " :");
+ if (!cp)
+ break;
+ cp++;
+ }
+
+ if (i != ntokens)
+ goto err_kfree;
+
+ *num_tokens = ntokens;
+ return tokenized_data;
+
+err_kfree:
+ kfree(tokenized_data);
+err:
+ return ERR_PTR(err);
+}
+
+static ssize_t show_target_loads(
+ struct cpufreq_interactive_tunables *tunables,
+ char *buf)
+{
+ int i;
+ ssize_t ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tunables->target_loads_lock, flags);
+
+ for (i = 0; i < tunables->ntarget_loads; i++)
+ ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i],
+ i & 0x1 ? ":" : " ");
+
+ sprintf(buf + ret - 1, "\n");
+ spin_unlock_irqrestore(&tunables->target_loads_lock, flags);
+ return ret;
+}
+
+static ssize_t store_target_loads(
+ struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
+{
+ int ntokens;
+ unsigned int *new_target_loads = NULL;
+ unsigned long flags;
+
+ new_target_loads = get_tokenized_data(buf, &ntokens);
+ if (IS_ERR(new_target_loads))
+ return PTR_RET(new_target_loads);
+
+ spin_lock_irqsave(&tunables->target_loads_lock, flags);
+ if (tunables->target_loads != default_target_loads)
+ kfree(tunables->target_loads);
+ tunables->target_loads = new_target_loads;
+ tunables->ntarget_loads = ntokens;
+ spin_unlock_irqrestore(&tunables->target_loads_lock, flags);
+ return count;
+}
+
+static ssize_t show_above_hispeed_delay(
+ struct cpufreq_interactive_tunables *tunables, char *buf)
+{
+ int i;
+ ssize_t ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags);
+
+ for (i = 0; i < tunables->nabove_hispeed_delay; i++)
+ ret += sprintf(buf + ret, "%u%s",
+ tunables->above_hispeed_delay[i],
+ i & 0x1 ? ":" : " ");
+
+ sprintf(buf + ret - 1, "\n");
+ spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags);
+ return ret;
+}
+
+static ssize_t store_above_hispeed_delay(
+ struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
+{
+ int ntokens;
+ unsigned int *new_above_hispeed_delay = NULL;
+ unsigned long flags;
+
+ new_above_hispeed_delay = get_tokenized_data(buf, &ntokens);
+ if (IS_ERR(new_above_hispeed_delay))
+ return PTR_RET(new_above_hispeed_delay);
+
+ spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags);
+ if (tunables->above_hispeed_delay != default_above_hispeed_delay)
+ kfree(tunables->above_hispeed_delay);
+ tunables->above_hispeed_delay = new_above_hispeed_delay;
+ tunables->nabove_hispeed_delay = ntokens;
+ spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags);
+ return count;
+
+}
+
+static ssize_t show_hispeed_freq(struct cpufreq_interactive_tunables *tunables,
+ char *buf)
+{
+ return sprintf(buf, "%u\n", tunables->hispeed_freq);
+}
+
+static ssize_t store_hispeed_freq(struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
+{
+ int ret;
+ long unsigned int val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ tunables->hispeed_freq = val;
+ return count;
+}
+
+static ssize_t show_go_hispeed_load(struct cpufreq_interactive_tunables
+ *tunables, char *buf)
+{
+ return sprintf(buf, "%lu\n", tunables->go_hispeed_load);
+}
+
+static ssize_t store_go_hispeed_load(struct cpufreq_interactive_tunables
+ *tunables, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ tunables->go_hispeed_load = val;
+ return count;
+}
+
+static ssize_t show_min_sample_time(struct cpufreq_interactive_tunables
+ *tunables, char *buf)
+{
+ return sprintf(buf, "%lu\n", tunables->min_sample_time);
+}
+
+static ssize_t store_min_sample_time(struct cpufreq_interactive_tunables
+ *tunables, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ tunables->min_sample_time = val;
+ return count;
+}
+
+static ssize_t show_timer_rate(struct cpufreq_interactive_tunables *tunables,
+ char *buf)
+{
+ return sprintf(buf, "%lu\n", tunables->timer_rate);
+}
+
+static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ tunables->timer_rate = val;
+ return count;
+}
+
+static ssize_t show_timer_slack(struct cpufreq_interactive_tunables *tunables,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", tunables->timer_slack_val);
+}
+
+static ssize_t store_timer_slack(struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ tunables->timer_slack_val = val;
+ return count;
+}
+
+static ssize_t show_boost(struct cpufreq_interactive_tunables *tunables,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", tunables->boost_val);
+}
+
+static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ tunables->boost_val = val;
+
+ if (tunables->boost_val) {
+ trace_cpufreq_interactive_boost("on");
+ cpufreq_interactive_boost();
+ } else {
+ trace_cpufreq_interactive_unboost("off");
+ }
+
+ return count;
+}
+
+static ssize_t store_boostpulse(struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ tunables->boostpulse_endtime = ktime_to_us(ktime_get()) +
+ tunables->boostpulse_duration_val;
+ trace_cpufreq_interactive_boost("pulse");
+ cpufreq_interactive_boost();
+ return count;
+}
+
+static ssize_t show_boostpulse_duration(struct cpufreq_interactive_tunables
+ *tunables, char *buf)
+{
+ return sprintf(buf, "%d\n", tunables->boostpulse_duration_val);
+}
+
+static ssize_t store_boostpulse_duration(struct cpufreq_interactive_tunables
+ *tunables, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ tunables->boostpulse_duration_val = val;
+ return count;
+}
+
+static ssize_t show_io_is_busy(struct cpufreq_interactive_tunables *tunables,
+ char *buf)
+{
+ return sprintf(buf, "%u\n", tunables->io_is_busy);
+}
+
+static ssize_t store_io_is_busy(struct cpufreq_interactive_tunables *tunables,
+ const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ tunables->io_is_busy = val;
+ return count;
+}
+
+/*
+ * Create show/store routines
+ * - sys: One governor instance for complete SYSTEM
+ * - pol: One governor instance per struct cpufreq_policy
+ */
+#define show_gov_pol_sys(file_name) \
+static ssize_t show_##file_name##_gov_sys \
+(struct kobject *kobj, struct attribute *attr, char *buf) \
+{ \
+ return show_##file_name(common_tunables, buf); \
+} \
+ \
+static ssize_t show_##file_name##_gov_pol \
+(struct cpufreq_policy *policy, char *buf) \
+{ \
+ return show_##file_name(policy->governor_data, buf); \
+}
+
+#define store_gov_pol_sys(file_name) \
+static ssize_t store_##file_name##_gov_sys \
+(struct kobject *kobj, struct attribute *attr, const char *buf, \
+ size_t count) \
+{ \
+ return store_##file_name(common_tunables, buf, count); \
+} \
+ \
+static ssize_t store_##file_name##_gov_pol \
+(struct cpufreq_policy *policy, const char *buf, size_t count) \
+{ \
+ return store_##file_name(policy->governor_data, buf, count); \
+}
+
+#define show_store_gov_pol_sys(file_name) \
+show_gov_pol_sys(file_name); \
+store_gov_pol_sys(file_name)
+
+show_store_gov_pol_sys(target_loads);
+show_store_gov_pol_sys(above_hispeed_delay);
+show_store_gov_pol_sys(hispeed_freq);
+show_store_gov_pol_sys(go_hispeed_load);
+show_store_gov_pol_sys(min_sample_time);
+show_store_gov_pol_sys(timer_rate);
+show_store_gov_pol_sys(timer_slack);
+show_store_gov_pol_sys(boost);
+store_gov_pol_sys(boostpulse);
+show_store_gov_pol_sys(boostpulse_duration);
+show_store_gov_pol_sys(io_is_busy);
+
+#define gov_sys_attr_rw(_name) \
+static struct global_attr _name##_gov_sys = \
+__ATTR(_name, 0644, show_##_name##_gov_sys, store_##_name##_gov_sys)
+
+#define gov_pol_attr_rw(_name) \
+static struct freq_attr _name##_gov_pol = \
+__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol)
+
+#define gov_sys_pol_attr_rw(_name) \
+ gov_sys_attr_rw(_name); \
+ gov_pol_attr_rw(_name)
+
+gov_sys_pol_attr_rw(target_loads);
+gov_sys_pol_attr_rw(above_hispeed_delay);
+gov_sys_pol_attr_rw(hispeed_freq);
+gov_sys_pol_attr_rw(go_hispeed_load);
+gov_sys_pol_attr_rw(min_sample_time);
+gov_sys_pol_attr_rw(timer_rate);
+gov_sys_pol_attr_rw(timer_slack);
+gov_sys_pol_attr_rw(boost);
+gov_sys_pol_attr_rw(boostpulse_duration);
+gov_sys_pol_attr_rw(io_is_busy);
+
+static struct global_attr boostpulse_gov_sys =
+ __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_sys);
+
+static struct freq_attr boostpulse_gov_pol =
+ __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_pol);
+
+/* One Governor instance for entire system */
+static struct attribute *interactive_attributes_gov_sys[] = {
+ &target_loads_gov_sys.attr,
+ &above_hispeed_delay_gov_sys.attr,
+ &hispeed_freq_gov_sys.attr,
+ &go_hispeed_load_gov_sys.attr,
+ &min_sample_time_gov_sys.attr,
+ &timer_rate_gov_sys.attr,
+ &timer_slack_gov_sys.attr,
+ &boost_gov_sys.attr,
+ &boostpulse_gov_sys.attr,
+ &boostpulse_duration_gov_sys.attr,
+ &io_is_busy_gov_sys.attr,
+ NULL,
+};
+
+static struct attribute_group interactive_attr_group_gov_sys = {
+ .attrs = interactive_attributes_gov_sys,
+ .name = "interactive",
+};
+
+/* Per policy governor instance */
+static struct attribute *interactive_attributes_gov_pol[] = {
+ &target_loads_gov_pol.attr,
+ &above_hispeed_delay_gov_pol.attr,
+ &hispeed_freq_gov_pol.attr,
+ &go_hispeed_load_gov_pol.attr,
+ &min_sample_time_gov_pol.attr,
+ &timer_rate_gov_pol.attr,
+ &timer_slack_gov_pol.attr,
+ &boost_gov_pol.attr,
+ &boostpulse_gov_pol.attr,
+ &boostpulse_duration_gov_pol.attr,
+ &io_is_busy_gov_pol.attr,
+ NULL,
+};
+
+static struct attribute_group interactive_attr_group_gov_pol = {
+ .attrs = interactive_attributes_gov_pol,
+ .name = "interactive",
+};
+
+static struct attribute_group *get_sysfs_attr(void)
+{
+ if (have_governor_per_policy())
+ return &interactive_attr_group_gov_pol;
+ else
+ return &interactive_attr_group_gov_sys;
+}
+
+static int cpufreq_interactive_idle_notifier(struct notifier_block *nb,
+ unsigned long val,
+ void *data)
+{
+ switch (val) {
+ case IDLE_START:
+ cpufreq_interactive_idle_start();
+ break;
+ case IDLE_END:
+ cpufreq_interactive_idle_end();
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block cpufreq_interactive_idle_nb = {
+ .notifier_call = cpufreq_interactive_idle_notifier,
+};
+
+static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
+ unsigned int event)
+{
+ int rc;
+ unsigned int j;
+ struct cpufreq_interactive_cpuinfo *pcpu;
+ struct cpufreq_frequency_table *freq_table;
+ struct cpufreq_interactive_tunables *tunables;
+
+ if (have_governor_per_policy())
+ tunables = policy->governor_data;
+ else
+ tunables = common_tunables;
+
+ WARN_ON(!tunables && (event != CPUFREQ_GOV_POLICY_INIT));
+
+ switch (event) {
+ case CPUFREQ_GOV_POLICY_INIT:
+ if (have_governor_per_policy()) {
+ WARN_ON(tunables);
+ } else if (tunables) {
+ tunables->usage_count++;
+ policy->governor_data = tunables;
+ return 0;
+ }
+
+ tunables = kzalloc(sizeof(*tunables), GFP_KERNEL);
+ if (!tunables) {
+ pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ tunables->usage_count = 1;
+ tunables->above_hispeed_delay = default_above_hispeed_delay;
+ tunables->nabove_hispeed_delay =
+ ARRAY_SIZE(default_above_hispeed_delay);
+ tunables->go_hispeed_load = DEFAULT_GO_HISPEED_LOAD;
+ tunables->target_loads = default_target_loads;
+ tunables->ntarget_loads = ARRAY_SIZE(default_target_loads);
+ tunables->min_sample_time = DEFAULT_MIN_SAMPLE_TIME;
+ tunables->timer_rate = DEFAULT_TIMER_RATE;
+ tunables->boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME;
+ tunables->timer_slack_val = DEFAULT_TIMER_SLACK;
+
+ spin_lock_init(&tunables->target_loads_lock);
+ spin_lock_init(&tunables->above_hispeed_delay_lock);
+
+ policy->governor_data = tunables;
+ if (!have_governor_per_policy())
+ common_tunables = tunables;
+
+ rc = sysfs_create_group(get_governor_parent_kobj(policy),
+ get_sysfs_attr());
+ if (rc) {
+ kfree(tunables);
+ policy->governor_data = NULL;
+ if (!have_governor_per_policy())
+ common_tunables = NULL;
+ return rc;
+ }
+
+ if (!policy->governor->initialized) {
+ idle_notifier_register(&cpufreq_interactive_idle_nb);
+ cpufreq_register_notifier(&cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ }
+
+ break;
+
+ case CPUFREQ_GOV_POLICY_EXIT:
+ if (!--tunables->usage_count) {
+ if (policy->governor->initialized == 1) {
+ cpufreq_unregister_notifier(&cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ idle_notifier_unregister(&cpufreq_interactive_idle_nb);
+ }
+
+ sysfs_remove_group(get_governor_parent_kobj(policy),
+ get_sysfs_attr());
+ kfree(tunables);
+ common_tunables = NULL;
+ }
+
+ policy->governor_data = NULL;
+ break;
+
+ case CPUFREQ_GOV_START:
+ mutex_lock(&gov_lock);
+
+ freq_table = cpufreq_frequency_get_table(policy->cpu);
+ if (!tunables->hispeed_freq)
+ tunables->hispeed_freq = policy->max;
+
+ for_each_cpu(j, policy->cpus) {
+ pcpu = &per_cpu(cpuinfo, j);
+ pcpu->policy = policy;
+ pcpu->target_freq = policy->cur;
+ pcpu->freq_table = freq_table;
+ pcpu->floor_freq = pcpu->target_freq;
+ pcpu->floor_validate_time =
+ ktime_to_us(ktime_get());
+ pcpu->hispeed_validate_time =
+ pcpu->floor_validate_time;
+ down_write(&pcpu->enable_sem);
+ del_timer_sync(&pcpu->cpu_timer);
+ del_timer_sync(&pcpu->cpu_slack_timer);
+ cpufreq_interactive_timer_start(tunables, j);
+ pcpu->governor_enabled = 1;
+ up_write(&pcpu->enable_sem);
+ }
+
+ mutex_unlock(&gov_lock);
+ break;
+
+ case CPUFREQ_GOV_STOP:
+ mutex_lock(&gov_lock);
+ for_each_cpu(j, policy->cpus) {
+ pcpu = &per_cpu(cpuinfo, j);
+ down_write(&pcpu->enable_sem);
+ pcpu->governor_enabled = 0;
+ del_timer_sync(&pcpu->cpu_timer);
+ del_timer_sync(&pcpu->cpu_slack_timer);
+ up_write(&pcpu->enable_sem);
+ }
+
+ mutex_unlock(&gov_lock);
+ break;
+
+ case CPUFREQ_GOV_LIMITS:
+ if (policy->max < policy->cur)
+ __cpufreq_driver_target(policy,
+ policy->max, CPUFREQ_RELATION_H);
+ else if (policy->min > policy->cur)
+ __cpufreq_driver_target(policy,
+ policy->min, CPUFREQ_RELATION_L);
+ for_each_cpu(j, policy->cpus) {
+ pcpu = &per_cpu(cpuinfo, j);
+
+ /* hold write semaphore to avoid race */
+ down_write(&pcpu->enable_sem);
+ if (pcpu->governor_enabled == 0) {
+ up_write(&pcpu->enable_sem);
+ continue;
+ }
+
+ /* update target_freq firstly */
+ if (policy->max < pcpu->target_freq)
+ pcpu->target_freq = policy->max;
+ else if (policy->min > pcpu->target_freq)
+ pcpu->target_freq = policy->min;
+
+ /* Reschedule timer.
+ * Delete the timers, else the timer callback may
+ * return without re-arm the timer when failed
+ * acquire the semaphore. This race may cause timer
+ * stopped unexpectedly.
+ */
+ del_timer_sync(&pcpu->cpu_timer);
+ del_timer_sync(&pcpu->cpu_slack_timer);
+ cpufreq_interactive_timer_start(tunables, j);
+ up_write(&pcpu->enable_sem);
+ }
+ break;
+ }
+ return 0;
+}
+
+#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE
+static
+#endif
+struct cpufreq_governor cpufreq_gov_interactive = {
+ .name = "interactive",
+ .governor = cpufreq_governor_interactive,
+ .max_transition_latency = 10000000,
+ .owner = THIS_MODULE,
+};
+
+static void cpufreq_interactive_nop_timer(unsigned long data)
+{
+}
+
+static int __init cpufreq_interactive_init(void)
+{
+ unsigned int i;
+ struct cpufreq_interactive_cpuinfo *pcpu;
+ struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
+
+ /* Initalize per-cpu timers */
+ for_each_possible_cpu(i) {
+ pcpu = &per_cpu(cpuinfo, i);
+ init_timer_deferrable(&pcpu->cpu_timer);
+ pcpu->cpu_timer.function = cpufreq_interactive_timer;
+ pcpu->cpu_timer.data = i;
+ init_timer(&pcpu->cpu_slack_timer);
+ pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer;
+ spin_lock_init(&pcpu->load_lock);
+ init_rwsem(&pcpu->enable_sem);
+ }
+
+ spin_lock_init(&speedchange_cpumask_lock);
+ mutex_init(&gov_lock);
+ speedchange_task =
+ kthread_create(cpufreq_interactive_speedchange_task, NULL,
+ "cfinteractive");
+ if (IS_ERR(speedchange_task))
+ return PTR_ERR(speedchange_task);
+
+ sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, &param);
+ get_task_struct(speedchange_task);
+
+ /* NB: wake up so the thread does not look hung to the freezer */
+ wake_up_process(speedchange_task);
+
+ return cpufreq_register_governor(&cpufreq_gov_interactive);
+}
+
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE
+fs_initcall(cpufreq_interactive_init);
+#else
+module_init(cpufreq_interactive_init);
+#endif
+
+static void __exit cpufreq_interactive_exit(void)
+{
+ cpufreq_unregister_governor(&cpufreq_gov_interactive);
+ kthread_stop(speedchange_task);
+ put_task_struct(speedchange_task);
+}
+
+module_exit(cpufreq_interactive_exit);
+
+MODULE_AUTHOR("Mike Chan <mike@android.com>");
+MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for "
+ "Latency sensitive workloads");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c
index 61a54310a1b9..46d8ef364c4d 100644
--- a/drivers/cpufreq/exynos4210-cpufreq.c
+++ b/drivers/cpufreq/exynos4210-cpufreq.c
@@ -63,20 +63,20 @@ static void exynos4210_set_clkdiv(unsigned int div_index)
tmp = apll_freq_4210[div_index].clk_div_cpu0;
- __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU);
+ writel_relaxed(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU);
do {
- tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU);
+ tmp = readl_relaxed(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU);
} while (tmp & 0x1111111);
/* Change Divider - CPU1 */
tmp = apll_freq_4210[div_index].clk_div_cpu1;
- __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU1);
+ writel_relaxed(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU1);
do {
- tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU1);
+ tmp = readl_relaxed(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU1);
} while (tmp & 0x11);
}
@@ -88,7 +88,7 @@ static void exynos4210_set_apll(unsigned int index)
clk_set_parent(moutcore, mout_mpll);
do {
- tmp = (__raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU)
+ tmp = (readl_relaxed(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU)
>> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
tmp &= 0x7;
} while (tmp != 0x2);
@@ -99,7 +99,7 @@ static void exynos4210_set_apll(unsigned int index)
clk_set_parent(moutcore, mout_apll);
do {
- tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU);
+ tmp = readl_relaxed(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU);
tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
} while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
}
diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c
index 351a2074cfea..09949c789fef 100644
--- a/drivers/cpufreq/exynos4x12-cpufreq.c
+++ b/drivers/cpufreq/exynos4x12-cpufreq.c
@@ -108,20 +108,20 @@ static void exynos4x12_set_clkdiv(unsigned int div_index)
tmp = apll_freq_4x12[div_index].clk_div_cpu0;
- __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU);
+ writel_relaxed(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU);
- while (__raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU)
+ while (readl_relaxed(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU)
& 0x11111111)
cpu_relax();
/* Change Divider - CPU1 */
tmp = apll_freq_4x12[div_index].clk_div_cpu1;
- __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU1);
+ writel_relaxed(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU1);
do {
cpu_relax();
- tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU1);
+ tmp = readl_relaxed(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU1);
} while (tmp != 0x0);
}
@@ -134,7 +134,7 @@ static void exynos4x12_set_apll(unsigned int index)
do {
cpu_relax();
- tmp = (__raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU)
+ tmp = (readl_relaxed(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU)
>> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
tmp &= 0x7;
} while (tmp != 0x2);
@@ -146,7 +146,7 @@ static void exynos4x12_set_apll(unsigned int index)
do {
cpu_relax();
- tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU);
+ tmp = readl_relaxed(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU);
tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
} while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
}
diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c
index c91ce69dc631..73695d8be221 100644
--- a/drivers/cpufreq/exynos5250-cpufreq.c
+++ b/drivers/cpufreq/exynos5250-cpufreq.c
@@ -88,18 +88,18 @@ static void set_clkdiv(unsigned int div_index)
tmp = apll_freq_5250[div_index].clk_div_cpu0;
- __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS5_CLKDIV_CPU0);
+ writel_relaxed(tmp, cpufreq->cmu_regs + EXYNOS5_CLKDIV_CPU0);
- while (__raw_readl(cpufreq->cmu_regs + EXYNOS5_CLKDIV_STATCPU0)
+ while (readl_relaxed(cpufreq->cmu_regs + EXYNOS5_CLKDIV_STATCPU0)
& 0x11111111)
cpu_relax();
/* Change Divider - CPU1 */
tmp = apll_freq_5250[div_index].clk_div_cpu1;
- __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS5_CLKDIV_CPU1);
+ writel_relaxed(tmp, cpufreq->cmu_regs + EXYNOS5_CLKDIV_CPU1);
- while (__raw_readl(cpufreq->cmu_regs + EXYNOS5_CLKDIV_STATCPU1) & 0x11)
+ while (readl_relaxed(cpufreq->cmu_regs + EXYNOS5_CLKDIV_STATCPU1) & 0x11)
cpu_relax();
}
@@ -113,7 +113,7 @@ static void set_apll(unsigned int index)
do {
cpu_relax();
- tmp = (__raw_readl(cpufreq->cmu_regs + EXYNOS5_CLKMUX_STATCPU)
+ tmp = (readl_relaxed(cpufreq->cmu_regs + EXYNOS5_CLKMUX_STATCPU)
>> 16);
tmp &= 0x7;
} while (tmp != 0x2);
@@ -125,7 +125,7 @@ static void set_apll(unsigned int index)
do {
cpu_relax();
- tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS5_CLKMUX_STATCPU);
+ tmp = readl_relaxed(cpufreq->cmu_regs + EXYNOS5_CLKMUX_STATCPU);
tmp &= (0x7 << 16);
} while (tmp != (0x1 << 16));
}
diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c
index f33f25b483ca..bc5e32640e8f 100644
--- a/drivers/cpufreq/exynos5440-cpufreq.c
+++ b/drivers/cpufreq/exynos5440-cpufreq.c
@@ -155,7 +155,7 @@ static int init_div_table(void)
tmp = (clk_div | ema_div | (volt_id << P0_7_VDD_SHIFT)
| ((freq / FREQ_UNIT) << P0_7_FREQ_SHIFT));
- __raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 *
+ writel_relaxed(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 *
(pos - freq_tbl));
}
@@ -169,17 +169,17 @@ static void exynos_enable_dvfs(unsigned int cur_frequency)
struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table;
struct cpufreq_frequency_table *pos;
/* Disable DVFS */
- __raw_writel(0, dvfs_info->base + XMU_DVFS_CTRL);
+ writel_relaxed(0, dvfs_info->base + XMU_DVFS_CTRL);
/* Enable PSTATE Change Event */
- tmp = __raw_readl(dvfs_info->base + XMU_PMUEVTEN);
+ tmp = readl_relaxed(dvfs_info->base + XMU_PMUEVTEN);
tmp |= (1 << PSTATE_CHANGED_EVTEN_SHIFT);
- __raw_writel(tmp, dvfs_info->base + XMU_PMUEVTEN);
+ writel_relaxed(tmp, dvfs_info->base + XMU_PMUEVTEN);
/* Enable PSTATE Change IRQ */
- tmp = __raw_readl(dvfs_info->base + XMU_PMUIRQEN);
+ tmp = readl_relaxed(dvfs_info->base + XMU_PMUIRQEN);
tmp |= (1 << PSTATE_CHANGED_IRQEN_SHIFT);
- __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQEN);
+ writel_relaxed(tmp, dvfs_info->base + XMU_PMUIRQEN);
/* Set initial performance index */
cpufreq_for_each_entry(pos, freq_table)
@@ -197,14 +197,14 @@ static void exynos_enable_dvfs(unsigned int cur_frequency)
cur_frequency);
for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) {
- tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
+ tmp = readl_relaxed(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT);
tmp |= ((pos - freq_table) << C0_3_PSTATE_NEW_SHIFT);
- __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
+ writel_relaxed(tmp, dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
}
/* Enable DVFS */
- __raw_writel(1 << XMU_DVFS_CTRL_EN_SHIFT,
+ writel_relaxed(1 << XMU_DVFS_CTRL_EN_SHIFT,
dvfs_info->base + XMU_DVFS_CTRL);
}
@@ -223,11 +223,11 @@ static int exynos_target(struct cpufreq_policy *policy, unsigned int index)
/* Set the target frequency in all C0_3_PSTATE register */
for_each_cpu(i, policy->cpus) {
- tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + i * 4);
+ tmp = readl_relaxed(dvfs_info->base + XMU_C0_3_PSTATE + i * 4);
tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT);
tmp |= (index << C0_3_PSTATE_NEW_SHIFT);
- __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + i * 4);
+ writel_relaxed(tmp, dvfs_info->base + XMU_C0_3_PSTATE + i * 4);
}
mutex_unlock(&cpufreq_lock);
return 0;
@@ -246,7 +246,7 @@ static void exynos_cpufreq_work(struct work_struct *work)
mutex_lock(&cpufreq_lock);
freqs.old = policy->cur;
- cur_pstate = __raw_readl(dvfs_info->base + XMU_P_STATUS);
+ cur_pstate = readl_relaxed(dvfs_info->base + XMU_P_STATUS);
if (cur_pstate >> C0_3_PSTATE_VALID_SHIFT & 0x1)
index = (cur_pstate >> C0_3_PSTATE_CURR_SHIFT) & P_VALUE_MASK;
else
@@ -270,9 +270,9 @@ static irqreturn_t exynos_cpufreq_irq(int irq, void *id)
{
unsigned int tmp;
- tmp = __raw_readl(dvfs_info->base + XMU_PMUIRQ);
+ tmp = readl_relaxed(dvfs_info->base + XMU_PMUIRQ);
if (tmp >> PSTATE_CHANGED_SHIFT & 0x1) {
- __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQ);
+ writel_relaxed(tmp, dvfs_info->base + XMU_PMUIRQ);
disable_irq_nosync(irq);
schedule_work(&dvfs_info->irq_work);
}
diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c
index bf8902a0866d..b464f29d8d54 100644
--- a/drivers/cpufreq/highbank-cpufreq.c
+++ b/drivers/cpufreq/highbank-cpufreq.c
@@ -19,7 +19,7 @@
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <linux/mailbox.h>
+#include <linux/pl320-ipc.h>
#include <linux/platform_device.h>
#define HB_CPUFREQ_CHANGE_NOTE 0x80000001
diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c
new file mode 100644
index 000000000000..60725199b9aa
--- /dev/null
+++ b/drivers/cpufreq/scpi-cpufreq.c
@@ -0,0 +1,99 @@
+/*
+ * SCPI CPUFreq Interface driver
+ *
+ * It provides necessary ops to arm_big_little cpufreq driver.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Sudeep Holla <sudeep.holla@arm.com>
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/scpi_protocol.h>
+#include <linux/types.h>
+
+#include "arm_big_little.h"
+
+static int scpi_init_opp_table(struct device *cpu_dev)
+{
+ u8 domain = topology_physical_package_id(cpu_dev->id);
+ struct scpi_opp *opp;
+ int idx, ret = 0, max_opp;
+ u32 *freqs;
+
+ opp = scpi_dvfs_get_opps(domain);
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+
+ freqs = opp->freqs;
+ max_opp = opp->count;
+ for (idx = 0; idx < max_opp; idx++, freqs++) {
+ ret = dev_pm_opp_add(cpu_dev, *freqs, 900000000 /* TODO */);
+ if (ret) {
+ dev_warn(cpu_dev, "failed to add opp %u\n", *freqs);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int scpi_get_transition_latency(struct device *cpu_dev)
+{
+ u8 domain = topology_physical_package_id(cpu_dev->id);
+ struct scpi_opp *opp;
+
+ opp = scpi_dvfs_get_opps(domain);
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+
+ return opp->latency * 1000; /* SCPI returns in uS */
+}
+
+static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = {
+ .name = "scpi",
+ .get_transition_latency = scpi_get_transition_latency,
+ .init_opp_table = scpi_init_opp_table,
+};
+
+static int scpi_cpufreq_probe(struct platform_device *pdev)
+{
+ return bL_cpufreq_register(&scpi_cpufreq_ops);
+}
+
+static int scpi_cpufreq_remove(struct platform_device *pdev)
+{
+ bL_cpufreq_unregister(&scpi_cpufreq_ops);
+ return 0;
+}
+
+static struct of_device_id scpi_cpufreq_of_match[] = {
+ { .compatible = "arm,scpi-cpufreq" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, scpi_cpufreq_of_match);
+
+static struct platform_driver scpi_cpufreq_platdrv = {
+ .driver = {
+ .name = "scpi-cpufreq",
+ .owner = THIS_MODULE,
+ .of_match_table = scpi_cpufreq_of_match,
+ },
+ .probe = scpi_cpufreq_probe,
+ .remove = scpi_cpufreq_remove,
+};
+module_platform_driver(scpi_cpufreq_platdrv);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpuidle/cpuidle-big_little.c b/drivers/cpuidle/cpuidle-big_little.c
index 344d79fa3407..ef94c3b81f18 100644
--- a/drivers/cpuidle/cpuidle-big_little.c
+++ b/drivers/cpuidle/cpuidle-big_little.c
@@ -138,25 +138,18 @@ static int bl_enter_powerdown(struct cpuidle_device *dev,
return idx;
}
-static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int cpu_id)
+static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int part_id)
{
- struct cpuinfo_arm *cpu_info;
struct cpumask *cpumask;
- unsigned long cpuid;
int cpu;
cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
if (!cpumask)
return -ENOMEM;
- for_each_possible_cpu(cpu) {
- cpu_info = &per_cpu(cpu_data, cpu);
- cpuid = is_smp() ? cpu_info->cpuid : read_cpuid_id();
-
- /* read cpu id part number */
- if ((cpuid & 0xFFF0) == cpu_id)
+ for_each_possible_cpu(cpu)
+ if (smp_cpuid_part(cpu) == part_id)
cpumask_set_cpu(cpu, cpumask);
- }
drv->cpumask = cpumask;
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 34db2fb3ef1e..9da7acaeb3e9 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -178,7 +178,12 @@ static inline int performance_multiplier(unsigned long nr_iowaiters, unsigned lo
/* for higher loadavg, we are more reluctant */
- mult += 2 * get_loadavg(load);
+ /*
+ * this doesn't work as intended - it is almost always 0, but can
+ * sometimes, depending on workload, spike very high into the hundreds
+ * even when the average cpu load is under 10%.
+ */
+ /* mult += 2 * get_loadavg(load); */
/* for IO wait tasks (per cpu!) we add 5x each */
mult += 10 * nr_iowaiters;
diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c
index cb98fa54573d..71a26d1f2838 100644
--- a/drivers/crypto/omap-aes.c
+++ b/drivers/crypto/omap-aes.c
@@ -195,7 +195,7 @@ static DEFINE_SPINLOCK(list_lock);
#define omap_aes_read(dd, offset) \
({ \
int _read_ret; \
- _read_ret = __raw_readl(dd->io_base + offset); \
+ _read_ret = readl_relaxed(dd->io_base + offset); \
pr_debug("omap_aes_read(" #offset "=%#x)= %#x\n", \
offset, _read_ret); \
_read_ret; \
@@ -203,7 +203,7 @@ static DEFINE_SPINLOCK(list_lock);
#else
static inline u32 omap_aes_read(struct omap_aes_dev *dd, u32 offset)
{
- return __raw_readl(dd->io_base + offset);
+ return readl_relaxed(dd->io_base + offset);
}
#endif
@@ -212,13 +212,13 @@ static inline u32 omap_aes_read(struct omap_aes_dev *dd, u32 offset)
do { \
pr_debug("omap_aes_write(" #offset "=%#x) value=%#x\n", \
offset, value); \
- __raw_writel(value, dd->io_base + offset); \
+ writel_relaxed(value, dd->io_base + offset); \
} while (0)
#else
static inline void omap_aes_write(struct omap_aes_dev *dd, u32 offset,
u32 value)
{
- __raw_writel(value, dd->io_base + offset);
+ writel_relaxed(value, dd->io_base + offset);
}
#endif
diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c
index 710d86386965..3c0858657e3f 100644
--- a/drivers/crypto/omap-sham.c
+++ b/drivers/crypto/omap-sham.c
@@ -244,13 +244,13 @@ static struct omap_sham_drv sham = {
static inline u32 omap_sham_read(struct omap_sham_dev *dd, u32 offset)
{
- return __raw_readl(dd->io_base + offset);
+ return readl_relaxed(dd->io_base + offset);
}
static inline void omap_sham_write(struct omap_sham_dev *dd,
u32 offset, u32 value)
{
- __raw_writel(value, dd->io_base + offset);
+ writel_relaxed(value, dd->io_base + offset);
}
static inline void omap_sham_write_mask(struct omap_sham_dev *dd, u32 address,
diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c
index 7a4bbb0f80a5..9fcde852a7a4 100644
--- a/drivers/dma/qcom_bam_dma.c
+++ b/drivers/dma/qcom_bam_dma.c
@@ -79,35 +79,49 @@ struct bam_async_desc {
struct bam_desc_hw desc[0];
};
-#define BAM_CTRL 0x0000
-#define BAM_REVISION 0x0004
-#define BAM_SW_REVISION 0x0080
-#define BAM_NUM_PIPES 0x003C
-#define BAM_TIMER 0x0040
-#define BAM_TIMER_CTRL 0x0044
-#define BAM_DESC_CNT_TRSHLD 0x0008
-#define BAM_IRQ_SRCS 0x000C
-#define BAM_IRQ_SRCS_MSK 0x0010
-#define BAM_IRQ_SRCS_UNMASKED 0x0030
-#define BAM_IRQ_STTS 0x0014
-#define BAM_IRQ_CLR 0x0018
-#define BAM_IRQ_EN 0x001C
-#define BAM_CNFG_BITS 0x007C
-#define BAM_IRQ_SRCS_EE(ee) (0x0800 + ((ee) * 0x80))
-#define BAM_IRQ_SRCS_MSK_EE(ee) (0x0804 + ((ee) * 0x80))
-#define BAM_P_CTRL(pipe) (0x1000 + ((pipe) * 0x1000))
-#define BAM_P_RST(pipe) (0x1004 + ((pipe) * 0x1000))
-#define BAM_P_HALT(pipe) (0x1008 + ((pipe) * 0x1000))
-#define BAM_P_IRQ_STTS(pipe) (0x1010 + ((pipe) * 0x1000))
-#define BAM_P_IRQ_CLR(pipe) (0x1014 + ((pipe) * 0x1000))
-#define BAM_P_IRQ_EN(pipe) (0x1018 + ((pipe) * 0x1000))
-#define BAM_P_EVNT_DEST_ADDR(pipe) (0x182C + ((pipe) * 0x1000))
-#define BAM_P_EVNT_REG(pipe) (0x1818 + ((pipe) * 0x1000))
-#define BAM_P_SW_OFSTS(pipe) (0x1800 + ((pipe) * 0x1000))
-#define BAM_P_DATA_FIFO_ADDR(pipe) (0x1824 + ((pipe) * 0x1000))
-#define BAM_P_DESC_FIFO_ADDR(pipe) (0x181C + ((pipe) * 0x1000))
-#define BAM_P_EVNT_TRSHLD(pipe) (0x1828 + ((pipe) * 0x1000))
-#define BAM_P_FIFO_SIZES(pipe) (0x1820 + ((pipe) * 0x1000))
+/* Register base offset and multplier values. Use version of map as index */
+static unsigned int ctrl_offs[] = { 0xf80, 0x0 };
+static unsigned int pipe_offs[] = { 0x0, 0x1000 };
+static unsigned int ee_offs[] = { 0x1800, 0x800 };
+static unsigned int evnt_offs[] = { 0x1000, 0x1800 };
+static unsigned int pipe_mult[] = { 0x80, 0x1000 };
+static unsigned int evnt_mult[] = { 0x40, 0x1000 };
+
+/* relative offset from ctrl register base */
+#define BAM_CTRL 0x00
+#define BAM_REVISION 0x04
+#define BAM_DESC_CNT_TRSHLD 0x08
+#define BAM_IRQ_SRCS 0x0C
+#define BAM_IRQ_SRCS_MSK 0x10
+#define BAM_IRQ_STTS 0x14
+#define BAM_IRQ_CLR 0x18
+#define BAM_IRQ_EN 0x1C
+#define BAM_IRQ_SRCS_UNMASKED 0x30
+#define BAM_NUM_PIPES 0x3c
+#define BAM_TIMER 0x40
+#define BAM_TIMER_CTRL 0x44
+#define BAM_CNFG_BITS 0x7c
+
+/* relative offset from irq register base */
+#define BAM_IRQ_SRCS_EE 0x00
+#define BAM_IRQ_SRCS_MSK_EE 0x04
+
+/* relative offset from pipe register base */
+#define BAM_P_CTRL 0x00
+#define BAM_P_RST 0x04
+#define BAM_P_HALT 0x08
+#define BAM_P_IRQ_STTS 0x10
+#define BAM_P_IRQ_CLR 0x14
+#define BAM_P_IRQ_EN 0x18
+
+/* relative offset from event register base */
+#define BAM_P_SW_OFSTS 0x00
+#define BAM_P_EVNT_REG 0x18
+#define BAM_P_DESC_FIFO_ADDR 0x1C
+#define BAM_P_FIFO_SIZES 0x20
+#define BAM_P_DATA_FIFO_ADDR 0x24
+#define BAM_P_EVNT_TRSHLD 0x28
+#define BAM_P_EVNT_DEST_ADDR 0x2C
/* BAM CTRL */
#define BAM_SW_RST BIT(0)
@@ -297,6 +311,8 @@ struct bam_device {
/* execution environment ID, from DT */
u32 ee;
+ u32 reg_ver;
+
struct clk *bamclk;
int irq;
@@ -304,6 +320,36 @@ struct bam_device {
struct tasklet_struct task;
};
+static inline void __iomem *ctrl_addr(struct bam_device *bdev, u32 reg)
+{
+ return bdev->regs + ctrl_offs[bdev->reg_ver] + reg;
+}
+
+static inline void __iomem *ee_addr(struct bam_device *bdev, u32 reg)
+{
+ u32 offset = ee_offs[bdev->reg_ver] + reg + (bdev->ee * 0x80);
+
+ return bdev->regs + offset;
+}
+
+static inline void __iomem *pipe_addr(struct bam_device *bdev, u32 pipe,
+ u32 reg)
+{
+ u32 offset = pipe_offs[bdev->reg_ver] + reg;
+
+ offset += pipe_mult[bdev->reg_ver] * pipe;
+ return bdev->regs + offset;
+}
+
+static inline void __iomem *evnt_addr(struct bam_device *bdev, u32 pipe,
+ u32 reg)
+{
+ u32 offset = evnt_offs[bdev->reg_ver] + reg;
+
+ offset += evnt_mult[bdev->reg_ver] * pipe;
+ return bdev->regs + offset;
+}
+
/**
* bam_reset_channel - Reset individual BAM DMA channel
* @bchan: bam channel
@@ -317,8 +363,8 @@ static void bam_reset_channel(struct bam_chan *bchan)
lockdep_assert_held(&bchan->vc.lock);
/* reset channel */
- writel_relaxed(1, bdev->regs + BAM_P_RST(bchan->id));
- writel_relaxed(0, bdev->regs + BAM_P_RST(bchan->id));
+ writel_relaxed(1, pipe_addr(bdev, bchan->id, BAM_P_RST));
+ writel_relaxed(0, pipe_addr(bdev, bchan->id, BAM_P_RST));
/* don't allow cpu to reorder BAM register accesses done after this */
wmb();
@@ -347,17 +393,18 @@ static void bam_chan_init_hw(struct bam_chan *bchan,
* because we allocated 1 more descriptor (8 bytes) than we can use
*/
writel_relaxed(ALIGN(bchan->fifo_phys, sizeof(struct bam_desc_hw)),
- bdev->regs + BAM_P_DESC_FIFO_ADDR(bchan->id));
- writel_relaxed(BAM_DESC_FIFO_SIZE, bdev->regs +
- BAM_P_FIFO_SIZES(bchan->id));
+ evnt_addr(bdev, bchan->id, BAM_P_DESC_FIFO_ADDR));
+ writel_relaxed(BAM_DESC_FIFO_SIZE, evnt_addr(bdev, bchan->id,
+ BAM_P_FIFO_SIZES));
/* enable the per pipe interrupts, enable EOT, ERR, and INT irqs */
- writel_relaxed(P_DEFAULT_IRQS_EN, bdev->regs + BAM_P_IRQ_EN(bchan->id));
+ writel_relaxed(P_DEFAULT_IRQS_EN,
+ pipe_addr(bdev, bchan->id, BAM_P_IRQ_EN));
/* unmask the specific pipe and EE combo */
- val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+ val = readl_relaxed(ee_addr(bdev, BAM_IRQ_SRCS_MSK_EE));
val |= BIT(bchan->id);
- writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+ writel_relaxed(val, ee_addr(bdev, BAM_IRQ_SRCS_MSK_EE));
/* don't allow cpu to reorder the channel enable done below */
wmb();
@@ -367,7 +414,7 @@ static void bam_chan_init_hw(struct bam_chan *bchan,
if (dir == DMA_DEV_TO_MEM)
val |= P_DIRECTION;
- writel_relaxed(val, bdev->regs + BAM_P_CTRL(bchan->id));
+ writel_relaxed(val, pipe_addr(bdev, bchan->id, BAM_P_CTRL));
bchan->initialized = 1;
@@ -432,12 +479,12 @@ static void bam_free_chan(struct dma_chan *chan)
bchan->fifo_virt = NULL;
/* mask irq for pipe/channel */
- val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+ val = readl_relaxed(ee_addr(bdev, BAM_IRQ_SRCS_MSK_EE));
val &= ~BIT(bchan->id);
- writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+ writel_relaxed(val, ee_addr(bdev, BAM_IRQ_SRCS_MSK_EE));
/* disable irq */
- writel_relaxed(0, bdev->regs + BAM_P_IRQ_EN(bchan->id));
+ writel_relaxed(0, pipe_addr(bdev, bchan->id, BAM_P_IRQ_EN));
}
/**
@@ -583,14 +630,14 @@ static int bam_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
switch (cmd) {
case DMA_PAUSE:
spin_lock_irqsave(&bchan->vc.lock, flag);
- writel_relaxed(1, bdev->regs + BAM_P_HALT(bchan->id));
+ writel_relaxed(1, pipe_addr(bdev, bchan->id, BAM_P_HALT));
bchan->paused = 1;
spin_unlock_irqrestore(&bchan->vc.lock, flag);
break;
case DMA_RESUME:
spin_lock_irqsave(&bchan->vc.lock, flag);
- writel_relaxed(0, bdev->regs + BAM_P_HALT(bchan->id));
+ writel_relaxed(0, pipe_addr(bdev, bchan->id, BAM_P_HALT));
bchan->paused = 0;
spin_unlock_irqrestore(&bchan->vc.lock, flag);
break;
@@ -626,7 +673,7 @@ static u32 process_channel_irqs(struct bam_device *bdev)
unsigned long flags;
struct bam_async_desc *async_desc;
- srcs = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_EE(bdev->ee));
+ srcs = readl_relaxed(ee_addr(bdev, BAM_IRQ_SRCS_EE));
/* return early if no pipe/channel interrupts are present */
if (!(srcs & P_IRQ))
@@ -639,11 +686,9 @@ static u32 process_channel_irqs(struct bam_device *bdev)
continue;
/* clear pipe irq */
- pipe_stts = readl_relaxed(bdev->regs +
- BAM_P_IRQ_STTS(i));
+ pipe_stts = readl_relaxed(pipe_addr(bdev, i, BAM_P_IRQ_STTS));
- writel_relaxed(pipe_stts, bdev->regs +
- BAM_P_IRQ_CLR(i));
+ writel_relaxed(pipe_stts, pipe_addr(bdev, i, BAM_P_IRQ_CLR));
spin_lock_irqsave(&bchan->vc.lock, flags);
async_desc = bchan->curr_txd;
@@ -694,12 +739,12 @@ static irqreturn_t bam_dma_irq(int irq, void *data)
tasklet_schedule(&bdev->task);
if (srcs & BAM_IRQ)
- clr_mask = readl_relaxed(bdev->regs + BAM_IRQ_STTS);
+ clr_mask = readl_relaxed(ctrl_addr(bdev, BAM_IRQ_STTS));
/* don't allow reorder of the various accesses to the BAM registers */
mb();
- writel_relaxed(clr_mask, bdev->regs + BAM_IRQ_CLR);
+ writel_relaxed(clr_mask, ctrl_addr(bdev, BAM_IRQ_CLR));
return IRQ_HANDLED;
}
@@ -763,7 +808,7 @@ static void bam_apply_new_config(struct bam_chan *bchan,
else
maxburst = bchan->slave.dst_maxburst;
- writel_relaxed(maxburst, bdev->regs + BAM_DESC_CNT_TRSHLD);
+ writel_relaxed(maxburst, ctrl_addr(bdev, BAM_DESC_CNT_TRSHLD));
bchan->reconfigure = 0;
}
@@ -830,7 +875,7 @@ static void bam_start_dma(struct bam_chan *bchan)
/* ensure descriptor writes and dma start not reordered */
wmb();
writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw),
- bdev->regs + BAM_P_EVNT_REG(bchan->id));
+ evnt_addr(bdev, bchan->id, BAM_P_EVNT_REG));
}
/**
@@ -918,43 +963,45 @@ static int bam_init(struct bam_device *bdev)
u32 val;
/* read revision and configuration information */
- val = readl_relaxed(bdev->regs + BAM_REVISION) >> NUM_EES_SHIFT;
+ val = readl_relaxed(ctrl_addr(bdev, BAM_REVISION)) >> NUM_EES_SHIFT;
val &= NUM_EES_MASK;
/* check that configured EE is within range */
if (bdev->ee >= val)
return -EINVAL;
- val = readl_relaxed(bdev->regs + BAM_NUM_PIPES);
+ val = readl_relaxed(ctrl_addr(bdev, BAM_NUM_PIPES));
bdev->num_channels = val & BAM_NUM_PIPES_MASK;
/* s/w reset bam */
/* after reset all pipes are disabled and idle */
- val = readl_relaxed(bdev->regs + BAM_CTRL);
+ val = readl_relaxed(ctrl_addr(bdev, BAM_CTRL));
val |= BAM_SW_RST;
- writel_relaxed(val, bdev->regs + BAM_CTRL);
+ writel_relaxed(val, ctrl_addr(bdev, BAM_CTRL));
val &= ~BAM_SW_RST;
- writel_relaxed(val, bdev->regs + BAM_CTRL);
+ writel_relaxed(val, ctrl_addr(bdev, BAM_CTRL));
/* make sure previous stores are visible before enabling BAM */
wmb();
/* enable bam */
val |= BAM_EN;
- writel_relaxed(val, bdev->regs + BAM_CTRL);
+ writel_relaxed(val, ctrl_addr(bdev, BAM_CTRL));
/* set descriptor threshhold, start with 4 bytes */
- writel_relaxed(DEFAULT_CNT_THRSHLD, bdev->regs + BAM_DESC_CNT_TRSHLD);
+ writel_relaxed(DEFAULT_CNT_THRSHLD,
+ ctrl_addr(bdev, BAM_DESC_CNT_TRSHLD));
/* Enable default set of h/w workarounds, ie all except BAM_FULL_PIPE */
- writel_relaxed(BAM_CNFG_BITS_DEFAULT, bdev->regs + BAM_CNFG_BITS);
+ writel_relaxed(BAM_CNFG_BITS_DEFAULT,
+ ctrl_addr(bdev, BAM_CNFG_BITS));
/* enable irqs for errors */
writel_relaxed(BAM_ERROR_EN | BAM_HRESP_ERR_EN,
- bdev->regs + BAM_IRQ_EN);
+ ctrl_addr(bdev, BAM_IRQ_EN));
/* unmask global bam interrupt */
- writel_relaxed(BAM_IRQ_MSK, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+ writel_relaxed(BAM_IRQ_MSK, ee_addr(bdev, BAM_IRQ_SRCS_MSK_EE));
return 0;
}
@@ -996,6 +1043,9 @@ static int bam_dma_probe(struct platform_device *pdev)
return ret;
}
+ if (of_device_is_compatible(pdev->dev.of_node, "qcom,bam-v1.4.0"))
+ bdev->reg_ver = 1;
+
bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk");
if (IS_ERR(bdev->bamclk))
return PTR_ERR(bdev->bamclk);
@@ -1084,7 +1134,7 @@ static int bam_dma_remove(struct platform_device *pdev)
dma_async_device_unregister(&bdev->common);
/* mask all interrupts for this execution environment */
- writel_relaxed(0, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+ writel_relaxed(0, ee_addr(bdev, BAM_IRQ_SRCS_MSK_EE));
devm_free_irq(bdev->dev, bdev->irq, bdev);
@@ -1105,6 +1155,7 @@ static int bam_dma_remove(struct platform_device *pdev)
}
static const struct of_device_id bam_of_match[] = {
+ { .compatible = "qcom,bam-v1.3.0", },
{ .compatible = "qcom,bam-v1.4.0", },
{}
};
diff --git a/drivers/gator/Kconfig b/drivers/gator/Kconfig
new file mode 100644
index 000000000000..31f0c6fb8bae
--- /dev/null
+++ b/drivers/gator/Kconfig
@@ -0,0 +1,38 @@
+config GATOR
+ tristate "Gator module for ARM's Streamline Performance Analyzer"
+ default m if (ARM || ARM64)
+ depends on PROFILING
+ depends on HIGH_RES_TIMERS
+ depends on PERF_EVENTS
+ depends on HW_PERF_EVENTS || !(ARM || ARM64)
+ select TRACING
+ help
+ Gator module for ARM's Streamline Performance Analyzer
+
+config GATOR_WITH_MALI_SUPPORT
+ bool
+
+choice
+ prompt "Enable Mali GPU support in Gator"
+ depends on GATOR
+ optional
+ help
+ Enable Mali GPU support in Gator
+
+config GATOR_MALI_4XXMP
+ bool "Mali-400MP or Mali-450MP"
+ select GATOR_WITH_MALI_SUPPORT
+
+config GATOR_MALI_T6XX
+ bool "Mali-T604 or Mali-T658"
+ select GATOR_WITH_MALI_SUPPORT
+
+endchoice
+
+config GATOR_MALI_PATH
+ string "Path to Mali driver"
+ depends on GATOR_WITH_MALI_SUPPORT
+ default "drivers/gpu/arm/mali400mp"
+ help
+ The gator code adds this to its include path so it can get the Mali
+ trace headers with: #include "linux/mali_linux_trace.h"
diff --git a/drivers/gator/LICENSE b/drivers/gator/LICENSE
new file mode 100644
index 000000000000..d159169d1050
--- /dev/null
+++ b/drivers/gator/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/drivers/gator/Makefile b/drivers/gator/Makefile
new file mode 100644
index 000000000000..2f86823313c6
--- /dev/null
+++ b/drivers/gator/Makefile
@@ -0,0 +1,81 @@
+ifneq ($(KERNELRELEASE),)
+
+# Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c
+# EXTRA_CFLAGS += -DGATOR_KERNEL_STACK_UNWINDING
+
+CONFIG_GATOR ?= m
+obj-$(CONFIG_GATOR) := gator.o
+
+gator-y := gator_main.o \
+ gator_events_block.o \
+ gator_events_irq.o \
+ gator_events_meminfo.o \
+ gator_events_mmapped.o \
+ gator_events_net.o \
+ gator_events_perf_pmu.o \
+ gator_events_sched.o \
+ gator_events_threads.o \
+
+# Convert the old GATOR_WITH_MALI_SUPPORT to the new kernel flags
+ifneq ($(GATOR_WITH_MALI_SUPPORT),)
+ CONFIG_GATOR_WITH_MALI_SUPPORT := y
+ ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx)
+ CONFIG_GATOR_MALI_4XXMP := n
+ CONFIG_GATOR_MALI_T6XX := y
+ else
+ CONFIG_GATOR_MALI_4XXMP := y
+ CONFIG_GATOR_MALI_T6XX := n
+ endif
+ EXTRA_CFLAGS += -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT)
+ ifneq ($(GATOR_MALI_INTERFACE_STYLE),)
+ EXTRA_CFLAGS += -DGATOR_MALI_INTERFACE_STYLE=$(GATOR_MALI_INTERFACE_STYLE)
+ endif
+endif
+
+ifeq ($(CONFIG_GATOR_WITH_MALI_SUPPORT),y)
+ ifeq ($(CONFIG_GATOR_MALI_T6XX),y)
+ gator-y += gator_events_mali_t6xx.o \
+ gator_events_mali_t6xx_hw.o
+ include $(src)/mali_t6xx.mk
+ else
+ gator-y += gator_events_mali_4xx.o
+ endif
+ gator-y += gator_events_mali_common.o
+
+ ifneq ($(CONFIG_GATOR_MALI_PATH),)
+ ccflags-y += -I$(CONFIG_GATOR_MALI_PATH)
+ endif
+ ccflags-$(CONFIG_GATOR_MALI_4XXMP) += -DMALI_SUPPORT=MALI_4xx
+ ccflags-$(CONFIG_GATOR_MALI_T6XX) += -DMALI_SUPPORT=MALI_T6xx
+endif
+
+# GATOR_TEST controls whether to include (=1) or exclude (=0) test code.
+GATOR_TEST ?= 0
+EXTRA_CFLAGS += -DGATOR_TEST=$(GATOR_TEST)
+
+# Should the original or new block_rq_complete API be used?
+OLD_BLOCK_RQ_COMPLETE := $(shell grep -A3 block_rq_complete include/trace/events/block.h | grep nr_bytes > /dev/null; echo $$?)
+EXTRA_CFLAGS += -DOLD_BLOCK_RQ_COMPLETE=$(OLD_BLOCK_RQ_COMPLETE)
+
+gator-$(CONFIG_ARM) += gator_events_armv6.o \
+ gator_events_armv7.o \
+ gator_events_ccn-504.o \
+ gator_events_l2c-310.o \
+ gator_events_scorpion.o
+
+gator-$(CONFIG_ARM64) += gator_events_ccn-504.o
+
+else
+
+all:
+ @echo
+ @echo "usage:"
+ @echo " make -C <kernel_build_dir> M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules"
+ @echo
+ $(error)
+
+clean:
+ rm -f *.o .*.cmd modules.order Module.symvers gator.ko gator.mod.c
+ rm -rf .tmp_versions
+
+endif
diff --git a/drivers/gator/gator.h b/drivers/gator/gator.h
new file mode 100644
index 000000000000..5ad0254d86a9
--- /dev/null
+++ b/drivers/gator/gator.h
@@ -0,0 +1,149 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 GATOR_H_
+#define GATOR_H_
+
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+
+#define GATOR_PERF_SUPPORT LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+#define GATOR_PERF_PMU_SUPPORT GATOR_PERF_SUPPORT && defined(CONFIG_PERF_EVENTS) && (!(defined(__arm__) || defined(__aarch64__)) || defined(CONFIG_HW_PERF_EVENTS))
+#define GATOR_NO_PERF_SUPPORT (!(GATOR_PERF_SUPPORT))
+#define GATOR_CPU_FREQ_SUPPORT (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) && defined(CONFIG_CPU_FREQ)
+#define GATOR_IKS_SUPPORT defined(CONFIG_BL_SWITCHER)
+
+// cpu ids
+#define ARM1136 0xb36
+#define ARM1156 0xb56
+#define ARM1176 0xb76
+#define ARM11MPCORE 0xb02
+#define CORTEX_A5 0xc05
+#define CORTEX_A7 0xc07
+#define CORTEX_A8 0xc08
+#define CORTEX_A9 0xc09
+#define CORTEX_A12 0xc0d
+#define CORTEX_A15 0xc0f
+#define CORTEX_A17 0xc0e
+#define SCORPION 0x00f
+#define SCORPIONMP 0x02d
+#define KRAITSIM 0x049
+#define KRAIT 0x04d
+#define KRAIT_S4_PRO 0x06f
+#define CORTEX_A53 0xd03
+#define CORTEX_A57 0xd07
+#define AARCH64 0xd0f
+#define OTHER 0xfff
+
+// gpu enums
+#define MALI_4xx 1
+#define MALI_T6xx 2
+
+#define MAXSIZE_CORE_NAME 32
+
+struct gator_cpu {
+ const int cpuid;
+ // Human readable name
+ const char core_name[MAXSIZE_CORE_NAME];
+ // gatorfs event and Perf PMU name
+ const char * const pmnc_name;
+ // compatible from Documentation/devicetree/bindings/arm/cpus.txt
+ const char * const dt_name;
+ const int pmnc_counters;
+};
+
+const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid);
+const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name);
+
+/******************************************************************************
+ * Filesystem
+ ******************************************************************************/
+struct dentry *gatorfs_mkdir(struct super_block *sb, struct dentry *root,
+ char const *name);
+
+int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
+ char const *name, unsigned long *val);
+
+int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
+ char const *name, unsigned long *val);
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+# error Kernels prior to 2.6.32 not supported
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+# define GATOR_DEFINE_PROBE(probe_name, proto) \
+ static void probe_##probe_name(PARAMS(proto))
+# define GATOR_REGISTER_TRACE(probe_name) \
+ register_trace_##probe_name(probe_##probe_name)
+# define GATOR_UNREGISTER_TRACE(probe_name) \
+ unregister_trace_##probe_name(probe_##probe_name)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+# define GATOR_DEFINE_PROBE(probe_name, proto) \
+ static void probe_##probe_name(void *data, PARAMS(proto))
+# define GATOR_REGISTER_TRACE(probe_name) \
+ register_trace_##probe_name(probe_##probe_name, NULL)
+# define GATOR_UNREGISTER_TRACE(probe_name) \
+ unregister_trace_##probe_name(probe_##probe_name, NULL)
+#else
+# define GATOR_DEFINE_PROBE(probe_name, proto) \
+ extern struct tracepoint *gator_tracepoint_##probe_name; \
+ static void probe_##probe_name(void *data, PARAMS(proto))
+# define GATOR_REGISTER_TRACE(probe_name) \
+ tracepoint_probe_register(gator_tracepoint_##probe_name, probe_##probe_name, NULL)
+# define GATOR_UNREGISTER_TRACE(probe_name) \
+ tracepoint_probe_unregister(gator_tracepoint_##probe_name, probe_##probe_name, NULL)
+#endif
+
+/******************************************************************************
+ * Events
+ ******************************************************************************/
+struct gator_interface {
+ void (*shutdown)(void); // Complementary function to init
+ int (*create_files)(struct super_block *sb, struct dentry *root);
+ int (*start)(void);
+ void (*stop)(void); // Complementary function to start
+ int (*online)(int **buffer, bool migrate);
+ int (*offline)(int **buffer, bool migrate);
+ void (*online_dispatch)(int cpu, bool migrate); // called in process context but may not be running on core 'cpu'
+ void (*offline_dispatch)(int cpu, bool migrate); // called in process context but may not be running on core 'cpu'
+ int (*read)(int **buffer);
+ int (*read64)(long long **buffer);
+ int (*read_proc)(long long **buffer, struct task_struct *);
+ struct list_head list;
+};
+
+int gator_events_install(struct gator_interface *interface);
+int gator_events_get_key(void);
+u32 gator_cpuid(void);
+
+void gator_backtrace_handler(struct pt_regs *const regs);
+
+void gator_marshal_activity_switch(int core, int key, int activity, int pid);
+
+#if !GATOR_IKS_SUPPORT
+
+#define get_physical_cpu() smp_processor_id()
+#define lcpu_to_pcpu(lcpu) lcpu
+#define pcpu_to_lcpu(pcpu) pcpu
+
+#else
+
+#define get_physical_cpu() lcpu_to_pcpu(get_logical_cpu())
+int lcpu_to_pcpu(const int lcpu);
+int pcpu_to_lcpu(const int pcpu);
+
+#endif
+
+#define get_logical_cpu() smp_processor_id()
+#define on_primary_core() (get_logical_cpu() == 0)
+
+#endif // GATOR_H_
diff --git a/drivers/gator/gator_annotate.c b/drivers/gator/gator_annotate.c
new file mode 100644
index 000000000000..7e2c6e5d8715
--- /dev/null
+++ b/drivers/gator/gator_annotate.c
@@ -0,0 +1,186 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/current.h>
+#include <linux/spinlock.h>
+
+static DEFINE_SPINLOCK(annotate_lock);
+static bool collect_annotations = false;
+
+static int annotate_copy(struct file *file, char const __user *buf, size_t count)
+{
+ int cpu = 0;
+ int write = per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF];
+
+ if (file == NULL) {
+ // copy from kernel
+ memcpy(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count);
+ } else {
+ // copy from user space
+ if (copy_from_user(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count) != 0)
+ return -1;
+ }
+ per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF] = (write + count) & gator_buffer_mask[ANNOTATE_BUF];
+
+ return 0;
+}
+
+static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count_orig, loff_t *offset)
+{
+ int pid, cpu, header_size, available, contiguous, length1, length2, size, count = count_orig & 0x7fffffff;
+ bool interrupt_context;
+
+ if (*offset) {
+ return -EINVAL;
+ }
+
+ interrupt_context = in_interrupt();
+ // Annotations are not supported in interrupt context, but may work if you comment out the the next four lines of code.
+ // By doing so, annotations in interrupt context can result in deadlocks and lost data.
+ if (interrupt_context) {
+ printk(KERN_WARNING "gator: Annotations are not supported in interrupt context. Edit gator_annotate.c in the gator driver to enable annotations in interrupt context.\n");
+ return -EINVAL;
+ }
+
+ retry:
+ // synchronize between cores and with collect_annotations
+ spin_lock(&annotate_lock);
+
+ if (!collect_annotations) {
+ // Not collecting annotations, tell the caller everything was written
+ size = count_orig;
+ goto annotate_write_out;
+ }
+
+ // Annotation only uses a single per-cpu buffer as the data must be in order to the engine
+ cpu = 0;
+
+ if (current == NULL) {
+ pid = 0;
+ } else {
+ pid = current->pid;
+ }
+
+ // determine total size of the payload
+ header_size = MAXSIZE_PACK32 * 3 + MAXSIZE_PACK64;
+ available = buffer_bytes_available(cpu, ANNOTATE_BUF) - header_size;
+ size = count < available ? count : available;
+
+ if (size <= 0) {
+ // Buffer is full, wait until space is available
+ spin_unlock(&annotate_lock);
+
+ // Drop the annotation as blocking is not allowed in interrupt context
+ if (interrupt_context) {
+ return -EINVAL;
+ }
+
+ wait_event_interruptible(gator_annotate_wait, buffer_bytes_available(cpu, ANNOTATE_BUF) > header_size || !collect_annotations);
+
+ // Check to see if a signal is pending
+ if (signal_pending(current)) {
+ return -EINTR;
+ }
+
+ goto retry;
+ }
+
+ // synchronize shared variables annotateBuf and annotatePos
+ if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF]) {
+ u64 time = gator_get_time();
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu());
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid);
+ gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, time);
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, size);
+
+ // determine the sizes to capture, length1 + length2 will equal size
+ contiguous = contiguous_space_available(cpu, ANNOTATE_BUF);
+ if (size < contiguous) {
+ length1 = size;
+ length2 = 0;
+ } else {
+ length1 = contiguous;
+ length2 = size - contiguous;
+ }
+
+ if (annotate_copy(file, buf, length1) != 0) {
+ size = -EINVAL;
+ goto annotate_write_out;
+ }
+
+ if (length2 > 0 && annotate_copy(file, &buf[length1], length2) != 0) {
+ size = -EINVAL;
+ goto annotate_write_out;
+ }
+
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, ANNOTATE_BUF, time);
+ }
+
+annotate_write_out:
+ spin_unlock(&annotate_lock);
+
+ // return the number of bytes written
+ return size;
+}
+
+#include "gator_annotate_kernel.c"
+
+static int annotate_release(struct inode *inode, struct file *file)
+{
+ int cpu = 0;
+
+ // synchronize between cores
+ spin_lock(&annotate_lock);
+
+ if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF] && buffer_check_space(cpu, ANNOTATE_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) {
+ uint32_t pid = current->pid;
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu());
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid);
+ gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, 0); // time
+ gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, 0); // size
+ }
+
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, ANNOTATE_BUF, gator_get_time());
+
+ spin_unlock(&annotate_lock);
+
+ return 0;
+}
+
+static const struct file_operations annotate_fops = {
+ .write = annotate_write,
+ .release = annotate_release
+};
+
+static int gator_annotate_create_files(struct super_block *sb, struct dentry *root)
+{
+ return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666);
+}
+
+static int gator_annotate_start(void)
+{
+ collect_annotations = true;
+ return 0;
+}
+
+static void gator_annotate_stop(void)
+{
+ // the spinlock here will ensure that when this function exits, we are not in the middle of an annotation
+ spin_lock(&annotate_lock);
+ collect_annotations = false;
+ wake_up(&gator_annotate_wait);
+ spin_unlock(&annotate_lock);
+}
diff --git a/drivers/gator/gator_annotate_kernel.c b/drivers/gator/gator_annotate_kernel.c
new file mode 100644
index 000000000000..010806825529
--- /dev/null
+++ b/drivers/gator/gator_annotate_kernel.c
@@ -0,0 +1,202 @@
+/**
+ * Copyright (C) ARM Limited 2012-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#define ESCAPE_CODE 0x1c
+#define STRING_ANNOTATION 0x06
+#define NAME_CHANNEL_ANNOTATION 0x07
+#define NAME_GROUP_ANNOTATION 0x08
+#define VISUAL_ANNOTATION 0x04
+#define MARKER_ANNOTATION 0x05
+
+static void kannotate_write(const char *ptr, unsigned int size)
+{
+ int retval;
+ int pos = 0;
+ loff_t offset = 0;
+ while (pos < size) {
+ retval = annotate_write(NULL, &ptr[pos], size - pos, &offset);
+ if (retval < 0) {
+ printk(KERN_WARNING "gator: kannotate_write failed with return value %d\n", retval);
+ return;
+ }
+ pos += retval;
+ }
+}
+
+static void marshal_u16(char *buf, u16 val)
+{
+ buf[0] = val & 0xff;
+ buf[1] = (val >> 8) & 0xff;
+}
+
+static void marshal_u32(char *buf, u32 val)
+{
+ buf[0] = val & 0xff;
+ buf[1] = (val >> 8) & 0xff;
+ buf[2] = (val >> 16) & 0xff;
+ buf[3] = (val >> 24) & 0xff;
+}
+
+void gator_annotate_channel(int channel, const char *str)
+{
+ const u16 str_size = strlen(str) & 0xffff;
+ char header[8];
+ header[0] = ESCAPE_CODE;
+ header[1] = STRING_ANNOTATION;
+ marshal_u32(header + 2, channel);
+ marshal_u16(header + 6, str_size);
+ kannotate_write(header, sizeof(header));
+ kannotate_write(str, str_size);
+}
+
+EXPORT_SYMBOL(gator_annotate_channel);
+
+void gator_annotate(const char *str)
+{
+ gator_annotate_channel(0, str);
+}
+
+EXPORT_SYMBOL(gator_annotate);
+
+void gator_annotate_channel_color(int channel, int color, const char *str)
+{
+ const u16 str_size = (strlen(str) + 4) & 0xffff;
+ char header[12];
+ header[0] = ESCAPE_CODE;
+ header[1] = STRING_ANNOTATION;
+ marshal_u32(header + 2, channel);
+ marshal_u16(header + 6, str_size);
+ marshal_u32(header + 8, color);
+ kannotate_write(header, sizeof(header));
+ kannotate_write(str, str_size - 4);
+}
+
+EXPORT_SYMBOL(gator_annotate_channel_color);
+
+void gator_annotate_color(int color, const char *str)
+{
+ gator_annotate_channel_color(0, color, str);
+}
+
+EXPORT_SYMBOL(gator_annotate_color);
+
+void gator_annotate_channel_end(int channel)
+{
+ char header[8];
+ header[0] = ESCAPE_CODE;
+ header[1] = STRING_ANNOTATION;
+ marshal_u32(header + 2, channel);
+ marshal_u16(header + 6, 0);
+ kannotate_write(header, sizeof(header));
+}
+
+EXPORT_SYMBOL(gator_annotate_channel_end);
+
+void gator_annotate_end(void)
+{
+ gator_annotate_channel_end(0);
+}
+
+EXPORT_SYMBOL(gator_annotate_end);
+
+void gator_annotate_name_channel(int channel, int group, const char* str)
+{
+ const u16 str_size = strlen(str) & 0xffff;
+ char header[12];
+ header[0] = ESCAPE_CODE;
+ header[1] = NAME_CHANNEL_ANNOTATION;
+ marshal_u32(header + 2, channel);
+ marshal_u32(header + 6, group);
+ marshal_u16(header + 10, str_size);
+ kannotate_write(header, sizeof(header));
+ kannotate_write(str, str_size);
+}
+
+EXPORT_SYMBOL(gator_annotate_name_channel);
+
+void gator_annotate_name_group(int group, const char* str)
+{
+ const u16 str_size = strlen(str) & 0xffff;
+ char header[8];
+ header[0] = ESCAPE_CODE;
+ header[1] = NAME_GROUP_ANNOTATION;
+ marshal_u32(header + 2, group);
+ marshal_u16(header + 6, str_size);
+ kannotate_write(header, sizeof(header));
+ kannotate_write(str, str_size);
+}
+
+EXPORT_SYMBOL(gator_annotate_name_group);
+
+void gator_annotate_visual(const char *data, unsigned int length, const char *str)
+{
+ const u16 str_size = strlen(str) & 0xffff;
+ char header[4];
+ char header_length[4];
+ header[0] = ESCAPE_CODE;
+ header[1] = VISUAL_ANNOTATION;
+ marshal_u16(header + 2, str_size);
+ marshal_u32(header_length, length);
+ kannotate_write(header, sizeof(header));
+ kannotate_write(str, str_size);
+ kannotate_write(header_length, sizeof(header_length));
+ kannotate_write(data, length);
+}
+
+EXPORT_SYMBOL(gator_annotate_visual);
+
+void gator_annotate_marker(void)
+{
+ char header[4];
+ header[0] = ESCAPE_CODE;
+ header[1] = MARKER_ANNOTATION;
+ marshal_u16(header + 2, 0);
+ kannotate_write(header, sizeof(header));
+}
+
+EXPORT_SYMBOL(gator_annotate_marker);
+
+void gator_annotate_marker_str(const char *str)
+{
+ const u16 str_size = strlen(str) & 0xffff;
+ char header[4];
+ header[0] = ESCAPE_CODE;
+ header[1] = MARKER_ANNOTATION;
+ marshal_u16(header + 2, str_size);
+ kannotate_write(header, sizeof(header));
+ kannotate_write(str, str_size);
+}
+
+EXPORT_SYMBOL(gator_annotate_marker_str);
+
+void gator_annotate_marker_color(int color)
+{
+ char header[8];
+ header[0] = ESCAPE_CODE;
+ header[1] = MARKER_ANNOTATION;
+ marshal_u16(header + 2, 4);
+ marshal_u32(header + 4, color);
+ kannotate_write(header, sizeof(header));
+}
+
+EXPORT_SYMBOL(gator_annotate_marker_color);
+
+void gator_annotate_marker_color_str(int color, const char *str)
+{
+ const u16 str_size = (strlen(str) + 4) & 0xffff;
+ char header[8];
+ header[0] = ESCAPE_CODE;
+ header[1] = MARKER_ANNOTATION;
+ marshal_u16(header + 2, str_size);
+ marshal_u32(header + 4, color);
+ kannotate_write(header, sizeof(header));
+ kannotate_write(str, str_size - 4);
+}
+
+EXPORT_SYMBOL(gator_annotate_marker_color_str);
diff --git a/drivers/gator/gator_backtrace.c b/drivers/gator/gator_backtrace.c
new file mode 100644
index 000000000000..e03c1653c5b5
--- /dev/null
+++ b/drivers/gator/gator_backtrace.c
@@ -0,0 +1,208 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * EABI backtrace stores {fp,lr} on the stack.
+ */
+struct stack_frame_eabi {
+ union {
+ struct {
+ unsigned long fp;
+ // May be the fp in the case of a leaf function or clang
+ unsigned long lr;
+ // If lr is really the fp, lr2 is the corresponding lr
+ unsigned long lr2;
+ };
+ // Used to read 32 bit fp/lr from a 64 bit kernel
+ struct {
+ u32 fp_32;
+ // same as lr above
+ u32 lr_32;
+ // same as lr2 above
+ u32 lr2_32;
+ };
+ };
+};
+
+static void gator_add_trace(int cpu, unsigned long address)
+{
+ off_t offset = 0;
+ unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset);
+
+ if (cookie == NO_COOKIE || cookie == UNRESOLVED_COOKIE) {
+ offset = address;
+ }
+
+ marshal_backtrace(offset & ~1, cookie, 0);
+}
+
+static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth)
+{
+#if defined(__arm__) || defined(__aarch64__)
+ struct stack_frame_eabi *curr;
+ struct stack_frame_eabi bufcurr;
+#if defined(__arm__)
+ const bool is_compat = false;
+ unsigned long fp = regs->ARM_fp;
+ unsigned long sp = regs->ARM_sp;
+ unsigned long lr = regs->ARM_lr;
+ const int gcc_frame_offset = sizeof(unsigned long);
+#else
+ // Is userspace aarch32 (32 bit)
+ const bool is_compat = compat_user_mode(regs);
+ unsigned long fp = (is_compat ? regs->regs[11] : regs->regs[29]);
+ unsigned long sp = (is_compat ? regs->compat_sp : regs->sp);
+ unsigned long lr = (is_compat ? regs->compat_lr : regs->regs[30]);
+ const int gcc_frame_offset = (is_compat ? sizeof(u32) : 0);
+#endif
+ // clang frame offset is always zero
+ int is_user_mode = user_mode(regs);
+
+ // pc (current function) has already been added
+
+ if (!is_user_mode) {
+ return;
+ }
+
+ // Add the lr (parent function)
+ // entry preamble may not have executed
+ gator_add_trace(cpu, lr);
+
+ // check fp is valid
+ if (fp == 0 || fp < sp) {
+ return;
+ }
+
+ // Get the current stack frame
+ curr = (struct stack_frame_eabi *)(fp - gcc_frame_offset);
+ if ((unsigned long)curr & 3) {
+ return;
+ }
+
+ while (depth-- && curr) {
+ if (!access_ok(VERIFY_READ, curr, sizeof(struct stack_frame_eabi)) ||
+ __copy_from_user_inatomic(&bufcurr, curr, sizeof(struct stack_frame_eabi))) {
+ return;
+ }
+
+ fp = (is_compat ? bufcurr.fp_32 : bufcurr.fp);
+ lr = (is_compat ? bufcurr.lr_32 : bufcurr.lr);
+
+#define calc_next(reg) ((reg) - gcc_frame_offset)
+ // Returns true if reg is a valid fp
+#define validate_next(reg, curr) \
+ ((reg) != 0 && (calc_next(reg) & 3) == 0 && (unsigned long)(curr) < calc_next(reg))
+
+ // Try lr from the stack as the fp because gcc leaf functions do not push lr
+ // If gcc_frame_offset is non-zero, the lr will also be the clang fp
+ // This assumes code is at a lower address than the stack
+ if (validate_next(lr, curr)) {
+ fp = lr;
+ lr = (is_compat ? bufcurr.lr2_32 : bufcurr.lr2);
+ }
+
+ gator_add_trace(cpu, lr);
+
+ if (!validate_next(fp, curr)) {
+ return;
+ }
+
+ // Move to the next stack frame
+ curr = (struct stack_frame_eabi *)calc_next(fp);
+ }
+#endif
+}
+
+#if defined(__arm__) || defined(__aarch64__)
+static int report_trace(struct stackframe *frame, void *d)
+{
+ unsigned int *depth = d, cookie = NO_COOKIE;
+ unsigned long addr = frame->pc;
+
+ if (*depth) {
+#if defined(MODULE)
+ unsigned int cpu = get_physical_cpu();
+ struct module *mod = __module_address(addr);
+ if (mod) {
+ cookie = get_cookie(cpu, current, mod->name, false);
+ addr = addr - (unsigned long)mod->module_core;
+ }
+#endif
+ marshal_backtrace(addr & ~1, cookie, 1);
+ (*depth)--;
+ }
+
+ return *depth == 0;
+}
+#endif
+
+// Uncomment the following line to enable kernel stack unwinding within gator, note it can also be defined from the Makefile
+// #define GATOR_KERNEL_STACK_UNWINDING
+
+#if (defined(__arm__) || defined(__aarch64__)) && !defined(GATOR_KERNEL_STACK_UNWINDING)
+// Disabled by default
+MODULE_PARM_DESC(kernel_stack_unwinding, "Allow kernel stack unwinding.");
+static bool kernel_stack_unwinding = 0;
+module_param(kernel_stack_unwinding, bool, 0644);
+#endif
+
+static void kernel_backtrace(int cpu, struct pt_regs *const regs)
+{
+#if defined(__arm__) || defined(__aarch64__)
+#ifdef GATOR_KERNEL_STACK_UNWINDING
+ int depth = gator_backtrace_depth;
+#else
+ int depth = (kernel_stack_unwinding ? gator_backtrace_depth : 1);
+#endif
+ struct stackframe frame;
+ if (depth == 0)
+ depth = 1;
+#if defined(__arm__)
+ frame.fp = regs->ARM_fp;
+ frame.sp = regs->ARM_sp;
+ frame.lr = regs->ARM_lr;
+ frame.pc = regs->ARM_pc;
+#else
+ frame.fp = regs->regs[29];
+ frame.sp = regs->sp;
+ frame.pc = regs->pc;
+#endif
+ walk_stackframe(&frame, report_trace, &depth);
+#else
+ marshal_backtrace(PC_REG & ~1, NO_COOKIE, 1);
+#endif
+}
+
+static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time)
+{
+ bool in_kernel;
+ unsigned long exec_cookie;
+
+ if (!regs)
+ return;
+
+ in_kernel = !user_mode(regs);
+ exec_cookie = get_exec_cookie(cpu, current);
+
+ if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, time))
+ return;
+
+ if (in_kernel) {
+ kernel_backtrace(cpu, regs);
+ } else {
+ // Cookie+PC
+ gator_add_trace(cpu, PC_REG);
+
+ // Backtrace
+ if (gator_backtrace_depth)
+ arm_backtrace_eabi(cpu, regs, gator_backtrace_depth);
+ }
+
+ marshal_backtrace_footer(time);
+}
diff --git a/drivers/gator/gator_buffer.c b/drivers/gator/gator_buffer.c
new file mode 100644
index 000000000000..dfbc97d80221
--- /dev/null
+++ b/drivers/gator/gator_buffer.c
@@ -0,0 +1,168 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+static void marshal_frame(int cpu, int buftype)
+{
+ int frame;
+
+ if (!per_cpu(gator_buffer, cpu)[buftype]) {
+ return;
+ }
+
+ switch (buftype) {
+ case SUMMARY_BUF:
+ frame = FRAME_SUMMARY;
+ break;
+ case BACKTRACE_BUF:
+ frame = FRAME_BACKTRACE;
+ break;
+ case NAME_BUF:
+ frame = FRAME_NAME;
+ break;
+ case COUNTER_BUF:
+ frame = FRAME_COUNTER;
+ break;
+ case BLOCK_COUNTER_BUF:
+ frame = FRAME_BLOCK_COUNTER;
+ break;
+ case ANNOTATE_BUF:
+ frame = FRAME_ANNOTATE;
+ break;
+ case SCHED_TRACE_BUF:
+ frame = FRAME_SCHED_TRACE;
+ break;
+ case IDLE_BUF:
+ frame = FRAME_IDLE;
+ break;
+ case ACTIVITY_BUF:
+ frame = FRAME_ACTIVITY;
+ break;
+ default:
+ frame = -1;
+ break;
+ }
+
+ // add response type
+ if (gator_response_type > 0) {
+ gator_buffer_write_packed_int(cpu, buftype, gator_response_type);
+ }
+
+ // leave space for 4-byte unpacked length
+ per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + sizeof(s32)) & gator_buffer_mask[buftype];
+
+ // add frame type and core number
+ gator_buffer_write_packed_int(cpu, buftype, frame);
+ gator_buffer_write_packed_int(cpu, buftype, cpu);
+}
+
+static int buffer_bytes_available(int cpu, int buftype)
+{
+ int remaining, filled;
+
+ filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype];
+ if (filled < 0) {
+ filled += gator_buffer_size[buftype];
+ }
+
+ remaining = gator_buffer_size[buftype] - filled;
+
+ if (per_cpu(buffer_space_available, cpu)[buftype]) {
+ // Give some extra room; also allows space to insert the overflow error packet
+ remaining -= 200;
+ } else {
+ // Hysteresis, prevents multiple overflow messages
+ remaining -= 2000;
+ }
+
+ return remaining;
+}
+
+static bool buffer_check_space(int cpu, int buftype, int bytes)
+{
+ int remaining = buffer_bytes_available(cpu, buftype);
+
+ if (remaining < bytes) {
+ per_cpu(buffer_space_available, cpu)[buftype] = false;
+ } else {
+ per_cpu(buffer_space_available, cpu)[buftype] = true;
+ }
+
+ return per_cpu(buffer_space_available, cpu)[buftype];
+}
+
+static int contiguous_space_available(int cpu, int buftype)
+{
+ int remaining = buffer_bytes_available(cpu, buftype);
+ int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype];
+ if (remaining < contiguous)
+ return remaining;
+ else
+ return contiguous;
+}
+
+static void gator_commit_buffer(int cpu, int buftype, u64 time)
+{
+ int type_length, commit, length, byte;
+ unsigned long flags;
+
+ if (!per_cpu(gator_buffer, cpu)[buftype])
+ return;
+
+ // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
+ local_irq_save(flags);
+ type_length = gator_response_type ? 1 : 0;
+ commit = per_cpu(gator_buffer_commit, cpu)[buftype];
+ length = per_cpu(gator_buffer_write, cpu)[buftype] - commit;
+ if (length < 0) {
+ length += gator_buffer_size[buftype];
+ }
+ length = length - type_length - sizeof(s32);
+
+ if (length <= FRAME_HEADER_SIZE) {
+ // Nothing to write, only the frame header is present
+ local_irq_restore(flags);
+ return;
+ }
+
+ for (byte = 0; byte < sizeof(s32); byte++) {
+ per_cpu(gator_buffer, cpu)[buftype][(commit + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF;
+ }
+
+ per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype];
+
+ if (gator_live_rate > 0) {
+ while (time > per_cpu(gator_buffer_commit_time, cpu)) {
+ per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate;
+ }
+ }
+
+ marshal_frame(cpu, buftype);
+ local_irq_restore(flags);
+
+ // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
+ if (per_cpu(in_scheduler_context, cpu)) {
+#ifndef CONFIG_PREEMPT_RT_FULL
+ // mod_timer can not be used in interrupt context in RT-Preempt full
+ mod_timer(&gator_buffer_wake_up_timer, jiffies + 1);
+#endif
+ } else {
+ up(&gator_buffer_wake_sem);
+ }
+}
+
+static void buffer_check(int cpu, int buftype, u64 time)
+{
+ int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype];
+ if (filled < 0) {
+ filled += gator_buffer_size[buftype];
+ }
+ if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) {
+ gator_commit_buffer(cpu, buftype, time);
+ }
+}
diff --git a/drivers/gator/gator_buffer_write.c b/drivers/gator/gator_buffer_write.c
new file mode 100644
index 000000000000..b621ba93ee5e
--- /dev/null
+++ b/drivers/gator/gator_buffer_write.c
@@ -0,0 +1,80 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+static void gator_buffer_write_packed_int(int cpu, int buftype, int x)
+{
+ uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype];
+ uint32_t mask = gator_buffer_mask[buftype];
+ char *buffer = per_cpu(gator_buffer, cpu)[buftype];
+ int packedBytes = 0;
+ int more = true;
+ while (more) {
+ // low order 7 bits of x
+ char b = x & 0x7f;
+ x >>= 7;
+
+ if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+
+ buffer[(write + packedBytes) & mask] = b;
+ packedBytes++;
+ }
+
+ per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask;
+}
+
+static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x)
+{
+ uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype];
+ uint32_t mask = gator_buffer_mask[buftype];
+ char *buffer = per_cpu(gator_buffer, cpu)[buftype];
+ int packedBytes = 0;
+ int more = true;
+ while (more) {
+ // low order 7 bits of x
+ char b = x & 0x7f;
+ x >>= 7;
+
+ if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+
+ buffer[(write + packedBytes) & mask] = b;
+ packedBytes++;
+ }
+
+ per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask;
+}
+
+static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len)
+{
+ int i;
+ u32 write = per_cpu(gator_buffer_write, cpu)[buftype];
+ u32 mask = gator_buffer_mask[buftype];
+ char *buffer = per_cpu(gator_buffer, cpu)[buftype];
+
+ for (i = 0; i < len; i++) {
+ buffer[write] = x[i];
+ write = (write + 1) & mask;
+ }
+
+ per_cpu(gator_buffer_write, cpu)[buftype] = write;
+}
+
+static void gator_buffer_write_string(int cpu, int buftype, const char *x)
+{
+ int len = strlen(x);
+ gator_buffer_write_packed_int(cpu, buftype, len);
+ gator_buffer_write_bytes(cpu, buftype, x, len);
+}
diff --git a/drivers/gator/gator_cookies.c b/drivers/gator/gator_cookies.c
new file mode 100644
index 000000000000..5c7d842070e0
--- /dev/null
+++ b/drivers/gator/gator_cookies.c
@@ -0,0 +1,437 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */
+#define TRANSLATE_BUFFER_SIZE 512 // must be a power of 2 - 512/4 = 128 entries
+#define TRANSLATE_TEXT_SIZE 256
+#define MAX_COLLISIONS 2
+
+static uint32_t *gator_crc32_table;
+static unsigned int translate_buffer_mask;
+
+struct cookie_args {
+ struct task_struct *task;
+ const char *text;
+};
+
+static DEFINE_PER_CPU(char *, translate_text);
+static DEFINE_PER_CPU(uint32_t, cookie_next_key);
+static DEFINE_PER_CPU(uint64_t *, cookie_keys);
+static DEFINE_PER_CPU(uint32_t *, cookie_values);
+static DEFINE_PER_CPU(int, translate_buffer_read);
+static DEFINE_PER_CPU(int, translate_buffer_write);
+static DEFINE_PER_CPU(struct cookie_args *, translate_buffer);
+
+static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq);
+static void wq_cookie_handler(struct work_struct *unused);
+static DECLARE_WORK(cookie_work, wq_cookie_handler);
+static struct timer_list app_process_wake_up_timer;
+static void app_process_wake_up_handler(unsigned long unused_data);
+
+static uint32_t cookiemap_code(uint64_t value64)
+{
+ uint32_t value = (uint32_t)((value64 >> 32) + value64);
+ uint32_t cookiecode = (value >> 24) & 0xff;
+ cookiecode = cookiecode * 31 + ((value >> 16) & 0xff);
+ cookiecode = cookiecode * 31 + ((value >> 8) & 0xff);
+ cookiecode = cookiecode * 31 + ((value >> 0) & 0xff);
+ cookiecode &= (COOKIEMAP_ENTRIES - 1);
+ return cookiecode * MAX_COLLISIONS;
+}
+
+static uint32_t gator_chksum_crc32(const char *data)
+{
+ register unsigned long crc;
+ const unsigned char *block = data;
+ int i, length = strlen(data);
+
+ crc = 0xFFFFFFFF;
+ for (i = 0; i < length; i++) {
+ crc = ((crc >> 8) & 0x00FFFFFF) ^ gator_crc32_table[(crc ^ *block++) & 0xFF];
+ }
+
+ return (crc ^ 0xFFFFFFFF);
+}
+
+/*
+ * Exists
+ * Pre: [0][1][v][3]..[n-1]
+ * Post: [v][0][1][3]..[n-1]
+ */
+static uint32_t cookiemap_exists(uint64_t key)
+{
+ unsigned long x, flags, retval = 0;
+ int cpu = get_physical_cpu();
+ uint32_t cookiecode = cookiemap_code(key);
+ uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]);
+ uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]);
+
+ // Can be called from interrupt handler or from work queue
+ local_irq_save(flags);
+ for (x = 0; x < MAX_COLLISIONS; x++) {
+ if (keys[x] == key) {
+ uint32_t value = values[x];
+ for (; x > 0; x--) {
+ keys[x] = keys[x - 1];
+ values[x] = values[x - 1];
+ }
+ keys[0] = key;
+ values[0] = value;
+ retval = value;
+ break;
+ }
+ }
+ local_irq_restore(flags);
+
+ return retval;
+}
+
+/*
+ * Add
+ * Pre: [0][1][2][3]..[n-1]
+ * Post: [v][0][1][2]..[n-2]
+ */
+static void cookiemap_add(uint64_t key, uint32_t value)
+{
+ int cpu = get_physical_cpu();
+ int cookiecode = cookiemap_code(key);
+ uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]);
+ uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]);
+ int x;
+
+ for (x = MAX_COLLISIONS - 1; x > 0; x--) {
+ keys[x] = keys[x - 1];
+ values[x] = values[x - 1];
+ }
+ keys[0] = key;
+ values[0] = value;
+}
+
+#ifndef CONFIG_PREEMPT_RT_FULL
+static void translate_buffer_write_args(int cpu, struct task_struct *task, const char *text)
+{
+ unsigned long flags;
+ int write;
+ int next_write;
+ struct cookie_args *args;
+
+ local_irq_save(flags);
+
+ write = per_cpu(translate_buffer_write, cpu);
+ next_write = (write + 1) & translate_buffer_mask;
+
+ // At least one entry must always remain available as when read == write, the queue is empty not full
+ if (next_write != per_cpu(translate_buffer_read, cpu)) {
+ args = &per_cpu(translate_buffer, cpu)[write];
+ args->task = task;
+ args->text = text;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ get_task_struct(task);
+#endif
+ per_cpu(translate_buffer_write, cpu) = next_write;
+ }
+
+ local_irq_restore(flags);
+}
+#endif
+
+static void translate_buffer_read_args(int cpu, struct cookie_args *args)
+{
+ unsigned long flags;
+ int read;
+
+ local_irq_save(flags);
+
+ read = per_cpu(translate_buffer_read, cpu);
+ *args = per_cpu(translate_buffer, cpu)[read];
+ per_cpu(translate_buffer_read, cpu) = (read + 1) & translate_buffer_mask;
+
+ local_irq_restore(flags);
+}
+
+static void wq_cookie_handler(struct work_struct *unused)
+{
+ struct cookie_args args;
+ int cpu = get_physical_cpu(), cookie;
+
+ mutex_lock(&start_mutex);
+
+ if (gator_started != 0) {
+ while (per_cpu(translate_buffer_read, cpu) != per_cpu(translate_buffer_write, cpu)) {
+ translate_buffer_read_args(cpu, &args);
+ cookie = get_cookie(cpu, args.task, args.text, true);
+ marshal_link(cookie, args.task->tgid, args.task->pid);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ put_task_struct(args.task);
+#endif
+ }
+ }
+
+ mutex_unlock(&start_mutex);
+}
+
+static void app_process_wake_up_handler(unsigned long unused_data)
+{
+ // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
+ schedule_work(&cookie_work);
+}
+
+// Retrieve full name from proc/pid/cmdline for java processes on Android
+static int translate_app_process(const char **text, int cpu, struct task_struct *task, bool from_wq)
+{
+ void *maddr;
+ unsigned int len;
+ unsigned long addr;
+ struct mm_struct *mm;
+ struct page *page = NULL;
+ struct vm_area_struct *page_vma;
+ int bytes, offset, retval = 0;
+ char *buf = per_cpu(translate_text, cpu);
+
+#ifndef CONFIG_PREEMPT_RT_FULL
+ // Push work into a work queue if in atomic context as the kernel functions below might sleep
+ // Rely on the in_interrupt variable rather than in_irq() or in_interrupt() kernel functions, as the value of these functions seems
+ // inconsistent during a context switch between android/linux versions
+ if (!from_wq) {
+ // Check if already in buffer
+ int pos = per_cpu(translate_buffer_read, cpu);
+ while (pos != per_cpu(translate_buffer_write, cpu)) {
+ if (per_cpu(translate_buffer, cpu)[pos].task == task)
+ goto out;
+ pos = (pos + 1) & translate_buffer_mask;
+ }
+
+ translate_buffer_write_args(cpu, task, *text);
+
+ // Not safe to call in RT-Preempt full in schedule switch context
+ mod_timer(&app_process_wake_up_timer, jiffies + 1);
+ goto out;
+ }
+#endif
+
+ mm = get_task_mm(task);
+ if (!mm)
+ goto out;
+ if (!mm->arg_end)
+ goto outmm;
+ addr = mm->arg_start;
+ len = mm->arg_end - mm->arg_start;
+
+ if (len > TRANSLATE_TEXT_SIZE)
+ len = TRANSLATE_TEXT_SIZE;
+
+ down_read(&mm->mmap_sem);
+ while (len) {
+ if (get_user_pages(task, mm, addr, 1, 0, 1, &page, &page_vma) <= 0)
+ goto outsem;
+
+ maddr = kmap(page);
+ offset = addr & (PAGE_SIZE - 1);
+ bytes = len;
+ if (bytes > PAGE_SIZE - offset)
+ bytes = PAGE_SIZE - offset;
+
+ copy_from_user_page(page_vma, page, addr, buf, maddr + offset, bytes);
+
+ kunmap(page); // release page allocated by get_user_pages()
+ page_cache_release(page);
+
+ len -= bytes;
+ buf += bytes;
+ addr += bytes;
+
+ *text = per_cpu(translate_text, cpu);
+ retval = 1;
+ }
+
+ // On app_process startup, /proc/pid/cmdline is initially "zygote" then "<pre-initialized>" but changes after an initial startup period
+ if (strcmp(*text, "zygote") == 0 || strcmp(*text, "<pre-initialized>") == 0)
+ retval = 0;
+
+outsem:
+ up_read(&mm->mmap_sem);
+outmm:
+ mmput(mm);
+out:
+ return retval;
+}
+
+static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq)
+{
+ unsigned long flags, cookie;
+ uint64_t key;
+
+ key = gator_chksum_crc32(text);
+ key = (key << 32) | (uint32_t)task->tgid;
+
+ cookie = cookiemap_exists(key);
+ if (cookie) {
+ return cookie;
+ }
+
+ if (strcmp(text, "app_process") == 0) {
+ if (!translate_app_process(&text, cpu, task, from_wq))
+ return UNRESOLVED_COOKIE;
+ }
+
+ // Can be called from interrupt handler or from work queue or from scheduler trace
+ local_irq_save(flags);
+
+ cookie = UNRESOLVED_COOKIE;
+ if (marshal_cookie_header(text)) {
+ cookie = per_cpu(cookie_next_key, cpu) += nr_cpu_ids;
+ cookiemap_add(key, cookie);
+ marshal_cookie(cookie, text);
+ }
+
+ local_irq_restore(flags);
+
+ return cookie;
+}
+
+static int get_exec_cookie(int cpu, struct task_struct *task)
+{
+ struct mm_struct *mm = task->mm;
+ const char *text;
+
+ // kernel threads have no address space
+ if (!mm)
+ return NO_COOKIE;
+
+ if (task && task->mm && task->mm->exe_file) {
+ text = task->mm->exe_file->f_path.dentry->d_name.name;
+ return get_cookie(cpu, task, text, false);
+ }
+
+ return UNRESOLVED_COOKIE;
+}
+
+static unsigned long get_address_cookie(int cpu, struct task_struct *task, unsigned long addr, off_t *offset)
+{
+ unsigned long cookie = NO_COOKIE;
+ struct mm_struct *mm = task->mm;
+ struct vm_area_struct *vma;
+ const char *text;
+
+ if (!mm)
+ return cookie;
+
+ for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) {
+ if (addr < vma->vm_start || addr >= vma->vm_end)
+ continue;
+
+ if (vma->vm_file) {
+ text = vma->vm_file->f_path.dentry->d_name.name;
+ cookie = get_cookie(cpu, task, text, false);
+ *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start;
+ } else {
+ /* must be an anonymous map */
+ *offset = addr;
+ }
+
+ break;
+ }
+
+ if (!vma)
+ cookie = UNRESOLVED_COOKIE;
+
+ return cookie;
+}
+
+static int cookies_initialize(void)
+{
+ uint32_t crc, poly;
+ int i, j, cpu, size, err = 0;
+
+ translate_buffer_mask = TRANSLATE_BUFFER_SIZE / sizeof(per_cpu(translate_buffer, 0)[0]) - 1;
+
+ for_each_present_cpu(cpu) {
+ per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu;
+
+ size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint64_t);
+ per_cpu(cookie_keys, cpu) = (uint64_t *)kmalloc(size, GFP_KERNEL);
+ if (!per_cpu(cookie_keys, cpu)) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
+ memset(per_cpu(cookie_keys, cpu), 0, size);
+
+ size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint32_t);
+ per_cpu(cookie_values, cpu) = (uint32_t *)kmalloc(size, GFP_KERNEL);
+ if (!per_cpu(cookie_values, cpu)) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
+ memset(per_cpu(cookie_values, cpu), 0, size);
+
+ per_cpu(translate_buffer, cpu) = (struct cookie_args *)kmalloc(TRANSLATE_BUFFER_SIZE, GFP_KERNEL);
+ if (!per_cpu(translate_buffer, cpu)) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
+
+ per_cpu(translate_buffer_write, cpu) = 0;
+ per_cpu(translate_buffer_read, cpu) = 0;
+
+ per_cpu(translate_text, cpu) = (char *)kmalloc(TRANSLATE_TEXT_SIZE, GFP_KERNEL);
+ if (!per_cpu(translate_text, cpu)) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
+ }
+
+ // build CRC32 table
+ poly = 0x04c11db7;
+ gator_crc32_table = (uint32_t *)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL);
+ if (!gator_crc32_table) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
+ for (i = 0; i < 256; i++) {
+ crc = i;
+ for (j = 8; j > 0; j--) {
+ if (crc & 1) {
+ crc = (crc >> 1) ^ poly;
+ } else {
+ crc >>= 1;
+ }
+ }
+ gator_crc32_table[i] = crc;
+ }
+
+ setup_timer(&app_process_wake_up_timer, app_process_wake_up_handler, 0);
+
+cookie_setup_error:
+ return err;
+}
+
+static void cookies_release(void)
+{
+ int cpu;
+
+ for_each_present_cpu(cpu) {
+ kfree(per_cpu(cookie_keys, cpu));
+ per_cpu(cookie_keys, cpu) = NULL;
+
+ kfree(per_cpu(cookie_values, cpu));
+ per_cpu(cookie_values, cpu) = NULL;
+
+ kfree(per_cpu(translate_buffer, cpu));
+ per_cpu(translate_buffer, cpu) = NULL;
+ per_cpu(translate_buffer_read, cpu) = 0;
+ per_cpu(translate_buffer_write, cpu) = 0;
+
+ kfree(per_cpu(translate_text, cpu));
+ per_cpu(translate_text, cpu) = NULL;
+ }
+
+ del_timer_sync(&app_process_wake_up_timer);
+ kfree(gator_crc32_table);
+ gator_crc32_table = NULL;
+}
diff --git a/drivers/gator/gator_events_armv6.c b/drivers/gator/gator_events_armv6.c
new file mode 100644
index 000000000000..353645622306
--- /dev/null
+++ b/drivers/gator/gator_events_armv6.c
@@ -0,0 +1,237 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+
+// gator_events_perf_pmu.c is used if perf is supported
+#if GATOR_NO_PERF_SUPPORT
+
+static const char *pmnc_name;
+
+/*
+ * Per-CPU PMCR
+ */
+#define PMCR_E (1 << 0) /* Enable */
+#define PMCR_P (1 << 1) /* Count reset */
+#define PMCR_C (1 << 2) /* Cycle counter reset */
+#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */
+#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */
+#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */
+
+#define PMN0 0
+#define PMN1 1
+#define CCNT 2
+#define CNTMAX (CCNT+1)
+
+static int pmnc_counters = 0;
+static unsigned long pmnc_enabled[CNTMAX];
+static unsigned long pmnc_event[CNTMAX];
+static unsigned long pmnc_key[CNTMAX];
+
+static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
+
+static inline void armv6_pmnc_write(u32 val)
+{
+ /* upper 4bits and 7, 11 are write-as-0 */
+ val &= 0x0ffff77f;
+ asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val));
+}
+
+static inline u32 armv6_pmnc_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val));
+ return val;
+}
+
+static void armv6_pmnc_reset_counter(unsigned int cnt)
+{
+ u32 val = 0;
+ switch (cnt) {
+ case CCNT:
+ asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val));
+ break;
+ case PMN0:
+ asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val));
+ break;
+ case PMN1:
+ asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val));
+ break;
+ }
+}
+
+int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+
+ pmnc_counters = 3;
+
+ for (i = PMN0; i <= CCNT; i++) {
+ char buf[40];
+ if (i == CCNT) {
+ snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
+ } else {
+ snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i);
+ }
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
+ if (i != CCNT) {
+ gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int gator_events_armv6_online(int **buffer, bool migrate)
+{
+ unsigned int cnt, len = 0, cpu = smp_processor_id();
+ u32 pmnc;
+
+ if (armv6_pmnc_read() & PMCR_E) {
+ armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E);
+ }
+
+ /* initialize PMNC, reset overflow, D bit, C bit and P bit. */
+ armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT |
+ PMCR_C | PMCR_P);
+
+ /* configure control register */
+ for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) {
+ unsigned long event;
+
+ if (!pmnc_enabled[cnt])
+ continue;
+
+ event = pmnc_event[cnt] & 255;
+
+ // Set event (if destined for PMNx counters)
+ if (cnt == PMN0) {
+ pmnc |= event << 20;
+ } else if (cnt == PMN1) {
+ pmnc |= event << 12;
+ }
+
+ // Reset counter
+ armv6_pmnc_reset_counter(cnt);
+ }
+ armv6_pmnc_write(pmnc | PMCR_E);
+
+ // return zero values, no need to read as the counters were just reset
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = 0;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static int gator_events_armv6_offline(int **buffer, bool migrate)
+{
+ unsigned int cnt;
+
+ armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E);
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ armv6_pmnc_reset_counter(cnt);
+ }
+
+ return 0;
+}
+
+static void gator_events_armv6_stop(void)
+{
+ unsigned int cnt;
+
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ }
+}
+
+static int gator_events_armv6_read(int **buffer)
+{
+ int cnt, len = 0;
+ int cpu = smp_processor_id();
+
+ // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled
+ if (!(armv6_pmnc_read() & PMCR_E)) {
+ return 0;
+ }
+
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ u32 value = 0;
+ switch (cnt) {
+ case CCNT:
+ asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value));
+ break;
+ case PMN0:
+ asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value));
+ break;
+ case PMN1:
+ asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value));
+ break;
+ }
+ armv6_pmnc_reset_counter(cnt);
+
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = value;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_armv6_interface = {
+ .create_files = gator_events_armv6_create_files,
+ .stop = gator_events_armv6_stop,
+ .online = gator_events_armv6_online,
+ .offline = gator_events_armv6_offline,
+ .read = gator_events_armv6_read,
+};
+
+int gator_events_armv6_init(void)
+{
+ unsigned int cnt;
+
+ switch (gator_cpuid()) {
+ case ARM1136:
+ case ARM1156:
+ case ARM1176:
+ pmnc_name = "ARM11";
+ break;
+ case ARM11MPCORE:
+ pmnc_name = "ARM11MPCore";
+ break;
+ default:
+ return -1;
+ }
+
+ for (cnt = PMN0; cnt <= CCNT; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ pmnc_key[cnt] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_armv6_interface);
+}
+
+#endif
diff --git a/drivers/gator/gator_events_armv7.c b/drivers/gator/gator_events_armv7.c
new file mode 100644
index 000000000000..bd8a9ba24e99
--- /dev/null
+++ b/drivers/gator/gator_events_armv7.c
@@ -0,0 +1,314 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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.
+ */
+
+/* Disabling interrupts
+ * Many of the functions below disable interrupts via local_irq_save(). This disabling of interrupts is done to prevent any race conditions
+ * between multiple entities (e.g. hrtimer interrupts and event based interrupts) calling the same functions. As accessing the pmu involves
+ * several steps (disable, select, read, enable), these steps must be performed atomically. Normal synchronization routines cannot be used
+ * as these functions are being called from interrupt context.
+ */
+
+#include "gator.h"
+
+// gator_events_perf_pmu.c is used if perf is supported
+#if GATOR_NO_PERF_SUPPORT
+
+// Per-CPU PMNC: config reg
+#define PMNC_E (1 << 0) /* Enable all counters */
+#define PMNC_P (1 << 1) /* Reset all counters */
+#define PMNC_C (1 << 2) /* Cycle counter reset */
+#define PMNC_MASK 0x3f /* Mask for writable bits */
+
+// ccnt reg
+#define CCNT_REG (1 << 31)
+
+#define CCNT 0
+#define CNT0 1
+#define CNTMAX (6+1)
+
+static const char *pmnc_name;
+static int pmnc_counters;
+
+static unsigned long pmnc_enabled[CNTMAX];
+static unsigned long pmnc_event[CNTMAX];
+static unsigned long pmnc_key[CNTMAX];
+
+static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
+
+inline void armv7_pmnc_write(u32 val)
+{
+ val &= PMNC_MASK;
+ asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
+}
+
+inline u32 armv7_pmnc_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
+ return val;
+}
+
+inline u32 armv7_ccnt_read(u32 reset_value)
+{
+ unsigned long flags;
+ u32 newval = -reset_value;
+ u32 den = CCNT_REG;
+ u32 val;
+
+ local_irq_save(flags);
+ asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
+ asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); // read
+ asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (newval)); // new value
+ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
+ local_irq_restore(flags);
+
+ return val;
+}
+
+inline u32 armv7_cntn_read(unsigned int cnt, u32 reset_value)
+{
+ unsigned long flags;
+ u32 newval = -reset_value;
+ u32 sel = (cnt - CNT0);
+ u32 den = 1 << sel;
+ u32 oldval;
+
+ local_irq_save(flags);
+ asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
+ asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel)); // select
+ asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (oldval)); // read
+ asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (newval)); // new value
+ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
+ local_irq_restore(flags);
+
+ return oldval;
+}
+
+static inline void armv7_pmnc_disable_interrupt(unsigned int cnt)
+{
+ u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31);
+ asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val));
+}
+
+inline u32 armv7_pmnc_reset_interrupt(void)
+{
+ // Get and reset overflow status flags
+ u32 flags;
+ asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (flags));
+ flags &= 0x8000003f;
+ asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (flags));
+ return flags;
+}
+
+static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
+{
+ u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
+ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
+ return cnt;
+}
+
+static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
+{
+ u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG;
+ asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
+ return cnt;
+}
+
+static inline int armv7_pmnc_select_counter(unsigned int cnt)
+{
+ u32 val = (cnt - CNT0);
+ asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
+ return cnt;
+}
+
+static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
+{
+ if (armv7_pmnc_select_counter(cnt) == cnt) {
+ asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
+ }
+}
+
+static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+
+ for (i = 0; i < pmnc_counters; i++) {
+ char buf[40];
+ if (i == 0) {
+ snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name);
+ } else {
+ snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i - 1);
+ }
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
+ if (i > 0) {
+ gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int gator_events_armv7_online(int **buffer, bool migrate)
+{
+ unsigned int cnt, len = 0, cpu = smp_processor_id();
+
+ if (armv7_pmnc_read() & PMNC_E) {
+ armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
+ }
+
+ // Initialize & Reset PMNC: C bit and P bit
+ armv7_pmnc_write(PMNC_P | PMNC_C);
+
+ // Reset overflow flags
+ armv7_pmnc_reset_interrupt();
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ unsigned long event;
+
+ if (!pmnc_enabled[cnt])
+ continue;
+
+ // Disable counter
+ armv7_pmnc_disable_counter(cnt);
+
+ event = pmnc_event[cnt] & 255;
+
+ // Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count
+ if (cnt != CCNT)
+ armv7_pmnc_write_evtsel(cnt, event);
+
+ armv7_pmnc_disable_interrupt(cnt);
+
+ // Reset counter
+ cnt ? armv7_cntn_read(cnt, 0) : armv7_ccnt_read(0);
+
+ // Enable counter
+ armv7_pmnc_enable_counter(cnt);
+ }
+
+ // enable
+ armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
+
+ // return zero values, no need to read as the counters were just reset
+ for (cnt = 0; cnt < pmnc_counters; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = 0;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static int gator_events_armv7_offline(int **buffer, bool migrate)
+{
+ // disable all counters, including PMCCNTR; overflow IRQs will not be signaled
+ armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
+
+ return 0;
+}
+
+static void gator_events_armv7_stop(void)
+{
+ unsigned int cnt;
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ }
+}
+
+static int gator_events_armv7_read(int **buffer)
+{
+ int cnt, len = 0;
+ int cpu = smp_processor_id();
+
+ // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled
+ if (!(armv7_pmnc_read() & PMNC_E)) {
+ return 0;
+ }
+
+ for (cnt = 0; cnt < pmnc_counters; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ int value;
+ if (cnt == CCNT) {
+ value = armv7_ccnt_read(0);
+ } else {
+ value = armv7_cntn_read(cnt, 0);
+ }
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = value;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_armv7_interface = {
+ .create_files = gator_events_armv7_create_files,
+ .stop = gator_events_armv7_stop,
+ .online = gator_events_armv7_online,
+ .offline = gator_events_armv7_offline,
+ .read = gator_events_armv7_read,
+};
+
+int gator_events_armv7_init(void)
+{
+ unsigned int cnt;
+
+ switch (gator_cpuid()) {
+ case CORTEX_A5:
+ pmnc_name = "ARMv7_Cortex_A5";
+ pmnc_counters = 2;
+ break;
+ case CORTEX_A7:
+ pmnc_name = "ARMv7_Cortex_A7";
+ pmnc_counters = 4;
+ break;
+ case CORTEX_A8:
+ pmnc_name = "ARMv7_Cortex_A8";
+ pmnc_counters = 4;
+ break;
+ case CORTEX_A9:
+ pmnc_name = "ARMv7_Cortex_A9";
+ pmnc_counters = 6;
+ break;
+ // ARM Cortex A12 is not supported by version of Linux before 3.0
+ case CORTEX_A15:
+ pmnc_name = "ARMv7_Cortex_A15";
+ pmnc_counters = 6;
+ break;
+ // ARM Cortex A17 is not supported by version of Linux before 3.0
+ default:
+ return -1;
+ }
+
+ pmnc_counters++; // CNT[n] + CCNT
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ pmnc_key[cnt] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_armv7_interface);
+}
+
+#endif
diff --git a/drivers/gator/gator_events_block.c b/drivers/gator/gator_events_block.c
new file mode 100644
index 000000000000..03eed4fb9ebb
--- /dev/null
+++ b/drivers/gator/gator_events_block.c
@@ -0,0 +1,163 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+#include <trace/events/block.h>
+
+#define BLOCK_RQ_WR 0
+#define BLOCK_RQ_RD 1
+
+#define BLOCK_TOTAL (BLOCK_RQ_RD+1)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+#define EVENTWRITE REQ_RW
+#else
+#define EVENTWRITE REQ_WRITE
+#endif
+
+static ulong block_rq_wr_enabled;
+static ulong block_rq_rd_enabled;
+static ulong block_rq_wr_key;
+static ulong block_rq_rd_key;
+static atomic_t blockCnt[BLOCK_TOTAL];
+static int blockGet[BLOCK_TOTAL * 4];
+
+// Tracepoint changed in 3.15 backported to older kernels. The Makefile tries to autodetect the correct value, but if it fails change the #if below
+#if OLD_BLOCK_RQ_COMPLETE
+GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq))
+#else
+GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq, unsigned int nr_bytes))
+#endif
+{
+ int write;
+ unsigned int size;
+
+ if (!rq)
+ return;
+
+ write = rq->cmd_flags & EVENTWRITE;
+#if OLD_BLOCK_RQ_COMPLETE
+ size = rq->resid_len;
+#else
+ size = nr_bytes;
+#endif
+
+ if (!size)
+ return;
+
+ if (write) {
+ if (block_rq_wr_enabled) {
+ atomic_add(size, &blockCnt[BLOCK_RQ_WR]);
+ }
+ } else {
+ if (block_rq_rd_enabled) {
+ atomic_add(size, &blockCnt[BLOCK_RQ_RD]);
+ }
+ }
+}
+
+static int gator_events_block_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ /* block_complete_wr */
+ dir = gatorfs_mkdir(sb, root, "Linux_block_rq_wr");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &block_rq_wr_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_wr_key);
+
+ /* block_complete_rd */
+ dir = gatorfs_mkdir(sb, root, "Linux_block_rq_rd");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &block_rq_rd_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_rd_key);
+
+ return 0;
+}
+
+static int gator_events_block_start(void)
+{
+ // register tracepoints
+ if (block_rq_wr_enabled || block_rq_rd_enabled)
+ if (GATOR_REGISTER_TRACE(block_rq_complete))
+ goto fail_block_rq_exit;
+ pr_debug("gator: registered block event tracepoints\n");
+
+ return 0;
+
+ // unregister tracepoints on error
+fail_block_rq_exit:
+ pr_err("gator: block event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+static void gator_events_block_stop(void)
+{
+ if (block_rq_wr_enabled || block_rq_rd_enabled)
+ GATOR_UNREGISTER_TRACE(block_rq_complete);
+ pr_debug("gator: unregistered block event tracepoints\n");
+
+ block_rq_wr_enabled = 0;
+ block_rq_rd_enabled = 0;
+}
+
+static int gator_events_block_read(int **buffer)
+{
+ int len, value, data = 0;
+
+ if (!on_primary_core()) {
+ return 0;
+ }
+
+ len = 0;
+ if (block_rq_wr_enabled && (value = atomic_read(&blockCnt[BLOCK_RQ_WR])) > 0) {
+ atomic_sub(value, &blockCnt[BLOCK_RQ_WR]);
+ blockGet[len++] = block_rq_wr_key;
+ blockGet[len++] = 0; // indicates to Streamline that value bytes were written now, not since the last message
+ blockGet[len++] = block_rq_wr_key;
+ blockGet[len++] = value;
+ data += value;
+ }
+ if (block_rq_rd_enabled && (value = atomic_read(&blockCnt[BLOCK_RQ_RD])) > 0) {
+ atomic_sub(value, &blockCnt[BLOCK_RQ_RD]);
+ blockGet[len++] = block_rq_rd_key;
+ blockGet[len++] = 0; // indicates to Streamline that value bytes were read now, not since the last message
+ blockGet[len++] = block_rq_rd_key;
+ blockGet[len++] = value;
+ data += value;
+ }
+
+ if (buffer)
+ *buffer = blockGet;
+
+ return len;
+}
+
+static struct gator_interface gator_events_block_interface = {
+ .create_files = gator_events_block_create_files,
+ .start = gator_events_block_start,
+ .stop = gator_events_block_stop,
+ .read = gator_events_block_read,
+};
+
+int gator_events_block_init(void)
+{
+ block_rq_wr_enabled = 0;
+ block_rq_rd_enabled = 0;
+
+ block_rq_wr_key = gator_events_get_key();
+ block_rq_rd_key = gator_events_get_key();
+
+ return gator_events_install(&gator_events_block_interface);
+}
diff --git a/drivers/gator/gator_events_ccn-504.c b/drivers/gator/gator_events_ccn-504.c
new file mode 100644
index 000000000000..024ffc2856aa
--- /dev/null
+++ b/drivers/gator/gator_events_ccn-504.c
@@ -0,0 +1,346 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 <linux/io.h>
+#include <linux/module.h>
+
+#include "gator.h"
+
+#define NUM_REGIONS 256
+#define REGION_SIZE (64*1024)
+#define REGION_DEBUG 1
+#define REGION_XP 64
+#define NUM_XPS 11
+
+// DT (Debug) region
+#define PMEVCNTSR0 0x0150
+#define PMCCNTRSR 0x0190
+#define PMCR 0x01A8
+#define PMSR 0x01B0
+#define PMSR_REQ 0x01B8
+#define PMSR_CLR 0x01C0
+
+// XP region
+#define DT_CONFIG 0x0300
+#define DT_CONTROL 0x0370
+
+// Multiple
+#define PMU_EVENT_SEL 0x0600
+#define OLY_ID 0xFF00
+
+#define CCNT 4
+#define CNTMAX (CCNT + 1)
+
+#define get_pmu_event_id(event) (((event) >> 0) & 0xFF)
+#define get_node_type(event) (((event) >> 8) & 0xFF)
+#define get_region(event) (((event) >> 16) & 0xFF)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+
+// From kernel/params.c
+#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \
+ int param_set_##name(const char *val, struct kernel_param *kp) \
+ { \
+ tmptype l; \
+ int ret; \
+ \
+ if (!val) return -EINVAL; \
+ ret = strtolfn(val, 0, &l); \
+ if (ret == -EINVAL || ((type)l != l)) \
+ return -EINVAL; \
+ *((type *)kp->arg) = l; \
+ return 0; \
+ } \
+ int param_get_##name(char *buffer, struct kernel_param *kp) \
+ { \
+ return sprintf(buffer, format, *((type *)kp->arg)); \
+ }
+
+#else
+
+// From kernel/params.c
+#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \
+ int param_set_##name(const char *val, const struct kernel_param *kp) \
+ { \
+ tmptype l; \
+ int ret; \
+ \
+ ret = strtolfn(val, 0, &l); \
+ if (ret < 0 || ((type)l != l)) \
+ return ret < 0 ? ret : -EINVAL; \
+ *((type *)kp->arg) = l; \
+ return 0; \
+ } \
+ int param_get_##name(char *buffer, const struct kernel_param *kp) \
+ { \
+ return scnprintf(buffer, PAGE_SIZE, format, \
+ *((type *)kp->arg)); \
+ } \
+ struct kernel_param_ops param_ops_##name = { \
+ .set = param_set_##name, \
+ .get = param_get_##name, \
+ }; \
+ EXPORT_SYMBOL(param_set_##name); \
+ EXPORT_SYMBOL(param_get_##name); \
+ EXPORT_SYMBOL(param_ops_##name)
+
+#endif
+
+STANDARD_PARAM_DEF(u64, u64, "%llu", u64, strict_strtoull);
+
+// From include/linux/moduleparam.h
+#define param_check_u64(name, p) __param_check(name, p, u64)
+
+MODULE_PARM_DESC(ccn504_addr, "CCN-504 physical base address");
+static u64 ccn504_addr = 0;
+module_param(ccn504_addr, u64, 0444);
+
+static void __iomem *gator_events_ccn504_base;
+static bool gator_events_ccn504_global_enabled;
+static unsigned long gator_events_ccn504_enabled[CNTMAX];
+static unsigned long gator_events_ccn504_event[CNTMAX];
+static unsigned long gator_events_ccn504_key[CNTMAX];
+static int gator_events_ccn504_buffer[2*CNTMAX];
+static int gator_events_ccn504_prev[CNTMAX];
+
+static void gator_events_ccn504_create_shutdown(void)
+{
+ if (gator_events_ccn504_base != NULL) {
+ iounmap(gator_events_ccn504_base);
+ }
+}
+
+static int gator_events_ccn504_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+ char buf[32];
+
+ for (i = 0; i < CNTMAX; ++i) {
+ if (i == CCNT) {
+ snprintf(buf, sizeof(buf), "CCN-504_ccnt");
+ } else {
+ snprintf(buf, sizeof(buf), "CCN-504_cnt%i", i);
+ }
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (!dir) {
+ return -1;
+ }
+
+ gatorfs_create_ulong(sb, dir, "enabled", &gator_events_ccn504_enabled[i]);
+ if (i != CCNT) {
+ gatorfs_create_ulong(sb, dir, "event", &gator_events_ccn504_event[i]);
+ }
+ gatorfs_create_ro_ulong(sb, dir, "key", &gator_events_ccn504_key[i]);
+ }
+
+ return 0;
+}
+
+static void gator_events_ccn504_set_dt_config(int xp_node_id, int event_num, int value)
+{
+ u32 dt_config;
+
+ dt_config = readl(gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
+ dt_config |= (value + event_num) << (4*event_num);
+ writel(dt_config, gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG);
+}
+
+static int gator_events_ccn504_start(void)
+{
+ int i;
+
+ gator_events_ccn504_global_enabled = 0;
+ for (i = 0; i < CNTMAX; ++i) {
+ if (gator_events_ccn504_enabled[i]) {
+ gator_events_ccn504_global_enabled = 1;
+ break;
+ }
+ }
+
+ if (!gator_events_ccn504_global_enabled) {
+ return 0;
+ }
+
+ memset(&gator_events_ccn504_prev, 0x80, sizeof(gator_events_ccn504_prev));
+
+ // Disable INTREQ on overflow
+ // [6] ovfl_intr_en = 0
+ // perhaps set to 1?
+ // [5] cntr_rst = 0
+ // No register paring
+ // [4:1] cntcfg = 0
+ // Enable PMU features
+ // [0] pmu_en = 1
+ writel(0x1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMCR);
+
+ // Configure the XPs
+ for (i = 0; i < NUM_XPS; ++i) {
+ int dt_control;
+
+ // Pass on all events
+ writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG);
+
+ // Enable PMU capability
+ // [0] dt_enable = 1
+ dt_control = readl(gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL);
+ dt_control |= 0x1;
+ writel(dt_control, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL);
+ }
+
+ // Assume no other pmu_event_sel registers are set
+
+ // cycle counter does not need to be enabled
+ for (i = 0; i < CCNT; ++i) {
+ int pmu_event_id;
+ int node_type;
+ int region;
+ u32 pmu_event_sel;
+ u32 oly_id_whole;
+ u32 oly_id;
+ u32 node_id;
+
+ if (!gator_events_ccn504_enabled[i]) {
+ continue;
+ }
+
+ pmu_event_id = get_pmu_event_id(gator_events_ccn504_event[i]);
+ node_type = get_node_type(gator_events_ccn504_event[i]);
+ region = get_region(gator_events_ccn504_event[i]);
+
+ // Verify the node_type
+ oly_id_whole = readl(gator_events_ccn504_base + region*REGION_SIZE + OLY_ID);
+ oly_id = oly_id_whole & 0x1F;
+ node_id = (oly_id_whole >> 8) & 0x7F;
+ if ((oly_id != node_type) ||
+ ((node_type == 0x16) && ((oly_id != 0x14) && (oly_id != 0x15) && (oly_id != 0x16) && (oly_id != 0x18) && (oly_id != 0x19) && (oly_id != 0x1A)))) {
+ printk(KERN_ERR "gator: oly_id is 0x%x expected 0x%x\n", oly_id, node_type);
+ return -1;
+ }
+
+ // Set the control register
+ pmu_event_sel = readl(gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
+ switch (node_type) {
+ case 0x08: // XP
+ pmu_event_sel |= pmu_event_id << (7*i);
+ gator_events_ccn504_set_dt_config(node_id, i, 0x4);
+ break;
+ case 0x04: // HN-F
+ case 0x16: // RN-I
+ case 0x10: // SBAS
+ pmu_event_sel |= pmu_event_id << (4*i);
+ gator_events_ccn504_set_dt_config(node_id/2, i, (node_id & 1) == 0 ? 0x8 : 0xC);
+ break;
+ }
+ writel(pmu_event_sel, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
+ }
+
+ return 0;
+}
+
+static void gator_events_ccn504_stop(void)
+{
+ int i;
+
+ if (!gator_events_ccn504_global_enabled) {
+ return;
+ }
+
+ // cycle counter does not need to be disabled
+ for (i = 0; i < CCNT; ++i) {
+ int region;
+
+ if (!gator_events_ccn504_enabled[i]) {
+ continue;
+ }
+
+ region = get_region(gator_events_ccn504_event[i]);
+
+ writel(0, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL);
+ }
+
+ // Clear dt_config
+ for (i = 0; i < NUM_XPS; ++i) {
+ writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG);
+ }
+}
+
+static int gator_events_ccn504_read(int **buffer)
+{
+ int i;
+ int len = 0;
+ int value;
+
+ if (!on_primary_core() || !gator_events_ccn504_global_enabled) {
+ return 0;
+ }
+
+ // Verify the pmsr register is zero
+ while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) != 0);
+
+ // Request a PMU snapshot
+ writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_REQ);
+
+ // Wait for the snapshot
+ while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) == 0);
+
+ // Read the shadow registers
+ for (i = 0; i < CNTMAX; ++i) {
+ if (!gator_events_ccn504_enabled[i]) {
+ continue;
+ }
+
+ value = readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + (i == CCNT ? PMCCNTRSR : PMEVCNTSR0 + 8*i));
+ if (gator_events_ccn504_prev[i] != 0x80808080) {
+ gator_events_ccn504_buffer[len++] = gator_events_ccn504_key[i];
+ gator_events_ccn504_buffer[len++] = value - gator_events_ccn504_prev[i];
+ }
+ gator_events_ccn504_prev[i] = value;
+
+ // Are the counters registers cleared when read? Is that what the cntr_rst bit on the pmcr register does?
+ }
+
+ // Clear the PMU snapshot status
+ writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_CLR);
+
+ if (buffer)
+ *buffer = gator_events_ccn504_buffer;
+
+ return len;
+}
+
+static struct gator_interface gator_events_ccn504_interface = {
+ .shutdown = gator_events_ccn504_create_shutdown,
+ .create_files = gator_events_ccn504_create_files,
+ .start = gator_events_ccn504_start,
+ .stop = gator_events_ccn504_stop,
+ .read = gator_events_ccn504_read,
+};
+
+int gator_events_ccn504_init(void)
+{
+ int i;
+
+ if (ccn504_addr == 0) {
+ return -1;
+ }
+
+ gator_events_ccn504_base = ioremap(ccn504_addr, NUM_REGIONS*REGION_SIZE);
+ if (gator_events_ccn504_base == NULL) {
+ printk(KERN_ERR "gator: ioremap returned NULL\n");
+ return -1;
+ }
+
+ for (i = 0; i < CNTMAX; ++i) {
+ gator_events_ccn504_enabled[i] = 0;
+ gator_events_ccn504_event[i] = 0;
+ gator_events_ccn504_key[i] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_ccn504_interface);
+}
diff --git a/drivers/gator/gator_events_irq.c b/drivers/gator/gator_events_irq.c
new file mode 100644
index 000000000000..facbdd62325e
--- /dev/null
+++ b/drivers/gator/gator_events_irq.c
@@ -0,0 +1,165 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+#include <trace/events/irq.h>
+
+#define HARDIRQ 0
+#define SOFTIRQ 1
+#define TOTALIRQ (SOFTIRQ+1)
+
+static ulong hardirq_enabled;
+static ulong softirq_enabled;
+static ulong hardirq_key;
+static ulong softirq_key;
+static DEFINE_PER_CPU(atomic_t[TOTALIRQ], irqCnt);
+static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet);
+
+GATOR_DEFINE_PROBE(irq_handler_exit,
+ TP_PROTO(int irq, struct irqaction *action, int ret))
+{
+ atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[HARDIRQ]);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h, struct softirq_action *vec))
+#else
+GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(unsigned int vec_nr))
+#endif
+{
+ atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[SOFTIRQ]);
+}
+
+static int gator_events_irq_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ /* irq */
+ dir = gatorfs_mkdir(sb, root, "Linux_irq_irq");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &hardirq_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &hardirq_key);
+
+ /* soft irq */
+ dir = gatorfs_mkdir(sb, root, "Linux_irq_softirq");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &softirq_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &softirq_key);
+
+ return 0;
+}
+
+static int gator_events_irq_online(int **buffer, bool migrate)
+{
+ int len = 0, cpu = get_physical_cpu();
+
+ // synchronization with the irq_exit functions is not necessary as the values are being reset
+ if (hardirq_enabled) {
+ atomic_set(&per_cpu(irqCnt, cpu)[HARDIRQ], 0);
+ per_cpu(irqGet, cpu)[len++] = hardirq_key;
+ per_cpu(irqGet, cpu)[len++] = 0;
+ }
+
+ if (softirq_enabled) {
+ atomic_set(&per_cpu(irqCnt, cpu)[SOFTIRQ], 0);
+ per_cpu(irqGet, cpu)[len++] = softirq_key;
+ per_cpu(irqGet, cpu)[len++] = 0;
+ }
+
+ if (buffer)
+ *buffer = per_cpu(irqGet, cpu);
+
+ return len;
+}
+
+static int gator_events_irq_start(void)
+{
+ // register tracepoints
+ if (hardirq_enabled)
+ if (GATOR_REGISTER_TRACE(irq_handler_exit))
+ goto fail_hardirq_exit;
+ if (softirq_enabled)
+ if (GATOR_REGISTER_TRACE(softirq_exit))
+ goto fail_softirq_exit;
+ pr_debug("gator: registered irq tracepoints\n");
+
+ return 0;
+
+ // unregister tracepoints on error
+fail_softirq_exit:
+ if (hardirq_enabled)
+ GATOR_UNREGISTER_TRACE(irq_handler_exit);
+fail_hardirq_exit:
+ pr_err("gator: irq tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+static void gator_events_irq_stop(void)
+{
+ if (hardirq_enabled)
+ GATOR_UNREGISTER_TRACE(irq_handler_exit);
+ if (softirq_enabled)
+ GATOR_UNREGISTER_TRACE(softirq_exit);
+ pr_debug("gator: unregistered irq tracepoints\n");
+
+ hardirq_enabled = 0;
+ softirq_enabled = 0;
+}
+
+static int gator_events_irq_read(int **buffer)
+{
+ int len, value;
+ int cpu = get_physical_cpu();
+
+ len = 0;
+ if (hardirq_enabled) {
+ value = atomic_read(&per_cpu(irqCnt, cpu)[HARDIRQ]);
+ atomic_sub(value, &per_cpu(irqCnt, cpu)[HARDIRQ]);
+
+ per_cpu(irqGet, cpu)[len++] = hardirq_key;
+ per_cpu(irqGet, cpu)[len++] = value;
+ }
+
+ if (softirq_enabled) {
+ value = atomic_read(&per_cpu(irqCnt, cpu)[SOFTIRQ]);
+ atomic_sub(value, &per_cpu(irqCnt, cpu)[SOFTIRQ]);
+
+ per_cpu(irqGet, cpu)[len++] = softirq_key;
+ per_cpu(irqGet, cpu)[len++] = value;
+ }
+
+ if (buffer)
+ *buffer = per_cpu(irqGet, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_irq_interface = {
+ .create_files = gator_events_irq_create_files,
+ .online = gator_events_irq_online,
+ .start = gator_events_irq_start,
+ .stop = gator_events_irq_stop,
+ .read = gator_events_irq_read,
+};
+
+int gator_events_irq_init(void)
+{
+ hardirq_key = gator_events_get_key();
+ softirq_key = gator_events_get_key();
+
+ hardirq_enabled = 0;
+ softirq_enabled = 0;
+
+ return gator_events_install(&gator_events_irq_interface);
+}
diff --git a/drivers/gator/gator_events_l2c-310.c b/drivers/gator/gator_events_l2c-310.c
new file mode 100644
index 000000000000..553f9707bdbf
--- /dev/null
+++ b/drivers/gator/gator_events_l2c-310.c
@@ -0,0 +1,208 @@
+/**
+ * l2c310 (L2 Cache Controller) event counters for gator
+ *
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#if defined(CONFIG_OF)
+#include <linux/of.h>
+#include <linux/of_address.h>
+#endif
+#include <asm/hardware/cache-l2x0.h>
+
+#include "gator.h"
+
+#define L2C310_COUNTERS_NUM 2
+
+static struct {
+ unsigned long enabled;
+ unsigned long event;
+ unsigned long key;
+} l2c310_counters[L2C310_COUNTERS_NUM];
+
+static int l2c310_buffer[L2C310_COUNTERS_NUM * 2];
+
+static void __iomem *l2c310_base;
+
+static void gator_events_l2c310_reset_counters(void)
+{
+ u32 val = readl(l2c310_base + L2X0_EVENT_CNT_CTRL);
+
+ val |= ((1 << L2C310_COUNTERS_NUM) - 1) << 1;
+
+ writel(val, l2c310_base + L2X0_EVENT_CNT_CTRL);
+}
+
+static int gator_events_l2c310_create_files(struct super_block *sb,
+ struct dentry *root)
+{
+ int i;
+
+ for (i = 0; i < L2C310_COUNTERS_NUM; i++) {
+ char buf[16];
+ struct dentry *dir;
+
+ snprintf(buf, sizeof(buf), "L2C-310_cnt%d", i);
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (WARN_ON(!dir))
+ return -1;
+ gatorfs_create_ulong(sb, dir, "enabled",
+ &l2c310_counters[i].enabled);
+ gatorfs_create_ulong(sb, dir, "event",
+ &l2c310_counters[i].event);
+ gatorfs_create_ro_ulong(sb, dir, "key",
+ &l2c310_counters[i].key);
+ }
+
+ return 0;
+}
+
+static int gator_events_l2c310_start(void)
+{
+ static const unsigned long l2x0_event_cntx_cfg[L2C310_COUNTERS_NUM] = {
+ L2X0_EVENT_CNT0_CFG,
+ L2X0_EVENT_CNT1_CFG,
+ };
+ int i;
+
+ /* Counter event sources */
+ for (i = 0; i < L2C310_COUNTERS_NUM; i++)
+ writel((l2c310_counters[i].event & 0xf) << 2,
+ l2c310_base + l2x0_event_cntx_cfg[i]);
+
+ gator_events_l2c310_reset_counters();
+
+ /* Event counter enable */
+ writel(1, l2c310_base + L2X0_EVENT_CNT_CTRL);
+
+ return 0;
+}
+
+static void gator_events_l2c310_stop(void)
+{
+ /* Event counter disable */
+ writel(0, l2c310_base + L2X0_EVENT_CNT_CTRL);
+}
+
+static int gator_events_l2c310_read(int **buffer)
+{
+ static const unsigned long l2x0_event_cntx_val[L2C310_COUNTERS_NUM] = {
+ L2X0_EVENT_CNT0_VAL,
+ L2X0_EVENT_CNT1_VAL,
+ };
+ int i;
+ int len = 0;
+
+ if (!on_primary_core())
+ return 0;
+
+ for (i = 0; i < L2C310_COUNTERS_NUM; i++) {
+ if (l2c310_counters[i].enabled) {
+ l2c310_buffer[len++] = l2c310_counters[i].key;
+ l2c310_buffer[len++] = readl(l2c310_base +
+ l2x0_event_cntx_val[i]);
+ }
+ }
+
+ /* l2c310 counters are saturating, not wrapping in case of overflow */
+ gator_events_l2c310_reset_counters();
+
+ if (buffer)
+ *buffer = l2c310_buffer;
+
+ return len;
+}
+
+static struct gator_interface gator_events_l2c310_interface = {
+ .create_files = gator_events_l2c310_create_files,
+ .start = gator_events_l2c310_start,
+ .stop = gator_events_l2c310_stop,
+ .read = gator_events_l2c310_read,
+};
+
+#define L2C310_ADDR_PROBE (~0)
+
+MODULE_PARM_DESC(l2c310_addr, "L2C310 physical base address (0 to disable)");
+static unsigned long l2c310_addr = L2C310_ADDR_PROBE;
+module_param(l2c310_addr, ulong, 0444);
+
+static void __iomem *gator_events_l2c310_probe(void)
+{
+ phys_addr_t variants[] = {
+#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_S5PV310)
+ 0x10502000,
+#endif
+#if defined(CONFIG_ARCH_OMAP4)
+ 0x48242000,
+#endif
+#if defined(CONFIG_ARCH_TEGRA)
+ 0x50043000,
+#endif
+#if defined(CONFIG_ARCH_U8500)
+ 0xa0412000,
+#endif
+#if defined(CONFIG_ARCH_VEXPRESS)
+ 0x1e00a000, // A9x4 core tile (HBI-0191)
+ 0x2c0f0000, // New memory map tiles
+#endif
+ };
+ int i;
+ void __iomem *base;
+#if defined(CONFIG_OF)
+ struct device_node *node = of_find_all_nodes(NULL);
+
+ if (node) {
+ of_node_put(node);
+
+ node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache");
+ base = of_iomap(node, 0);
+ of_node_put(node);
+
+ return base;
+ }
+#endif
+
+ for (i = 0; i < ARRAY_SIZE(variants); i++) {
+ base = ioremap(variants[i], SZ_4K);
+ if (base) {
+ u32 cache_id = readl(base + L2X0_CACHE_ID);
+
+ if ((cache_id & 0xff0003c0) == 0x410000c0)
+ return base;
+
+ iounmap(base);
+ }
+ }
+
+ return NULL;
+}
+
+int gator_events_l2c310_init(void)
+{
+ int i;
+
+ if (gator_cpuid() != CORTEX_A5 && gator_cpuid() != CORTEX_A9)
+ return -1;
+
+ if (l2c310_addr == L2C310_ADDR_PROBE)
+ l2c310_base = gator_events_l2c310_probe();
+ else if (l2c310_addr)
+ l2c310_base = ioremap(l2c310_addr, SZ_4K);
+
+ if (!l2c310_base)
+ return -1;
+
+ for (i = 0; i < L2C310_COUNTERS_NUM; i++) {
+ l2c310_counters[i].enabled = 0;
+ l2c310_counters[i].key = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_l2c310_interface);
+}
diff --git a/drivers/gator/gator_events_mali_4xx.c b/drivers/gator/gator_events_mali_4xx.c
new file mode 100644
index 000000000000..9e1c7064bd73
--- /dev/null
+++ b/drivers/gator/gator_events_mali_4xx.c
@@ -0,0 +1,669 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+
+#include "linux/mali_linux_trace.h"
+
+#include "gator_events_mali_common.h"
+#include "gator_events_mali_4xx.h"
+
+/*
+* There have been four different variants of the comms between gator and Mali depending on driver version:
+* # | DDK vsn range | Support | Notes
+*
+* 1 | (obsolete) | No software counter support | Obsolete patches
+* 2 | (obsolete) | Tracepoint called for each separate s/w counter value as it appears | Obsolete patches
+* 3 | r3p0-04rel0 - r3p2-01rel2 | Single tracepoint for all s/w counters in a bundle. |
+* 4 | r3p2-01rel3 - date | As above but with extensions for MP devices (Mali-450) | At least r4p0-00rel1
+*/
+
+#if !defined(GATOR_MALI_INTERFACE_STYLE)
+#define GATOR_MALI_INTERFACE_STYLE (4)
+#endif
+
+#if GATOR_MALI_INTERFACE_STYLE == 1
+#error GATOR_MALI_INTERFACE_STYLE 1 is obsolete
+#elif GATOR_MALI_INTERFACE_STYLE == 2
+#error GATOR_MALI_INTERFACE_STYLE 2 is obsolete
+#elif GATOR_MALI_INTERFACE_STYLE >= 3
+// Valid GATOR_MALI_INTERFACE_STYLE
+#else
+#error Unknown GATOR_MALI_INTERFACE_STYLE option.
+#endif
+
+#if GATOR_MALI_INTERFACE_STYLE < 4
+#include "mali/mali_mjollnir_profiling_gator_api.h"
+#else
+#include "mali/mali_utgard_profiling_gator_api.h"
+#endif
+
+/*
+ * Check that the MALI_SUPPORT define is set to one of the allowable device codes.
+ */
+#if (MALI_SUPPORT != MALI_4xx)
+#error MALI_SUPPORT set to an invalid device code: expecting MALI_4xx
+#endif
+
+static const char mali_name[] = "Mali-4xx";
+
+/* gatorfs variables for counter enable state,
+ * the event the counter should count and the
+ * 'key' (a unique id set by gatord and returned
+ * by gator.ko)
+ */
+static unsigned long counter_enabled[NUMBER_OF_EVENTS];
+static unsigned long counter_event[NUMBER_OF_EVENTS];
+static unsigned long counter_key[NUMBER_OF_EVENTS];
+
+/* The data we have recorded */
+static u32 counter_data[NUMBER_OF_EVENTS];
+/* The address to sample (or 0 if samples are sent to us) */
+static u32 *counter_address[NUMBER_OF_EVENTS];
+
+/* An array used to return the data we recorded
+ * as key,value pairs hence the *2
+ */
+static unsigned long counter_dump[NUMBER_OF_EVENTS * 2];
+static unsigned long counter_prev[NUMBER_OF_EVENTS];
+static bool prev_set[NUMBER_OF_EVENTS];
+
+/* Note whether tracepoints have been registered */
+static int trace_registered;
+
+/*
+ * These numbers define the actual numbers of each block type that exist in the system. Initially
+ * these are set to the maxima defined above; if the driver is capable of being queried (newer
+ * drivers only) then the values may be revised.
+ */
+static unsigned int n_vp_cores = MAX_NUM_VP_CORES;
+static unsigned int n_l2_cores = MAX_NUM_L2_CACHE_CORES;
+static unsigned int n_fp_cores = MAX_NUM_FP_CORES;
+
+extern mali_counter mali_activity[2];
+static const char* const mali_activity_names[] = {
+ "fragment",
+ "vertex",
+};
+
+/**
+ * Returns non-zero if the given counter ID is an activity counter.
+ */
+static inline int is_activity_counter(unsigned int event_id)
+{
+ return (event_id >= FIRST_ACTIVITY_EVENT &&
+ event_id <= LAST_ACTIVITY_EVENT);
+}
+
+/**
+ * Returns non-zero if the given counter ID is a hardware counter.
+ */
+static inline int is_hw_counter(unsigned int event_id)
+{
+ return (event_id >= FIRST_HW_COUNTER && event_id <= LAST_HW_COUNTER);
+}
+
+/*
+ * These are provided for utgard compatibility.
+ */
+typedef void _mali_profiling_get_mali_version_type(struct _mali_profiling_mali_version *values);
+typedef u32 _mali_profiling_get_l2_counters_type(_mali_profiling_l2_counter_values *values);
+
+/* Probe for continuously sampled counter */
+#if 0 //WE_DONT_CURRENTLY_USE_THIS_SO_SUPPRESS_WARNING
+GATOR_DEFINE_PROBE(mali_sample_address, TP_PROTO(unsigned int event_id, u32 *addr))
+{
+ /* Turning on too many pr_debug statements in frequently called functions
+ * can cause stability and/or performance problems
+ */
+ //pr_debug("gator: mali_sample_address %d %d\n", event_id, addr);
+ if (event_id >= ACTIVITY_VP && event_id <= COUNTER_FP3_C1) {
+ counter_address[event_id] = addr;
+ }
+}
+#endif
+
+/* Probe for hardware counter events */
+GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int value))
+{
+ /* Turning on too many pr_debug statements in frequently called functions
+ * can cause stability and/or performance problems
+ */
+ //pr_debug("gator: mali_hw_counter %d %d\n", event_id, value);
+ if (is_hw_counter(event_id)) {
+ counter_data[event_id] = value;
+ }
+}
+
+GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void *surface_id, unsigned int *counters))
+{
+ u32 i;
+
+ /* Copy over the values for those counters which are enabled. */
+ for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) {
+ if (counter_enabled[i]) {
+ counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]);
+ }
+ }
+}
+
+/**
+ * Create a single filesystem entry for a specified event.
+ * @param sb the superblock
+ * @param root Filesystem root
+ * @param name The name of the entry to create
+ * @param event The ID of the event
+ * @param create_event_item boolean indicating whether to create an 'event' filesystem entry. True to create.
+ *
+ * @return 0 if ok, non-zero if the create failed.
+ */
+static int create_fs_entry(struct super_block *sb, struct dentry *root, const char *name, int event, int create_event_item)
+{
+ struct dentry *dir;
+
+ dir = gatorfs_mkdir(sb, root, name);
+
+ if (!dir) {
+ return -1;
+ }
+
+ if (create_event_item) {
+ gatorfs_create_ulong(sb, dir, "event", &counter_event[event]);
+ }
+
+ gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]);
+
+ return 0;
+}
+
+#if GATOR_MALI_INTERFACE_STYLE > 3
+/*
+ * Read the version info structure if available
+ */
+static void initialise_version_info(void)
+{
+ _mali_profiling_get_mali_version_type *mali_profiling_get_mali_version_symbol;
+
+ mali_profiling_get_mali_version_symbol = symbol_get(_mali_profiling_get_mali_version);
+
+ if (mali_profiling_get_mali_version_symbol) {
+ struct _mali_profiling_mali_version version_info;
+
+ pr_debug("gator: mali online _mali_profiling_get_mali_version symbol @ %p\n",
+ mali_profiling_get_mali_version_symbol);
+
+ /*
+ * Revise the number of each different core type using information derived from the DDK.
+ */
+ mali_profiling_get_mali_version_symbol(&version_info);
+
+ n_fp_cores = version_info.num_of_fp_cores;
+ n_vp_cores = version_info.num_of_vp_cores;
+ n_l2_cores = version_info.num_of_l2_cores;
+
+ /* Release the function - we're done with it. */
+ symbol_put(_mali_profiling_get_mali_version);
+ } else {
+ printk("gator: mali online _mali_profiling_get_mali_version symbol not found\n");
+ printk("gator: check your Mali DDK version versus the GATOR_MALI_INTERFACE_STYLE setting\n");
+ }
+}
+#endif
+
+static int create_files(struct super_block *sb, struct dentry *root)
+{
+ int event;
+
+ char buf[40];
+ int core_id;
+ int counter_number;
+
+ pr_debug("gator: Initialising counters with style = %d\n", GATOR_MALI_INTERFACE_STYLE);
+
+#if GATOR_MALI_INTERFACE_STYLE > 3
+ /*
+ * Initialise first: this sets up the number of cores available (on compatible DDK versions).
+ * Ideally this would not need guarding but other parts of the code depend on the interface style being set
+ * correctly; if it is not then the system can enter an inconsistent state.
+ */
+ initialise_version_info();
+#endif
+
+ mali_activity[0].cores = n_fp_cores;
+ mali_activity[1].cores = n_vp_cores;
+ for (event = 0; event < ARRAY_SIZE(mali_activity); event++) {
+ if (gator_mali_create_file_system(mali_name, mali_activity_names[event], sb, root, &mali_activity[event], NULL) != 0) {
+ return -1;
+ }
+ }
+
+ /* Vertex processor counters */
+ for (core_id = 0; core_id < n_vp_cores; core_id++) {
+ int activity_counter_id = ACTIVITY_VP_0;
+ snprintf(buf, sizeof buf, "ARM_%s_VP_%d_active", mali_name, core_id);
+ if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) {
+ return -1;
+ }
+
+ for (counter_number = 0; counter_number < 2; counter_number++) {
+ int counter_id = COUNTER_VP_0_C0 + (2 * core_id) + counter_number;
+
+ snprintf(buf, sizeof buf, "ARM_%s_VP_%d_cnt%d", mali_name, core_id, counter_number);
+ if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) {
+ return -1;
+ }
+ }
+ }
+
+ /* Fragment processors' counters */
+ for (core_id = 0; core_id < n_fp_cores; core_id++) {
+ int activity_counter_id = ACTIVITY_FP_0 + core_id;
+
+ snprintf(buf, sizeof buf, "ARM_%s_FP_%d_active", mali_name, core_id);
+ if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) {
+ return -1;
+ }
+
+ for (counter_number = 0; counter_number < 2; counter_number++) {
+ int counter_id = COUNTER_FP_0_C0 + (2 * core_id) + counter_number;
+
+ snprintf(buf, sizeof buf, "ARM_%s_FP_%d_cnt%d", mali_name, core_id, counter_number);
+ if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) {
+ return -1;
+ }
+ }
+ }
+
+ /* L2 Cache counters */
+ for (core_id = 0; core_id < n_l2_cores; core_id++) {
+ for (counter_number = 0; counter_number < 2; counter_number++) {
+ int counter_id = COUNTER_L2_0_C0 + (2 * core_id) + counter_number;
+
+ snprintf(buf, sizeof buf, "ARM_%s_L2_%d_cnt%d", mali_name, core_id, counter_number);
+ if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) {
+ return -1;
+ }
+ }
+ }
+
+ /* Now set up the software counter entries */
+ for (event = FIRST_SW_COUNTER; event <= LAST_SW_COUNTER; event++) {
+ snprintf(buf, sizeof(buf), "ARM_%s_SW_%d", mali_name, event - FIRST_SW_COUNTER);
+
+ if (create_fs_entry(sb, root, buf, event, 0) != 0) {
+ return -1;
+ }
+ }
+
+ /* Now set up the special counter entries */
+ snprintf(buf, sizeof(buf), "ARM_%s_Filmstrip_cnt0", mali_name);
+ if (create_fs_entry(sb, root, buf, COUNTER_FILMSTRIP, 1) != 0) {
+ return -1;
+ }
+
+#ifdef DVFS_REPORTED_BY_DDK
+ snprintf(buf, sizeof(buf), "ARM_%s_Frequency", mali_name);
+ if (create_fs_entry(sb, root, buf, COUNTER_FREQUENCY, 1) != 0) {
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "ARM_%s_Voltage", mali_name);
+ if (create_fs_entry(sb, root, buf, COUNTER_VOLTAGE, 1) != 0) {
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+/*
+ * Local store for the get_counters entry point into the DDK.
+ * This is stored here since it is used very regularly.
+ */
+static mali_profiling_get_counters_type *mali_get_counters = NULL;
+static _mali_profiling_get_l2_counters_type *mali_get_l2_counters = NULL;
+
+/*
+ * Examine list of counters between two index limits and determine if any one is enabled.
+ * Returns 1 if any counter is enabled, 0 if none is.
+ */
+static int is_any_counter_enabled(unsigned int first_counter, unsigned int last_counter)
+{
+ unsigned int i;
+
+ for (i = first_counter; i <= last_counter; i++) {
+ if (counter_enabled[i]) {
+ return 1; /* At least one counter is enabled */
+ }
+ }
+
+ return 0; /* No s/w counters enabled */
+}
+
+static void init_counters(unsigned int from_counter, unsigned int to_counter)
+{
+ unsigned int counter_id;
+
+ /* If a Mali driver is present and exporting the appropriate symbol
+ * then we can request the HW counters (of which there are only 2)
+ * be configured to count the desired events
+ */
+ mali_profiling_set_event_type *mali_set_hw_event;
+
+ mali_set_hw_event = symbol_get(_mali_profiling_set_event);
+
+ if (mali_set_hw_event) {
+ pr_debug("gator: mali online _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event);
+
+ for (counter_id = from_counter; counter_id <= to_counter; counter_id++) {
+ if (counter_enabled[counter_id]) {
+ mali_set_hw_event(counter_id, counter_event[counter_id]);
+ } else {
+ mali_set_hw_event(counter_id, 0xFFFFFFFF);
+ }
+ }
+
+ symbol_put(_mali_profiling_set_event);
+ } else {
+ printk("gator: mali online _mali_profiling_set_event symbol not found\n");
+ }
+}
+
+static void mali_counter_initialize(void)
+{
+ int i;
+
+ mali_profiling_control_type *mali_control;
+
+ init_counters(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores) - 1);
+ init_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1);
+ init_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1);
+
+ /* Generic control interface for Mali DDK. */
+ mali_control = symbol_get(_mali_profiling_control);
+ if (mali_control) {
+ /* The event attribute in the XML file keeps the actual frame rate. */
+ unsigned int rate = counter_event[COUNTER_FILMSTRIP] & 0xff;
+ unsigned int resize_factor = (counter_event[COUNTER_FILMSTRIP] >> 8) & 0xff;
+
+ pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control);
+
+ mali_control(SW_COUNTER_ENABLE, (is_any_counter_enabled(FIRST_SW_COUNTER, LAST_SW_COUNTER) ? 1 : 0));
+ mali_control(FBDUMP_CONTROL_ENABLE, (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0));
+ mali_control(FBDUMP_CONTROL_RATE, rate);
+ mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor);
+
+ pr_debug("gator: sent mali_control enabled=%d, rate=%d\n", (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0), rate);
+
+ symbol_put(_mali_profiling_control);
+ } else {
+ printk("gator: mali online _mali_profiling_control symbol not found\n");
+ }
+
+ mali_get_counters = symbol_get(_mali_profiling_get_counters);
+ if (mali_get_counters) {
+ pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", mali_get_counters);
+
+ } else {
+ pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined");
+ }
+
+ mali_get_l2_counters = symbol_get(_mali_profiling_get_l2_counters);
+ if (mali_get_l2_counters) {
+ pr_debug("gator: mali online _mali_profiling_get_l2_counters symbol @ %p\n", mali_get_l2_counters);
+
+ } else {
+ pr_debug("gator WARNING: mali _mali_profiling_get_l2_counters symbol not defined");
+ }
+
+ if (!mali_get_counters && !mali_get_l2_counters) {
+ pr_debug("gator: WARNING: no L2 counters available");
+ n_l2_cores = 0;
+ }
+
+ /* Clear counters in the start */
+ for (i = 0; i < NUMBER_OF_EVENTS; i++) {
+ counter_data[i] = 0;
+ prev_set[i] = false;
+ }
+}
+
+static void mali_counter_deinitialize(void)
+{
+ mali_profiling_set_event_type *mali_set_hw_event;
+ mali_profiling_control_type *mali_control;
+
+ mali_set_hw_event = symbol_get(_mali_profiling_set_event);
+
+ if (mali_set_hw_event) {
+ int i;
+
+ pr_debug("gator: mali offline _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event);
+ for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) {
+ mali_set_hw_event(i, 0xFFFFFFFF);
+ }
+
+ symbol_put(_mali_profiling_set_event);
+ } else {
+ printk("gator: mali offline _mali_profiling_set_event symbol not found\n");
+ }
+
+ /* Generic control interface for Mali DDK. */
+ mali_control = symbol_get(_mali_profiling_control);
+
+ if (mali_control) {
+ pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_control);
+
+ /* Reset the DDK state - disable counter collection */
+ mali_control(SW_COUNTER_ENABLE, 0);
+
+ mali_control(FBDUMP_CONTROL_ENABLE, 0);
+
+ symbol_put(_mali_profiling_control);
+ } else {
+ printk("gator: mali offline _mali_profiling_control symbol not found\n");
+ }
+
+ if (mali_get_counters) {
+ symbol_put(_mali_profiling_get_counters);
+ }
+
+ if (mali_get_l2_counters) {
+ symbol_put(_mali_profiling_get_l2_counters);
+ }
+}
+
+static int start(void)
+{
+ // register tracepoints
+ if (GATOR_REGISTER_TRACE(mali_hw_counter)) {
+ printk("gator: mali_hw_counter tracepoint failed to activate\n");
+ return -1;
+ }
+
+ /* For Mali drivers with built-in support. */
+ if (GATOR_REGISTER_TRACE(mali_sw_counters)) {
+ printk("gator: mali_sw_counters tracepoint failed to activate\n");
+ return -1;
+ }
+
+ trace_registered = 1;
+
+ mali_counter_initialize();
+ return 0;
+}
+
+static void stop(void)
+{
+ unsigned int cnt;
+
+ pr_debug("gator: mali stop\n");
+
+ if (trace_registered) {
+ GATOR_UNREGISTER_TRACE(mali_hw_counter);
+
+ /* For Mali drivers with built-in support. */
+ GATOR_UNREGISTER_TRACE(mali_sw_counters);
+
+ pr_debug("gator: mali timeline tracepoint deactivated\n");
+
+ trace_registered = 0;
+ }
+
+ for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) {
+ counter_enabled[cnt] = 0;
+ counter_event[cnt] = 0;
+ counter_address[cnt] = NULL;
+ }
+
+ mali_counter_deinitialize();
+}
+
+static void dump_counters(unsigned int from_counter, unsigned int to_counter, unsigned int *len)
+{
+ unsigned int counter_id;
+
+ for (counter_id = from_counter; counter_id <= to_counter; counter_id++) {
+ if (counter_enabled[counter_id]) {
+ counter_dump[(*len)++] = counter_key[counter_id];
+ counter_dump[(*len)++] = counter_data[counter_id];
+
+ counter_data[counter_id] = 0;
+ }
+ }
+}
+
+static int read(int **buffer)
+{
+ int len = 0;
+
+ if (!on_primary_core())
+ return 0;
+
+ // Read the L2 C0 and C1 here.
+ if (n_l2_cores > 0 && is_any_counter_enabled(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores))) {
+ unsigned int unavailable_l2_caches = 0;
+ _mali_profiling_l2_counter_values cache_values;
+ unsigned int cache_id;
+ struct _mali_profiling_core_counters *per_core;
+
+ /* Poke the driver to get the counter values - older style; only one L2 cache */
+ if (mali_get_l2_counters) {
+ unavailable_l2_caches = mali_get_l2_counters(&cache_values);
+ } else if (mali_get_counters) {
+ per_core = &cache_values.cores[0];
+ mali_get_counters(&per_core->source0, &per_core->value0, &per_core->source1, &per_core->value1);
+ } else {
+ /* This should never happen, as n_l2_caches is only set > 0 if one of the above functions is found. */
+ }
+
+ /* Fill in the two cache counter values for each cache block. */
+ for (cache_id = 0; cache_id < n_l2_cores; cache_id++) {
+ unsigned int counter_id_0 = COUNTER_L2_0_C0 + (2 * cache_id);
+ unsigned int counter_id_1 = counter_id_0 + 1;
+
+ if ((1 << cache_id) & unavailable_l2_caches) {
+ continue; /* This cache is unavailable (powered-off, possibly). */
+ }
+
+ per_core = &cache_values.cores[cache_id];
+
+ if (counter_enabled[counter_id_0] && prev_set[counter_id_0]) {
+ // Calculate and save src0's counter val0
+ counter_dump[len++] = counter_key[counter_id_0];
+ counter_dump[len++] = per_core->value0 - counter_prev[counter_id_0];
+ }
+
+ if (counter_enabled[counter_id_1] && prev_set[counter_id_1]) {
+ // Calculate and save src1's counter val1
+ counter_dump[len++] = counter_key[counter_id_1];
+ counter_dump[len++] = per_core->value1 - counter_prev[counter_id_1];
+ }
+
+ // Save the previous values for the counters.
+ counter_prev[counter_id_0] = per_core->value0;
+ prev_set[counter_id_0] = true;
+ counter_prev[counter_id_1] = per_core->value1;
+ prev_set[counter_id_1] = true;
+ }
+ }
+
+ /* Process other (non-timeline) counters. */
+ dump_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1, &len);
+ dump_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1, &len);
+
+ dump_counters(FIRST_SW_COUNTER, LAST_SW_COUNTER, &len);
+
+#ifdef DVFS_REPORTED_BY_DDK
+ {
+ int cnt;
+ /*
+ * Add in the voltage and frequency counters if enabled. Note that, since these are
+ * actually passed as events, the counter value should not be cleared.
+ */
+ cnt = COUNTER_FREQUENCY;
+ if (counter_enabled[cnt]) {
+ counter_dump[len++] = counter_key[cnt];
+ counter_dump[len++] = counter_data[cnt];
+ }
+
+ cnt = COUNTER_VOLTAGE;
+ if (counter_enabled[cnt]) {
+ counter_dump[len++] = counter_key[cnt];
+ counter_dump[len++] = counter_data[cnt];
+ }
+ }
+#endif
+
+ if (buffer) {
+ *buffer = (int *)counter_dump;
+ }
+
+ return len;
+}
+
+static struct gator_interface gator_events_mali_interface = {
+ .create_files = create_files,
+ .start = start,
+ .stop = stop,
+ .read = read,
+};
+
+extern void gator_events_mali_log_dvfs_event(unsigned int frequency_mhz, unsigned int voltage_mv)
+{
+#ifdef DVFS_REPORTED_BY_DDK
+ counter_data[COUNTER_FREQUENCY] = frequency_mhz;
+ counter_data[COUNTER_VOLTAGE] = voltage_mv;
+#endif
+}
+
+int gator_events_mali_init(void)
+{
+ unsigned int cnt;
+
+ pr_debug("gator: mali init\n");
+
+ gator_mali_initialise_counters(mali_activity, ARRAY_SIZE(mali_activity));
+
+ for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) {
+ counter_enabled[cnt] = 0;
+ counter_event[cnt] = 0;
+ counter_key[cnt] = gator_events_get_key();
+ counter_address[cnt] = NULL;
+ counter_data[cnt] = 0;
+ }
+
+ trace_registered = 0;
+
+ return gator_events_install(&gator_events_mali_interface);
+}
diff --git a/drivers/gator/gator_events_mali_4xx.h b/drivers/gator/gator_events_mali_4xx.h
new file mode 100644
index 000000000000..976ca8c4cfa1
--- /dev/null
+++ b/drivers/gator/gator_events_mali_4xx.h
@@ -0,0 +1,18 @@
+/**
+ * Copyright (C) ARM Limited 2011-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Header contains common definitions for the Mali-4xx processors.
+ */
+#if !defined(GATOR_EVENTS_MALI_4xx_H)
+#define GATOR_EVENTS_MALI_4xx_H
+
+extern void gator_events_mali_log_dvfs_event(unsigned int d0, unsigned int d1);
+
+#endif /* GATOR_EVENTS_MALI_4xx_H */
diff --git a/drivers/gator/gator_events_mali_common.c b/drivers/gator/gator_events_mali_common.c
new file mode 100644
index 000000000000..4f2cce4ce67b
--- /dev/null
+++ b/drivers/gator/gator_events_mali_common.c
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) ARM Limited 2012-2014. All rights reserved.
+ *
+ * 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 "gator_events_mali_common.h"
+
+extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter, unsigned long *event)
+{
+ int err;
+ char buf[255];
+ struct dentry *dir;
+
+ /* If the counter name is empty ignore it */
+ if (strlen(event_name) != 0) {
+ /* Set up the filesystem entry for this event. */
+ snprintf(buf, sizeof(buf), "ARM_%s_%s", mali_name, event_name);
+
+ dir = gatorfs_mkdir(sb, root, buf);
+
+ if (dir == NULL) {
+ pr_debug("gator: %s: error creating file system for: %s (%s)", mali_name, event_name, buf);
+ return -1;
+ }
+
+ err = gatorfs_create_ulong(sb, dir, "enabled", &counter->enabled);
+ if (err != 0) {
+ pr_debug("gator: %s: error calling gatorfs_create_ulong for: %s (%s)", mali_name, event_name, buf);
+ return -1;
+ }
+ err = gatorfs_create_ro_ulong(sb, dir, "key", &counter->key);
+ if (err != 0) {
+ pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)", mali_name, event_name, buf);
+ return -1;
+ }
+ if (counter->cores != -1) {
+ err = gatorfs_create_ro_ulong(sb, dir, "cores", &counter->cores);
+ if (err != 0) {
+ pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)", mali_name, event_name, buf);
+ return -1;
+ }
+ }
+ if (event != NULL) {
+ err = gatorfs_create_ulong(sb, dir, "event", event);
+ if (err != 0) {
+ pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)", mali_name, event_name, buf);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters)
+{
+ unsigned int cnt;
+
+ for (cnt = 0; cnt < n_counters; cnt++) {
+ mali_counter *counter = &counters[cnt];
+
+ counter->key = gator_events_get_key();
+ counter->enabled = 0;
+ counter->cores = -1;
+ }
+}
diff --git a/drivers/gator/gator_events_mali_common.h b/drivers/gator/gator_events_mali_common.h
new file mode 100644
index 000000000000..91d871bc915a
--- /dev/null
+++ b/drivers/gator/gator_events_mali_common.h
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) ARM Limited 2012-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#if !defined(GATOR_EVENTS_MALI_COMMON_H)
+#define GATOR_EVENTS_MALI_COMMON_H
+
+#include "gator.h"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+/* Ensure that MALI_SUPPORT has been defined to something. */
+#ifndef MALI_SUPPORT
+#error MALI_SUPPORT not defined!
+#endif
+
+/* Values for the supported activity event types */
+#define ACTIVITY_START (1)
+#define ACTIVITY_STOP (2)
+
+/*
+ * Runtime state information for a counter.
+ */
+typedef struct {
+ // 'key' (a unique id set by gatord and returned by gator.ko)
+ unsigned long key;
+ // counter enable state
+ unsigned long enabled;
+ // for activity counters, the number of cores, otherwise -1
+ unsigned long cores;
+} mali_counter;
+
+/*
+ * Mali-4xx
+ */
+typedef int mali_profiling_set_event_type(unsigned int, int);
+typedef void mali_profiling_control_type(unsigned int, unsigned int);
+typedef void mali_profiling_get_counters_type(unsigned int *, unsigned int *, unsigned int *, unsigned int *);
+
+/*
+ * Driver entry points for functions called directly by gator.
+ */
+extern int _mali_profiling_set_event(unsigned int, int);
+extern void _mali_profiling_control(unsigned int, unsigned int);
+extern void _mali_profiling_get_counters(unsigned int *, unsigned int *, unsigned int *, unsigned int *);
+
+/**
+ * Creates a filesystem entry under /dev/gator relating to the specified event name and key, and
+ * associate the key/enable values with this entry point.
+ *
+ * @param event_name The name of the event.
+ * @param sb Linux super block
+ * @param root Directory under which the entry will be created.
+ * @param counter_key Ptr to location which will be associated with the counter key.
+ * @param counter_enabled Ptr to location which will be associated with the counter enable state.
+ *
+ * @return 0 if entry point was created, non-zero if not.
+ */
+extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter, unsigned long *event);
+
+/**
+ * Initializes the counter array.
+ *
+ * @param keys The array of counters
+ * @param n_counters The number of entries in each of the arrays.
+ */
+extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters);
+
+#endif /* GATOR_EVENTS_MALI_COMMON_H */
diff --git a/drivers/gator/gator_events_mali_t6xx.c b/drivers/gator/gator_events_mali_t6xx.c
new file mode 100644
index 000000000000..e56ba84aefb8
--- /dev/null
+++ b/drivers/gator/gator_events_mali_t6xx.c
@@ -0,0 +1,567 @@
+/**
+ * Copyright (C) ARM Limited 2011-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#ifdef MALI_DIR_MIDGARD
+/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/
+#include "mali_linux_trace.h"
+#else
+/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/
+#include "linux/mali_linux_trace.h"
+#endif
+
+#include "gator_events_mali_common.h"
+
+/*
+ * Check that the MALI_SUPPORT define is set to one of the allowable device codes.
+ */
+#if (MALI_SUPPORT != MALI_T6xx)
+#error MALI_SUPPORT set to an invalid device code: expecting MALI_T6xx
+#endif
+
+static const char mali_name[] = "Mali-T6xx";
+
+/* Counters for Mali-T6xx:
+ *
+ * - Timeline events
+ * They are tracepoints, but instead of reporting a number they report a START/STOP event.
+ * They are reported in Streamline as number of microseconds while that particular counter was active.
+ *
+ * - SW counters
+ * They are tracepoints reporting a particular number.
+ * They are accumulated in sw_counter_data array until they are passed to Streamline, then they are zeroed.
+ *
+ * - Accumulators
+ * They are the same as software counters but their value is not zeroed.
+ */
+
+/* Timeline (start/stop) activity */
+static const char *timeline_event_names[] = {
+ "PM_SHADER_0",
+ "PM_SHADER_1",
+ "PM_SHADER_2",
+ "PM_SHADER_3",
+ "PM_SHADER_4",
+ "PM_SHADER_5",
+ "PM_SHADER_6",
+ "PM_SHADER_7",
+ "PM_TILER_0",
+ "PM_L2_0",
+ "PM_L2_1",
+ "MMU_AS_0",
+ "MMU_AS_1",
+ "MMU_AS_2",
+ "MMU_AS_3"
+};
+
+enum {
+ PM_SHADER_0 = 0,
+ PM_SHADER_1,
+ PM_SHADER_2,
+ PM_SHADER_3,
+ PM_SHADER_4,
+ PM_SHADER_5,
+ PM_SHADER_6,
+ PM_SHADER_7,
+ PM_TILER_0,
+ PM_L2_0,
+ PM_L2_1,
+ MMU_AS_0,
+ MMU_AS_1,
+ MMU_AS_2,
+ MMU_AS_3
+};
+/* The number of shader blocks in the enum above */
+#define NUM_PM_SHADER (8)
+
+/* Software Counters */
+static const char *software_counter_names[] = {
+ "MMU_PAGE_FAULT_0",
+ "MMU_PAGE_FAULT_1",
+ "MMU_PAGE_FAULT_2",
+ "MMU_PAGE_FAULT_3"
+};
+
+enum {
+ MMU_PAGE_FAULT_0 = 0,
+ MMU_PAGE_FAULT_1,
+ MMU_PAGE_FAULT_2,
+ MMU_PAGE_FAULT_3
+};
+
+/* Software Counters */
+static const char *accumulators_names[] = {
+ "TOTAL_ALLOC_PAGES"
+};
+
+enum {
+ TOTAL_ALLOC_PAGES = 0
+};
+
+#define FIRST_TIMELINE_EVENT (0)
+#define NUMBER_OF_TIMELINE_EVENTS (sizeof(timeline_event_names) / sizeof(timeline_event_names[0]))
+#define FIRST_SOFTWARE_COUNTER (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS)
+#define NUMBER_OF_SOFTWARE_COUNTERS (sizeof(software_counter_names) / sizeof(software_counter_names[0]))
+#define FIRST_ACCUMULATOR (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS)
+#define NUMBER_OF_ACCUMULATORS (sizeof(accumulators_names) / sizeof(accumulators_names[0]))
+#define FILMSTRIP (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS)
+#define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS + 1)
+
+/*
+ * gatorfs variables for counter enable state
+ */
+static mali_counter counters[NUMBER_OF_EVENTS];
+static unsigned long filmstrip_event;
+
+/* An array used to return the data we recorded
+ * as key,value pairs hence the *2
+ */
+static unsigned long counter_dump[NUMBER_OF_EVENTS * 2];
+
+/*
+ * Array holding counter start times (in ns) for each counter. A zero here
+ * indicates that the activity monitored by this counter is not running.
+ */
+static struct timespec timeline_event_starttime[NUMBER_OF_TIMELINE_EVENTS];
+
+/* The data we have recorded */
+static unsigned int timeline_data[NUMBER_OF_TIMELINE_EVENTS];
+static unsigned int sw_counter_data[NUMBER_OF_SOFTWARE_COUNTERS];
+static unsigned int accumulators_data[NUMBER_OF_ACCUMULATORS];
+
+/* Hold the previous timestamp, used to calculate the sample interval. */
+static struct timespec prev_timestamp;
+
+/**
+ * Returns the timespan (in microseconds) between the two specified timestamps.
+ *
+ * @param start Ptr to the start timestamp
+ * @param end Ptr to the end timestamp
+ *
+ * @return Number of microseconds between the two timestamps (can be negative if start follows end).
+ */
+static inline long get_duration_us(const struct timespec *start, const struct timespec *end)
+{
+ long event_duration_us = (end->tv_nsec - start->tv_nsec) / 1000;
+ event_duration_us += (end->tv_sec - start->tv_sec) * 1000000;
+
+ return event_duration_us;
+}
+
+static void record_timeline_event(unsigned int timeline_index, unsigned int type)
+{
+ struct timespec event_timestamp;
+ struct timespec *event_start = &timeline_event_starttime[timeline_index];
+
+ switch (type) {
+ case ACTIVITY_START:
+ /* Get the event time... */
+ getnstimeofday(&event_timestamp);
+
+ /* Remember the start time if the activity is not already started */
+ if (event_start->tv_sec == 0) {
+ *event_start = event_timestamp; /* Structure copy */
+ }
+ break;
+
+ case ACTIVITY_STOP:
+ /* if the counter was started... */
+ if (event_start->tv_sec != 0) {
+ /* Get the event time... */
+ getnstimeofday(&event_timestamp);
+
+ /* Accumulate the duration in us */
+ timeline_data[timeline_index] += get_duration_us(event_start, &event_timestamp);
+
+ /* Reset the start time to indicate the activity is stopped. */
+ event_start->tv_sec = 0;
+ }
+ break;
+
+ default:
+ /* Other activity events are ignored. */
+ break;
+ }
+}
+
+/*
+ * Documentation about the following tracepoints is in mali_linux_trace.h
+ */
+
+GATOR_DEFINE_PROBE(mali_pm_status, TP_PROTO(unsigned int event_id, unsigned long long value))
+{
+#define SHADER_PRESENT_LO 0x100 /* (RO) Shader core present bitmap, low word */
+#define TILER_PRESENT_LO 0x110 /* (RO) Tiler core present bitmap, low word */
+#define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */
+#define BIT_AT(value, pos) ((value >> pos) & 1)
+
+ static unsigned long long previous_shader_bitmask = 0;
+ static unsigned long long previous_tiler_bitmask = 0;
+ static unsigned long long previous_l2_bitmask = 0;
+
+ switch (event_id) {
+ case SHADER_PRESENT_LO:
+ {
+ unsigned long long changed_bitmask = previous_shader_bitmask ^ value;
+ int pos;
+
+ for (pos = 0; pos < NUM_PM_SHADER; ++pos) {
+ if (BIT_AT(changed_bitmask, pos)) {
+ record_timeline_event(PM_SHADER_0 + pos, BIT_AT(value, pos) ? ACTIVITY_START : ACTIVITY_STOP);
+ }
+ }
+
+ previous_shader_bitmask = value;
+ break;
+ }
+
+ case TILER_PRESENT_LO:
+ {
+ unsigned long long changed = previous_tiler_bitmask ^ value;
+
+ if (BIT_AT(changed, 0)) {
+ record_timeline_event(PM_TILER_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
+ }
+
+ previous_tiler_bitmask = value;
+ break;
+ }
+
+ case L2_PRESENT_LO:
+ {
+ unsigned long long changed = previous_l2_bitmask ^ value;
+
+ if (BIT_AT(changed, 0)) {
+ record_timeline_event(PM_L2_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
+ }
+ if (BIT_AT(changed, 4)) {
+ record_timeline_event(PM_L2_1, BIT_AT(value, 4) ? ACTIVITY_START : ACTIVITY_STOP);
+ }
+
+ previous_l2_bitmask = value;
+ break;
+ }
+
+ default:
+ /* No other blocks are supported at present */
+ break;
+ }
+
+#undef SHADER_PRESENT_LO
+#undef TILER_PRESENT_LO
+#undef L2_PRESENT_LO
+#undef BIT_AT
+}
+
+GATOR_DEFINE_PROBE(mali_page_fault_insert_pages, TP_PROTO(int event_id, unsigned long value))
+{
+ /* We add to the previous since we may receive many tracepoints in one sample period */
+ sw_counter_data[MMU_PAGE_FAULT_0 + event_id] += value;
+}
+
+GATOR_DEFINE_PROBE(mali_mmu_as_in_use, TP_PROTO(int event_id))
+{
+ record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_START);
+}
+
+GATOR_DEFINE_PROBE(mali_mmu_as_released, TP_PROTO(int event_id))
+{
+ record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_STOP);
+}
+
+GATOR_DEFINE_PROBE(mali_total_alloc_pages_change, TP_PROTO(long long int event_id))
+{
+ accumulators_data[TOTAL_ALLOC_PAGES] = event_id;
+}
+
+static int create_files(struct super_block *sb, struct dentry *root)
+{
+ int event;
+ /*
+ * Create the filesystem for all events
+ */
+ int counter_index = 0;
+ mali_profiling_control_type *mali_control;
+
+ for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) {
+ if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event], NULL) != 0) {
+ return -1;
+ }
+ counter_index++;
+ }
+ counter_index = 0;
+ for (event = FIRST_SOFTWARE_COUNTER; event < FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS; event++) {
+ if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event], NULL) != 0) {
+ return -1;
+ }
+ counter_index++;
+ }
+ counter_index = 0;
+ for (event = FIRST_ACCUMULATOR; event < FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS; event++) {
+ if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event], NULL) != 0) {
+ return -1;
+ }
+ counter_index++;
+ }
+
+ mali_control = symbol_get(_mali_profiling_control);
+ if (mali_control) {
+ if (gator_mali_create_file_system(mali_name, "Filmstrip_cnt0", sb, root, &counters[FILMSTRIP], &filmstrip_event) != 0) {
+ return -1;
+ }
+ symbol_put(_mali_profiling_control);
+ }
+
+ return 0;
+}
+
+static int register_tracepoints(void)
+{
+ if (GATOR_REGISTER_TRACE(mali_pm_status)) {
+ pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint failed to activate\n");
+ return 0;
+ }
+
+ if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) {
+ pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint failed to activate\n");
+ return 0;
+ }
+
+ if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) {
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint failed to activate\n");
+ return 0;
+ }
+
+ if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) {
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint failed to activate\n");
+ return 0;
+ }
+
+ if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) {
+ pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint failed to activate\n");
+ return 0;
+ }
+
+ pr_debug("gator: Mali-T6xx: start\n");
+ pr_debug("gator: Mali-T6xx: mali_pm_status probe is at %p\n", &probe_mali_pm_status);
+ pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages probe is at %p\n", &probe_mali_page_fault_insert_pages);
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use probe is at %p\n", &probe_mali_mmu_as_in_use);
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_released probe is at %p\n", &probe_mali_mmu_as_released);
+ pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change probe is at %p\n", &probe_mali_total_alloc_pages_change);
+
+ return 1;
+}
+
+static int start(void)
+{
+ unsigned int cnt;
+ mali_profiling_control_type *mali_control;
+
+ /* Clean all data for the next capture */
+ for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) {
+ timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0;
+ timeline_data[cnt] = 0;
+ }
+
+ for (cnt = 0; cnt < NUMBER_OF_SOFTWARE_COUNTERS; cnt++) {
+ sw_counter_data[cnt] = 0;
+ }
+
+ for (cnt = 0; cnt < NUMBER_OF_ACCUMULATORS; cnt++) {
+ accumulators_data[cnt] = 0;
+ }
+
+ /* Register tracepoints */
+ if (register_tracepoints() == 0) {
+ return -1;
+ }
+
+ /* Generic control interface for Mali DDK. */
+ mali_control = symbol_get(_mali_profiling_control);
+ if (mali_control) {
+ /* The event attribute in the XML file keeps the actual frame rate. */
+ unsigned int enabled = counters[FILMSTRIP].enabled ? 1 : 0;
+ unsigned int rate = filmstrip_event & 0xff;
+ unsigned int resize_factor = (filmstrip_event >> 8) & 0xff;
+
+ pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control);
+
+#define FBDUMP_CONTROL_ENABLE (1)
+#define FBDUMP_CONTROL_RATE (2)
+#define FBDUMP_CONTROL_RESIZE_FACTOR (4)
+ mali_control(FBDUMP_CONTROL_ENABLE, enabled);
+ mali_control(FBDUMP_CONTROL_RATE, rate);
+ mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor);
+
+ pr_debug("gator: sent mali_control enabled=%d, rate=%d, resize_factor=%d\n", enabled, rate, resize_factor);
+
+ symbol_put(_mali_profiling_control);
+ } else {
+ printk("gator: mali online _mali_profiling_control symbol not found\n");
+ }
+
+ /*
+ * Set the first timestamp for calculating the sample interval. The first interval could be quite long,
+ * since it will be the time between 'start' and the first 'read'.
+ * This means that timeline values will be divided by a big number for the first sample.
+ */
+ getnstimeofday(&prev_timestamp);
+
+ return 0;
+}
+
+static void stop(void)
+{
+ mali_profiling_control_type *mali_control;
+
+ pr_debug("gator: Mali-T6xx: stop\n");
+
+ /*
+ * It is safe to unregister traces even if they were not successfully
+ * registered, so no need to check.
+ */
+ GATOR_UNREGISTER_TRACE(mali_pm_status);
+ pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint deactivated\n");
+
+ GATOR_UNREGISTER_TRACE(mali_page_fault_insert_pages);
+ pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint deactivated\n");
+
+ GATOR_UNREGISTER_TRACE(mali_mmu_as_in_use);
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint deactivated\n");
+
+ GATOR_UNREGISTER_TRACE(mali_mmu_as_released);
+ pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint deactivated\n");
+
+ GATOR_UNREGISTER_TRACE(mali_total_alloc_pages_change);
+ pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint deactivated\n");
+
+ /* Generic control interface for Mali DDK. */
+ mali_control = symbol_get(_mali_profiling_control);
+ if (mali_control) {
+ pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_control);
+
+ mali_control(FBDUMP_CONTROL_ENABLE, 0);
+
+ symbol_put(_mali_profiling_control);
+ } else {
+ printk("gator: mali offline _mali_profiling_control symbol not found\n");
+ }
+}
+
+static int read(int **buffer)
+{
+ int cnt;
+ int len = 0;
+ long sample_interval_us = 0;
+ struct timespec read_timestamp;
+
+ if (!on_primary_core()) {
+ return 0;
+ }
+
+ /* Get the start of this sample period. */
+ getnstimeofday(&read_timestamp);
+
+ /*
+ * Calculate the sample interval if the previous sample time is valid.
+ * We use tv_sec since it will not be 0.
+ */
+ if (prev_timestamp.tv_sec != 0) {
+ sample_interval_us = get_duration_us(&prev_timestamp, &read_timestamp);
+ }
+
+ /* Structure copy. Update the previous timestamp. */
+ prev_timestamp = read_timestamp;
+
+ /*
+ * Report the timeline counters (ACTIVITY_START/STOP)
+ */
+ for (cnt = FIRST_TIMELINE_EVENT; cnt < (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS); cnt++) {
+ mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ const int index = cnt - FIRST_TIMELINE_EVENT;
+ unsigned int value;
+
+ /* If the activity is still running, reset its start time to the start of this sample period
+ * to correct the count. Add the time up to the end of the sample onto the count. */
+ if (timeline_event_starttime[index].tv_sec != 0) {
+ const long event_duration = get_duration_us(&timeline_event_starttime[index], &read_timestamp);
+ timeline_data[index] += event_duration;
+ timeline_event_starttime[index] = read_timestamp; /* Activity is still running. */
+ }
+
+ if (sample_interval_us != 0) {
+ /* Convert the counter to a percent-of-sample value */
+ value = (timeline_data[index] * 100) / sample_interval_us;
+ } else {
+ pr_debug("gator: Mali-T6xx: setting value to zero\n");
+ value = 0;
+ }
+
+ /* Clear the counter value ready for the next sample. */
+ timeline_data[index] = 0;
+
+ counter_dump[len++] = counter->key;
+ counter_dump[len++] = value;
+ }
+ }
+
+ /* Report the software counters */
+ for (cnt = FIRST_SOFTWARE_COUNTER; cnt < (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS); cnt++) {
+ const mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ const int index = cnt - FIRST_SOFTWARE_COUNTER;
+ counter_dump[len++] = counter->key;
+ counter_dump[len++] = sw_counter_data[index];
+ /* Set the value to zero for the next time */
+ sw_counter_data[index] = 0;
+ }
+ }
+
+ /* Report the accumulators */
+ for (cnt = FIRST_ACCUMULATOR; cnt < (FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS); cnt++) {
+ const mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ const int index = cnt - FIRST_ACCUMULATOR;
+ counter_dump[len++] = counter->key;
+ counter_dump[len++] = accumulators_data[index];
+ /* Do not zero the accumulator */
+ }
+ }
+
+ /* Update the buffer */
+ if (buffer) {
+ *buffer = (int *)counter_dump;
+ }
+
+ return len;
+}
+
+static struct gator_interface gator_events_mali_t6xx_interface = {
+ .create_files = create_files,
+ .start = start,
+ .stop = stop,
+ .read = read
+};
+
+extern int gator_events_mali_t6xx_init(void)
+{
+ pr_debug("gator: Mali-T6xx: sw_counters init\n");
+
+ gator_mali_initialise_counters(counters, NUMBER_OF_EVENTS);
+
+ return gator_events_install(&gator_events_mali_t6xx_interface);
+}
diff --git a/drivers/gator/gator_events_mali_t6xx_hw.c b/drivers/gator/gator_events_mali_t6xx_hw.c
new file mode 100644
index 000000000000..3a072bb6ac06
--- /dev/null
+++ b/drivers/gator/gator_events_mali_t6xx_hw.c
@@ -0,0 +1,913 @@
+/**
+ * Copyright (C) ARM Limited 2012-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+/* Mali T6xx DDK includes */
+#if defined(MALI_SIMPLE_API)
+/* Header with wrapper functions to kbase structures and functions */
+#include "mali/mali_dd_gator_api.h"
+#elif defined(MALI_DIR_MIDGARD)
+/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/
+#include "mali_linux_trace.h"
+#include "mali_kbase.h"
+#include "mali_kbase_mem_linux.h"
+#else
+/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/
+#include "linux/mali_linux_trace.h"
+#include "kbase/src/common/mali_kbase.h"
+#include "kbase/src/linux/mali_kbase_mem_linux.h"
+#endif
+
+/* If API version is not specified then assume API version 1. */
+#ifndef MALI_DDK_GATOR_API_VERSION
+#define MALI_DDK_GATOR_API_VERSION 1
+#endif
+
+#if (MALI_DDK_GATOR_API_VERSION != 1) && (MALI_DDK_GATOR_API_VERSION != 2) && (MALI_DDK_GATOR_API_VERSION != 3)
+#error MALI_DDK_GATOR_API_VERSION is invalid (must be 1 for r1/r2 DDK, or 2 for r3 DDK, or 3 for r? DDK).
+#endif
+
+#include "gator_events_mali_common.h"
+
+/*
+ * Mali-T6xx
+ */
+#if MALI_DDK_GATOR_API_VERSION == 3
+typedef uint32_t kbase_dd_instr_hwcnt_dump_irq_type(struct mali_dd_hwcnt_handles *);
+typedef uint32_t kbase_dd_instr_hwcnt_dump_complete_type(struct mali_dd_hwcnt_handles *, uint32_t *);
+typedef struct mali_dd_hwcnt_handles* mali_dd_hwcnt_init_type(struct mali_dd_hwcnt_info *);
+typedef void mali_dd_hwcnt_clear_type(struct mali_dd_hwcnt_info *, struct mali_dd_hwcnt_handles *);
+
+static kbase_dd_instr_hwcnt_dump_irq_type *kbase_dd_instr_hwcnt_dump_irq_symbol;
+static kbase_dd_instr_hwcnt_dump_complete_type *kbase_dd_instr_hwcnt_dump_complete_symbol;
+static mali_dd_hwcnt_init_type *mali_dd_hwcnt_init_symbol;
+static mali_dd_hwcnt_clear_type *mali_dd_hwcnt_clear_symbol;
+
+#else
+typedef struct kbase_device *kbase_find_device_type(int);
+typedef struct kbase_context *kbase_create_context_type(struct kbase_device *);
+typedef void kbase_destroy_context_type(struct kbase_context *);
+
+#if MALI_DDK_GATOR_API_VERSION == 1
+typedef void *kbase_va_alloc_type(struct kbase_context *, u32);
+typedef void kbase_va_free_type(struct kbase_context *, void *);
+#elif MALI_DDK_GATOR_API_VERSION == 2
+typedef void *kbase_va_alloc_type(struct kbase_context *, u32, kbase_hwc_dma_mapping * handle);
+typedef void kbase_va_free_type(struct kbase_context *, kbase_hwc_dma_mapping * handle);
+#endif
+
+typedef mali_error kbase_instr_hwcnt_enable_type(struct kbase_context *, struct kbase_uk_hwcnt_setup *);
+typedef mali_error kbase_instr_hwcnt_disable_type(struct kbase_context *);
+typedef mali_error kbase_instr_hwcnt_clear_type(struct kbase_context *);
+typedef mali_error kbase_instr_hwcnt_dump_irq_type(struct kbase_context *);
+typedef mali_bool kbase_instr_hwcnt_dump_complete_type(struct kbase_context *, mali_bool *);
+
+static kbase_find_device_type *kbase_find_device_symbol;
+static kbase_create_context_type *kbase_create_context_symbol;
+static kbase_va_alloc_type *kbase_va_alloc_symbol;
+static kbase_instr_hwcnt_enable_type *kbase_instr_hwcnt_enable_symbol;
+static kbase_instr_hwcnt_clear_type *kbase_instr_hwcnt_clear_symbol;
+static kbase_instr_hwcnt_dump_irq_type *kbase_instr_hwcnt_dump_irq_symbol;
+static kbase_instr_hwcnt_dump_complete_type *kbase_instr_hwcnt_dump_complete_symbol;
+static kbase_instr_hwcnt_disable_type *kbase_instr_hwcnt_disable_symbol;
+static kbase_va_free_type *kbase_va_free_symbol;
+static kbase_destroy_context_type *kbase_destroy_context_symbol;
+#endif
+
+static long shader_present_low = 0;
+
+/** The interval between reads, in ns.
+ *
+ * Earlier we introduced
+ * a 'hold off for 1ms after last read' to resolve MIDBASE-2178 and MALINE-724.
+ * However, the 1ms hold off is too long if no context switches occur as there is a race
+ * between this value and the tick of the read clock in gator which is also 1ms. If we 'miss' the
+ * current read, the counter values are effectively 'spread' over 2ms and the values seen are half
+ * what they should be (since Streamline averages over sample time). In the presence of context switches
+ * this spread can vary and markedly affect the counters. Currently there is no 'proper' solution to
+ * this, but empirically we have found that reducing the minimum read interval to 950us causes the
+ * counts to be much more stable.
+ */
+static const int READ_INTERVAL_NSEC = 950000;
+
+#if GATOR_TEST
+#include "gator_events_mali_t6xx_hw_test.c"
+#endif
+
+/* Blocks for HW counters */
+enum {
+ JM_BLOCK = 0,
+ TILER_BLOCK,
+ SHADER_BLOCK,
+ MMU_BLOCK
+};
+
+static const char mali_name[] = "Mali-T6xx";
+
+/* Counters for Mali-T6xx:
+ *
+ * - HW counters, 4 blocks
+ * For HW counters we need strings to create /dev/gator/events files.
+ * Enums are not needed because the position of the HW name in the array is the same
+ * of the corresponding value in the received block of memory.
+ * HW counters are requested by calculating a bitmask, passed then to the driver.
+ * Every millisecond a HW counters dump is requested, and if the previous has been completed they are read.
+ */
+
+/* Hardware Counters */
+static const char *const hardware_counter_names[] = {
+ /* Job Manager */
+ "",
+ "",
+ "",
+ "",
+ "MESSAGES_SENT",
+ "MESSAGES_RECEIVED",
+ "GPU_ACTIVE", /* 6 */
+ "IRQ_ACTIVE",
+ "JS0_JOBS",
+ "JS0_TASKS",
+ "JS0_ACTIVE",
+ "",
+ "JS0_WAIT_READ",
+ "JS0_WAIT_ISSUE",
+ "JS0_WAIT_DEPEND",
+ "JS0_WAIT_FINISH",
+ "JS1_JOBS",
+ "JS1_TASKS",
+ "JS1_ACTIVE",
+ "",
+ "JS1_WAIT_READ",
+ "JS1_WAIT_ISSUE",
+ "JS1_WAIT_DEPEND",
+ "JS1_WAIT_FINISH",
+ "JS2_JOBS",
+ "JS2_TASKS",
+ "JS2_ACTIVE",
+ "",
+ "JS2_WAIT_READ",
+ "JS2_WAIT_ISSUE",
+ "JS2_WAIT_DEPEND",
+ "JS2_WAIT_FINISH",
+ "JS3_JOBS",
+ "JS3_TASKS",
+ "JS3_ACTIVE",
+ "",
+ "JS3_WAIT_READ",
+ "JS3_WAIT_ISSUE",
+ "JS3_WAIT_DEPEND",
+ "JS3_WAIT_FINISH",
+ "JS4_JOBS",
+ "JS4_TASKS",
+ "JS4_ACTIVE",
+ "",
+ "JS4_WAIT_READ",
+ "JS4_WAIT_ISSUE",
+ "JS4_WAIT_DEPEND",
+ "JS4_WAIT_FINISH",
+ "JS5_JOBS",
+ "JS5_TASKS",
+ "JS5_ACTIVE",
+ "",
+ "JS5_WAIT_READ",
+ "JS5_WAIT_ISSUE",
+ "JS5_WAIT_DEPEND",
+ "JS5_WAIT_FINISH",
+ "JS6_JOBS",
+ "JS6_TASKS",
+ "JS6_ACTIVE",
+ "",
+ "JS6_WAIT_READ",
+ "JS6_WAIT_ISSUE",
+ "JS6_WAIT_DEPEND",
+ "JS6_WAIT_FINISH",
+
+ /*Tiler */
+ "",
+ "",
+ "",
+ "JOBS_PROCESSED",
+ "TRIANGLES",
+ "QUADS",
+ "POLYGONS",
+ "POINTS",
+ "LINES",
+ "VCACHE_HIT",
+ "VCACHE_MISS",
+ "FRONT_FACING",
+ "BACK_FACING",
+ "PRIM_VISIBLE",
+ "PRIM_CULLED",
+ "PRIM_CLIPPED",
+ "LEVEL0",
+ "LEVEL1",
+ "LEVEL2",
+ "LEVEL3",
+ "LEVEL4",
+ "LEVEL5",
+ "LEVEL6",
+ "LEVEL7",
+ "COMMAND_1",
+ "COMMAND_2",
+ "COMMAND_3",
+ "COMMAND_4",
+ "COMMAND_4_7",
+ "COMMAND_8_15",
+ "COMMAND_16_63",
+ "COMMAND_64",
+ "COMPRESS_IN",
+ "COMPRESS_OUT",
+ "COMPRESS_FLUSH",
+ "TIMESTAMPS",
+ "PCACHE_HIT",
+ "PCACHE_MISS",
+ "PCACHE_LINE",
+ "PCACHE_STALL",
+ "WRBUF_HIT",
+ "WRBUF_MISS",
+ "WRBUF_LINE",
+ "WRBUF_PARTIAL",
+ "WRBUF_STALL",
+ "ACTIVE",
+ "LOADING_DESC",
+ "INDEX_WAIT",
+ "INDEX_RANGE_WAIT",
+ "VERTEX_WAIT",
+ "PCACHE_WAIT",
+ "WRBUF_WAIT",
+ "BUS_READ",
+ "BUS_WRITE",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "UTLB_STALL",
+ "UTLB_REPLAY_MISS",
+ "UTLB_REPLAY_FULL",
+ "UTLB_NEW_MISS",
+ "UTLB_HIT",
+
+ /* Shader Core */
+ "",
+ "",
+ "",
+ "SHADER_CORE_ACTIVE",
+ "FRAG_ACTIVE",
+ "FRAG_PRIMATIVES",
+ "FRAG_PRIMATIVES_DROPPED",
+ "FRAG_CYCLE_DESC",
+ "FRAG_CYCLES_PLR",
+ "FRAG_CYCLES_VERT",
+ "FRAG_CYCLES_TRISETUP",
+ "FRAG_CYCLES_RAST",
+ "FRAG_THREADS",
+ "FRAG_DUMMY_THREADS",
+ "FRAG_QUADS_RAST",
+ "FRAG_QUADS_EZS_TEST",
+ "FRAG_QUADS_EZS_KILLED",
+ "FRAG_QUADS_LZS_TEST",
+ "FRAG_QUADS_LZS_KILLED",
+ "FRAG_CYCLE_NO_TILE",
+ "FRAG_NUM_TILES",
+ "FRAG_TRANS_ELIM",
+ "COMPUTE_ACTIVE",
+ "COMPUTE_TASKS",
+ "COMPUTE_THREADS",
+ "COMPUTE_CYCLES_DESC",
+ "TRIPIPE_ACTIVE",
+ "ARITH_WORDS",
+ "ARITH_CYCLES_REG",
+ "ARITH_CYCLES_L0",
+ "ARITH_FRAG_DEPEND",
+ "LS_WORDS",
+ "LS_ISSUES",
+ "LS_RESTARTS",
+ "LS_REISSUES_MISS",
+ "LS_REISSUES_VD",
+ "LS_REISSUE_ATTRIB_MISS",
+ "LS_NO_WB",
+ "TEX_WORDS",
+ "TEX_BUBBLES",
+ "TEX_WORDS_L0",
+ "TEX_WORDS_DESC",
+ "TEX_THREADS",
+ "TEX_RECIRC_FMISS",
+ "TEX_RECIRC_DESC",
+ "TEX_RECIRC_MULTI",
+ "TEX_RECIRC_PMISS",
+ "TEX_RECIRC_CONF",
+ "LSC_READ_HITS",
+ "LSC_READ_MISSES",
+ "LSC_WRITE_HITS",
+ "LSC_WRITE_MISSES",
+ "LSC_ATOMIC_HITS",
+ "LSC_ATOMIC_MISSES",
+ "LSC_LINE_FETCHES",
+ "LSC_DIRTY_LINE",
+ "LSC_SNOOPS",
+ "AXI_TLB_STALL",
+ "AXI_TLB_MIESS",
+ "AXI_TLB_TRANSACTION",
+ "LS_TLB_MISS",
+ "LS_TLB_HIT",
+ "AXI_BEATS_READ",
+ "AXI_BEATS_WRITTEN",
+
+ /*L2 and MMU */
+ "",
+ "",
+ "",
+ "",
+ "MMU_HIT",
+ "MMU_NEW_MISS",
+ "MMU_REPLAY_FULL",
+ "MMU_REPLAY_MISS",
+ "MMU_TABLE_WALK",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "UTLB_HIT",
+ "UTLB_NEW_MISS",
+ "UTLB_REPLAY_FULL",
+ "UTLB_REPLAY_MISS",
+ "UTLB_STALL",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "L2_WRITE_BEATS",
+ "L2_READ_BEATS",
+ "L2_ANY_LOOKUP",
+ "L2_READ_LOOKUP",
+ "L2_SREAD_LOOKUP",
+ "L2_READ_REPLAY",
+ "L2_READ_SNOOP",
+ "L2_READ_HIT",
+ "L2_CLEAN_MISS",
+ "L2_WRITE_LOOKUP",
+ "L2_SWRITE_LOOKUP",
+ "L2_WRITE_REPLAY",
+ "L2_WRITE_SNOOP",
+ "L2_WRITE_HIT",
+ "L2_EXT_READ_FULL",
+ "L2_EXT_READ_HALF",
+ "L2_EXT_WRITE_FULL",
+ "L2_EXT_WRITE_HALF",
+ "L2_EXT_READ",
+ "L2_EXT_READ_LINE",
+ "L2_EXT_WRITE",
+ "L2_EXT_WRITE_LINE",
+ "L2_EXT_WRITE_SMALL",
+ "L2_EXT_BARRIER",
+ "L2_EXT_AR_STALL",
+ "L2_EXT_R_BUF_FULL",
+ "L2_EXT_RD_BUF_FULL",
+ "L2_EXT_R_RAW",
+ "L2_EXT_W_STALL",
+ "L2_EXT_W_BUF_FULL",
+ "L2_EXT_R_W_HAZARD",
+ "L2_TAG_HAZARD",
+ "L2_SNOOP_FULL",
+ "L2_REPLAY_FULL"
+};
+
+#define NUMBER_OF_HARDWARE_COUNTERS (sizeof(hardware_counter_names) / sizeof(hardware_counter_names[0]))
+
+#define GET_HW_BLOCK(c) (((c) >> 6) & 0x3)
+#define GET_COUNTER_OFFSET(c) ((c) & 0x3f)
+
+#if MALI_DDK_GATOR_API_VERSION == 3
+/* Opaque handles for kbase_context and kbase_hwc_dma_mapping */
+static struct mali_dd_hwcnt_handles *handles;
+
+/* Information about hardware counters */
+static struct mali_dd_hwcnt_info *in_out_info;
+
+#else
+/* Memory to dump hardware counters into */
+static void *kernel_dump_buffer;
+
+#if MALI_DDK_GATOR_API_VERSION == 2
+/* DMA state used to manage lifetime of the buffer */
+kbase_hwc_dma_mapping kernel_dump_buffer_handle;
+#endif
+
+/* kbase context and device */
+static struct kbase_context *kbcontext = NULL;
+static struct kbase_device *kbdevice = NULL;
+#endif
+
+static volatile bool kbase_device_busy = false;
+static unsigned int num_hardware_counters_enabled;
+
+/*
+ * gatorfs variables for counter enable state
+ */
+static mali_counter counters[NUMBER_OF_HARDWARE_COUNTERS];
+
+/* An array used to return the data we recorded
+ * as key,value pairs hence the *2
+ */
+static unsigned long counter_dump[NUMBER_OF_HARDWARE_COUNTERS * 2];
+
+extern mali_counter mali_activity[3];
+static const char* const mali_activity_names[] = {
+ "fragment",
+ "vertex",
+ "opencl",
+};
+
+#define SYMBOL_GET(FUNCTION, ERROR_COUNT) \
+ if(FUNCTION ## _symbol) \
+ { \
+ printk("gator: mali " #FUNCTION " symbol was already registered\n"); \
+ (ERROR_COUNT)++; \
+ } \
+ else \
+ { \
+ FUNCTION ## _symbol = symbol_get(FUNCTION); \
+ if(! FUNCTION ## _symbol) \
+ { \
+ printk("gator: mali online " #FUNCTION " symbol not found\n"); \
+ (ERROR_COUNT)++; \
+ } \
+ }
+
+#define SYMBOL_CLEANUP(FUNCTION) \
+ if(FUNCTION ## _symbol) \
+ { \
+ symbol_put(FUNCTION); \
+ FUNCTION ## _symbol = NULL; \
+ }
+
+/**
+ * Execute symbol_get for all the Mali symbols and check for success.
+ * @return the number of symbols not loaded.
+ */
+static int init_symbols(void)
+{
+ int error_count = 0;
+#if MALI_DDK_GATOR_API_VERSION == 3
+ SYMBOL_GET(kbase_dd_instr_hwcnt_dump_irq, error_count);
+ SYMBOL_GET(kbase_dd_instr_hwcnt_dump_complete, error_count);
+ SYMBOL_GET(mali_dd_hwcnt_init, error_count);
+ SYMBOL_GET(mali_dd_hwcnt_clear, error_count);
+#else
+ SYMBOL_GET(kbase_find_device, error_count);
+ SYMBOL_GET(kbase_create_context, error_count);
+ SYMBOL_GET(kbase_va_alloc, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_enable, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_clear, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_dump_irq, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_dump_complete, error_count);
+ SYMBOL_GET(kbase_instr_hwcnt_disable, error_count);
+ SYMBOL_GET(kbase_va_free, error_count);
+ SYMBOL_GET(kbase_destroy_context, error_count);
+#endif
+
+ return error_count;
+}
+
+/**
+ * Execute symbol_put for all the registered Mali symbols.
+ */
+static void clean_symbols(void)
+{
+#if MALI_DDK_GATOR_API_VERSION == 3
+ SYMBOL_CLEANUP(kbase_dd_instr_hwcnt_dump_irq);
+ SYMBOL_CLEANUP(kbase_dd_instr_hwcnt_dump_complete);
+ SYMBOL_CLEANUP(mali_dd_hwcnt_init);
+ SYMBOL_CLEANUP(mali_dd_hwcnt_clear);
+#else
+ SYMBOL_CLEANUP(kbase_find_device);
+ SYMBOL_CLEANUP(kbase_create_context);
+ SYMBOL_CLEANUP(kbase_va_alloc);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_enable);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_clear);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_dump_irq);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_dump_complete);
+ SYMBOL_CLEANUP(kbase_instr_hwcnt_disable);
+ SYMBOL_CLEANUP(kbase_va_free);
+ SYMBOL_CLEANUP(kbase_destroy_context);
+#endif
+}
+
+/**
+ * Determines whether a read should take place
+ * @param current_time The current time, obtained from getnstimeofday()
+ * @param prev_time_s The number of seconds at the previous read attempt.
+ * @param next_read_time_ns The time (in ns) when the next read should be allowed.
+ *
+ * Note that this function has been separated out here to allow it to be tested.
+ */
+static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns)
+{
+ /* If the current ns count rolls over a second, roll the next read time too. */
+ if (current_time->tv_sec != *prev_time_s) {
+ *next_read_time_ns = *next_read_time_ns - NSEC_PER_SEC;
+ }
+
+ /* Abort the read if the next read time has not arrived. */
+ if (current_time->tv_nsec < *next_read_time_ns) {
+ return 0;
+ }
+
+ /* Set the next read some fixed time after this one, and update the read timestamp. */
+ *next_read_time_ns = current_time->tv_nsec + READ_INTERVAL_NSEC;
+
+ *prev_time_s = current_time->tv_sec;
+ return 1;
+}
+
+static int start(void)
+{
+#if MALI_DDK_GATOR_API_VERSION < 3
+ struct kbase_uk_hwcnt_setup setup;
+ unsigned long long shadersPresent = 0;
+ u16 bitmask[] = { 0, 0, 0, 0 };
+ mali_error err;
+#endif
+ int cnt;
+
+ /* Setup HW counters */
+ num_hardware_counters_enabled = 0;
+
+ if (NUMBER_OF_HARDWARE_COUNTERS != 256) {
+ pr_debug("Unexpected number of hardware counters defined: expecting 256, got %d\n", NUMBER_OF_HARDWARE_COUNTERS);
+ }
+
+#if MALI_DDK_GATOR_API_VERSION == 3
+ /* Declare and initialise mali_dd_hwcnt_info structure */
+ in_out_info = kmalloc(sizeof(struct mali_dd_hwcnt_info), GFP_KERNEL);
+ for (cnt = 0; cnt < 4; cnt++){
+ in_out_info->bitmask[cnt] = 0;
+ }
+#endif
+ /* Calculate enable bitmasks based on counters_enabled array */
+ for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) {
+ const mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ int block = GET_HW_BLOCK(cnt);
+ int enable_bit = GET_COUNTER_OFFSET(cnt) / 4;
+#if MALI_DDK_GATOR_API_VERSION == 3
+ in_out_info->bitmask[block] |= (1 << enable_bit);
+#else
+ bitmask[block] |= (1 << enable_bit);
+#endif
+ pr_debug("gator: Mali-T6xx: hardware counter %s selected [%d]\n", hardware_counter_names[cnt], cnt);
+ num_hardware_counters_enabled++;
+ }
+ }
+
+#if MALI_DDK_GATOR_API_VERSION == 3
+ /* Create a kbase context for HW counters */
+ if (num_hardware_counters_enabled > 0) {
+ if (init_symbols() > 0) {
+ clean_symbols();
+ /* No Mali driver code entrypoints found - not a fault. */
+ return 0;
+ }
+
+ handles = mali_dd_hwcnt_init_symbol(in_out_info);
+
+ if(handles == NULL) {
+ goto out;
+ }
+
+ /* See if we can get the number of shader cores */
+ shader_present_low = (unsigned long)in_out_info->shader_present_bitmap;
+
+ kbase_device_busy = false;
+ }
+
+ return 0;
+#else
+ /* Create a kbase context for HW counters */
+ if (num_hardware_counters_enabled > 0) {
+ if (init_symbols() > 0) {
+ clean_symbols();
+ /* No Mali driver code entrypoints found - not a fault. */
+ return 0;
+ }
+
+ kbdevice = kbase_find_device_symbol(-1);
+
+ /* If we already got a context, fail */
+ if (kbcontext) {
+ pr_debug("gator: Mali-T6xx: error context already present\n");
+ goto out;
+ }
+
+ /* kbcontext will only be valid after all the Mali symbols are loaded successfully */
+ kbcontext = kbase_create_context_symbol(kbdevice);
+ if (!kbcontext) {
+ pr_debug("gator: Mali-T6xx: error creating kbase context\n");
+ goto out;
+ }
+
+
+ /* See if we can get the number of shader cores */
+ shadersPresent = kbdevice->shader_present_bitmap;
+ shader_present_low = (unsigned long)shadersPresent;
+
+ /*
+ * The amount of memory needed to store the dump (bytes)
+ * DUMP_SIZE = number of core groups
+ * * number of blocks (always 8 for midgard)
+ * * number of counters per block (always 64 for midgard)
+ * * number of bytes per counter (always 4 in midgard)
+ * For a Mali-T6xx with a single core group = 1 * 8 * 64 * 4 = 2048
+ * For a Mali-T6xx with a dual core group = 2 * 8 * 64 * 4 = 4096
+ */
+#if MALI_DDK_GATOR_API_VERSION == 1
+ kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096);
+#elif MALI_DDK_GATOR_API_VERSION == 2
+ kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096, &kernel_dump_buffer_handle);
+#endif
+ if (!kernel_dump_buffer) {
+ pr_debug("gator: Mali-T6xx: error trying to allocate va\n");
+ goto destroy_context;
+ }
+
+ setup.dump_buffer = (uintptr_t)kernel_dump_buffer;
+ setup.jm_bm = bitmask[JM_BLOCK];
+ setup.tiler_bm = bitmask[TILER_BLOCK];
+ setup.shader_bm = bitmask[SHADER_BLOCK];
+ setup.mmu_l2_bm = bitmask[MMU_BLOCK];
+ /* These counters do not exist on Mali-T60x */
+ setup.l3_cache_bm = 0;
+
+ /* Use kbase API to enable hardware counters and provide dump buffer */
+ err = kbase_instr_hwcnt_enable_symbol(kbcontext, &setup);
+ if (err != MALI_ERROR_NONE) {
+ pr_debug("gator: Mali-T6xx: can't setup hardware counters\n");
+ goto free_buffer;
+ }
+ pr_debug("gator: Mali-T6xx: hardware counters enabled\n");
+ kbase_instr_hwcnt_clear_symbol(kbcontext);
+ pr_debug("gator: Mali-T6xx: hardware counters cleared \n");
+
+ kbase_device_busy = false;
+ }
+
+ return 0;
+
+free_buffer:
+#if MALI_DDK_GATOR_API_VERSION == 1
+ kbase_va_free_symbol(kbcontext, kernel_dump_buffer);
+#elif MALI_DDK_GATOR_API_VERSION == 2
+ kbase_va_free_symbol(kbcontext, &kernel_dump_buffer_handle);
+#endif
+
+destroy_context:
+ kbase_destroy_context_symbol(kbcontext);
+#endif
+
+out:
+ clean_symbols();
+ return -1;
+}
+
+static void stop(void)
+{
+ unsigned int cnt;
+#if MALI_DDK_GATOR_API_VERSION == 3
+ struct mali_dd_hwcnt_handles *temp_hand;
+#else
+ struct kbase_context *temp_kbcontext;
+#endif
+
+ pr_debug("gator: Mali-T6xx: stop\n");
+
+ /* Set all counters as disabled */
+ for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) {
+ counters[cnt].enabled = 0;
+ }
+
+ /* Destroy the context for HW counters */
+#if MALI_DDK_GATOR_API_VERSION == 3
+ if (num_hardware_counters_enabled > 0 && handles != NULL) {
+ /*
+ * Set the global variable to NULL before destroying it, because
+ * other function will check this before using it.
+ */
+ temp_hand = handles;
+ handles = NULL;
+
+ mali_dd_hwcnt_clear_symbol(in_out_info, temp_hand);
+
+ kfree(in_out_info);
+
+#else
+ if (num_hardware_counters_enabled > 0 && kbcontext != NULL) {
+ /*
+ * Set the global variable to NULL before destroying it, because
+ * other function will check this before using it.
+ */
+ temp_kbcontext = kbcontext;
+ kbcontext = NULL;
+
+ kbase_instr_hwcnt_disable_symbol(temp_kbcontext);
+
+#if MALI_DDK_GATOR_API_VERSION == 1
+ kbase_va_free_symbol(temp_kbcontext, kernel_dump_buffer);
+#elif MALI_DDK_GATOR_API_VERSION == 2
+ kbase_va_free_symbol(temp_kbcontext, &kernel_dump_buffer_handle);
+#endif
+
+ kbase_destroy_context_symbol(temp_kbcontext);
+#endif
+
+ pr_debug("gator: Mali-T6xx: hardware counters stopped\n");
+
+ clean_symbols();
+ }
+}
+
+static int read(int **buffer)
+{
+ int cnt;
+ int len = 0;
+ u32 value = 0;
+ uint32_t success;
+
+ struct timespec current_time;
+ static u32 prev_time_s = 0;
+ static s32 next_read_time_ns = 0;
+
+ if (!on_primary_core()) {
+ return 0;
+ }
+
+ getnstimeofday(&current_time);
+
+ /*
+ * Discard reads unless a respectable time has passed. This reduces the load on the GPU without sacrificing
+ * accuracy on the Streamline display.
+ */
+ if (!is_read_scheduled(&current_time, &prev_time_s, &next_read_time_ns)) {
+ return 0;
+ }
+
+ /*
+ * Report the HW counters
+ * Only process hardware counters if at least one of the hardware counters is enabled.
+ */
+ if (num_hardware_counters_enabled > 0) {
+ const unsigned int vithar_blocks[] = {
+ 0x700, /* VITHAR_JOB_MANAGER, Block 0 */
+ 0x400, /* VITHAR_TILER, Block 1 */
+ 0x000, /* VITHAR_SHADER_CORE, Block 2 */
+ 0x500 /* VITHAR_MEMORY_SYSTEM, Block 3 */
+ };
+
+#if MALI_DDK_GATOR_API_VERSION == 3
+ if (!handles) {
+ return -1;
+ }
+
+ /* Mali symbols can be called safely since a kbcontext is valid */
+ if (kbase_dd_instr_hwcnt_dump_complete_symbol(handles, &success) == MALI_TRUE) {
+#else
+ if (!kbcontext) {
+ return -1;
+ }
+
+ /* Mali symbols can be called safely since a kbcontext is valid */
+ if (kbase_instr_hwcnt_dump_complete_symbol(kbcontext, &success) == MALI_TRUE) {
+#endif
+ kbase_device_busy = false;
+
+ if (success == MALI_TRUE) {
+ /* Cycle through hardware counters and accumulate totals */
+ for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) {
+ const mali_counter *counter = &counters[cnt];
+ if (counter->enabled) {
+ const int block = GET_HW_BLOCK(cnt);
+ const int counter_offset = GET_COUNTER_OFFSET(cnt);
+
+#if MALI_DDK_GATOR_API_VERSION == 3
+ const char* block_base_address = (char*)in_out_info->kernel_dump_buffer + vithar_blocks[block];
+#else
+ const char* block_base_address = (char*)kernel_dump_buffer + vithar_blocks[block];
+#endif
+
+ /* If counter belongs to shader block need to take into account all cores */
+ if (block == SHADER_BLOCK) {
+ int i = 0;
+ int shader_core_count = 0;
+ value = 0;
+
+ for (i = 0; i < 4; i++) {
+ if ((shader_present_low >> i) & 1) {
+ value += *((u32*) (block_base_address + (0x100 * i)) + counter_offset);
+ shader_core_count++;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if((shader_present_low >> (i+4)) & 1) {
+ value += *((u32*)(block_base_address + (0x100 * i) + 0x800) + counter_offset);
+ shader_core_count++;
+ }
+ }
+
+ /* Need to total by number of cores to produce an average */
+ if (shader_core_count != 0) {
+ value /= shader_core_count;
+ }
+ } else {
+ value = *((u32*)block_base_address + counter_offset);
+ }
+
+ counter_dump[len++] = counter->key;
+ counter_dump[len++] = value;
+ }
+ }
+ }
+ }
+
+ if (!kbase_device_busy) {
+ kbase_device_busy = true;
+#if MALI_DDK_GATOR_API_VERSION == 3
+ kbase_dd_instr_hwcnt_dump_irq_symbol(handles);
+#else
+ kbase_instr_hwcnt_dump_irq_symbol(kbcontext);
+#endif
+ }
+ }
+
+ /* Update the buffer */
+ if (buffer) {
+ *buffer = (int *)counter_dump;
+ }
+
+ return len;
+}
+
+static int create_files(struct super_block *sb, struct dentry *root)
+{
+ unsigned int event;
+ /*
+ * Create the filesystem for all events
+ */
+ int counter_index = 0;
+
+ for (event = 0; event < ARRAY_SIZE(mali_activity); event++) {
+ if (gator_mali_create_file_system(mali_name, mali_activity_names[event], sb, root, &mali_activity[event], NULL) != 0) {
+ return -1;
+ }
+ }
+
+ for (event = 0; event < NUMBER_OF_HARDWARE_COUNTERS; event++) {
+ if (gator_mali_create_file_system(mali_name, hardware_counter_names[counter_index], sb, root, &counters[event], NULL) != 0)
+ return -1;
+ counter_index++;
+ }
+
+ return 0;
+}
+
+static struct gator_interface gator_events_mali_t6xx_interface = {
+ .create_files = create_files,
+ .start = start,
+ .stop = stop,
+ .read = read
+};
+
+int gator_events_mali_t6xx_hw_init(void)
+{
+ pr_debug("gator: Mali-T6xx: sw_counters init\n");
+
+#if GATOR_TEST
+ test_all_is_read_scheduled();
+#endif
+
+ gator_mali_initialise_counters(mali_activity, ARRAY_SIZE(mali_activity));
+ gator_mali_initialise_counters(counters, NUMBER_OF_HARDWARE_COUNTERS);
+
+ return gator_events_install(&gator_events_mali_t6xx_interface);
+}
diff --git a/drivers/gator/gator_events_mali_t6xx_hw_test.c b/drivers/gator/gator_events_mali_t6xx_hw_test.c
new file mode 100644
index 000000000000..ba6553f3540f
--- /dev/null
+++ b/drivers/gator/gator_events_mali_t6xx_hw_test.c
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) ARM Limited 2012-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+/**
+ * Test functions for mali_t600_hw code.
+ */
+
+static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns);
+
+static int test_is_read_scheduled(u32 s, u32 ns, u32 prev_s, s32 next_ns, int expected_result, s32 expected_next_ns)
+{
+ struct timespec current_time;
+ u32 prev_time_s = prev_s;
+ s32 next_read_time_ns = next_ns;
+
+ current_time.tv_sec = s;
+ current_time.tv_nsec = ns;
+
+ if (is_read_scheduled(&current_time, &prev_time_s, &next_read_time_ns) != expected_result) {
+ printk("Failed do_read(%u, %u, %u, %d): expected %d\n", s, ns, prev_s, next_ns, expected_result);
+ return 0;
+ }
+
+ if (next_read_time_ns != expected_next_ns) {
+ printk("Failed: next_read_ns expected=%d, actual=%d\n", expected_next_ns, next_read_time_ns);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void test_all_is_read_scheduled(void)
+{
+ const int HIGHEST_NS = 999999999;
+ int n_tests_passed = 0;
+
+ printk("gator: running tests on %s\n", __FILE__);
+
+ n_tests_passed += test_is_read_scheduled(0, 0, 0, 0, 1, READ_INTERVAL_NSEC); /* Null time */
+ n_tests_passed += test_is_read_scheduled(100, 1000, 0, 0, 1, READ_INTERVAL_NSEC + 1000); /* Initial values */
+
+ n_tests_passed += test_is_read_scheduled(100, HIGHEST_NS, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500);
+ n_tests_passed += test_is_read_scheduled(101, 0001, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500 - NSEC_PER_SEC);
+ n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500 - NSEC_PER_SEC, 1, 600 + READ_INTERVAL_NSEC);
+
+ n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500, 1, 600 + READ_INTERVAL_NSEC);
+
+ printk("gator: %d tests passed\n", n_tests_passed);
+}
diff --git a/drivers/gator/gator_events_meminfo.c b/drivers/gator/gator_events_meminfo.c
new file mode 100644
index 000000000000..c633dfdce306
--- /dev/null
+++ b/drivers/gator/gator_events_meminfo.c
@@ -0,0 +1,409 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+
+#include <linux/hardirq.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/workqueue.h>
+#include <trace/events/kmem.h>
+
+enum {
+ MEMINFO_MEMFREE,
+ MEMINFO_MEMUSED,
+ MEMINFO_BUFFERRAM,
+ MEMINFO_TOTAL,
+};
+
+enum {
+ PROC_SIZE,
+ PROC_SHARE,
+ PROC_TEXT,
+ PROC_DATA,
+ PROC_COUNT,
+};
+
+static const char * const meminfo_names[] = {
+ "Linux_meminfo_memfree",
+ "Linux_meminfo_memused",
+ "Linux_meminfo_bufferram",
+};
+
+static const char * const proc_names[] = {
+ "Linux_proc_statm_size",
+ "Linux_proc_statm_share",
+ "Linux_proc_statm_text",
+ "Linux_proc_statm_data",
+};
+
+static bool meminfo_global_enabled;
+static ulong meminfo_enabled[MEMINFO_TOTAL];
+static ulong meminfo_keys[MEMINFO_TOTAL];
+static long long meminfo_buffer[2 * (MEMINFO_TOTAL + 2)];
+static int meminfo_length = 0;
+static bool new_data_avail;
+
+static bool proc_global_enabled;
+static ulong proc_enabled[PROC_COUNT];
+static ulong proc_keys[PROC_COUNT];
+static DEFINE_PER_CPU(long long, proc_buffer[2 * (PROC_COUNT + 3)]);
+
+static int gator_meminfo_func(void *data);
+static bool gator_meminfo_run;
+// Initialize semaphore unlocked to initialize memory values
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+static DECLARE_MUTEX(gator_meminfo_sem);
+#else
+static DEFINE_SEMAPHORE(gator_meminfo_sem);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order))
+#else
+GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order))
+#endif
+{
+ up(&gator_meminfo_sem);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold))
+#else
+GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold))
+#endif
+{
+ up(&gator_meminfo_sem);
+}
+
+GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype))
+{
+ up(&gator_meminfo_sem);
+}
+
+static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+
+ for (i = 0; i < MEMINFO_TOTAL; i++) {
+ dir = gatorfs_mkdir(sb, root, meminfo_names[i]);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_keys[i]);
+ }
+
+ for (i = 0; i < PROC_COUNT; ++i) {
+ dir = gatorfs_mkdir(sb, root, proc_names[i]);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &proc_enabled[i]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &proc_keys[i]);
+ }
+
+ return 0;
+}
+
+static int gator_events_meminfo_start(void)
+{
+ int i;
+
+ new_data_avail = false;
+ meminfo_global_enabled = 0;
+ for (i = 0; i < MEMINFO_TOTAL; i++) {
+ if (meminfo_enabled[i]) {
+ meminfo_global_enabled = 1;
+ break;
+ }
+ }
+
+ proc_global_enabled = 0;
+ for (i = 0; i < PROC_COUNT; ++i) {
+ if (proc_enabled[i]) {
+ proc_global_enabled = 1;
+ break;
+ }
+ }
+ if (meminfo_enabled[MEMINFO_MEMUSED]) {
+ proc_global_enabled = 1;
+ }
+
+ if (meminfo_global_enabled == 0)
+ return 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ if (GATOR_REGISTER_TRACE(mm_page_free_direct))
+#else
+ if (GATOR_REGISTER_TRACE(mm_page_free))
+#endif
+ goto mm_page_free_exit;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ if (GATOR_REGISTER_TRACE(mm_pagevec_free))
+#else
+ if (GATOR_REGISTER_TRACE(mm_page_free_batched))
+#endif
+ goto mm_page_free_batched_exit;
+ if (GATOR_REGISTER_TRACE(mm_page_alloc))
+ goto mm_page_alloc_exit;
+
+ // Start worker thread
+ gator_meminfo_run = true;
+ // Since the mutex starts unlocked, memory values will be initialized
+ if (IS_ERR(kthread_run(gator_meminfo_func, NULL, "gator_meminfo")))
+ goto kthread_run_exit;
+
+ return 0;
+
+kthread_run_exit:
+ GATOR_UNREGISTER_TRACE(mm_page_alloc);
+mm_page_alloc_exit:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ GATOR_UNREGISTER_TRACE(mm_pagevec_free);
+#else
+ GATOR_UNREGISTER_TRACE(mm_page_free_batched);
+#endif
+mm_page_free_batched_exit:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ GATOR_UNREGISTER_TRACE(mm_page_free_direct);
+#else
+ GATOR_UNREGISTER_TRACE(mm_page_free);
+#endif
+mm_page_free_exit:
+ return -1;
+}
+
+static void gator_events_meminfo_stop(void)
+{
+ if (meminfo_global_enabled) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ GATOR_UNREGISTER_TRACE(mm_page_free_direct);
+ GATOR_UNREGISTER_TRACE(mm_pagevec_free);
+#else
+ GATOR_UNREGISTER_TRACE(mm_page_free);
+ GATOR_UNREGISTER_TRACE(mm_page_free_batched);
+#endif
+ GATOR_UNREGISTER_TRACE(mm_page_alloc);
+
+ // Stop worker thread
+ gator_meminfo_run = false;
+ up(&gator_meminfo_sem);
+ }
+}
+
+// Must be run in process context as the kernel function si_meminfo() can sleep
+static int gator_meminfo_func(void *data)
+{
+ struct sysinfo info;
+ int i, len;
+ unsigned long long value;
+
+ for (;;) {
+ if (down_killable(&gator_meminfo_sem)) {
+ break;
+ }
+
+ // Eat up any pending events
+ while (!down_trylock(&gator_meminfo_sem));
+
+ if (!gator_meminfo_run) {
+ break;
+ }
+
+ meminfo_length = len = 0;
+
+ si_meminfo(&info);
+ for (i = 0; i < MEMINFO_TOTAL; i++) {
+ if (meminfo_enabled[i]) {
+ switch (i) {
+ case MEMINFO_MEMFREE:
+ value = info.freeram * PAGE_SIZE;
+ break;
+ case MEMINFO_MEMUSED:
+ // pid -1 means system wide
+ meminfo_buffer[len++] = 1;
+ meminfo_buffer[len++] = -1;
+ // Emit value
+ meminfo_buffer[len++] = meminfo_keys[MEMINFO_MEMUSED];
+ meminfo_buffer[len++] = (info.totalram - info.freeram) * PAGE_SIZE;
+ // Clear pid
+ meminfo_buffer[len++] = 1;
+ meminfo_buffer[len++] = 0;
+ continue;
+ case MEMINFO_BUFFERRAM:
+ value = info.bufferram * PAGE_SIZE;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ meminfo_buffer[len++] = meminfo_keys[i];
+ meminfo_buffer[len++] = value;
+ }
+ }
+
+ meminfo_length = len;
+ new_data_avail = true;
+ }
+
+ return 0;
+}
+
+static int gator_events_meminfo_read(long long **buffer)
+{
+ if (!on_primary_core() || !meminfo_global_enabled)
+ return 0;
+
+ if (!new_data_avail)
+ return 0;
+
+ new_data_avail = false;
+
+ if (buffer)
+ *buffer = meminfo_buffer;
+
+ return meminfo_length;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+
+static inline unsigned long gator_get_mm_counter(struct mm_struct *mm, int member)
+{
+#ifdef SPLIT_RSS_COUNTING
+ long val = atomic_long_read(&mm->rss_stat.count[member]);
+ if (val < 0)
+ val = 0;
+ return (unsigned long)val;
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
+ return mm->rss_stat.count[member];
+#else
+ return atomic_long_read(&mm->rss_stat.count[member]);
+#endif
+#endif
+}
+
+#define get_mm_counter(mm, member) gator_get_mm_counter(mm, member)
+
+#endif
+
+static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct *task)
+{
+ struct mm_struct *mm;
+ u64 share = 0;
+ int i;
+ long long value;
+ int len = 0;
+ int cpu = get_physical_cpu();
+ long long *buf = per_cpu(proc_buffer, cpu);
+
+ if (!proc_global_enabled) {
+ return 0;
+ }
+
+ // Collect the memory stats of the process instead of the thread
+ if (task->group_leader != NULL) {
+ task = task->group_leader;
+ }
+
+ // get_task_mm/mmput is not needed in this context because the task and it's mm are required as part of the sched_switch
+ mm = task->mm;
+ if (mm == NULL) {
+ return 0;
+ }
+
+ // Derived from task_statm in fs/proc/task_mmu.c
+ if (meminfo_enabled[MEMINFO_MEMUSED] || proc_enabled[PROC_SHARE]) {
+ share = get_mm_counter(mm,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
+ file_rss
+#else
+ MM_FILEPAGES
+#endif
+ );
+ }
+
+ // key of 1 indicates a pid
+ buf[len++] = 1;
+ buf[len++] = task->pid;
+
+ for (i = 0; i < PROC_COUNT; ++i) {
+ if (proc_enabled[i]) {
+ switch (i) {
+ case PROC_SIZE:
+ value = mm->total_vm;
+ break;
+ case PROC_SHARE:
+ value = share;
+ break;
+ case PROC_TEXT:
+ value = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> PAGE_SHIFT;
+ break;
+ case PROC_DATA:
+ value = mm->total_vm - mm->shared_vm;
+ break;
+ }
+
+ buf[len++] = proc_keys[i];
+ buf[len++] = value * PAGE_SIZE;
+ }
+ }
+
+ if (meminfo_enabled[MEMINFO_MEMUSED]) {
+ value = share + get_mm_counter(mm,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
+ anon_rss
+#else
+ MM_ANONPAGES
+#endif
+ );
+ // Send resident for this pid
+ buf[len++] = meminfo_keys[MEMINFO_MEMUSED];
+ buf[len++] = value * PAGE_SIZE;
+ }
+
+ // Clear pid
+ buf[len++] = 1;
+ buf[len++] = 0;
+
+ if (buffer)
+ *buffer = buf;
+
+ return len;
+}
+
+static struct gator_interface gator_events_meminfo_interface = {
+ .create_files = gator_events_meminfo_create_files,
+ .start = gator_events_meminfo_start,
+ .stop = gator_events_meminfo_stop,
+ .read64 = gator_events_meminfo_read,
+ .read_proc = gator_events_meminfo_read_proc,
+};
+
+int gator_events_meminfo_init(void)
+{
+ int i;
+
+ meminfo_global_enabled = 0;
+ for (i = 0; i < MEMINFO_TOTAL; i++) {
+ meminfo_enabled[i] = 0;
+ meminfo_keys[i] = gator_events_get_key();
+ }
+
+ proc_global_enabled = 0;
+ for (i = 0; i < PROC_COUNT; ++i) {
+ proc_enabled[i] = 0;
+ proc_keys[i] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_meminfo_interface);
+}
diff --git a/drivers/gator/gator_events_mmapped.c b/drivers/gator/gator_events_mmapped.c
new file mode 100644
index 000000000000..5bc01c42c3a2
--- /dev/null
+++ b/drivers/gator/gator_events_mmapped.c
@@ -0,0 +1,209 @@
+/*
+ * Example events provider
+ *
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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.
+ *
+ * Similar entries to those below must be present in the events.xml file.
+ * To add them to the events.xml, create an events-mmap.xml with the
+ * following contents and rebuild gatord:
+ *
+ * <category name="mmapped">
+ * <event counter="mmapped_cnt0" title="Simulated1" name="Sine" display="maximum" class="absolute" description="Sort-of-sine"/>
+ * <event counter="mmapped_cnt1" title="Simulated2" name="Triangle" display="maximum" class="absolute" description="Triangular wave"/>
+ * <event counter="mmapped_cnt2" title="Simulated3" name="PWM" display="maximum" class="absolute" description="PWM Signal"/>
+ * </category>
+ *
+ * When adding custom events, be sure to do the following:
+ * - add any needed .c files to the gator driver Makefile
+ * - call gator_events_install in the events init function
+ * - add the init function to GATOR_EVENTS_LIST in gator_main.c
+ * - add a new events-*.xml file to the gator daemon and rebuild
+ *
+ * Troubleshooting:
+ * - verify the new events are part of events.xml, which is created when building the daemon
+ * - verify the new events exist at /dev/gator/events/ once gatord is launched
+ * - verify the counter name in the XML matches the name at /dev/gator/events
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ratelimit.h>
+
+#include "gator.h"
+
+#define MMAPPED_COUNTERS_NUM 3
+
+static int mmapped_global_enabled;
+
+static struct {
+ unsigned long enabled;
+ unsigned long key;
+} mmapped_counters[MMAPPED_COUNTERS_NUM];
+
+static int mmapped_buffer[MMAPPED_COUNTERS_NUM * 2];
+
+static s64 prev_time;
+
+/* Adds mmapped_cntX directories and enabled, event, and key files to /dev/gator/events */
+static int gator_events_mmapped_create_files(struct super_block *sb,
+ struct dentry *root)
+{
+ int i;
+
+ for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) {
+ char buf[16];
+ struct dentry *dir;
+
+ snprintf(buf, sizeof(buf), "mmapped_cnt%d", i);
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (WARN_ON(!dir))
+ return -1;
+ gatorfs_create_ulong(sb, dir, "enabled",
+ &mmapped_counters[i].enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key",
+ &mmapped_counters[i].key);
+ }
+
+ return 0;
+}
+
+static int gator_events_mmapped_start(void)
+{
+ int i;
+ struct timespec ts;
+
+ getnstimeofday(&ts);
+ prev_time = timespec_to_ns(&ts);
+
+ mmapped_global_enabled = 0;
+ for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) {
+ if (mmapped_counters[i].enabled) {
+ mmapped_global_enabled = 1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void gator_events_mmapped_stop(void)
+{
+}
+
+/* This function "simulates" counters, generating values of fancy
+ * functions like sine or triangle... */
+static int mmapped_simulate(int counter, int delta_in_us)
+{
+ int result = 0;
+
+ switch (counter) {
+ case 0: /* sort-of-sine */
+ {
+ static int t = 0;
+ int x;
+
+ t += delta_in_us;
+ if (t > 2048000)
+ t = 0;
+
+ if (t % 1024000 < 512000)
+ x = 512000 - (t % 512000);
+ else
+ x = t % 512000;
+
+ result = 32 * x / 512000;
+ result = result * result;
+
+ if (t < 1024000)
+ result = 1922 - result;
+ }
+ break;
+ case 1: /* triangle */
+ {
+ static int v, d = 1;
+
+ v = v + d * delta_in_us;
+ if (v < 0) {
+ v = 0;
+ d = 1;
+ } else if (v > 1000000) {
+ v = 1000000;
+ d = -1;
+ }
+
+ result = v;
+ }
+ break;
+ case 2: /* PWM signal */
+ {
+ static int dc, x, t = 0;
+
+ t += delta_in_us;
+ if (t > 1000000)
+ t = 0;
+ if (x / 1000000 != (x + delta_in_us) / 1000000)
+ dc = (dc + 100000) % 1000000;
+ x += delta_in_us;
+
+ result = t < dc ? 0 : 10;
+ }
+ break;
+ }
+
+ return result;
+}
+
+static int gator_events_mmapped_read(int **buffer)
+{
+ int i;
+ int len = 0;
+ int delta_in_us;
+ struct timespec ts;
+ s64 time;
+
+ /* System wide counters - read from one core only */
+ if (!on_primary_core() || !mmapped_global_enabled)
+ return 0;
+
+ getnstimeofday(&ts);
+ time = timespec_to_ns(&ts);
+ delta_in_us = (int)(time - prev_time) / 1000;
+ prev_time = time;
+
+ for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) {
+ if (mmapped_counters[i].enabled) {
+ mmapped_buffer[len++] = mmapped_counters[i].key;
+ mmapped_buffer[len++] =
+ mmapped_simulate(i, delta_in_us);
+ }
+ }
+
+ if (buffer)
+ *buffer = mmapped_buffer;
+
+ return len;
+}
+
+static struct gator_interface gator_events_mmapped_interface = {
+ .create_files = gator_events_mmapped_create_files,
+ .start = gator_events_mmapped_start,
+ .stop = gator_events_mmapped_stop,
+ .read = gator_events_mmapped_read,
+};
+
+/* Must not be static! */
+int __init gator_events_mmapped_init(void)
+{
+ int i;
+
+ for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) {
+ mmapped_counters[i].enabled = 0;
+ mmapped_counters[i].key = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_mmapped_interface);
+}
diff --git a/drivers/gator/gator_events_net.c b/drivers/gator/gator_events_net.c
new file mode 100644
index 000000000000..11c10e375511
--- /dev/null
+++ b/drivers/gator/gator_events_net.c
@@ -0,0 +1,172 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+#include <linux/netdevice.h>
+#include <linux/hardirq.h>
+
+#define NETRX 0
+#define NETTX 1
+#define TOTALNET 2
+
+static ulong netrx_enabled;
+static ulong nettx_enabled;
+static ulong netrx_key;
+static ulong nettx_key;
+static int rx_total, tx_total;
+static ulong netPrev[TOTALNET];
+static int netGet[TOTALNET * 4];
+
+static struct timer_list net_wake_up_timer;
+
+// Must be run in process context as the kernel function dev_get_stats() can sleep
+static void get_network_stats(struct work_struct *wsptr)
+{
+ int rx = 0, tx = 0;
+ struct net_device *dev;
+
+ for_each_netdev(&init_net, dev) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+ const struct net_device_stats *stats = dev_get_stats(dev);
+#else
+ struct rtnl_link_stats64 temp;
+ const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
+#endif
+ rx += stats->rx_bytes;
+ tx += stats->tx_bytes;
+ }
+ rx_total = rx;
+ tx_total = tx;
+}
+
+DECLARE_WORK(wq_get_stats, get_network_stats);
+
+static void net_wake_up_handler(unsigned long unused_data)
+{
+ // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
+ schedule_work(&wq_get_stats);
+}
+
+static void calculate_delta(int *rx, int *tx)
+{
+ int rx_calc, tx_calc;
+
+ rx_calc = (int)(rx_total - netPrev[NETRX]);
+ if (rx_calc < 0)
+ rx_calc = 0;
+ netPrev[NETRX] += rx_calc;
+
+ tx_calc = (int)(tx_total - netPrev[NETTX]);
+ if (tx_calc < 0)
+ tx_calc = 0;
+ netPrev[NETTX] += tx_calc;
+
+ *rx = rx_calc;
+ *tx = tx_calc;
+}
+
+static int gator_events_net_create_files(struct super_block *sb, struct dentry *root)
+{
+ // Network counters are not currently supported in RT-Preempt full because mod_timer is used
+#ifndef CONFIG_PREEMPT_RT_FULL
+ struct dentry *dir;
+
+ dir = gatorfs_mkdir(sb, root, "Linux_net_rx");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &netrx_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &netrx_key);
+
+ dir = gatorfs_mkdir(sb, root, "Linux_net_tx");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &nettx_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &nettx_key);
+#endif
+
+ return 0;
+}
+
+static int gator_events_net_start(void)
+{
+ get_network_stats(0);
+ netPrev[NETRX] = rx_total;
+ netPrev[NETTX] = tx_total;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+ setup_timer(&net_wake_up_timer, net_wake_up_handler, 0);
+#else
+ setup_deferrable_timer_on_stack(&net_wake_up_timer, net_wake_up_handler, 0);
+#endif
+ return 0;
+}
+
+static void gator_events_net_stop(void)
+{
+ del_timer_sync(&net_wake_up_timer);
+ netrx_enabled = 0;
+ nettx_enabled = 0;
+}
+
+static int gator_events_net_read(int **buffer)
+{
+ int len, rx_delta, tx_delta;
+ static int last_rx_delta = 0, last_tx_delta = 0;
+
+ if (!on_primary_core())
+ return 0;
+
+ if (!netrx_enabled && !nettx_enabled)
+ return 0;
+
+ mod_timer(&net_wake_up_timer, jiffies + 1);
+
+ calculate_delta(&rx_delta, &tx_delta);
+
+ len = 0;
+ if (netrx_enabled && last_rx_delta != rx_delta) {
+ last_rx_delta = rx_delta;
+ netGet[len++] = netrx_key;
+ netGet[len++] = 0; // indicates to Streamline that rx_delta bytes were transmitted now, not since the last message
+ netGet[len++] = netrx_key;
+ netGet[len++] = rx_delta;
+ }
+
+ if (nettx_enabled && last_tx_delta != tx_delta) {
+ last_tx_delta = tx_delta;
+ netGet[len++] = nettx_key;
+ netGet[len++] = 0; // indicates to Streamline that tx_delta bytes were transmitted now, not since the last message
+ netGet[len++] = nettx_key;
+ netGet[len++] = tx_delta;
+ }
+
+ if (buffer)
+ *buffer = netGet;
+
+ return len;
+}
+
+static struct gator_interface gator_events_net_interface = {
+ .create_files = gator_events_net_create_files,
+ .start = gator_events_net_start,
+ .stop = gator_events_net_stop,
+ .read = gator_events_net_read,
+};
+
+int gator_events_net_init(void)
+{
+ netrx_key = gator_events_get_key();
+ nettx_key = gator_events_get_key();
+
+ netrx_enabled = 0;
+ nettx_enabled = 0;
+
+ return gator_events_install(&gator_events_net_interface);
+}
diff --git a/drivers/gator/gator_events_perf_pmu.c b/drivers/gator/gator_events_perf_pmu.c
new file mode 100644
index 000000000000..06bbad5b10c3
--- /dev/null
+++ b/drivers/gator/gator_events_perf_pmu.c
@@ -0,0 +1,587 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+
+// gator_events_armvX.c is used for Linux 2.6.x
+#if GATOR_PERF_PMU_SUPPORT
+
+#include <linux/io.h>
+#ifdef CONFIG_OF
+#include <linux/of_address.h>
+#endif
+#include <linux/perf_event.h>
+#include <linux/slab.h>
+
+extern bool event_based_sampling;
+
+// Maximum number of per-core counters - currently reserves enough space for two full hardware PMUs for big.LITTLE
+#define CNTMAX 16
+#define CCI_400 4
+// Maximum number of uncore counters
+// + 1 for the cci-400 cycles counter
+#define UCCNT (CCI_400 + 1)
+
+// Default to 0 if unable to probe the revision which was the previous behavior
+#define DEFAULT_CCI_REVISION 0
+
+// A gator_attr is needed for every counter
+struct gator_attr {
+ // Set once in gator_events_perf_pmu_*_init - the name of the event in the gatorfs
+ char name[40];
+ // Exposed in gatorfs - set by gatord to enable this counter
+ unsigned long enabled;
+ // Set once in gator_events_perf_pmu_*_init - the perf type to use, see perf_type_id in the perf_event.h header file.
+ unsigned long type;
+ // Exposed in gatorfs - set by gatord to select the event to collect
+ unsigned long event;
+ // Exposed in gatorfs - set by gatord with the sample period to use and enable EBS for this counter
+ unsigned long count;
+ // Exposed as read only in gatorfs - set once in __attr_init as the key to use in the APC data
+ unsigned long key;
+};
+
+// Per-core counter attributes
+static struct gator_attr attrs[CNTMAX];
+// Number of initialized per-core counters
+static int attr_count;
+// Uncore counter attributes
+static struct gator_attr uc_attrs[UCCNT];
+// Number of initialized uncore counters
+static int uc_attr_count;
+
+struct gator_event {
+ int curr;
+ int prev;
+ int prev_delta;
+ bool zero;
+ struct perf_event *pevent;
+ struct perf_event_attr *pevent_attr;
+};
+
+static DEFINE_PER_CPU(struct gator_event[CNTMAX], events);
+static struct gator_event uc_events[UCCNT];
+static DEFINE_PER_CPU(int[(CNTMAX + UCCNT)*2], perf_cnt);
+
+static void gator_events_perf_pmu_stop(void);
+
+static int __create_files(struct super_block *sb, struct dentry *root, struct gator_attr *const attr)
+{
+ struct dentry *dir;
+
+ if (attr->name[0] == '\0') {
+ return 0;
+ }
+ dir = gatorfs_mkdir(sb, root, attr->name);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &attr->enabled);
+ gatorfs_create_ulong(sb, dir, "count", &attr->count);
+ gatorfs_create_ro_ulong(sb, dir, "key", &attr->key);
+ gatorfs_create_ulong(sb, dir, "event", &attr->event);
+
+ return 0;
+}
+
+static int gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root)
+{
+ int cnt;
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ if (__create_files(sb, root, &attrs[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ if (__create_files(sb, root, &uc_attrs[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+static void ebs_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs)
+#else
+static void ebs_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs)
+#endif
+{
+ gator_backtrace_handler(regs);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+static void dummy_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs)
+#else
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs)
+#endif
+{
+// Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll
+}
+
+static int gator_events_perf_pmu_read(int **buffer);
+
+static int gator_events_perf_pmu_online(int **buffer, bool migrate)
+{
+ return gator_events_perf_pmu_read(buffer);
+}
+
+static void __online_dispatch(int cpu, bool migrate, struct gator_attr *const attr, struct gator_event *const event)
+{
+ perf_overflow_handler_t handler;
+
+ event->zero = true;
+
+ if (event->pevent != NULL || event->pevent_attr == 0 || migrate) {
+ return;
+ }
+
+ if (attr->count > 0) {
+ handler = ebs_overflow_handler;
+ } else {
+ handler = dummy_handler;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+ event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler);
+#else
+ event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler, 0);
+#endif
+ if (IS_ERR(event->pevent)) {
+ pr_debug("gator: unable to online a counter on cpu %d\n", cpu);
+ event->pevent = NULL;
+ return;
+ }
+
+ if (event->pevent->state != PERF_EVENT_STATE_ACTIVE) {
+ pr_debug("gator: inactive counter on cpu %d\n", cpu);
+ perf_event_release_kernel(event->pevent);
+ event->pevent = NULL;
+ return;
+ }
+}
+
+static void gator_events_perf_pmu_online_dispatch(int cpu, bool migrate)
+{
+ int cnt;
+
+ cpu = pcpu_to_lcpu(cpu);
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __online_dispatch(cpu, migrate, &attrs[cnt], &per_cpu(events, cpu)[cnt]);
+ }
+
+ if (cpu == 0) {
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __online_dispatch(cpu, migrate, &uc_attrs[cnt], &uc_events[cnt]);
+ }
+ }
+}
+
+static void __offline_dispatch(int cpu, struct gator_event *const event)
+{
+ struct perf_event *pe = NULL;
+
+ if (event->pevent) {
+ pe = event->pevent;
+ event->pevent = NULL;
+ }
+
+ if (pe) {
+ perf_event_release_kernel(pe);
+ }
+}
+
+static void gator_events_perf_pmu_offline_dispatch(int cpu, bool migrate)
+{
+ int cnt;
+
+ if (migrate) {
+ return;
+ }
+ cpu = pcpu_to_lcpu(cpu);
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __offline_dispatch(cpu, &per_cpu(events, cpu)[cnt]);
+ }
+
+ if (cpu == 0) {
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __offline_dispatch(cpu, &uc_events[cnt]);
+ }
+ }
+}
+
+static int __check_ebs(struct gator_attr *const attr)
+{
+ if (attr->count > 0) {
+ if (!event_based_sampling) {
+ event_based_sampling = true;
+ } else {
+ printk(KERN_WARNING "gator: Only one ebs counter is allowed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int __start(struct gator_attr *const attr, struct gator_event *const event)
+{
+ u32 size = sizeof(struct perf_event_attr);
+
+ event->pevent = NULL;
+ if (!attr->enabled) { // Skip disabled counters
+ return 0;
+ }
+
+ event->prev = 0;
+ event->curr = 0;
+ event->prev_delta = 0;
+ event->pevent_attr = kmalloc(size, GFP_KERNEL);
+ if (!event->pevent_attr) {
+ gator_events_perf_pmu_stop();
+ return -1;
+ }
+
+ memset(event->pevent_attr, 0, size);
+ event->pevent_attr->type = attr->type;
+ event->pevent_attr->size = size;
+ event->pevent_attr->config = attr->event;
+ event->pevent_attr->sample_period = attr->count;
+ event->pevent_attr->pinned = 1;
+
+ return 0;
+}
+
+static int gator_events_perf_pmu_start(void)
+{
+ int cnt, cpu;
+
+ event_based_sampling = false;
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ if (__check_ebs(&attrs[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ if (__check_ebs(&uc_attrs[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ for_each_present_cpu(cpu) {
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ if (__start(&attrs[cnt], &per_cpu(events, cpu)[cnt]) != 0) {
+ return -1;
+ }
+ }
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ if (__start(&uc_attrs[cnt], &uc_events[cnt]) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void __event_stop(struct gator_event *const event)
+{
+ if (event->pevent_attr) {
+ kfree(event->pevent_attr);
+ event->pevent_attr = NULL;
+ }
+}
+
+static void __attr_stop(struct gator_attr *const attr)
+{
+ attr->enabled = 0;
+ attr->event = 0;
+ attr->count = 0;
+}
+
+static void gator_events_perf_pmu_stop(void)
+{
+ unsigned int cnt, cpu;
+
+ for_each_present_cpu(cpu) {
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __event_stop(&per_cpu(events, cpu)[cnt]);
+ }
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __event_stop(&uc_events[cnt]);
+ }
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __attr_stop(&attrs[cnt]);
+ }
+
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __attr_stop(&uc_attrs[cnt]);
+ }
+}
+
+static void __read(int *const len, int cpu, struct gator_attr *const attr, struct gator_event *const event)
+{
+ int delta;
+
+ struct perf_event *const ev = event->pevent;
+ if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) {
+ /* After creating the perf counter in __online_dispatch, there
+ * is a race condition between gator_events_perf_pmu_online and
+ * gator_events_perf_pmu_read. So have
+ * gator_events_perf_pmu_online call gator_events_perf_pmu_read
+ * and in __read check to see if it's the first call after
+ * __online_dispatch and if so, run the online code.
+ */
+ if (event->zero) {
+ ev->pmu->read(ev);
+ event->prev = event->curr = local64_read(&ev->count);
+ event->prev_delta = 0;
+ per_cpu(perf_cnt, cpu)[(*len)++] = attr->key;
+ per_cpu(perf_cnt, cpu)[(*len)++] = 0;
+ event->zero = false;
+ } else {
+ ev->pmu->read(ev);
+ event->curr = local64_read(&ev->count);
+ delta = event->curr - event->prev;
+ if (delta != 0 || delta != event->prev_delta) {
+ event->prev_delta = delta;
+ event->prev = event->curr;
+ per_cpu(perf_cnt, cpu)[(*len)++] = attr->key;
+ if (delta < 0) {
+ delta *= -1;
+ }
+ per_cpu(perf_cnt, cpu)[(*len)++] = delta;
+ }
+ }
+ }
+}
+
+static int gator_events_perf_pmu_read(int **buffer)
+{
+ int cnt, len = 0;
+ const int cpu = get_logical_cpu();
+
+ for (cnt = 0; cnt < attr_count; cnt++) {
+ __read(&len, cpu, &attrs[cnt], &per_cpu(events, cpu)[cnt]);
+ }
+
+ if (cpu == 0) {
+ for (cnt = 0; cnt < uc_attr_count; cnt++) {
+ __read(&len, cpu, &uc_attrs[cnt], &uc_events[cnt]);
+ }
+ }
+
+ if (buffer) {
+ *buffer = per_cpu(perf_cnt, cpu);
+ }
+
+ return len;
+}
+
+static struct gator_interface gator_events_perf_pmu_interface = {
+ .create_files = gator_events_perf_pmu_create_files,
+ .start = gator_events_perf_pmu_start,
+ .stop = gator_events_perf_pmu_stop,
+ .online = gator_events_perf_pmu_online,
+ .online_dispatch = gator_events_perf_pmu_online_dispatch,
+ .offline_dispatch = gator_events_perf_pmu_offline_dispatch,
+ .read = gator_events_perf_pmu_read,
+};
+
+static void __attr_init(struct gator_attr *const attr)
+{
+ attr->name[0] = '\0';
+ attr->enabled = 0;
+ attr->type = 0;
+ attr->event = 0;
+ attr->count = 0;
+ attr->key = gator_events_get_key();
+}
+
+#ifdef CONFIG_OF
+
+static const struct of_device_id arm_cci_matches[] = {
+ {.compatible = "arm,cci-400" },
+ {},
+};
+
+static int probe_cci_revision(void)
+{
+ struct device_node *np;
+ struct resource res;
+ void __iomem *cci_ctrl_base;
+ int rev;
+ int ret = DEFAULT_CCI_REVISION;
+
+ np = of_find_matching_node(NULL, arm_cci_matches);
+ if (!np) {
+ return ret;
+ }
+
+ if (of_address_to_resource(np, 0, &res)) {
+ goto node_put;
+ }
+
+ cci_ctrl_base = ioremap(res.start, resource_size(&res));
+
+ rev = (readl_relaxed(cci_ctrl_base + 0xfe8) >> 4) & 0xf;
+
+ if (rev <= 4) {
+ ret = 0;
+ } else if (rev <= 6) {
+ ret = 1;
+ }
+
+ iounmap(cci_ctrl_base);
+
+ node_put:
+ of_node_put(np);
+
+ return ret;
+}
+
+#else
+
+static int probe_cci_revision(void)
+{
+ return DEFAULT_CCI_REVISION;
+}
+
+#endif
+
+static void gator_events_perf_pmu_cci_init(const int type)
+{
+ int cnt;
+ const char *cci_name;
+
+ switch (probe_cci_revision()) {
+ case 0:
+ cci_name = "CCI_400";
+ break;
+ case 1:
+ cci_name = "CCI_400-r1";
+ break;
+ default:
+ pr_debug("gator: unrecognized cci-400 revision\n");
+ return;
+ }
+
+ snprintf(uc_attrs[uc_attr_count].name, sizeof(uc_attrs[uc_attr_count].name), "%s_ccnt", cci_name);
+ uc_attrs[uc_attr_count].type = type;
+ ++uc_attr_count;
+
+ for (cnt = 0; cnt < CCI_400; ++cnt, ++uc_attr_count) {
+ struct gator_attr *const attr = &uc_attrs[uc_attr_count];
+ snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", cci_name, cnt);
+ attr->type = type;
+ }
+}
+
+static void gator_events_perf_pmu_cpu_init(const struct gator_cpu *const gator_cpu, const int type)
+{
+ int cnt;
+
+ snprintf(attrs[attr_count].name, sizeof(attrs[attr_count].name), "%s_ccnt", gator_cpu->pmnc_name);
+ attrs[attr_count].type = type;
+ ++attr_count;
+
+ for (cnt = 0; cnt < gator_cpu->pmnc_counters; ++cnt, ++attr_count) {
+ struct gator_attr *const attr = &attrs[attr_count];
+ snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", gator_cpu->pmnc_name, cnt);
+ attr->type = type;
+ }
+}
+
+int gator_events_perf_pmu_init(void)
+{
+ struct perf_event_attr pea;
+ struct perf_event *pe;
+ const struct gator_cpu *gator_cpu;
+ int type;
+ int cpu;
+ int cnt;
+ bool found_cpu = false;
+
+ for (cnt = 0; cnt < CNTMAX; cnt++) {
+ __attr_init(&attrs[cnt]);
+ }
+ for (cnt = 0; cnt < UCCNT; cnt++) {
+ __attr_init(&uc_attrs[cnt]);
+ }
+
+ memset(&pea, 0, sizeof(pea));
+ pea.size = sizeof(pea);
+ pea.config = 0xFF;
+ attr_count = 0;
+ uc_attr_count = 0;
+ for (type = PERF_TYPE_MAX; type < 0x20; ++type) {
+ pea.type = type;
+
+ // A particular PMU may work on some but not all cores, so try on each core
+ pe = NULL;
+ for_each_present_cpu(cpu) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+ pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler);
+#else
+ pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler, 0);
+#endif
+ if (!IS_ERR(pe)) {
+ break;
+ }
+ }
+ // Assume that valid PMUs are contiguous
+ if (IS_ERR(pe)) {
+ break;
+ }
+
+ if (pe->pmu != NULL && type == pe->pmu->type) {
+ if (strcmp("CCI", pe->pmu->name) == 0 || strcmp("CCI_400", pe->pmu->name) == 0 || strcmp("CCI_400-r1", pe->pmu->name) == 0) {
+ gator_events_perf_pmu_cci_init(type);
+ } else if ((gator_cpu = gator_find_cpu_by_pmu_name(pe->pmu->name)) != NULL) {
+ found_cpu = true;
+ gator_events_perf_pmu_cpu_init(gator_cpu, type);
+ }
+ // Initialize gator_attrs for dynamic PMUs here
+ }
+
+ perf_event_release_kernel(pe);
+ }
+
+ if (!found_cpu) {
+ const struct gator_cpu *const gator_cpu = gator_find_cpu_by_cpuid(gator_cpuid());
+ if (gator_cpu == NULL) {
+ return -1;
+ }
+ gator_events_perf_pmu_cpu_init(gator_cpu, PERF_TYPE_RAW);
+ }
+
+ // Initialize gator_attrs for non-dynamic PMUs here
+
+ if (attr_count > CNTMAX) {
+ printk(KERN_ERR "gator: Too many perf counters\n");
+ return -1;
+ }
+
+ if (uc_attr_count > UCCNT) {
+ printk(KERN_ERR "gator: Too many perf uncore counters\n");
+ return -1;
+ }
+
+ return gator_events_install(&gator_events_perf_pmu_interface);
+}
+
+#endif
diff --git a/drivers/gator/gator_events_sched.c b/drivers/gator/gator_events_sched.c
new file mode 100644
index 000000000000..9e3915830182
--- /dev/null
+++ b/drivers/gator/gator_events_sched.c
@@ -0,0 +1,113 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+#include <trace/events/sched.h>
+
+#define SCHED_SWITCH 0
+#define SCHED_TOTAL (SCHED_SWITCH+1)
+
+static ulong sched_switch_enabled;
+static ulong sched_switch_key;
+static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt);
+static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
+#else
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
+#endif
+{
+ unsigned long flags;
+
+ // disable interrupts to synchronize with gator_events_sched_read()
+ // spinlocks not needed since percpu buffers are used
+ local_irq_save(flags);
+ per_cpu(schedCnt, get_physical_cpu())[SCHED_SWITCH]++;
+ local_irq_restore(flags);
+}
+
+static int gator_events_sched_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ /* switch */
+ dir = gatorfs_mkdir(sb, root, "Linux_sched_switch");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &sched_switch_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &sched_switch_key);
+
+ return 0;
+}
+
+static int gator_events_sched_start(void)
+{
+ // register tracepoints
+ if (sched_switch_enabled)
+ if (GATOR_REGISTER_TRACE(sched_switch))
+ goto sched_switch_exit;
+ pr_debug("gator: registered scheduler event tracepoints\n");
+
+ return 0;
+
+ // unregister tracepoints on error
+sched_switch_exit:
+ pr_err("gator: scheduler event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+static void gator_events_sched_stop(void)
+{
+ if (sched_switch_enabled)
+ GATOR_UNREGISTER_TRACE(sched_switch);
+ pr_debug("gator: unregistered scheduler event tracepoints\n");
+
+ sched_switch_enabled = 0;
+}
+
+static int gator_events_sched_read(int **buffer)
+{
+ unsigned long flags;
+ int len, value;
+ int cpu = get_physical_cpu();
+
+ len = 0;
+ if (sched_switch_enabled) {
+ local_irq_save(flags);
+ value = per_cpu(schedCnt, cpu)[SCHED_SWITCH];
+ per_cpu(schedCnt, cpu)[SCHED_SWITCH] = 0;
+ local_irq_restore(flags);
+ per_cpu(schedGet, cpu)[len++] = sched_switch_key;
+ per_cpu(schedGet, cpu)[len++] = value;
+ }
+
+ if (buffer)
+ *buffer = per_cpu(schedGet, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_sched_interface = {
+ .create_files = gator_events_sched_create_files,
+ .start = gator_events_sched_start,
+ .stop = gator_events_sched_stop,
+ .read = gator_events_sched_read,
+};
+
+int gator_events_sched_init(void)
+{
+ sched_switch_enabled = 0;
+
+ sched_switch_key = gator_events_get_key();
+
+ return gator_events_install(&gator_events_sched_interface);
+}
diff --git a/drivers/gator/gator_events_scorpion.c b/drivers/gator/gator_events_scorpion.c
new file mode 100644
index 000000000000..2e5be8d50e9d
--- /dev/null
+++ b/drivers/gator/gator_events_scorpion.c
@@ -0,0 +1,669 @@
+/**
+ * Copyright (C) ARM Limited 2011-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+
+// gator_events_perf_pmu.c is used if perf is supported
+#if GATOR_NO_PERF_SUPPORT
+
+static const char *pmnc_name;
+static int pmnc_counters;
+
+// Per-CPU PMNC: config reg
+#define PMNC_E (1 << 0) /* Enable all counters */
+#define PMNC_P (1 << 1) /* Reset all counters */
+#define PMNC_C (1 << 2) /* Cycle counter reset */
+#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */
+#define PMNC_X (1 << 4) /* Export to ETM */
+#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug */
+#define PMNC_MASK 0x3f /* Mask for writable bits */
+
+// ccnt reg
+#define CCNT_REG (1 << 31)
+
+#define CCNT 0
+#define CNT0 1
+#define CNTMAX (4+1)
+
+static unsigned long pmnc_enabled[CNTMAX];
+static unsigned long pmnc_event[CNTMAX];
+static unsigned long pmnc_key[CNTMAX];
+
+static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
+
+enum scorpion_perf_types {
+ SCORPION_ICACHE_EXPL_INV = 0x4c,
+ SCORPION_ICACHE_MISS = 0x4d,
+ SCORPION_ICACHE_ACCESS = 0x4e,
+ SCORPION_ICACHE_CACHEREQ_L2 = 0x4f,
+ SCORPION_ICACHE_NOCACHE_L2 = 0x50,
+ SCORPION_HIQUP_NOPED = 0x51,
+ SCORPION_DATA_ABORT = 0x52,
+ SCORPION_IRQ = 0x53,
+ SCORPION_FIQ = 0x54,
+ SCORPION_ALL_EXCPT = 0x55,
+ SCORPION_UNDEF = 0x56,
+ SCORPION_SVC = 0x57,
+ SCORPION_SMC = 0x58,
+ SCORPION_PREFETCH_ABORT = 0x59,
+ SCORPION_INDEX_CHECK = 0x5a,
+ SCORPION_NULL_CHECK = 0x5b,
+ SCORPION_EXPL_ICIALLU = 0x5c,
+ SCORPION_IMPL_ICIALLU = 0x5d,
+ SCORPION_NONICIALLU_BTAC_INV = 0x5e,
+ SCORPION_ICIMVAU_IMPL_ICIALLU = 0x5f,
+ SCORPION_SPIPE_ONLY_CYCLES = 0x60,
+ SCORPION_XPIPE_ONLY_CYCLES = 0x61,
+ SCORPION_DUAL_CYCLES = 0x62,
+ SCORPION_DISPATCH_ANY_CYCLES = 0x63,
+ SCORPION_FIFO_FULLBLK_CMT = 0x64,
+ SCORPION_FAIL_COND_INST = 0x65,
+ SCORPION_PASS_COND_INST = 0x66,
+ SCORPION_ALLOW_VU_CLK = 0x67,
+ SCORPION_VU_IDLE = 0x68,
+ SCORPION_ALLOW_L2_CLK = 0x69,
+ SCORPION_L2_IDLE = 0x6a,
+ SCORPION_DTLB_IMPL_INV_SCTLR_DACR = 0x6b,
+ SCORPION_DTLB_EXPL_INV = 0x6c,
+ SCORPION_DTLB_MISS = 0x6d,
+ SCORPION_DTLB_ACCESS = 0x6e,
+ SCORPION_ITLB_MISS = 0x6f,
+ SCORPION_ITLB_IMPL_INV = 0x70,
+ SCORPION_ITLB_EXPL_INV = 0x71,
+ SCORPION_UTLB_D_MISS = 0x72,
+ SCORPION_UTLB_D_ACCESS = 0x73,
+ SCORPION_UTLB_I_MISS = 0x74,
+ SCORPION_UTLB_I_ACCESS = 0x75,
+ SCORPION_UTLB_INV_ASID = 0x76,
+ SCORPION_UTLB_INV_MVA = 0x77,
+ SCORPION_UTLB_INV_ALL = 0x78,
+ SCORPION_S2_HOLD_RDQ_UNAVAIL = 0x79,
+ SCORPION_S2_HOLD = 0x7a,
+ SCORPION_S2_HOLD_DEV_OP = 0x7b,
+ SCORPION_S2_HOLD_ORDER = 0x7c,
+ SCORPION_S2_HOLD_BARRIER = 0x7d,
+ SCORPION_VIU_DUAL_CYCLE = 0x7e,
+ SCORPION_VIU_SINGLE_CYCLE = 0x7f,
+ SCORPION_VX_PIPE_WAR_STALL_CYCLES = 0x80,
+ SCORPION_VX_PIPE_WAW_STALL_CYCLES = 0x81,
+ SCORPION_VX_PIPE_RAW_STALL_CYCLES = 0x82,
+ SCORPION_VX_PIPE_LOAD_USE_STALL = 0x83,
+ SCORPION_VS_PIPE_WAR_STALL_CYCLES = 0x84,
+ SCORPION_VS_PIPE_WAW_STALL_CYCLES = 0x85,
+ SCORPION_VS_PIPE_RAW_STALL_CYCLES = 0x86,
+ SCORPION_EXCEPTIONS_INV_OPERATION = 0x87,
+ SCORPION_EXCEPTIONS_DIV_BY_ZERO = 0x88,
+ SCORPION_COND_INST_FAIL_VX_PIPE = 0x89,
+ SCORPION_COND_INST_FAIL_VS_PIPE = 0x8a,
+ SCORPION_EXCEPTIONS_OVERFLOW = 0x8b,
+ SCORPION_EXCEPTIONS_UNDERFLOW = 0x8c,
+ SCORPION_EXCEPTIONS_DENORM = 0x8d,
+#ifdef CONFIG_ARCH_MSM_SCORPIONMP
+ SCORPIONMP_NUM_BARRIERS = 0x8e,
+ SCORPIONMP_BARRIER_CYCLES = 0x8f,
+#else
+ SCORPION_BANK_AB_HIT = 0x8e,
+ SCORPION_BANK_AB_ACCESS = 0x8f,
+ SCORPION_BANK_CD_HIT = 0x90,
+ SCORPION_BANK_CD_ACCESS = 0x91,
+ SCORPION_BANK_AB_DSIDE_HIT = 0x92,
+ SCORPION_BANK_AB_DSIDE_ACCESS = 0x93,
+ SCORPION_BANK_CD_DSIDE_HIT = 0x94,
+ SCORPION_BANK_CD_DSIDE_ACCESS = 0x95,
+ SCORPION_BANK_AB_ISIDE_HIT = 0x96,
+ SCORPION_BANK_AB_ISIDE_ACCESS = 0x97,
+ SCORPION_BANK_CD_ISIDE_HIT = 0x98,
+ SCORPION_BANK_CD_ISIDE_ACCESS = 0x99,
+ SCORPION_ISIDE_RD_WAIT = 0x9a,
+ SCORPION_DSIDE_RD_WAIT = 0x9b,
+ SCORPION_BANK_BYPASS_WRITE = 0x9c,
+ SCORPION_BANK_AB_NON_CASTOUT = 0x9d,
+ SCORPION_BANK_AB_L2_CASTOUT = 0x9e,
+ SCORPION_BANK_CD_NON_CASTOUT = 0x9f,
+ SCORPION_BANK_CD_L2_CASTOUT = 0xa0,
+#endif
+ MSM_MAX_EVT
+};
+
+struct scorp_evt {
+ u32 evt_type;
+ u32 val;
+ u8 grp;
+ u32 evt_type_act;
+};
+
+static const struct scorp_evt sc_evt[] = {
+ {SCORPION_ICACHE_EXPL_INV, 0x80000500, 0, 0x4d},
+ {SCORPION_ICACHE_MISS, 0x80050000, 0, 0x4e},
+ {SCORPION_ICACHE_ACCESS, 0x85000000, 0, 0x4f},
+ {SCORPION_ICACHE_CACHEREQ_L2, 0x86000000, 0, 0x4f},
+ {SCORPION_ICACHE_NOCACHE_L2, 0x87000000, 0, 0x4f},
+ {SCORPION_HIQUP_NOPED, 0x80080000, 0, 0x4e},
+ {SCORPION_DATA_ABORT, 0x8000000a, 0, 0x4c},
+ {SCORPION_IRQ, 0x80000a00, 0, 0x4d},
+ {SCORPION_FIQ, 0x800a0000, 0, 0x4e},
+ {SCORPION_ALL_EXCPT, 0x8a000000, 0, 0x4f},
+ {SCORPION_UNDEF, 0x8000000b, 0, 0x4c},
+ {SCORPION_SVC, 0x80000b00, 0, 0x4d},
+ {SCORPION_SMC, 0x800b0000, 0, 0x4e},
+ {SCORPION_PREFETCH_ABORT, 0x8b000000, 0, 0x4f},
+ {SCORPION_INDEX_CHECK, 0x8000000c, 0, 0x4c},
+ {SCORPION_NULL_CHECK, 0x80000c00, 0, 0x4d},
+ {SCORPION_EXPL_ICIALLU, 0x8000000d, 0, 0x4c},
+ {SCORPION_IMPL_ICIALLU, 0x80000d00, 0, 0x4d},
+ {SCORPION_NONICIALLU_BTAC_INV, 0x800d0000, 0, 0x4e},
+ {SCORPION_ICIMVAU_IMPL_ICIALLU, 0x8d000000, 0, 0x4f},
+
+ {SCORPION_SPIPE_ONLY_CYCLES, 0x80000600, 1, 0x51},
+ {SCORPION_XPIPE_ONLY_CYCLES, 0x80060000, 1, 0x52},
+ {SCORPION_DUAL_CYCLES, 0x86000000, 1, 0x53},
+ {SCORPION_DISPATCH_ANY_CYCLES, 0x89000000, 1, 0x53},
+ {SCORPION_FIFO_FULLBLK_CMT, 0x8000000d, 1, 0x50},
+ {SCORPION_FAIL_COND_INST, 0x800d0000, 1, 0x52},
+ {SCORPION_PASS_COND_INST, 0x8d000000, 1, 0x53},
+ {SCORPION_ALLOW_VU_CLK, 0x8000000e, 1, 0x50},
+ {SCORPION_VU_IDLE, 0x80000e00, 1, 0x51},
+ {SCORPION_ALLOW_L2_CLK, 0x800e0000, 1, 0x52},
+ {SCORPION_L2_IDLE, 0x8e000000, 1, 0x53},
+
+ {SCORPION_DTLB_IMPL_INV_SCTLR_DACR, 0x80000001, 2, 0x54},
+ {SCORPION_DTLB_EXPL_INV, 0x80000100, 2, 0x55},
+ {SCORPION_DTLB_MISS, 0x80010000, 2, 0x56},
+ {SCORPION_DTLB_ACCESS, 0x81000000, 2, 0x57},
+ {SCORPION_ITLB_MISS, 0x80000200, 2, 0x55},
+ {SCORPION_ITLB_IMPL_INV, 0x80020000, 2, 0x56},
+ {SCORPION_ITLB_EXPL_INV, 0x82000000, 2, 0x57},
+ {SCORPION_UTLB_D_MISS, 0x80000003, 2, 0x54},
+ {SCORPION_UTLB_D_ACCESS, 0x80000300, 2, 0x55},
+ {SCORPION_UTLB_I_MISS, 0x80030000, 2, 0x56},
+ {SCORPION_UTLB_I_ACCESS, 0x83000000, 2, 0x57},
+ {SCORPION_UTLB_INV_ASID, 0x80000400, 2, 0x55},
+ {SCORPION_UTLB_INV_MVA, 0x80040000, 2, 0x56},
+ {SCORPION_UTLB_INV_ALL, 0x84000000, 2, 0x57},
+ {SCORPION_S2_HOLD_RDQ_UNAVAIL, 0x80000800, 2, 0x55},
+ {SCORPION_S2_HOLD, 0x88000000, 2, 0x57},
+ {SCORPION_S2_HOLD_DEV_OP, 0x80000900, 2, 0x55},
+ {SCORPION_S2_HOLD_ORDER, 0x80090000, 2, 0x56},
+ {SCORPION_S2_HOLD_BARRIER, 0x89000000, 2, 0x57},
+
+ {SCORPION_VIU_DUAL_CYCLE, 0x80000001, 4, 0x5c},
+ {SCORPION_VIU_SINGLE_CYCLE, 0x80000100, 4, 0x5d},
+ {SCORPION_VX_PIPE_WAR_STALL_CYCLES, 0x80000005, 4, 0x5c},
+ {SCORPION_VX_PIPE_WAW_STALL_CYCLES, 0x80000500, 4, 0x5d},
+ {SCORPION_VX_PIPE_RAW_STALL_CYCLES, 0x80050000, 4, 0x5e},
+ {SCORPION_VX_PIPE_LOAD_USE_STALL, 0x80000007, 4, 0x5c},
+ {SCORPION_VS_PIPE_WAR_STALL_CYCLES, 0x80000008, 4, 0x5c},
+ {SCORPION_VS_PIPE_WAW_STALL_CYCLES, 0x80000800, 4, 0x5d},
+ {SCORPION_VS_PIPE_RAW_STALL_CYCLES, 0x80080000, 4, 0x5e},
+ {SCORPION_EXCEPTIONS_INV_OPERATION, 0x8000000b, 4, 0x5c},
+ {SCORPION_EXCEPTIONS_DIV_BY_ZERO, 0x80000b00, 4, 0x5d},
+ {SCORPION_COND_INST_FAIL_VX_PIPE, 0x800b0000, 4, 0x5e},
+ {SCORPION_COND_INST_FAIL_VS_PIPE, 0x8b000000, 4, 0x5f},
+ {SCORPION_EXCEPTIONS_OVERFLOW, 0x8000000c, 4, 0x5c},
+ {SCORPION_EXCEPTIONS_UNDERFLOW, 0x80000c00, 4, 0x5d},
+ {SCORPION_EXCEPTIONS_DENORM, 0x8c000000, 4, 0x5f},
+
+#ifdef CONFIG_ARCH_MSM_SCORPIONMP
+ {SCORPIONMP_NUM_BARRIERS, 0x80000e00, 3, 0x59},
+ {SCORPIONMP_BARRIER_CYCLES, 0x800e0000, 3, 0x5a},
+#else
+ {SCORPION_BANK_AB_HIT, 0x80000001, 3, 0x58},
+ {SCORPION_BANK_AB_ACCESS, 0x80000100, 3, 0x59},
+ {SCORPION_BANK_CD_HIT, 0x80010000, 3, 0x5a},
+ {SCORPION_BANK_CD_ACCESS, 0x81000000, 3, 0x5b},
+ {SCORPION_BANK_AB_DSIDE_HIT, 0x80000002, 3, 0x58},
+ {SCORPION_BANK_AB_DSIDE_ACCESS, 0x80000200, 3, 0x59},
+ {SCORPION_BANK_CD_DSIDE_HIT, 0x80020000, 3, 0x5a},
+ {SCORPION_BANK_CD_DSIDE_ACCESS, 0x82000000, 3, 0x5b},
+ {SCORPION_BANK_AB_ISIDE_HIT, 0x80000003, 3, 0x58},
+ {SCORPION_BANK_AB_ISIDE_ACCESS, 0x80000300, 3, 0x59},
+ {SCORPION_BANK_CD_ISIDE_HIT, 0x80030000, 3, 0x5a},
+ {SCORPION_BANK_CD_ISIDE_ACCESS, 0x83000000, 3, 0x5b},
+ {SCORPION_ISIDE_RD_WAIT, 0x80000009, 3, 0x58},
+ {SCORPION_DSIDE_RD_WAIT, 0x80090000, 3, 0x5a},
+ {SCORPION_BANK_BYPASS_WRITE, 0x8000000a, 3, 0x58},
+ {SCORPION_BANK_AB_NON_CASTOUT, 0x8000000c, 3, 0x58},
+ {SCORPION_BANK_AB_L2_CASTOUT, 0x80000c00, 3, 0x59},
+ {SCORPION_BANK_CD_NON_CASTOUT, 0x800c0000, 3, 0x5a},
+ {SCORPION_BANK_CD_L2_CASTOUT, 0x8c000000, 3, 0x5b},
+#endif
+};
+
+static inline void scorpion_pmnc_write(u32 val)
+{
+ val &= PMNC_MASK;
+ asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
+}
+
+static inline u32 scorpion_pmnc_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
+ return val;
+}
+
+static inline u32 scorpion_ccnt_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
+ return val;
+}
+
+static inline u32 scorpion_cntn_read(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
+ return val;
+}
+
+static inline u32 scorpion_pmnc_enable_counter(unsigned int cnt)
+{
+ u32 val;
+
+ if (cnt >= CNTMAX) {
+ pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
+ return -1;
+ }
+
+ if (cnt == CCNT)
+ val = CCNT_REG;
+ else
+ val = (1 << (cnt - CNT0));
+
+ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
+
+ return cnt;
+}
+
+static inline u32 scorpion_pmnc_disable_counter(unsigned int cnt)
+{
+ u32 val;
+
+ if (cnt >= CNTMAX) {
+ pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
+ return -1;
+ }
+
+ if (cnt == CCNT)
+ val = CCNT_REG;
+ else
+ val = (1 << (cnt - CNT0));
+
+ asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
+
+ return cnt;
+}
+
+static inline int scorpion_pmnc_select_counter(unsigned int cnt)
+{
+ u32 val;
+
+ if ((cnt == CCNT) || (cnt >= CNTMAX)) {
+ pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt);
+ return -1;
+ }
+
+ val = (cnt - CNT0);
+ asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
+
+ return cnt;
+}
+
+static u32 scorpion_read_lpm0(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_lpm0(u32 val)
+{
+ asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_lpm1(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_lpm1(u32 val)
+{
+ asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_lpm2(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_lpm2(u32 val)
+{
+ asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_l2lpm(void)
+{
+ u32 val;
+ asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_l2lpm(u32 val)
+{
+ asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_vlpm(void)
+{
+ u32 val;
+ asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_write_vlpm(u32 val)
+{
+ asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val));
+}
+
+struct scorpion_access_funcs {
+ u32(*read)(void);
+ void (*write)(u32);
+};
+
+struct scorpion_access_funcs scor_func[] = {
+ {scorpion_read_lpm0, scorpion_write_lpm0},
+ {scorpion_read_lpm1, scorpion_write_lpm1},
+ {scorpion_read_lpm2, scorpion_write_lpm2},
+ {scorpion_read_l2lpm, scorpion_write_l2lpm},
+ {scorpion_read_vlpm, scorpion_write_vlpm},
+};
+
+u32 venum_orig_val;
+u32 fp_orig_val;
+
+static void scorpion_pre_vlpm(void)
+{
+ u32 venum_new_val;
+ u32 fp_new_val;
+
+ /* CPACR Enable CP10 access */
+ asm volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (venum_orig_val));
+ venum_new_val = venum_orig_val | 0x00300000;
+ asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_new_val));
+ /* Enable FPEXC */
+ asm volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (fp_orig_val));
+ fp_new_val = fp_orig_val | 0x40000000;
+ asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_new_val));
+}
+
+static void scorpion_post_vlpm(void)
+{
+ /* Restore FPEXC */
+ asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_orig_val));
+ /* Restore CPACR */
+ asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_orig_val));
+}
+
+#define COLMN0MASK 0x000000ff
+#define COLMN1MASK 0x0000ff00
+#define COLMN2MASK 0x00ff0000
+static u32 scorpion_get_columnmask(u32 setval)
+{
+ if (setval & COLMN0MASK)
+ return 0xffffff00;
+ else if (setval & COLMN1MASK)
+ return 0xffff00ff;
+ else if (setval & COLMN2MASK)
+ return 0xff00ffff;
+ else
+ return 0x80ffffff;
+}
+
+static void scorpion_evt_setup(u32 gr, u32 setval)
+{
+ u32 val;
+ if (gr == 4)
+ scorpion_pre_vlpm();
+ val = scorpion_get_columnmask(setval) & scor_func[gr].read();
+ val = val | setval;
+ scor_func[gr].write(val);
+ if (gr == 4)
+ scorpion_post_vlpm();
+}
+
+static int get_scorpion_evtinfo(unsigned int evt_type, struct scorp_evt *evtinfo)
+{
+ u32 idx;
+ if ((evt_type < 0x4c) || (evt_type >= MSM_MAX_EVT))
+ return 0;
+ idx = evt_type - 0x4c;
+ if (sc_evt[idx].evt_type == evt_type) {
+ evtinfo->val = sc_evt[idx].val;
+ evtinfo->grp = sc_evt[idx].grp;
+ evtinfo->evt_type_act = sc_evt[idx].evt_type_act;
+ return 1;
+ }
+ return 0;
+}
+
+static inline void scorpion_pmnc_write_evtsel(unsigned int cnt, u32 val)
+{
+ if (scorpion_pmnc_select_counter(cnt) == cnt) {
+ if (val < 0x40) {
+ asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
+ } else {
+ u32 zero = 0;
+ struct scorp_evt evtinfo;
+ // extract evtinfo.grp and evtinfo.tevt_type_act from val
+ if (get_scorpion_evtinfo(val, &evtinfo) == 0)
+ return;
+ asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (evtinfo.evt_type_act));
+ asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (zero));
+ scorpion_evt_setup(evtinfo.grp, val);
+ }
+ }
+}
+
+static void scorpion_pmnc_reset_counter(unsigned int cnt)
+{
+ u32 val = 0;
+
+ if (cnt == CCNT) {
+ scorpion_pmnc_disable_counter(cnt);
+
+ asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
+
+ if (pmnc_enabled[cnt] != 0)
+ scorpion_pmnc_enable_counter(cnt);
+
+ } else if (cnt >= CNTMAX) {
+ pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt);
+ } else {
+ scorpion_pmnc_disable_counter(cnt);
+
+ if (scorpion_pmnc_select_counter(cnt) == cnt)
+ asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
+
+ if (pmnc_enabled[cnt] != 0)
+ scorpion_pmnc_enable_counter(cnt);
+ }
+}
+
+static int gator_events_scorpion_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int i;
+
+ for (i = 0; i < pmnc_counters; i++) {
+ char buf[40];
+ if (i == 0) {
+ snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name);
+ } else {
+ snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i - 1);
+ }
+ dir = gatorfs_mkdir(sb, root, buf);
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
+ if (i > 0) {
+ gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int gator_events_scorpion_online(int **buffer, bool migrate)
+{
+ unsigned int cnt, len = 0, cpu = smp_processor_id();
+
+ if (scorpion_pmnc_read() & PMNC_E) {
+ scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E);
+ }
+
+ /* Initialize & Reset PMNC: C bit and P bit */
+ scorpion_pmnc_write(PMNC_P | PMNC_C);
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ unsigned long event;
+
+ if (!pmnc_enabled[cnt])
+ continue;
+
+ // disable counter
+ scorpion_pmnc_disable_counter(cnt);
+
+ event = pmnc_event[cnt] & 255;
+
+ // Set event (if destined for PMNx counters), We don't need to set the event if it's a cycle count
+ if (cnt != CCNT)
+ scorpion_pmnc_write_evtsel(cnt, event);
+
+ // reset counter
+ scorpion_pmnc_reset_counter(cnt);
+
+ // Enable counter, do not enable interrupt for this counter
+ scorpion_pmnc_enable_counter(cnt);
+ }
+
+ // enable
+ scorpion_pmnc_write(scorpion_pmnc_read() | PMNC_E);
+
+ // read the counters and toss the invalid data, return zero instead
+ for (cnt = 0; cnt < pmnc_counters; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ if (cnt == CCNT) {
+ scorpion_ccnt_read();
+ } else if (scorpion_pmnc_select_counter(cnt) == cnt) {
+ scorpion_cntn_read();
+ }
+ scorpion_pmnc_reset_counter(cnt);
+
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = 0;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static int gator_events_scorpion_offline(int **buffer, bool migrate)
+{
+ scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E);
+ return 0;
+}
+
+static void gator_events_scorpion_stop(void)
+{
+ unsigned int cnt;
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ }
+}
+
+static int gator_events_scorpion_read(int **buffer)
+{
+ int cnt, len = 0;
+ int cpu = smp_processor_id();
+
+ // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled
+ if (!(scorpion_pmnc_read() & PMNC_E)) {
+ return 0;
+ }
+
+ for (cnt = 0; cnt < pmnc_counters; cnt++) {
+ if (pmnc_enabled[cnt]) {
+ int value;
+ if (cnt == CCNT) {
+ value = scorpion_ccnt_read();
+ } else if (scorpion_pmnc_select_counter(cnt) == cnt) {
+ value = scorpion_cntn_read();
+ } else {
+ value = 0;
+ }
+ scorpion_pmnc_reset_counter(cnt);
+
+ per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
+ per_cpu(perfCnt, cpu)[len++] = value;
+ }
+ }
+
+ if (buffer)
+ *buffer = per_cpu(perfCnt, cpu);
+
+ return len;
+}
+
+static struct gator_interface gator_events_scorpion_interface = {
+ .create_files = gator_events_scorpion_create_files,
+ .stop = gator_events_scorpion_stop,
+ .online = gator_events_scorpion_online,
+ .offline = gator_events_scorpion_offline,
+ .read = gator_events_scorpion_read,
+};
+
+int gator_events_scorpion_init(void)
+{
+ unsigned int cnt;
+
+ switch (gator_cpuid()) {
+ case SCORPION:
+ pmnc_name = "Scorpion";
+ pmnc_counters = 4;
+ break;
+ case SCORPIONMP:
+ pmnc_name = "ScorpionMP";
+ pmnc_counters = 4;
+ break;
+ default:
+ return -1;
+ }
+
+ pmnc_counters++; // CNT[n] + CCNT
+
+ for (cnt = CCNT; cnt < CNTMAX; cnt++) {
+ pmnc_enabled[cnt] = 0;
+ pmnc_event[cnt] = 0;
+ pmnc_key[cnt] = gator_events_get_key();
+ }
+
+ return gator_events_install(&gator_events_scorpion_interface);
+}
+
+#endif
diff --git a/drivers/gator/gator_events_threads.c b/drivers/gator/gator_events_threads.c
new file mode 100644
index 000000000000..9de85862fe6c
--- /dev/null
+++ b/drivers/gator/gator_events_threads.c
@@ -0,0 +1,115 @@
+/*
+ * Sample activity provider
+ *
+ * Copyright (C) ARM Limited 2014. All rights reserved.
+ *
+ * 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.
+ *
+ * See gator_events_mmapped.c for additional directions and
+ * troubleshooting.
+ *
+ * For this sample to work these entries must be present in the
+ * events.xml file. So create an events-threads.xml in the gator
+ * daemon source directory with the following contents and rebuild
+ * gatord:
+ *
+ * <category name="threads">
+ * <event counter="Linux_threads" title="Linux" name="Threads" class="activity" activity1="odd" activity_color1="0x000000ff" rendering_type="bar" average_selection="yes" average_cores="yes" percentage="yes" description="Linux syscall activity"/>
+ * </category>
+ */
+
+#include <trace/events/sched.h>
+
+#include "gator.h"
+
+static ulong threads_enabled;
+static ulong threads_key;
+static ulong threads_cores;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
+#else
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
+#endif
+{
+ int cpu = get_physical_cpu();
+ int pid = next->pid;
+ if (pid == 0) {
+ // idle
+ gator_marshal_activity_switch(cpu, threads_key, 0, 0);
+ } else if (pid & 1) {
+ // odd
+ gator_marshal_activity_switch(cpu, threads_key, 1, pid);
+ } else {
+ // even
+ //gator_marshal_activity_switch(cpu, threads_key, 2, current->pid);
+ // Multiple activities are not yet supported so emit idle
+ gator_marshal_activity_switch(cpu, threads_key, 0, 0);
+ }
+}
+
+// Adds Linux_threads directory and enabled, key, and cores files to /dev/gator/events
+static int gator_events_threads_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ dir = gatorfs_mkdir(sb, root, "Linux_threads");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &threads_enabled);
+ gatorfs_create_ro_ulong(sb, dir, "key", &threads_key);
+ // Number of cores associated with this activity
+ gatorfs_create_ro_ulong(sb, dir, "cores", &threads_cores);
+
+ return 0;
+}
+
+static int gator_events_threads_start(void)
+{
+ int cpu;
+
+ if (threads_enabled) {
+ preempt_disable();
+ for (cpu = 0; cpu < nr_cpu_ids; ++cpu) {
+ gator_marshal_activity_switch(cpu, threads_key, 0, 0);
+ }
+ preempt_enable();
+
+ if (GATOR_REGISTER_TRACE(sched_switch)) {
+ goto fail_sched_switch;
+ }
+ }
+
+ return 0;
+
+fail_sched_switch:
+ return -1;
+}
+
+static void gator_events_threads_stop(void)
+{
+ if (threads_enabled) {
+ GATOR_UNREGISTER_TRACE(sched_switch);
+ }
+
+ threads_enabled = 0;
+}
+
+static struct gator_interface gator_events_threads_interface = {
+ .create_files = gator_events_threads_create_files,
+ .start = gator_events_threads_start,
+ .stop = gator_events_threads_stop,
+};
+
+// Must not be static. Ensure that this init function is added to GATOR_EVENTS_LIST in gator_main.c
+int __init gator_events_threads_init(void)
+{
+ threads_enabled = 0;
+ threads_key = gator_events_get_key();
+ threads_cores = nr_cpu_ids;
+
+ return gator_events_install(&gator_events_threads_interface);
+}
diff --git a/drivers/gator/gator_fs.c b/drivers/gator/gator_fs.c
new file mode 100644
index 000000000000..166cfe7d681d
--- /dev/null
+++ b/drivers/gator/gator_fs.c
@@ -0,0 +1,365 @@
+/**
+ * @file gatorfs.c
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ *
+ * A simple filesystem for configuration and
+ * access of oprofile.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <asm/uaccess.h>
+
+#define gatorfs_MAGIC 0x24051020
+#define TMPBUFSIZE 50
+DEFINE_SPINLOCK(gatorfs_lock);
+
+static struct inode *gatorfs_get_inode(struct super_block *sb, int mode)
+{
+ struct inode *inode = new_inode(sb);
+
+ if (inode) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+ inode->i_ino = get_next_ino();
+#endif
+ inode->i_mode = mode;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ }
+ return inode;
+}
+
+static const struct super_operations s_ops = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+};
+
+static ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
+{
+ char tmpbuf[TMPBUFSIZE];
+ size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val);
+ if (maxlen > TMPBUFSIZE)
+ maxlen = TMPBUFSIZE;
+ return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
+}
+
+static ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *offset)
+{
+ char tmpbuf[TMPBUFSIZE];
+ size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%llu\n", val);
+ if (maxlen > TMPBUFSIZE)
+ maxlen = TMPBUFSIZE;
+ return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
+}
+
+static int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
+{
+ char tmpbuf[TMPBUFSIZE];
+ unsigned long flags;
+
+ if (!count)
+ return 0;
+
+ if (count > TMPBUFSIZE - 1)
+ return -EINVAL;
+
+ memset(tmpbuf, 0x0, TMPBUFSIZE);
+
+ if (copy_from_user(tmpbuf, buf, count))
+ return -EFAULT;
+
+ spin_lock_irqsave(&gatorfs_lock, flags);
+ *val = simple_strtoul(tmpbuf, NULL, 0);
+ spin_unlock_irqrestore(&gatorfs_lock, flags);
+ return 0;
+}
+
+static int gatorfs_u64_from_user(u64 *val, char const __user *buf, size_t count)
+{
+ char tmpbuf[TMPBUFSIZE];
+ unsigned long flags;
+
+ if (!count)
+ return 0;
+
+ if (count > TMPBUFSIZE - 1)
+ return -EINVAL;
+
+ memset(tmpbuf, 0x0, TMPBUFSIZE);
+
+ if (copy_from_user(tmpbuf, buf, count))
+ return -EFAULT;
+
+ spin_lock_irqsave(&gatorfs_lock, flags);
+ *val = simple_strtoull(tmpbuf, NULL, 0);
+ spin_unlock_irqrestore(&gatorfs_lock, flags);
+ return 0;
+}
+
+static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ unsigned long *val = file->private_data;
+ return gatorfs_ulong_to_user(*val, buf, count, offset);
+}
+
+static ssize_t u64_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ u64 *val = file->private_data;
+ return gatorfs_u64_to_user(*val, buf, count, offset);
+}
+
+static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
+{
+ unsigned long *value = file->private_data;
+ int retval;
+
+ if (*offset)
+ return -EINVAL;
+
+ retval = gatorfs_ulong_from_user(value, buf, count);
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static ssize_t u64_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
+{
+ u64 *value = file->private_data;
+ int retval;
+
+ if (*offset)
+ return -EINVAL;
+
+ retval = gatorfs_u64_from_user(value, buf, count);
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static int default_open(struct inode *inode, struct file *filp)
+{
+ if (inode->i_private)
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations ulong_fops = {
+ .read = ulong_read_file,
+ .write = ulong_write_file,
+ .open = default_open,
+};
+
+static const struct file_operations u64_fops = {
+ .read = u64_read_file,
+ .write = u64_write_file,
+ .open = default_open,
+};
+
+static const struct file_operations ulong_ro_fops = {
+ .read = ulong_read_file,
+ .open = default_open,
+};
+
+static const struct file_operations u64_ro_fops = {
+ .read = u64_read_file,
+ .open = default_open,
+};
+
+static struct dentry *__gatorfs_create_file(struct super_block *sb,
+ struct dentry *root,
+ char const *name,
+ const struct file_operations *fops,
+ int perm)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+
+ dentry = d_alloc_name(root, name);
+ if (!dentry)
+ return NULL;
+ inode = gatorfs_get_inode(sb, S_IFREG | perm);
+ if (!inode) {
+ dput(dentry);
+ return NULL;
+ }
+ inode->i_fop = fops;
+ d_add(dentry, inode);
+ return dentry;
+}
+
+int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
+ char const *name, unsigned long *val)
+{
+ struct dentry *d = __gatorfs_create_file(sb, root, name,
+ &ulong_fops, 0644);
+ if (!d)
+ return -EFAULT;
+
+ d->d_inode->i_private = val;
+ return 0;
+}
+
+static int gatorfs_create_u64(struct super_block *sb, struct dentry *root,
+ char const *name, u64 *val)
+{
+ struct dentry *d = __gatorfs_create_file(sb, root, name,
+ &u64_fops, 0644);
+ if (!d)
+ return -EFAULT;
+
+ d->d_inode->i_private = val;
+ return 0;
+}
+
+int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
+ char const *name, unsigned long *val)
+{
+ struct dentry *d = __gatorfs_create_file(sb, root, name,
+ &ulong_ro_fops, 0444);
+ if (!d)
+ return -EFAULT;
+
+ d->d_inode->i_private = val;
+ return 0;
+}
+
+static int gatorfs_create_ro_u64(struct super_block *sb, struct dentry *root,
+ char const *name, u64 * val)
+{
+ struct dentry *d =
+ __gatorfs_create_file(sb, root, name, &u64_ro_fops, 0444);
+ if (!d)
+ return -EFAULT;
+
+ d->d_inode->i_private = val;
+ return 0;
+}
+
+static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ atomic_t *val = file->private_data;
+ return gatorfs_ulong_to_user(atomic_read(val), buf, count, offset);
+}
+
+static const struct file_operations atomic_ro_fops = {
+ .read = atomic_read_file,
+ .open = default_open,
+};
+
+static int gatorfs_create_file(struct super_block *sb, struct dentry *root,
+ char const *name, const struct file_operations *fops)
+{
+ if (!__gatorfs_create_file(sb, root, name, fops, 0644))
+ return -EFAULT;
+ return 0;
+}
+
+static int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
+ char const *name,
+ const struct file_operations *fops, int perm)
+{
+ if (!__gatorfs_create_file(sb, root, name, fops, perm))
+ return -EFAULT;
+ return 0;
+}
+
+struct dentry *gatorfs_mkdir(struct super_block *sb,
+ struct dentry *root, char const *name)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+
+ dentry = d_alloc_name(root, name);
+ if (!dentry)
+ return NULL;
+ inode = gatorfs_get_inode(sb, S_IFDIR | 0755);
+ if (!inode) {
+ dput(dentry);
+ return NULL;
+ }
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ d_add(dentry, inode);
+ return dentry;
+}
+
+static int gatorfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct inode *root_inode;
+ struct dentry *root_dentry;
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = gatorfs_MAGIC;
+ sb->s_op = &s_ops;
+ sb->s_time_gran = 1;
+
+ root_inode = gatorfs_get_inode(sb, S_IFDIR | 0755);
+ if (!root_inode)
+ return -ENOMEM;
+ root_inode->i_op = &simple_dir_inode_operations;
+ root_inode->i_fop = &simple_dir_operations;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+ root_dentry = d_alloc_root(root_inode);
+#else
+ root_dentry = d_make_root(root_inode);
+#endif
+
+ if (!root_dentry) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+ iput(root_inode);
+#endif
+ return -ENOMEM;
+ }
+
+ sb->s_root = root_dentry;
+
+ gator_op_create_files(sb, root_dentry);
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
+static int gatorfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data,
+ struct vfsmount *mnt)
+{
+ return get_sb_single(fs_type, flags, data, gatorfs_fill_super, mnt);
+}
+#else
+static struct dentry *gatorfs_mount(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return mount_nodev(fs_type, flags, data, gatorfs_fill_super);
+}
+#endif
+
+static struct file_system_type gatorfs_type = {
+ .owner = THIS_MODULE,
+ .name = "gatorfs",
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
+ .get_sb = gatorfs_get_sb,
+#else
+ .mount = gatorfs_mount,
+#endif
+
+ .kill_sb = kill_litter_super,
+};
+
+static int __init gatorfs_register(void)
+{
+ return register_filesystem(&gatorfs_type);
+}
+
+static void gatorfs_unregister(void)
+{
+ unregister_filesystem(&gatorfs_type);
+}
diff --git a/drivers/gator/gator_hrtimer_gator.c b/drivers/gator/gator_hrtimer_gator.c
new file mode 100644
index 000000000000..76584554b00f
--- /dev/null
+++ b/drivers/gator/gator_hrtimer_gator.c
@@ -0,0 +1,80 @@
+/**
+ * Copyright (C) ARM Limited 2011-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+void (*callback)(void);
+DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer);
+DEFINE_PER_CPU(ktime_t, hrtimer_expire);
+DEFINE_PER_CPU(int, hrtimer_is_active);
+static ktime_t profiling_interval;
+static void gator_hrtimer_online(void);
+static void gator_hrtimer_offline(void);
+
+static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer)
+{
+ int cpu = get_logical_cpu();
+ hrtimer_forward(hrtimer, per_cpu(hrtimer_expire, cpu), profiling_interval);
+ per_cpu(hrtimer_expire, cpu) = ktime_add(per_cpu(hrtimer_expire, cpu), profiling_interval);
+ (*callback)();
+ return HRTIMER_RESTART;
+}
+
+static void gator_hrtimer_online(void)
+{
+ int cpu = get_logical_cpu();
+ struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu);
+
+ if (per_cpu(hrtimer_is_active, cpu) || profiling_interval.tv64 == 0)
+ return;
+
+ per_cpu(hrtimer_is_active, cpu) = 1;
+ hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ hrtimer->function = gator_hrtimer_notify;
+#ifdef CONFIG_PREEMPT_RT_BASE
+ hrtimer->irqsafe = 1;
+#endif
+ per_cpu(hrtimer_expire, cpu) = ktime_add(hrtimer->base->get_time(), profiling_interval);
+ hrtimer_start(hrtimer, per_cpu(hrtimer_expire, cpu), HRTIMER_MODE_ABS_PINNED);
+}
+
+static void gator_hrtimer_offline(void)
+{
+ int cpu = get_logical_cpu();
+ struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu);
+
+ if (!per_cpu(hrtimer_is_active, cpu))
+ return;
+
+ per_cpu(hrtimer_is_active, cpu) = 0;
+ hrtimer_cancel(hrtimer);
+}
+
+static int gator_hrtimer_init(int interval, void (*func)(void))
+{
+ int cpu;
+
+ (callback) = (func);
+
+ for_each_present_cpu(cpu) {
+ per_cpu(hrtimer_is_active, cpu) = 0;
+ }
+
+ // calculate profiling interval
+ if (interval > 0) {
+ profiling_interval = ns_to_ktime(1000000000UL / interval);
+ } else {
+ profiling_interval.tv64 = 0;
+ }
+
+ return 0;
+}
+
+static void gator_hrtimer_shutdown(void)
+{
+ /* empty */
+}
diff --git a/drivers/gator/gator_iks.c b/drivers/gator/gator_iks.c
new file mode 100644
index 000000000000..9180b874457a
--- /dev/null
+++ b/drivers/gator/gator_iks.c
@@ -0,0 +1,197 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#if GATOR_IKS_SUPPORT
+
+#include <linux/of.h>
+#include <asm/bL_switcher.h>
+#include <asm/smp_plat.h>
+#include <trace/events/power_cpu_migrate.h>
+
+static bool map_cpuids;
+static int mpidr_cpuids[NR_CPUS];
+static const struct gator_cpu * mpidr_cpus[NR_CPUS];
+static int __lcpu_to_pcpu[NR_CPUS];
+
+static const struct gator_cpu *gator_find_cpu_by_dt_name(const char *const name)
+{
+ int i;
+
+ for (i = 0; gator_cpus[i].cpuid != 0; ++i) {
+ const struct gator_cpu *const gator_cpu = &gator_cpus[i];
+ if (gator_cpu->dt_name != NULL && strcmp(gator_cpu->dt_name, name) == 0) {
+ return gator_cpu;
+ }
+ }
+
+ return NULL;
+}
+
+static void calc_first_cluster_size(void)
+{
+ int len;
+ const u32 *val;
+ const char *compatible;
+ struct device_node *cn = NULL;
+ int mpidr_cpuids_count = 0;
+
+ // Zero is a valid cpuid, so initialize the array to 0xff's
+ memset(&mpidr_cpuids, 0xff, sizeof(mpidr_cpuids));
+ memset(&mpidr_cpus, 0, sizeof(mpidr_cpus));
+
+ while ((cn = of_find_node_by_type(cn, "cpu"))) {
+ BUG_ON(mpidr_cpuids_count >= NR_CPUS);
+
+ val = of_get_property(cn, "reg", &len);
+ if (!val || len != 4) {
+ pr_err("%s missing reg property\n", cn->full_name);
+ continue;
+ }
+ compatible = of_get_property(cn, "compatible", NULL);
+ if (compatible == NULL) {
+ pr_err("%s missing compatible property\n", cn->full_name);
+ continue;
+ }
+
+ mpidr_cpuids[mpidr_cpuids_count] = be32_to_cpup(val);
+ mpidr_cpus[mpidr_cpuids_count] = gator_find_cpu_by_dt_name(compatible);
+ ++mpidr_cpuids_count;
+ }
+
+ map_cpuids = (mpidr_cpuids_count == nr_cpu_ids);
+}
+
+static int linearize_mpidr(int mpidr)
+{
+ int i;
+ for (i = 0; i < nr_cpu_ids; ++i) {
+ if (mpidr_cpuids[i] == mpidr) {
+ return i;
+ }
+ }
+
+ BUG();
+}
+
+int lcpu_to_pcpu(const int lcpu)
+{
+ int pcpu;
+
+ if (!map_cpuids)
+ return lcpu;
+
+ BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0);
+ pcpu = __lcpu_to_pcpu[lcpu];
+ BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0);
+ return pcpu;
+}
+
+int pcpu_to_lcpu(const int pcpu)
+{
+ int lcpu;
+
+ if (!map_cpuids)
+ return pcpu;
+
+ BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0);
+ for (lcpu = 0; lcpu < nr_cpu_ids; ++lcpu) {
+ if (__lcpu_to_pcpu[lcpu] == pcpu) {
+ BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0);
+ return lcpu;
+ }
+ }
+ BUG();
+}
+
+static void gator_update_cpu_mapping(u32 cpu_hwid)
+{
+ int lcpu = smp_processor_id();
+ int pcpu = linearize_mpidr(cpu_hwid & MPIDR_HWID_BITMASK);
+ BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0);
+ BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0);
+ __lcpu_to_pcpu[lcpu] = pcpu;
+}
+
+GATOR_DEFINE_PROBE(cpu_migrate_begin, TP_PROTO(u64 timestamp, u32 cpu_hwid))
+{
+ const int cpu = get_physical_cpu();
+
+ gator_timer_offline((void *)1);
+ gator_timer_offline_dispatch(cpu, true);
+}
+
+GATOR_DEFINE_PROBE(cpu_migrate_finish, TP_PROTO(u64 timestamp, u32 cpu_hwid))
+{
+ int cpu;
+
+ gator_update_cpu_mapping(cpu_hwid);
+
+ // get_physical_cpu must be called after gator_update_cpu_mapping
+ cpu = get_physical_cpu();
+ gator_timer_online_dispatch(cpu, true);
+ gator_timer_online((void *)1);
+}
+
+GATOR_DEFINE_PROBE(cpu_migrate_current, TP_PROTO(u64 timestamp, u32 cpu_hwid))
+{
+ gator_update_cpu_mapping(cpu_hwid);
+}
+
+static void gator_send_iks_core_names(void)
+{
+ int cpu;
+ // Send the cpu names
+ preempt_disable();
+ for (cpu = 0; cpu < nr_cpu_ids; ++cpu) {
+ if (mpidr_cpus[cpu] != NULL) {
+ gator_send_core_name(cpu, mpidr_cpus[cpu]->cpuid);
+ }
+ }
+ preempt_enable();
+}
+
+static int gator_migrate_start(void)
+{
+ int retval = 0;
+
+ if (!map_cpuids)
+ return retval;
+
+ if (retval == 0)
+ retval = GATOR_REGISTER_TRACE(cpu_migrate_begin);
+ if (retval == 0)
+ retval = GATOR_REGISTER_TRACE(cpu_migrate_finish);
+ if (retval == 0)
+ retval = GATOR_REGISTER_TRACE(cpu_migrate_current);
+ if (retval == 0) {
+ // Initialize the logical to physical cpu mapping
+ memset(&__lcpu_to_pcpu, 0xff, sizeof(__lcpu_to_pcpu));
+ bL_switcher_trace_trigger();
+ }
+ return retval;
+}
+
+static void gator_migrate_stop(void)
+{
+ if (!map_cpuids)
+ return;
+
+ GATOR_UNREGISTER_TRACE(cpu_migrate_current);
+ GATOR_UNREGISTER_TRACE(cpu_migrate_finish);
+ GATOR_UNREGISTER_TRACE(cpu_migrate_begin);
+}
+
+#else
+
+#define calc_first_cluster_size()
+#define gator_send_iks_core_names()
+#define gator_migrate_start() 0
+#define gator_migrate_stop()
+
+#endif
diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c
new file mode 100644
index 000000000000..284120319ffd
--- /dev/null
+++ b/drivers/gator/gator_main.c
@@ -0,0 +1,1467 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 version must match the gator daemon version
+#define PROTOCOL_VERSION 19
+static unsigned long gator_protocol_version = PROTOCOL_VERSION;
+
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/vmalloc.h>
+#include <linux/hardirq.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/perf_event.h>
+#include <linux/utsname.h>
+#include <linux/kthread.h>
+#include <asm/stacktrace.h>
+#include <asm/uaccess.h>
+
+#include "gator.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+#error kernels prior to 2.6.32 are not supported
+#endif
+
+#if defined(MODULE) && !defined(CONFIG_MODULES)
+#error Cannot build a module against a kernel that does not support modules. To resolve, either rebuild the kernel to support modules or build gator as part of the kernel.
+#endif
+
+#if !defined(CONFIG_GENERIC_TRACER) && !defined(CONFIG_TRACING)
+#error gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined
+#endif
+
+#ifndef CONFIG_PROFILING
+#error gator requires the kernel to have CONFIG_PROFILING defined
+#endif
+
+#ifndef CONFIG_HIGH_RES_TIMERS
+#error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined to support PC sampling
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && defined(__arm__) && defined(CONFIG_SMP) && !defined(CONFIG_LOCAL_TIMERS)
+#error gator requires the kernel to have CONFIG_LOCAL_TIMERS defined on SMP systems
+#endif
+
+#if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT))
+#ifndef CONFIG_PERF_EVENTS
+#error gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters
+#elif !defined CONFIG_HW_PERF_EVENTS
+#error gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters
+#endif
+#endif
+
+/******************************************************************************
+ * DEFINES
+ ******************************************************************************/
+#define SUMMARY_BUFFER_SIZE (1*1024)
+#define BACKTRACE_BUFFER_SIZE (128*1024)
+#define NAME_BUFFER_SIZE (64*1024)
+#define COUNTER_BUFFER_SIZE (64*1024) // counters have the core as part of the data and the core value in the frame header may be discarded
+#define BLOCK_COUNTER_BUFFER_SIZE (128*1024)
+#define ANNOTATE_BUFFER_SIZE (128*1024) // annotate counters have the core as part of the data and the core value in the frame header may be discarded
+#define SCHED_TRACE_BUFFER_SIZE (128*1024)
+#define IDLE_BUFFER_SIZE (32*1024) // idle counters have the core as part of the data and the core value in the frame header may be discarded
+#define ACTIVITY_BUFFER_SIZE (128*1024)
+
+#define NO_COOKIE 0U
+#define UNRESOLVED_COOKIE ~0U
+
+#define FRAME_SUMMARY 1
+#define FRAME_BACKTRACE 2
+#define FRAME_NAME 3
+#define FRAME_COUNTER 4
+#define FRAME_BLOCK_COUNTER 5
+#define FRAME_ANNOTATE 6
+#define FRAME_SCHED_TRACE 7
+#define FRAME_IDLE 9
+#define FRAME_ACTIVITY 13
+
+#define MESSAGE_END_BACKTRACE 1
+
+// Name Frame Messages
+#define MESSAGE_COOKIE 1
+#define MESSAGE_THREAD_NAME 2
+#define MESSAGE_LINK 4
+
+// Scheduler Trace Frame Messages
+#define MESSAGE_SCHED_SWITCH 1
+#define MESSAGE_SCHED_EXIT 2
+
+// Idle Frame Messages
+#define MESSAGE_IDLE_ENTER 1
+#define MESSAGE_IDLE_EXIT 2
+
+// Summary Frame Messages
+#define MESSAGE_SUMMARY 1
+#define MESSAGE_CORE_NAME 3
+
+// Activity Frame Messages
+#define MESSAGE_SWITCH 2
+#define MESSAGE_EXIT 3
+
+#define MAXSIZE_PACK32 5
+#define MAXSIZE_PACK64 10
+
+#define FRAME_HEADER_SIZE 3
+
+#if defined(__arm__)
+#define PC_REG regs->ARM_pc
+#elif defined(__aarch64__)
+#define PC_REG regs->pc
+#else
+#define PC_REG regs->ip
+#endif
+
+enum {
+ SUMMARY_BUF,
+ BACKTRACE_BUF,
+ NAME_BUF,
+ COUNTER_BUF,
+ BLOCK_COUNTER_BUF,
+ ANNOTATE_BUF,
+ SCHED_TRACE_BUF,
+ IDLE_BUF,
+ ACTIVITY_BUF,
+ NUM_GATOR_BUFS
+};
+
+/******************************************************************************
+ * Globals
+ ******************************************************************************/
+static unsigned long gator_cpu_cores;
+// Size of the largest buffer. Effectively constant, set in gator_op_create_files
+static unsigned long userspace_buffer_size;
+static unsigned long gator_backtrace_depth;
+// How often to commit the buffers for live in nanoseconds
+static u64 gator_live_rate;
+
+static unsigned long gator_started;
+static u64 gator_monotonic_started;
+static u64 gator_hibernate_time;
+static unsigned long gator_buffer_opened;
+static unsigned long gator_timer_count;
+static unsigned long gator_response_type;
+static DEFINE_MUTEX(start_mutex);
+static DEFINE_MUTEX(gator_buffer_mutex);
+
+bool event_based_sampling;
+
+static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait);
+static DECLARE_WAIT_QUEUE_HEAD(gator_annotate_wait);
+static struct timer_list gator_buffer_wake_up_timer;
+static bool gator_buffer_wake_run;
+// Initialize semaphore unlocked to initialize memory values
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+static DECLARE_MUTEX(gator_buffer_wake_sem);
+#else
+static DEFINE_SEMAPHORE(gator_buffer_wake_sem);
+#endif
+static struct task_struct *gator_buffer_wake_thread;
+static LIST_HEAD(gator_events);
+
+static DEFINE_PER_CPU(u64, last_timestamp);
+
+static bool printed_monotonic_warning;
+
+static u32 gator_cpuids[NR_CPUS];
+static bool sent_core_name[NR_CPUS];
+
+static DEFINE_PER_CPU(bool, in_scheduler_context);
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+static u64 gator_get_time(void);
+static void gator_op_create_files(struct super_block *sb, struct dentry *root);
+
+// gator_buffer is protected by being per_cpu and by having IRQs disabled when writing to it.
+// Most marshal_* calls take care of this except for marshal_cookie*, marshal_backtrace* and marshal_frame where the caller is responsible for doing so.
+// No synchronization is needed with the backtrace buffer as it is per cpu and is only used from the hrtimer.
+// The annotate_lock must be held when using the annotation buffer as it is not per cpu.
+// collect_counters which is the sole writer to the block counter frame is additionally protected by the per cpu collecting flag
+
+// Size of the buffer, must be a power of 2. Effectively constant, set in gator_op_setup.
+static uint32_t gator_buffer_size[NUM_GATOR_BUFS];
+// gator_buffer_size - 1, bitwise and with pos to get offset into the array. Effectively constant, set in gator_op_setup.
+static uint32_t gator_buffer_mask[NUM_GATOR_BUFS];
+// Read position in the buffer. Initialized to zero in gator_op_setup and incremented after bytes are read by userspace in userspace_buffer_read
+static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_read);
+// Write position in the buffer. Initialized to zero in gator_op_setup and incremented after bytes are written to the buffer
+static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_write);
+// Commit position in the buffer. Initialized to zero in gator_op_setup and incremented after a frame is ready to be read by userspace
+static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_commit);
+// If set to false, decreases the number of bytes returned by buffer_bytes_available. Set in buffer_check_space if no space is remaining. Initialized to true in gator_op_setup
+// This means that if we run out of space, continue to report that no space is available until bytes are read by userspace
+static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], buffer_space_available);
+// The buffer. Allocated in gator_op_setup
+static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer);
+// The time after which the buffer should be committed for live display
+static DEFINE_PER_CPU(u64, gator_buffer_commit_time);
+
+// List of all gator events - new events must be added to this list
+#define GATOR_EVENTS_LIST \
+ GATOR_EVENT(gator_events_armv6_init) \
+ GATOR_EVENT(gator_events_armv7_init) \
+ GATOR_EVENT(gator_events_block_init) \
+ GATOR_EVENT(gator_events_ccn504_init) \
+ GATOR_EVENT(gator_events_irq_init) \
+ GATOR_EVENT(gator_events_l2c310_init) \
+ GATOR_EVENT(gator_events_mali_init) \
+ GATOR_EVENT(gator_events_mali_t6xx_hw_init) \
+ GATOR_EVENT(gator_events_mali_t6xx_init) \
+ GATOR_EVENT(gator_events_meminfo_init) \
+ GATOR_EVENT(gator_events_mmapped_init) \
+ GATOR_EVENT(gator_events_net_init) \
+ GATOR_EVENT(gator_events_perf_pmu_init) \
+ GATOR_EVENT(gator_events_sched_init) \
+ GATOR_EVENT(gator_events_scorpion_init) \
+ GATOR_EVENT(gator_events_threads_init) \
+
+#define GATOR_EVENT(EVENT_INIT) __weak int EVENT_INIT(void);
+GATOR_EVENTS_LIST
+#undef GATOR_EVENT
+
+static int (*gator_events_list[])(void) = {
+#define GATOR_EVENT(EVENT_INIT) EVENT_INIT,
+GATOR_EVENTS_LIST
+#undef GATOR_EVENT
+};
+
+/******************************************************************************
+ * Application Includes
+ ******************************************************************************/
+#include "gator_fs.c"
+#include "gator_buffer_write.c"
+#include "gator_buffer.c"
+#include "gator_marshaling.c"
+#include "gator_hrtimer_gator.c"
+#include "gator_cookies.c"
+#include "gator_annotate.c"
+#include "gator_trace_sched.c"
+#include "gator_trace_power.c"
+#include "gator_trace_gpu.c"
+#include "gator_backtrace.c"
+
+/******************************************************************************
+ * Misc
+ ******************************************************************************/
+
+static const struct gator_cpu gator_cpus[] = {
+ {
+ .cpuid = ARM1136,
+ .core_name = "ARM1136",
+ .pmnc_name = "ARM_ARM11",
+ .dt_name = "arm,arm1136",
+ .pmnc_counters = 3,
+ },
+ {
+ .cpuid = ARM1156,
+ .core_name = "ARM1156",
+ .pmnc_name = "ARM_ARM11",
+ .dt_name = "arm,arm1156",
+ .pmnc_counters = 3,
+ },
+ {
+ .cpuid = ARM1176,
+ .core_name = "ARM1176",
+ .pmnc_name = "ARM_ARM11",
+ .dt_name = "arm,arm1176",
+ .pmnc_counters = 3,
+ },
+ {
+ .cpuid = ARM11MPCORE,
+ .core_name = "ARM11MPCore",
+ .pmnc_name = "ARM_ARM11MPCore",
+ .dt_name = "arm,arm11mpcore",
+ .pmnc_counters = 3,
+ },
+ {
+ .cpuid = CORTEX_A5,
+ .core_name = "Cortex-A5",
+ .pmnc_name = "ARMv7_Cortex_A5",
+ .dt_name = "arm,cortex-a5",
+ .pmnc_counters = 2,
+ },
+ {
+ .cpuid = CORTEX_A7,
+ .core_name = "Cortex-A7",
+ .pmnc_name = "ARMv7_Cortex_A7",
+ .dt_name = "arm,cortex-a7",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = CORTEX_A8,
+ .core_name = "Cortex-A8",
+ .pmnc_name = "ARMv7_Cortex_A8",
+ .dt_name = "arm,cortex-a8",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = CORTEX_A9,
+ .core_name = "Cortex-A9",
+ .pmnc_name = "ARMv7_Cortex_A9",
+ .dt_name = "arm,cortex-a9",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = CORTEX_A12,
+ .core_name = "Cortex-A12",
+ .pmnc_name = "ARMv7_Cortex_A12",
+ .dt_name = "arm,cortex-a12",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = CORTEX_A15,
+ .core_name = "Cortex-A15",
+ .pmnc_name = "ARMv7_Cortex_A15",
+ .dt_name = "arm,cortex-a15",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = CORTEX_A17,
+ .core_name = "Cortex-A17",
+ .pmnc_name = "ARMv7_Cortex_A17",
+ .dt_name = "arm,cortex-a17",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = SCORPION,
+ .core_name = "Scorpion",
+ .pmnc_name = "Scorpion",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = SCORPIONMP,
+ .core_name = "ScorpionMP",
+ .pmnc_name = "ScorpionMP",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = KRAITSIM,
+ .core_name = "KraitSIM",
+ .pmnc_name = "Krait",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = KRAIT,
+ .core_name = "Krait",
+ .pmnc_name = "Krait",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = KRAIT_S4_PRO,
+ .core_name = "Krait S4 Pro",
+ .pmnc_name = "Krait",
+ .pmnc_counters = 4,
+ },
+ {
+ .cpuid = CORTEX_A53,
+ .core_name = "Cortex-A53",
+ .pmnc_name = "ARM_Cortex-A53",
+ .dt_name = "arm,cortex-a53",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = CORTEX_A57,
+ .core_name = "Cortex-A57",
+ .pmnc_name = "ARM_Cortex-A57",
+ .dt_name = "arm,cortex-a57",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = AARCH64,
+ .core_name = "AArch64",
+ .pmnc_name = "ARM_AArch64",
+ .pmnc_counters = 6,
+ },
+ {
+ .cpuid = OTHER,
+ .core_name = "Other",
+ .pmnc_name = "Other",
+ .pmnc_counters = 6,
+ },
+ {}
+};
+
+const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid)
+{
+ int i;
+
+ for (i = 0; gator_cpus[i].cpuid != 0; ++i) {
+ const struct gator_cpu *const gator_cpu = &gator_cpus[i];
+ if (gator_cpu->cpuid == cpuid) {
+ return gator_cpu;
+ }
+ }
+
+ return NULL;
+}
+
+const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name)
+{
+ int i;
+
+ for (i = 0; gator_cpus[i].cpuid != 0; ++i) {
+ const struct gator_cpu *const gator_cpu = &gator_cpus[i];
+ if (gator_cpu->pmnc_name != NULL && strcmp(gator_cpu->pmnc_name, name) == 0) {
+ return gator_cpu;
+ }
+ }
+
+ return NULL;
+}
+
+u32 gator_cpuid(void)
+{
+#if defined(__arm__) || defined(__aarch64__)
+ u32 val;
+#if !defined(__aarch64__)
+ asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val));
+#else
+ asm volatile("mrs %0, midr_el1" : "=r" (val));
+#endif
+ return (val >> 4) & 0xfff;
+#else
+ return OTHER;
+#endif
+}
+
+static void gator_buffer_wake_up(unsigned long data)
+{
+ wake_up(&gator_buffer_wait);
+}
+
+static int gator_buffer_wake_func(void *data)
+{
+ for (;;) {
+ if (down_killable(&gator_buffer_wake_sem)) {
+ break;
+ }
+
+ // Eat up any pending events
+ while (!down_trylock(&gator_buffer_wake_sem));
+
+ if (!gator_buffer_wake_run) {
+ break;
+ }
+
+ gator_buffer_wake_up(0);
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ * Commit interface
+ ******************************************************************************/
+static bool buffer_commit_ready(int *cpu, int *buftype)
+{
+ int cpu_x, x;
+ for_each_present_cpu(cpu_x) {
+ for (x = 0; x < NUM_GATOR_BUFS; x++)
+ if (per_cpu(gator_buffer_commit, cpu_x)[x] != per_cpu(gator_buffer_read, cpu_x)[x]) {
+ *cpu = cpu_x;
+ *buftype = x;
+ return true;
+ }
+ }
+ *cpu = -1;
+ *buftype = -1;
+ return false;
+}
+
+/******************************************************************************
+ * hrtimer interrupt processing
+ ******************************************************************************/
+static void gator_timer_interrupt(void)
+{
+ struct pt_regs *const regs = get_irq_regs();
+ gator_backtrace_handler(regs);
+}
+
+void gator_backtrace_handler(struct pt_regs *const regs)
+{
+ u64 time = gator_get_time();
+ int cpu = get_physical_cpu();
+
+ // Output backtrace
+ gator_add_sample(cpu, regs, time);
+
+ // Collect counters
+ if (!per_cpu(collecting, cpu)) {
+ collect_counters(time, NULL);
+ }
+
+ // No buffer flushing occurs during sched switch for RT-Preempt full. The block counter frame will be flushed by collect_counters, but the sched buffer needs to be explicitly flushed
+#ifdef CONFIG_PREEMPT_RT_FULL
+ buffer_check(cpu, SCHED_TRACE_BUF, time);
+#endif
+}
+
+static int gator_running;
+
+// This function runs in interrupt context and on the appropriate core
+static void gator_timer_offline(void *migrate)
+{
+ struct gator_interface *gi;
+ int i, len, cpu = get_physical_cpu();
+ int *buffer;
+ u64 time;
+
+ gator_trace_sched_offline();
+ gator_trace_power_offline();
+
+ if (!migrate) {
+ gator_hrtimer_offline();
+ }
+
+ // Offline any events and output counters
+ time = gator_get_time();
+ if (marshal_event_header(time)) {
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->offline) {
+ len = gi->offline(&buffer, migrate);
+ marshal_event(len, buffer);
+ }
+ }
+ // Only check after writing all counters so that time and corresponding counters appear in the same frame
+ buffer_check(cpu, BLOCK_COUNTER_BUF, time);
+ }
+
+ // Flush all buffers on this core
+ for (i = 0; i < NUM_GATOR_BUFS; i++)
+ gator_commit_buffer(cpu, i, time);
+}
+
+// This function runs in interrupt context and may be running on a core other than core 'cpu'
+static void gator_timer_offline_dispatch(int cpu, bool migrate)
+{
+ struct gator_interface *gi;
+
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->offline_dispatch) {
+ gi->offline_dispatch(cpu, migrate);
+ }
+ }
+}
+
+static void gator_timer_stop(void)
+{
+ int cpu;
+
+ if (gator_running) {
+ on_each_cpu(gator_timer_offline, NULL, 1);
+ for_each_online_cpu(cpu) {
+ gator_timer_offline_dispatch(lcpu_to_pcpu(cpu), false);
+ }
+
+ gator_running = 0;
+ gator_hrtimer_shutdown();
+ }
+}
+
+static void gator_send_core_name(const int cpu, const u32 cpuid)
+{
+#if defined(__arm__) || defined(__aarch64__)
+ if (!sent_core_name[cpu] || (cpuid != gator_cpuids[cpu])) {
+ const struct gator_cpu *const gator_cpu = gator_find_cpu_by_cpuid(cpuid);
+ const char *core_name = NULL;
+ char core_name_buf[32];
+
+ // Save off this cpuid
+ gator_cpuids[cpu] = cpuid;
+ if (gator_cpu != NULL) {
+ core_name = gator_cpu->core_name;
+ } else {
+ if (cpuid == -1) {
+ snprintf(core_name_buf, sizeof(core_name_buf), "Unknown");
+ } else {
+ snprintf(core_name_buf, sizeof(core_name_buf), "Unknown (0x%.3x)", cpuid);
+ }
+ core_name = core_name_buf;
+ }
+
+ marshal_core_name(cpu, cpuid, core_name);
+ sent_core_name[cpu] = true;
+ }
+#endif
+}
+
+static void gator_read_cpuid(void * arg)
+{
+ gator_cpuids[get_physical_cpu()] = gator_cpuid();
+}
+
+// This function runs in interrupt context and on the appropriate core
+static void gator_timer_online(void *migrate)
+{
+ struct gator_interface *gi;
+ int len, cpu = get_physical_cpu();
+ int *buffer;
+ u64 time;
+
+ // Send what is currently running on this core
+ marshal_sched_trace_switch(current->pid, 0);
+
+ gator_trace_power_online();
+
+ // online any events and output counters
+ time = gator_get_time();
+ if (marshal_event_header(time)) {
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->online) {
+ len = gi->online(&buffer, migrate);
+ marshal_event(len, buffer);
+ }
+ }
+ // Only check after writing all counters so that time and corresponding counters appear in the same frame
+ buffer_check(cpu, BLOCK_COUNTER_BUF, time);
+ }
+
+ if (!migrate) {
+ gator_hrtimer_online();
+ }
+
+ gator_send_core_name(cpu, gator_cpuid());
+}
+
+// This function runs in interrupt context and may be running on a core other than core 'cpu'
+static void gator_timer_online_dispatch(int cpu, bool migrate)
+{
+ struct gator_interface *gi;
+
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->online_dispatch) {
+ gi->online_dispatch(cpu, migrate);
+ }
+ }
+}
+
+#include "gator_iks.c"
+
+static int gator_timer_start(unsigned long sample_rate)
+{
+ int cpu;
+
+ if (gator_running) {
+ pr_notice("gator: already running\n");
+ return 0;
+ }
+
+ gator_running = 1;
+
+ // event based sampling trumps hr timer based sampling
+ if (event_based_sampling) {
+ sample_rate = 0;
+ }
+
+ if (gator_hrtimer_init(sample_rate, gator_timer_interrupt) == -1)
+ return -1;
+
+ // Send off the previously saved cpuids
+ for_each_present_cpu(cpu) {
+ preempt_disable();
+ gator_send_core_name(cpu, gator_cpuids[cpu]);
+ preempt_enable();
+ }
+
+ gator_send_iks_core_names();
+ for_each_online_cpu(cpu) {
+ gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false);
+ }
+ on_each_cpu(gator_timer_online, NULL, 1);
+
+ return 0;
+}
+
+static u64 gator_get_time(void)
+{
+ struct timespec ts;
+ u64 timestamp;
+ u64 prev_timestamp;
+ u64 delta;
+ int cpu = smp_processor_id();
+
+ // Match clock_gettime(CLOCK_MONOTONIC_RAW, &ts) from userspace
+ getrawmonotonic(&ts);
+ timestamp = timespec_to_ns(&ts);
+
+ // getrawmonotonic is not monotonic on all systems. Detect and attempt to correct these cases.
+ // up to 0.5ms delta has been seen on some systems, which can skew Streamline data when viewing at high resolution.
+ // This doesn't work well with interrupts, but that it's OK - the real concern is to catch big jumps in time
+ prev_timestamp = per_cpu(last_timestamp, cpu);
+ if (prev_timestamp <= timestamp) {
+ per_cpu(last_timestamp, cpu) = timestamp;
+ } else {
+ delta = prev_timestamp - timestamp;
+ // Log the error once
+ if (!printed_monotonic_warning && delta > 500000) {
+ printk(KERN_ERR "%s: getrawmonotonic is not monotonic cpu: %i delta: %lli\nSkew in Streamline data may be present at the fine zoom levels\n", __FUNCTION__, cpu, delta);
+ printed_monotonic_warning = true;
+ }
+ timestamp = prev_timestamp;
+ }
+
+ return timestamp - gator_monotonic_started;
+}
+
+/******************************************************************************
+ * cpu hotplug and pm notifiers
+ ******************************************************************************/
+static int __cpuinit gator_hotcpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
+{
+ int cpu = lcpu_to_pcpu((long)hcpu);
+
+ switch (action) {
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ smp_call_function_single(cpu, gator_timer_offline, NULL, 1);
+ gator_timer_offline_dispatch(cpu, false);
+ break;
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ gator_timer_online_dispatch(cpu, false);
+ smp_call_function_single(cpu, gator_timer_online, NULL, 1);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata gator_hotcpu_notifier = {
+ .notifier_call = gator_hotcpu_notify,
+};
+
+// n.b. calling "on_each_cpu" only runs on those that are online
+// Registered linux events are not disabled, so their counters will continue to collect
+static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy)
+{
+ int cpu;
+ struct timespec ts;
+
+ switch (event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ unregister_hotcpu_notifier(&gator_hotcpu_notifier);
+ unregister_scheduler_tracepoints();
+ on_each_cpu(gator_timer_offline, NULL, 1);
+ for_each_online_cpu(cpu) {
+ gator_timer_offline_dispatch(lcpu_to_pcpu(cpu), false);
+ }
+
+ // Record the wallclock hibernate time
+ getnstimeofday(&ts);
+ gator_hibernate_time = timespec_to_ns(&ts) - gator_get_time();
+ break;
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ // Adjust gator_monotonic_started for the time spent sleeping, as gator_get_time does not account for it
+ if (gator_hibernate_time > 0) {
+ getnstimeofday(&ts);
+ gator_monotonic_started += gator_hibernate_time + gator_get_time() - timespec_to_ns(&ts);
+ gator_hibernate_time = 0;
+ }
+
+ for_each_online_cpu(cpu) {
+ gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false);
+ }
+ on_each_cpu(gator_timer_online, NULL, 1);
+ register_scheduler_tracepoints();
+ register_hotcpu_notifier(&gator_hotcpu_notifier);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block gator_pm_notifier = {
+ .notifier_call = gator_pm_notify,
+};
+
+static int gator_notifier_start(void)
+{
+ int retval;
+ retval = register_hotcpu_notifier(&gator_hotcpu_notifier);
+ if (retval == 0)
+ retval = register_pm_notifier(&gator_pm_notifier);
+ return retval;
+}
+
+static void gator_notifier_stop(void)
+{
+ unregister_pm_notifier(&gator_pm_notifier);
+ unregister_hotcpu_notifier(&gator_hotcpu_notifier);
+}
+
+/******************************************************************************
+ * Main
+ ******************************************************************************/
+static void gator_summary(void)
+{
+ u64 timestamp, uptime;
+ struct timespec ts;
+ char uname_buf[512];
+
+ snprintf(uname_buf, sizeof(uname_buf), "%s %s %s %s %s GNU/Linux", utsname()->sysname, utsname()->nodename, utsname()->release, utsname()->version, utsname()->machine);
+
+ getnstimeofday(&ts);
+ timestamp = timespec_to_ns(&ts);
+
+ // Similar to reading /proc/uptime from fs/proc/uptime.c, calculate uptime
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+ {
+ void (*m2b)(struct timespec *ts);
+ do_posix_clock_monotonic_gettime(&ts);
+ // monotonic_to_bootbased is not defined for some versions of Android
+ m2b = symbol_get(monotonic_to_bootbased);
+ if (m2b) {
+ m2b(&ts);
+ }
+ }
+#else
+ get_monotonic_boottime(&ts);
+#endif
+ uptime = timespec_to_ns(&ts);
+
+ // Disable preemption as gator_get_time calls smp_processor_id to verify time is monotonic
+ preempt_disable();
+ // Set monotonic_started to zero as gator_get_time is uptime minus monotonic_started
+ gator_monotonic_started = 0;
+ gator_monotonic_started = gator_get_time();
+
+ marshal_summary(timestamp, uptime, gator_monotonic_started, uname_buf);
+ preempt_enable();
+}
+
+int gator_events_install(struct gator_interface *interface)
+{
+ list_add_tail(&interface->list, &gator_events);
+
+ return 0;
+}
+
+int gator_events_get_key(void)
+{
+ // key 0 is reserved as a timestamp
+ // key 1 is reserved as the marker for thread specific counters
+ // Odd keys are assigned by the driver, even keys by the daemon
+ static int key = 3;
+
+ const int ret = key;
+ key += 2;
+ return ret;
+}
+
+static int gator_init(void)
+{
+ int i;
+
+ calc_first_cluster_size();
+
+ // events sources
+ for (i = 0; i < ARRAY_SIZE(gator_events_list); i++)
+ if (gator_events_list[i])
+ gator_events_list[i]();
+
+ gator_trace_sched_init();
+ gator_trace_power_init();
+
+ return 0;
+}
+
+static void gator_exit(void)
+{
+ struct gator_interface *gi;
+
+ list_for_each_entry(gi, &gator_events, list)
+ if (gi->shutdown)
+ gi->shutdown();
+}
+
+static int gator_start(void)
+{
+ unsigned long cpu, i;
+ struct gator_interface *gi;
+
+ gator_buffer_wake_run = true;
+ if (IS_ERR(gator_buffer_wake_thread = kthread_run(gator_buffer_wake_func, NULL, "gator_bwake"))) {
+ goto bwake_failure;
+ }
+
+ if (gator_migrate_start())
+ goto migrate_failure;
+
+ // Initialize the buffer with the frame type and core
+ for_each_present_cpu(cpu) {
+ for (i = 0; i < NUM_GATOR_BUFS; i++) {
+ marshal_frame(cpu, i);
+ }
+ per_cpu(last_timestamp, cpu) = 0;
+ }
+ printed_monotonic_warning = false;
+
+ // Capture the start time
+ gator_summary();
+
+ // start all events
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->start && gi->start() != 0) {
+ struct list_head *ptr = gi->list.prev;
+
+ while (ptr != &gator_events) {
+ gi = list_entry(ptr, struct gator_interface, list);
+
+ if (gi->stop)
+ gi->stop();
+
+ ptr = ptr->prev;
+ }
+ goto events_failure;
+ }
+ }
+
+ // cookies shall be initialized before trace_sched_start() and gator_timer_start()
+ if (cookies_initialize())
+ goto cookies_failure;
+ if (gator_annotate_start())
+ goto annotate_failure;
+ if (gator_trace_sched_start())
+ goto sched_failure;
+ if (gator_trace_power_start())
+ goto power_failure;
+ if (gator_trace_gpu_start())
+ goto gpu_failure;
+ if (gator_timer_start(gator_timer_count))
+ goto timer_failure;
+ if (gator_notifier_start())
+ goto notifier_failure;
+
+ return 0;
+
+notifier_failure:
+ gator_timer_stop();
+timer_failure:
+ gator_trace_gpu_stop();
+gpu_failure:
+ gator_trace_power_stop();
+power_failure:
+ gator_trace_sched_stop();
+sched_failure:
+ gator_annotate_stop();
+annotate_failure:
+ cookies_release();
+cookies_failure:
+ // stop all events
+ list_for_each_entry(gi, &gator_events, list)
+ if (gi->stop)
+ gi->stop();
+events_failure:
+ gator_migrate_stop();
+migrate_failure:
+ gator_buffer_wake_run = false;
+ up(&gator_buffer_wake_sem);
+ gator_buffer_wake_thread = NULL;
+bwake_failure:
+
+ return -1;
+}
+
+static void gator_stop(void)
+{
+ struct gator_interface *gi;
+
+ gator_annotate_stop();
+ gator_trace_sched_stop();
+ gator_trace_power_stop();
+ gator_trace_gpu_stop();
+
+ // stop all interrupt callback reads before tearing down other interfaces
+ gator_notifier_stop(); // should be called before gator_timer_stop to avoid re-enabling the hrtimer after it has been offlined
+ gator_timer_stop();
+
+ // stop all events
+ list_for_each_entry(gi, &gator_events, list)
+ if (gi->stop)
+ gi->stop();
+
+ gator_migrate_stop();
+
+ gator_buffer_wake_run = false;
+ up(&gator_buffer_wake_sem);
+ gator_buffer_wake_thread = NULL;
+}
+
+/******************************************************************************
+ * Filesystem
+ ******************************************************************************/
+/* fopen("buffer") */
+static int gator_op_setup(void)
+{
+ int err = 0;
+ int cpu, i;
+
+ mutex_lock(&start_mutex);
+
+ gator_buffer_size[SUMMARY_BUF] = SUMMARY_BUFFER_SIZE;
+ gator_buffer_mask[SUMMARY_BUF] = SUMMARY_BUFFER_SIZE - 1;
+
+ gator_buffer_size[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE;
+ gator_buffer_mask[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE - 1;
+
+ gator_buffer_size[NAME_BUF] = NAME_BUFFER_SIZE;
+ gator_buffer_mask[NAME_BUF] = NAME_BUFFER_SIZE - 1;
+
+ gator_buffer_size[COUNTER_BUF] = COUNTER_BUFFER_SIZE;
+ gator_buffer_mask[COUNTER_BUF] = COUNTER_BUFFER_SIZE - 1;
+
+ gator_buffer_size[BLOCK_COUNTER_BUF] = BLOCK_COUNTER_BUFFER_SIZE;
+ gator_buffer_mask[BLOCK_COUNTER_BUF] = BLOCK_COUNTER_BUFFER_SIZE - 1;
+
+ gator_buffer_size[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE;
+ gator_buffer_mask[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE - 1;
+
+ gator_buffer_size[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE;
+ gator_buffer_mask[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE - 1;
+
+ gator_buffer_size[IDLE_BUF] = IDLE_BUFFER_SIZE;
+ gator_buffer_mask[IDLE_BUF] = IDLE_BUFFER_SIZE - 1;
+
+ gator_buffer_size[ACTIVITY_BUF] = ACTIVITY_BUFFER_SIZE;
+ gator_buffer_mask[ACTIVITY_BUF] = ACTIVITY_BUFFER_SIZE - 1;
+
+ // Initialize percpu per buffer variables
+ for (i = 0; i < NUM_GATOR_BUFS; i++) {
+ // Verify buffers are a power of 2
+ if (gator_buffer_size[i] & (gator_buffer_size[i] - 1)) {
+ err = -ENOEXEC;
+ goto setup_error;
+ }
+
+ for_each_present_cpu(cpu) {
+ per_cpu(gator_buffer_read, cpu)[i] = 0;
+ per_cpu(gator_buffer_write, cpu)[i] = 0;
+ per_cpu(gator_buffer_commit, cpu)[i] = 0;
+ per_cpu(buffer_space_available, cpu)[i] = true;
+ per_cpu(gator_buffer_commit_time, cpu) = gator_live_rate;
+
+ // Annotation is a special case that only uses a single buffer
+ if (cpu > 0 && i == ANNOTATE_BUF) {
+ per_cpu(gator_buffer, cpu)[i] = NULL;
+ continue;
+ }
+
+ per_cpu(gator_buffer, cpu)[i] = vmalloc(gator_buffer_size[i]);
+ if (!per_cpu(gator_buffer, cpu)[i]) {
+ err = -ENOMEM;
+ goto setup_error;
+ }
+ }
+ }
+
+setup_error:
+ mutex_unlock(&start_mutex);
+ return err;
+}
+
+/* Actually start profiling (echo 1>/dev/gator/enable) */
+static int gator_op_start(void)
+{
+ int err = 0;
+
+ mutex_lock(&start_mutex);
+
+ if (gator_started || gator_start())
+ err = -EINVAL;
+ else
+ gator_started = 1;
+
+ mutex_unlock(&start_mutex);
+
+ return err;
+}
+
+/* echo 0>/dev/gator/enable */
+static void gator_op_stop(void)
+{
+ mutex_lock(&start_mutex);
+
+ if (gator_started) {
+ gator_stop();
+
+ mutex_lock(&gator_buffer_mutex);
+
+ gator_started = 0;
+ gator_monotonic_started = 0;
+ cookies_release();
+ wake_up(&gator_buffer_wait);
+
+ mutex_unlock(&gator_buffer_mutex);
+ }
+
+ mutex_unlock(&start_mutex);
+}
+
+static void gator_shutdown(void)
+{
+ int cpu, i;
+
+ mutex_lock(&start_mutex);
+
+ for_each_present_cpu(cpu) {
+ mutex_lock(&gator_buffer_mutex);
+ for (i = 0; i < NUM_GATOR_BUFS; i++) {
+ vfree(per_cpu(gator_buffer, cpu)[i]);
+ per_cpu(gator_buffer, cpu)[i] = NULL;
+ per_cpu(gator_buffer_read, cpu)[i] = 0;
+ per_cpu(gator_buffer_write, cpu)[i] = 0;
+ per_cpu(gator_buffer_commit, cpu)[i] = 0;
+ per_cpu(buffer_space_available, cpu)[i] = true;
+ per_cpu(gator_buffer_commit_time, cpu) = 0;
+ }
+ mutex_unlock(&gator_buffer_mutex);
+ }
+
+ memset(&sent_core_name, 0, sizeof(sent_core_name));
+
+ mutex_unlock(&start_mutex);
+}
+
+static int gator_set_backtrace(unsigned long val)
+{
+ int err = 0;
+
+ mutex_lock(&start_mutex);
+
+ if (gator_started)
+ err = -EBUSY;
+ else
+ gator_backtrace_depth = val;
+
+ mutex_unlock(&start_mutex);
+
+ return err;
+}
+
+static ssize_t enable_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ return gatorfs_ulong_to_user(gator_started, buf, count, offset);
+}
+
+static ssize_t enable_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
+{
+ unsigned long val;
+ int retval;
+
+ if (*offset)
+ return -EINVAL;
+
+ retval = gatorfs_ulong_from_user(&val, buf, count);
+ if (retval)
+ return retval;
+
+ if (val)
+ retval = gator_op_start();
+ else
+ gator_op_stop();
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static const struct file_operations enable_fops = {
+ .read = enable_read,
+ .write = enable_write,
+};
+
+static int userspace_buffer_open(struct inode *inode, struct file *file)
+{
+ int err = -EPERM;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (test_and_set_bit_lock(0, &gator_buffer_opened))
+ return -EBUSY;
+
+ if ((err = gator_op_setup()))
+ goto fail;
+
+ /* NB: the actual start happens from userspace
+ * echo 1 >/dev/gator/enable
+ */
+
+ return 0;
+
+fail:
+ __clear_bit_unlock(0, &gator_buffer_opened);
+ return err;
+}
+
+static int userspace_buffer_release(struct inode *inode, struct file *file)
+{
+ gator_op_stop();
+ gator_shutdown();
+ __clear_bit_unlock(0, &gator_buffer_opened);
+ return 0;
+}
+
+static ssize_t userspace_buffer_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ int commit, length1, length2, read;
+ char *buffer1;
+ char *buffer2;
+ int cpu, buftype;
+ int written = 0;
+
+ // ensure there is enough space for a whole frame
+ if (count < userspace_buffer_size || *offset) {
+ return -EINVAL;
+ }
+
+ // sleep until the condition is true or a signal is received
+ // the condition is checked each time gator_buffer_wait is woken up
+ wait_event_interruptible(gator_buffer_wait, buffer_commit_ready(&cpu, &buftype) || !gator_started);
+
+ if (signal_pending(current)) {
+ return -EINTR;
+ }
+
+ if (buftype == -1 || cpu == -1) {
+ return 0;
+ }
+
+ mutex_lock(&gator_buffer_mutex);
+
+ do {
+ read = per_cpu(gator_buffer_read, cpu)[buftype];
+ commit = per_cpu(gator_buffer_commit, cpu)[buftype];
+
+ // May happen if the buffer is freed during pending reads.
+ if (!per_cpu(gator_buffer, cpu)[buftype]) {
+ break;
+ }
+
+ // determine the size of two halves
+ length1 = commit - read;
+ length2 = 0;
+ buffer1 = &(per_cpu(gator_buffer, cpu)[buftype][read]);
+ buffer2 = &(per_cpu(gator_buffer, cpu)[buftype][0]);
+ if (length1 < 0) {
+ length1 = gator_buffer_size[buftype] - read;
+ length2 = commit;
+ }
+
+ if (length1 + length2 > count - written) {
+ break;
+ }
+
+ // start, middle or end
+ if (length1 > 0 && copy_to_user(&buf[written], buffer1, length1)) {
+ break;
+ }
+
+ // possible wrap around
+ if (length2 > 0 && copy_to_user(&buf[written + length1], buffer2, length2)) {
+ break;
+ }
+
+ per_cpu(gator_buffer_read, cpu)[buftype] = commit;
+ written += length1 + length2;
+
+ // Wake up annotate_write if more space is available
+ if (buftype == ANNOTATE_BUF) {
+ wake_up(&gator_annotate_wait);
+ }
+ } while (buffer_commit_ready(&cpu, &buftype));
+
+ mutex_unlock(&gator_buffer_mutex);
+
+ // kick just in case we've lost an SMP event
+ wake_up(&gator_buffer_wait);
+
+ return written > 0 ? written : -EFAULT;
+}
+
+static const struct file_operations gator_event_buffer_fops = {
+ .open = userspace_buffer_open,
+ .release = userspace_buffer_release,
+ .read = userspace_buffer_read,
+};
+
+static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ return gatorfs_ulong_to_user(gator_backtrace_depth, buf, count, offset);
+}
+
+static ssize_t depth_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
+{
+ unsigned long val;
+ int retval;
+
+ if (*offset)
+ return -EINVAL;
+
+ retval = gatorfs_ulong_from_user(&val, buf, count);
+ if (retval)
+ return retval;
+
+ retval = gator_set_backtrace(val);
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static const struct file_operations depth_fops = {
+ .read = depth_read,
+ .write = depth_write
+};
+
+static void gator_op_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ struct gator_interface *gi;
+ int cpu;
+
+ /* reinitialize default values */
+ gator_cpu_cores = 0;
+ for_each_present_cpu(cpu) {
+ gator_cpu_cores++;
+ }
+ userspace_buffer_size = BACKTRACE_BUFFER_SIZE;
+ gator_response_type = 1;
+ gator_live_rate = 0;
+
+ gatorfs_create_file(sb, root, "enable", &enable_fops);
+ gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops);
+ gatorfs_create_file(sb, root, "backtrace_depth", &depth_fops);
+ gatorfs_create_ro_ulong(sb, root, "cpu_cores", &gator_cpu_cores);
+ gatorfs_create_ro_ulong(sb, root, "buffer_size", &userspace_buffer_size);
+ gatorfs_create_ulong(sb, root, "tick", &gator_timer_count);
+ gatorfs_create_ulong(sb, root, "response_type", &gator_response_type);
+ gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version);
+ gatorfs_create_ro_u64(sb, root, "started", &gator_monotonic_started);
+ gatorfs_create_u64(sb, root, "live_rate", &gator_live_rate);
+
+ // Annotate interface
+ gator_annotate_create_files(sb, root);
+
+ // Linux Events
+ dir = gatorfs_mkdir(sb, root, "events");
+ list_for_each_entry(gi, &gator_events, list)
+ if (gi->create_files)
+ gi->create_files(sb, dir);
+
+ // Sched Events
+ sched_trace_create_files(sb, dir);
+
+ // Power interface
+ gator_trace_power_create_files(sb, dir);
+}
+
+/******************************************************************************
+ * Module
+ ******************************************************************************/
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+
+#define GATOR_TRACEPOINTS \
+ GATOR_HANDLE_TRACEPOINT(block_rq_complete); \
+ GATOR_HANDLE_TRACEPOINT(cpu_frequency); \
+ GATOR_HANDLE_TRACEPOINT(cpu_idle); \
+ GATOR_HANDLE_TRACEPOINT(cpu_migrate_begin); \
+ GATOR_HANDLE_TRACEPOINT(cpu_migrate_current); \
+ GATOR_HANDLE_TRACEPOINT(cpu_migrate_finish); \
+ GATOR_HANDLE_TRACEPOINT(irq_handler_exit); \
+ GATOR_HANDLE_TRACEPOINT(mali_hw_counter); \
+ GATOR_HANDLE_TRACEPOINT(mali_job_slots_event); \
+ GATOR_HANDLE_TRACEPOINT(mali_mmu_as_in_use); \
+ GATOR_HANDLE_TRACEPOINT(mali_mmu_as_released); \
+ GATOR_HANDLE_TRACEPOINT(mali_page_fault_insert_pages); \
+ GATOR_HANDLE_TRACEPOINT(mali_pm_status); \
+ GATOR_HANDLE_TRACEPOINT(mali_sw_counter); \
+ GATOR_HANDLE_TRACEPOINT(mali_sw_counters); \
+ GATOR_HANDLE_TRACEPOINT(mali_timeline_event); \
+ GATOR_HANDLE_TRACEPOINT(mali_total_alloc_pages_change); \
+ GATOR_HANDLE_TRACEPOINT(mm_page_alloc); \
+ GATOR_HANDLE_TRACEPOINT(mm_page_free); \
+ GATOR_HANDLE_TRACEPOINT(mm_page_free_batched); \
+ GATOR_HANDLE_TRACEPOINT(sched_process_exec); \
+ GATOR_HANDLE_TRACEPOINT(sched_process_fork); \
+ GATOR_HANDLE_TRACEPOINT(sched_process_free); \
+ GATOR_HANDLE_TRACEPOINT(sched_switch); \
+ GATOR_HANDLE_TRACEPOINT(softirq_exit); \
+
+#define GATOR_HANDLE_TRACEPOINT(probe_name) \
+ struct tracepoint *gator_tracepoint_##probe_name
+GATOR_TRACEPOINTS;
+#undef GATOR_HANDLE_TRACEPOINT
+
+static void gator_fct(struct tracepoint *tp, void *priv)
+{
+#define GATOR_HANDLE_TRACEPOINT(probe_name) \
+ if (strcmp(tp->name, #probe_name) == 0) { \
+ gator_tracepoint_##probe_name = tp; \
+ return; \
+ }
+GATOR_TRACEPOINTS;
+#undef GATOR_HANDLE_TRACEPOINT
+}
+
+#else
+
+#define for_each_kernel_tracepoint(fct, priv)
+
+#endif
+
+static int __init gator_module_init(void)
+{
+ for_each_kernel_tracepoint(gator_fct, NULL);
+
+ if (gatorfs_register()) {
+ return -1;
+ }
+
+ if (gator_init()) {
+ gatorfs_unregister();
+ return -1;
+ }
+
+ setup_timer(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0);
+
+ // Initialize the list of cpuids
+ memset(gator_cpuids, -1, sizeof(gator_cpuids));
+ on_each_cpu(gator_read_cpuid, NULL, 1);
+
+ return 0;
+}
+
+static void __exit gator_module_exit(void)
+{
+ del_timer_sync(&gator_buffer_wake_up_timer);
+ tracepoint_synchronize_unregister();
+ gator_exit();
+ gatorfs_unregister();
+}
+
+module_init(gator_module_init);
+module_exit(gator_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("ARM Ltd");
+MODULE_DESCRIPTION("Gator system profiler");
+#define STRIFY2(ARG) #ARG
+#define STRIFY(ARG) STRIFY2(ARG)
+MODULE_VERSION(STRIFY(PROTOCOL_VERSION));
diff --git a/drivers/gator/gator_marshaling.c b/drivers/gator/gator_marshaling.c
new file mode 100644
index 000000000000..97b4ae6f9d4d
--- /dev/null
+++ b/drivers/gator/gator_marshaling.c
@@ -0,0 +1,362 @@
+/**
+ * Copyright (C) ARM Limited 2012-2014. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#define NEWLINE_CANARY \
+ /* Unix */ \
+ "1\n" \
+ /* Windows */ \
+ "2\r\n" \
+ /* Mac OS */ \
+ "3\r" \
+ /* RISC OS */ \
+ "4\n\r" \
+ /* Add another character so the length isn't 0x0a bytes */ \
+ "5"
+
+#ifdef MALI_SUPPORT
+#include "gator_events_mali_common.h"
+#endif
+
+static void marshal_summary(long long timestamp, long long uptime, long long monotonic_delta, const char * uname)
+{
+ unsigned long flags;
+ int cpu = 0;
+
+ local_irq_save(flags);
+ gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_SUMMARY);
+ gator_buffer_write_string(cpu, SUMMARY_BUF, NEWLINE_CANARY);
+ gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, timestamp);
+ gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, uptime);
+ gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, monotonic_delta);
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "uname");
+ gator_buffer_write_string(cpu, SUMMARY_BUF, uname);
+#if GATOR_IKS_SUPPORT
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "iks");
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "");
+#endif
+ // Let Streamline know which GPU is used so that it can label the GPU Activity appropriately. This is a temporary fix, to be improved in a future release.
+#ifdef MALI_SUPPORT
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "mali_type");
+#if (MALI_SUPPORT == MALI_4xx)
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "4xx");
+#elif (MALI_SUPPORT == MALI_T6xx)
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "6xx");
+#else
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "unknown");
+#endif
+#endif
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "");
+ // Commit the buffer now so it can be one of the first frames read by Streamline
+ local_irq_restore(flags);
+ gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time());
+}
+
+static bool marshal_cookie_header(const char *text)
+{
+ int cpu = get_physical_cpu();
+ return buffer_check_space(cpu, NAME_BUF, strlen(text) + 3 * MAXSIZE_PACK32);
+}
+
+static void marshal_cookie(int cookie, const char *text)
+{
+ int cpu = get_physical_cpu();
+ // buffer_check_space already called by marshal_cookie_header
+ gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_COOKIE);
+ gator_buffer_write_packed_int(cpu, NAME_BUF, cookie);
+ gator_buffer_write_string(cpu, NAME_BUF, text);
+ buffer_check(cpu, NAME_BUF, gator_get_time());
+}
+
+static void marshal_thread_name(int pid, char *name)
+{
+ unsigned long flags, cpu;
+ u64 time;
+ local_irq_save(flags);
+ cpu = get_physical_cpu();
+ time = gator_get_time();
+ if (buffer_check_space(cpu, NAME_BUF, TASK_COMM_LEN + 3 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) {
+ gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_THREAD_NAME);
+ gator_buffer_write_packed_int64(cpu, NAME_BUF, time);
+ gator_buffer_write_packed_int(cpu, NAME_BUF, pid);
+ gator_buffer_write_string(cpu, NAME_BUF, name);
+ }
+ local_irq_restore(flags);
+ buffer_check(cpu, NAME_BUF, time);
+}
+
+static void marshal_link(int cookie, int tgid, int pid)
+{
+ unsigned long cpu = get_physical_cpu(), flags;
+ u64 time;
+
+ local_irq_save(flags);
+ time = gator_get_time();
+ if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_LINK);
+ gator_buffer_write_packed_int64(cpu, NAME_BUF, time);
+ gator_buffer_write_packed_int(cpu, NAME_BUF, cookie);
+ gator_buffer_write_packed_int(cpu, NAME_BUF, tgid);
+ gator_buffer_write_packed_int(cpu, NAME_BUF, pid);
+ }
+ local_irq_restore(flags);
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, NAME_BUF, time);
+}
+
+static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, u64 time)
+{
+ int cpu = get_physical_cpu();
+ if (!buffer_check_space(cpu, BACKTRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32 + gator_backtrace_depth * 2 * MAXSIZE_PACK32)) {
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, BACKTRACE_BUF, time);
+
+ return false;
+ }
+
+ gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, time);
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, exec_cookie);
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, tgid);
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, pid);
+
+ return true;
+}
+
+static void marshal_backtrace(unsigned long address, int cookie, int in_kernel)
+{
+ int cpu = get_physical_cpu();
+ if (cookie == 0 && !in_kernel) {
+ cookie = UNRESOLVED_COOKIE;
+ }
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie);
+ gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, address);
+}
+
+static void marshal_backtrace_footer(u64 time)
+{
+ int cpu = get_physical_cpu();
+ gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_END_BACKTRACE);
+
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, BACKTRACE_BUF, time);
+}
+
+static bool marshal_event_header(u64 time)
+{
+ unsigned long flags, cpu = get_physical_cpu();
+ bool retval = false;
+
+ local_irq_save(flags);
+ if (buffer_check_space(cpu, BLOCK_COUNTER_BUF, MAXSIZE_PACK32 + MAXSIZE_PACK64)) {
+ gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, 0); // key of zero indicates a timestamp
+ gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, time);
+ retval = true;
+ }
+ local_irq_restore(flags);
+
+ return retval;
+}
+
+static void marshal_event(int len, int *buffer)
+{
+ unsigned long i, flags, cpu = get_physical_cpu();
+
+ if (len <= 0)
+ return;
+
+ // length must be even since all data is a (key, value) pair
+ if (len & 0x1) {
+ pr_err("gator: invalid counter data detected and discarded");
+ return;
+ }
+
+ // events must be written in key,value pairs
+ local_irq_save(flags);
+ for (i = 0; i < len; i += 2) {
+ if (!buffer_check_space(cpu, BLOCK_COUNTER_BUF, 2 * MAXSIZE_PACK32)) {
+ break;
+ }
+ gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i]);
+ gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i + 1]);
+ }
+ local_irq_restore(flags);
+}
+
+static void marshal_event64(int len, long long *buffer64)
+{
+ unsigned long i, flags, cpu = get_physical_cpu();
+
+ if (len <= 0)
+ return;
+
+ // length must be even since all data is a (key, value) pair
+ if (len & 0x1) {
+ pr_err("gator: invalid counter data detected and discarded");
+ return;
+ }
+
+ // events must be written in key,value pairs
+ local_irq_save(flags);
+ for (i = 0; i < len; i += 2) {
+ if (!buffer_check_space(cpu, BLOCK_COUNTER_BUF, 2 * MAXSIZE_PACK64)) {
+ break;
+ }
+ gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i]);
+ gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i + 1]);
+ }
+ local_irq_restore(flags);
+}
+
+#if GATOR_CPU_FREQ_SUPPORT
+static void marshal_event_single(int core, int key, int value)
+{
+ unsigned long flags, cpu;
+ u64 time;
+
+ local_irq_save(flags);
+ cpu = get_physical_cpu();
+ time = gator_get_time();
+ if (buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int64(cpu, COUNTER_BUF, time);
+ gator_buffer_write_packed_int(cpu, COUNTER_BUF, core);
+ gator_buffer_write_packed_int(cpu, COUNTER_BUF, key);
+ gator_buffer_write_packed_int(cpu, COUNTER_BUF, value);
+ }
+ local_irq_restore(flags);
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, COUNTER_BUF, time);
+}
+
+static void marshal_event_single64(int core, int key, long long value)
+{
+ unsigned long flags, cpu;
+ u64 time;
+
+ local_irq_save(flags);
+ cpu = get_physical_cpu();
+ time = gator_get_time();
+ if (buffer_check_space(cpu, COUNTER_BUF, 2 * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int64(cpu, COUNTER_BUF, time);
+ gator_buffer_write_packed_int(cpu, COUNTER_BUF, core);
+ gator_buffer_write_packed_int(cpu, COUNTER_BUF, key);
+ gator_buffer_write_packed_int64(cpu, COUNTER_BUF, value);
+ }
+ local_irq_restore(flags);
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, COUNTER_BUF, time);
+}
+#endif
+
+static void marshal_sched_trace_switch(int pid, int state)
+{
+ unsigned long cpu = get_physical_cpu(), flags;
+ u64 time;
+
+ if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF])
+ return;
+
+ local_irq_save(flags);
+ time = gator_get_time();
+ if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_SWITCH);
+ gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, state);
+ }
+ local_irq_restore(flags);
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, SCHED_TRACE_BUF, time);
+}
+
+static void marshal_sched_trace_exit(int tgid, int pid)
+{
+ unsigned long cpu = get_physical_cpu(), flags;
+ u64 time;
+
+ if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF])
+ return;
+
+ local_irq_save(flags);
+ time = gator_get_time();
+ if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_EXIT);
+ gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time);
+ gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid);
+ }
+ local_irq_restore(flags);
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, SCHED_TRACE_BUF, time);
+}
+
+#if GATOR_CPU_FREQ_SUPPORT
+static void marshal_idle(int core, int state)
+{
+ unsigned long flags, cpu;
+ u64 time;
+
+ local_irq_save(flags);
+ cpu = get_physical_cpu();
+ time = gator_get_time();
+ if (buffer_check_space(cpu, IDLE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, IDLE_BUF, state);
+ gator_buffer_write_packed_int64(cpu, IDLE_BUF, time);
+ gator_buffer_write_packed_int(cpu, IDLE_BUF, core);
+ }
+ local_irq_restore(flags);
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, IDLE_BUF, time);
+}
+#endif
+
+#if defined(__arm__) || defined(__aarch64__)
+static void marshal_core_name(const int core, const int cpuid, const char *name)
+{
+ int cpu = get_physical_cpu();
+ unsigned long flags;
+ local_irq_save(flags);
+ if (buffer_check_space(cpu, SUMMARY_BUF, MAXSIZE_PACK32 + MAXSIZE_CORE_NAME)) {
+ gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_CORE_NAME);
+ gator_buffer_write_packed_int(cpu, SUMMARY_BUF, core);
+ gator_buffer_write_packed_int(cpu, SUMMARY_BUF, cpuid);
+ gator_buffer_write_string(cpu, SUMMARY_BUF, name);
+ }
+ // Commit core names now so that they can show up in live
+ local_irq_restore(flags);
+ gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time());
+}
+#endif
+
+static void marshal_activity_switch(int core, int key, int activity, int pid, int state)
+{
+ unsigned long cpu = get_physical_cpu(), flags;
+ u64 time;
+
+ if (!per_cpu(gator_buffer, cpu)[ACTIVITY_BUF])
+ return;
+
+ local_irq_save(flags);
+ time = gator_get_time();
+ if (buffer_check_space(cpu, ACTIVITY_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) {
+ gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, MESSAGE_SWITCH);
+ gator_buffer_write_packed_int64(cpu, ACTIVITY_BUF, time);
+ gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, core);
+ gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, key);
+ gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, activity);
+ gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, pid);
+ gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, state);
+ }
+ local_irq_restore(flags);
+ // Check and commit; commit is set to occur once buffer is 3/4 full
+ buffer_check(cpu, ACTIVITY_BUF, time);
+}
+
+void gator_marshal_activity_switch(int core, int key, int activity, int pid)
+{
+ // state is reserved for cpu use only
+ marshal_activity_switch(core, key, activity, pid, 0);
+}
diff --git a/drivers/gator/gator_trace_gpu.c b/drivers/gator/gator_trace_gpu.c
new file mode 100644
index 000000000000..a8b9e7d61ece
--- /dev/null
+++ b/drivers/gator/gator_trace_gpu.c
@@ -0,0 +1,333 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "gator.h"
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+
+#ifdef MALI_SUPPORT
+#ifdef MALI_DIR_MIDGARD
+/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/
+#include "mali_linux_trace.h"
+#else
+/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/
+#include "linux/mali_linux_trace.h"
+#endif
+#endif
+
+/*
+ * Taken from MALI_PROFILING_EVENT_TYPE_* items in Mali DDK.
+ */
+#define EVENT_TYPE_SINGLE 0
+#define EVENT_TYPE_START 1
+#define EVENT_TYPE_STOP 2
+#define EVENT_TYPE_SUSPEND 3
+#define EVENT_TYPE_RESUME 4
+
+/* Note whether tracepoints have been registered */
+static int mali_timeline_trace_registered;
+static int mali_job_slots_trace_registered;
+
+enum {
+ GPU_UNIT_NONE = 0,
+ GPU_UNIT_VP,
+ GPU_UNIT_FP,
+ GPU_UNIT_CL,
+ NUMBER_OF_GPU_UNITS
+};
+
+#if defined(MALI_SUPPORT)
+
+struct mali_activity {
+ int core;
+ int key;
+ int count;
+ int last_activity;
+ int last_pid;
+};
+
+#define NUMBER_OF_GPU_CORES 16
+static struct mali_activity mali_activities[NUMBER_OF_GPU_UNITS*NUMBER_OF_GPU_CORES];
+static DEFINE_SPINLOCK(mali_activities_lock);
+
+/* Only one event should be running on a unit and core at a time (ie, a start
+ * event can only be followed by a stop and vice versa), but because the kernel
+ * only knows when a job is enqueued and not started, it is possible for a
+ * start1, start2, stop1, stop2. Change it back into start1, stop1, start2,
+ * stop2 by queueing up start2 and releasing it when stop1 is received.
+ */
+
+static int mali_activity_index(int core, int key)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mali_activities); ++i) {
+ if ((mali_activities[i].core == core) && (mali_activities[i].key == key)) {
+ break;
+ }
+ if ((mali_activities[i].core == 0) && (mali_activities[i].key == 0)) {
+ mali_activities[i].core = core;
+ mali_activities[i].key = key;
+ break;
+ }
+ }
+ BUG_ON(i >= ARRAY_SIZE(mali_activities));
+
+ return i;
+}
+
+static void mali_activity_enqueue(int core, int key, int activity, int pid)
+{
+ int i;
+ int count;
+
+ spin_lock(&mali_activities_lock);
+ i = mali_activity_index(core, key);
+
+ count = mali_activities[i].count;
+ BUG_ON(count < 0);
+ ++mali_activities[i].count;
+ if (count) {
+ mali_activities[i].last_activity = activity;
+ mali_activities[i].last_pid = pid;
+ }
+ spin_unlock(&mali_activities_lock);
+
+ if (!count) {
+ gator_marshal_activity_switch(core, key, activity, pid);
+ }
+}
+
+static void mali_activity_stop(int core, int key)
+{
+ int i;
+ int count;
+ int last_activity = 0;
+ int last_pid = 0;
+
+ spin_lock(&mali_activities_lock);
+ i = mali_activity_index(core, key);
+
+ if (mali_activities[i].count == 0) {
+ spin_unlock(&mali_activities_lock);
+ return;
+ }
+ --mali_activities[i].count;
+ count = mali_activities[i].count;
+ if (count) {
+ last_activity = mali_activities[i].last_activity;
+ last_pid = mali_activities[i].last_pid;
+ }
+ spin_unlock(&mali_activities_lock);
+
+ gator_marshal_activity_switch(core, key, 0, 0);
+ if (count) {
+ gator_marshal_activity_switch(core, key, last_activity, last_pid);
+ }
+}
+
+void mali_activity_clear(mali_counter mali_activity[], size_t mali_activity_size)
+{
+ int activity;
+ int cores;
+ int core;
+
+ for (activity = 0; activity < mali_activity_size; ++activity) {
+ cores = mali_activity[activity].cores;
+ if (cores < 0) {
+ cores = 1;
+ }
+ for (core = 0; core < cores; ++core) {
+ if (mali_activity[activity].enabled) {
+ gator_marshal_activity_switch(core, mali_activity[activity].key, 0, 0);
+ }
+ }
+ }
+}
+
+#endif
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx)
+#include "gator_events_mali_4xx.h"
+
+/*
+ * Taken from MALI_PROFILING_EVENT_CHANNEL_* in Mali DDK.
+ */
+enum {
+ EVENT_CHANNEL_SOFTWARE = 0,
+ EVENT_CHANNEL_VP0 = 1,
+ EVENT_CHANNEL_FP0 = 5,
+ EVENT_CHANNEL_FP1,
+ EVENT_CHANNEL_FP2,
+ EVENT_CHANNEL_FP3,
+ EVENT_CHANNEL_FP4,
+ EVENT_CHANNEL_FP5,
+ EVENT_CHANNEL_FP6,
+ EVENT_CHANNEL_FP7,
+ EVENT_CHANNEL_GPU = 21
+};
+
+/**
+ * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel
+ */
+enum {
+ EVENT_REASON_SINGLE_GPU_NONE = 0,
+ EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1,
+};
+
+mali_counter mali_activity[2];
+
+GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4))
+{
+ unsigned int component, state;
+
+ // do as much work as possible before disabling interrupts
+ component = (event_id >> 16) & 0xFF; // component is an 8-bit field
+ state = (event_id >> 24) & 0xF; // state is a 4-bit field
+
+ switch (state) {
+ case EVENT_TYPE_START:
+ if (component == EVENT_CHANNEL_VP0) {
+ /* tgid = d0; pid = d1; */
+ if (mali_activity[1].enabled) {
+ mali_activity_enqueue(0, mali_activity[1].key, 1, d1);
+ }
+ } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) {
+ /* tgid = d0; pid = d1; */
+ if (mali_activity[0].enabled) {
+ mali_activity_enqueue(component - EVENT_CHANNEL_FP0, mali_activity[0].key, 1, d1);
+ }
+ }
+ break;
+
+ case EVENT_TYPE_STOP:
+ if (component == EVENT_CHANNEL_VP0) {
+ if (mali_activity[1].enabled) {
+ mali_activity_stop(0, mali_activity[1].key);
+ }
+ } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) {
+ if (mali_activity[0].enabled) {
+ mali_activity_stop(component - EVENT_CHANNEL_FP0, mali_activity[0].key);
+ }
+ }
+ break;
+
+ case EVENT_TYPE_SINGLE:
+ if (component == EVENT_CHANNEL_GPU) {
+ unsigned int reason = (event_id & 0xffff);
+
+ if (reason == EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE) {
+ gator_events_mali_log_dvfs_event(d0, d1);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+#endif
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx)
+
+mali_counter mali_activity[3];
+
+#if defined(MALI_JOB_SLOTS_EVENT_CHANGED)
+GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid, unsigned char job_id))
+#else
+GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid))
+#endif
+{
+ unsigned int component, state, unit;
+#if !defined(MALI_JOB_SLOTS_EVENT_CHANGED)
+ unsigned char job_id = 0;
+#endif
+
+ component = (event_id >> 16) & 0xFF; // component is an 8-bit field
+ state = (event_id >> 24) & 0xF; // state is a 4-bit field
+
+ switch (component) {
+ case 0:
+ unit = GPU_UNIT_FP;
+ break;
+ case 1:
+ unit = GPU_UNIT_VP;
+ break;
+ case 2:
+ unit = GPU_UNIT_CL;
+ break;
+ default:
+ unit = GPU_UNIT_NONE;
+ }
+
+ if (unit != GPU_UNIT_NONE) {
+ switch (state) {
+ case EVENT_TYPE_START:
+ if (mali_activity[component].enabled) {
+ mali_activity_enqueue(0, mali_activity[component].key, 1, (pid != 0 ? pid : tgid));
+ }
+ break;
+ case EVENT_TYPE_STOP:
+ default: // Some jobs can be soft-stopped, so ensure that this terminates the activity trace.
+ if (mali_activity[component].enabled) {
+ mali_activity_stop(0, mali_activity[component].key);
+ }
+ break;
+ }
+ }
+}
+#endif
+
+static int gator_trace_gpu_start(void)
+{
+ /*
+ * Returns nonzero for installation failed
+ * Absence of gpu trace points is not an error
+ */
+
+#if defined(MALI_SUPPORT)
+ memset(&mali_activities, 0, sizeof(mali_activities));
+#endif
+ mali_timeline_trace_registered = mali_job_slots_trace_registered = 0;
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx)
+ mali_activity_clear(mali_activity, ARRAY_SIZE(mali_activity));
+ if (!GATOR_REGISTER_TRACE(mali_timeline_event)) {
+ mali_timeline_trace_registered = 1;
+ }
+#endif
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx)
+ mali_activity_clear(mali_activity, ARRAY_SIZE(mali_activity));
+ if (!GATOR_REGISTER_TRACE(mali_job_slots_event)) {
+ mali_job_slots_trace_registered = 1;
+ }
+#endif
+
+ return 0;
+}
+
+static void gator_trace_gpu_stop(void)
+{
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx)
+ if (mali_timeline_trace_registered) {
+ GATOR_UNREGISTER_TRACE(mali_timeline_event);
+ }
+#endif
+
+#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx)
+ if (mali_job_slots_trace_registered) {
+ GATOR_UNREGISTER_TRACE(mali_job_slots_event);
+ }
+#endif
+
+ mali_timeline_trace_registered = mali_job_slots_trace_registered = 0;
+}
diff --git a/drivers/gator/gator_trace_power.c b/drivers/gator/gator_trace_power.c
new file mode 100644
index 000000000000..f2754b1c2b56
--- /dev/null
+++ b/drivers/gator/gator_trace_power.c
@@ -0,0 +1,203 @@
+/**
+ * Copyright (C) ARM Limited 2011-2014. All rights reserved.
+ *
+ * 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 <linux/cpufreq.h>
+#include <trace/events/power.h>
+
+#if defined(__arm__)
+
+#include <asm/mach-types.h>
+
+#define implements_wfi() (!machine_is_omap3_beagle())
+
+#else
+
+#define implements_wfi() false
+
+#endif
+
+// cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38
+// the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86
+#if GATOR_CPU_FREQ_SUPPORT
+enum {
+ POWER_CPU_FREQ,
+ POWER_CPU_IDLE,
+ POWER_TOTAL
+};
+
+static DEFINE_PER_CPU(ulong, idle_prev_state);
+static ulong power_cpu_enabled[POWER_TOTAL];
+static ulong power_cpu_key[POWER_TOTAL];
+
+static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+ int cpu;
+ bool found_nonzero_freq = false;
+
+ // Even if CONFIG_CPU_FREQ is defined, it still may not be used. Check
+ // for non-zero values from cpufreq_quick_get
+ for_each_online_cpu(cpu) {
+ if (cpufreq_quick_get(cpu) > 0) {
+ found_nonzero_freq = true;
+ break;
+ }
+ }
+
+ if (found_nonzero_freq) {
+ // cpu_frequency
+ dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]);
+ }
+
+ // cpu_idle
+ dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]);
+
+ return 0;
+}
+
+// 'cpu' may not equal smp_processor_id(), i.e. may not be running on the core that is having the freq/idle state change
+GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu))
+{
+ cpu = lcpu_to_pcpu(cpu);
+ marshal_event_single64(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000L);
+}
+
+GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu))
+{
+ cpu = lcpu_to_pcpu(cpu);
+
+ if (state == per_cpu(idle_prev_state, cpu)) {
+ return;
+ }
+
+ if (implements_wfi()) {
+ if (state == PWR_EVENT_EXIT) {
+ // transition from wfi to non-wfi
+ marshal_idle(cpu, MESSAGE_IDLE_EXIT);
+ } else {
+ // transition from non-wfi to wfi
+ marshal_idle(cpu, MESSAGE_IDLE_ENTER);
+ }
+ }
+
+ per_cpu(idle_prev_state, cpu) = state;
+
+ if (power_cpu_enabled[POWER_CPU_IDLE]) {
+ // Increment state so that no negative numbers are sent
+ marshal_event_single(cpu, power_cpu_key[POWER_CPU_IDLE], state + 1);
+ }
+}
+
+static void gator_trace_power_online(void)
+{
+ int pcpu = get_physical_cpu();
+ int lcpu = get_logical_cpu();
+ if (power_cpu_enabled[POWER_CPU_FREQ]) {
+ marshal_event_single64(pcpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(lcpu) * 1000L);
+ }
+}
+
+static void gator_trace_power_offline(void)
+{
+ // Set frequency to zero on an offline
+ int cpu = get_physical_cpu();
+ if (power_cpu_enabled[POWER_CPU_FREQ]) {
+ marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], 0);
+ }
+}
+
+static int gator_trace_power_start(void)
+{
+ int cpu;
+
+ // register tracepoints
+ if (power_cpu_enabled[POWER_CPU_FREQ])
+ if (GATOR_REGISTER_TRACE(cpu_frequency))
+ goto fail_cpu_frequency_exit;
+
+ // Always register for cpu:idle for detecting WFI, independent of power_cpu_enabled[POWER_CPU_IDLE]
+ if (GATOR_REGISTER_TRACE(cpu_idle))
+ goto fail_cpu_idle_exit;
+ pr_debug("gator: registered power event tracepoints\n");
+
+ for_each_present_cpu(cpu) {
+ per_cpu(idle_prev_state, cpu) = 0;
+ }
+
+ return 0;
+
+ // unregister tracepoints on error
+fail_cpu_idle_exit:
+ if (power_cpu_enabled[POWER_CPU_FREQ])
+ GATOR_UNREGISTER_TRACE(cpu_frequency);
+fail_cpu_frequency_exit:
+ pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+static void gator_trace_power_stop(void)
+{
+ int i;
+
+ if (power_cpu_enabled[POWER_CPU_FREQ])
+ GATOR_UNREGISTER_TRACE(cpu_frequency);
+ GATOR_UNREGISTER_TRACE(cpu_idle);
+ pr_debug("gator: unregistered power event tracepoints\n");
+
+ for (i = 0; i < POWER_TOTAL; i++) {
+ power_cpu_enabled[i] = 0;
+ }
+}
+
+static void gator_trace_power_init(void)
+{
+ int i;
+ for (i = 0; i < POWER_TOTAL; i++) {
+ power_cpu_enabled[i] = 0;
+ power_cpu_key[i] = gator_events_get_key();
+ }
+}
+#else
+static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root)
+{
+ return 0;
+}
+
+static void gator_trace_power_online(void)
+{
+}
+
+static void gator_trace_power_offline(void)
+{
+}
+
+static int gator_trace_power_start(void)
+{
+ return 0;
+}
+
+static void gator_trace_power_stop(void)
+{
+}
+
+static void gator_trace_power_init(void)
+{
+}
+#endif
diff --git a/drivers/gator/gator_trace_sched.c b/drivers/gator/gator_trace_sched.c
new file mode 100644
index 000000000000..655008628933
--- /dev/null
+++ b/drivers/gator/gator_trace_sched.c
@@ -0,0 +1,297 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 <trace/events/sched.h>
+#include "gator.h"
+
+#define TASK_MAP_ENTRIES 1024 /* must be power of 2 */
+#define TASK_MAX_COLLISIONS 2
+
+enum {
+ STATE_WAIT_ON_OTHER = 0,
+ STATE_CONTENTION,
+ STATE_WAIT_ON_IO,
+ CPU_WAIT_TOTAL
+};
+
+static DEFINE_PER_CPU(uint64_t *, taskname_keys);
+static DEFINE_PER_CPU(int, collecting);
+
+// this array is never read as the cpu wait charts are derived counters
+// the files are needed, nonetheless, to show that these counters are available
+static ulong cpu_wait_enabled[CPU_WAIT_TOTAL];
+static ulong sched_cpu_key[CPU_WAIT_TOTAL];
+
+static int sched_trace_create_files(struct super_block *sb, struct dentry *root)
+{
+ struct dentry *dir;
+
+ // CPU Wait - Contention
+ dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_contention");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_CONTENTION]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_CONTENTION]);
+
+ // CPU Wait - I/O
+ dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_io");
+ if (!dir) {
+ return -1;
+ }
+ gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_WAIT_ON_IO]);
+ gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_WAIT_ON_IO]);
+
+ return 0;
+}
+
+static void emit_pid_name(struct task_struct *task)
+{
+ bool found = false;
+ char taskcomm[TASK_COMM_LEN + 3];
+ unsigned long x, cpu = get_physical_cpu();
+ uint64_t *keys = &(per_cpu(taskname_keys, cpu)[(task->pid & 0xFF) * TASK_MAX_COLLISIONS]);
+ uint64_t value;
+
+ value = gator_chksum_crc32(task->comm);
+ value = (value << 32) | (uint32_t)task->pid;
+
+ // determine if the thread name was emitted already
+ for (x = 0; x < TASK_MAX_COLLISIONS; x++) {
+ if (keys[x] == value) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // shift values, new value always in front
+ uint64_t oldv, newv = value;
+ for (x = 0; x < TASK_MAX_COLLISIONS; x++) {
+ oldv = keys[x];
+ keys[x] = newv;
+ newv = oldv;
+ }
+
+ // emit pid names, cannot use get_task_comm, as it's not exported on all kernel versions
+ if (strlcpy(taskcomm, task->comm, TASK_COMM_LEN) == TASK_COMM_LEN - 1) {
+ // append ellipses if task->comm has length of TASK_COMM_LEN - 1
+ strcat(taskcomm, "...");
+ }
+
+ marshal_thread_name(task->pid, taskcomm);
+ }
+}
+
+static void collect_counters(u64 time, struct task_struct *task)
+{
+ int *buffer, len, cpu = get_physical_cpu();
+ long long *buffer64;
+ struct gator_interface *gi;
+
+ if (marshal_event_header(time)) {
+ list_for_each_entry(gi, &gator_events, list) {
+ if (gi->read) {
+ len = gi->read(&buffer);
+ marshal_event(len, buffer);
+ } else if (gi->read64) {
+ len = gi->read64(&buffer64);
+ marshal_event64(len, buffer64);
+ }
+ if (gi->read_proc && task != NULL) {
+ len = gi->read_proc(&buffer64, task);
+ marshal_event64(len, buffer64);
+ }
+ }
+ // Only check after writing all counters so that time and corresponding counters appear in the same frame
+ buffer_check(cpu, BLOCK_COUNTER_BUF, time);
+
+ // Commit buffers on timeout
+ if (gator_live_rate > 0 && time >= per_cpu(gator_buffer_commit_time, cpu)) {
+ static const int buftypes[] = { NAME_BUF, COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF, ACTIVITY_BUF };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(buftypes); ++i) {
+ gator_commit_buffer(cpu, buftypes[i], time);
+ }
+
+ // spinlocks are noops on uniprocessor machines and mutexes do not work in sched_switch context in
+ // RT-Preempt full, so disable proactive flushing of the annotate frame on uniprocessor machines.
+#ifdef CONFIG_SMP
+ // Try to preemptively flush the annotate buffer to reduce the chance of the buffer being full
+ if (on_primary_core() && spin_trylock(&annotate_lock)) {
+ gator_commit_buffer(0, ANNOTATE_BUF, time);
+ spin_unlock(&annotate_lock);
+ }
+#endif
+ }
+ }
+}
+
+// special case used during a suspend of the system
+static void trace_sched_insert_idle(void)
+{
+ marshal_sched_trace_switch(0, 0);
+}
+
+static void gator_trace_emit_link(struct task_struct *p)
+{
+ int cookie;
+ int cpu = get_physical_cpu();
+
+ cookie = get_exec_cookie(cpu, p);
+ emit_pid_name(p);
+
+ marshal_link(cookie, p->tgid, p->pid);
+}
+
+GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, struct task_struct *child))
+{
+ gator_trace_emit_link(child);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+GATOR_DEFINE_PROBE(sched_process_exec, TP_PROTO(struct task_struct *p, pid_t old_pid, struct linux_binprm *bprm))
+{
+ gator_trace_emit_link(p);
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
+#else
+GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
+#endif
+{
+ int state;
+ int cpu = get_physical_cpu();
+
+ per_cpu(in_scheduler_context, cpu) = true;
+
+ // do as much work as possible before disabling interrupts
+ if (prev->state == TASK_RUNNING) {
+ state = STATE_CONTENTION;
+ } else if (prev->in_iowait) {
+ state = STATE_WAIT_ON_IO;
+ } else {
+ state = STATE_WAIT_ON_OTHER;
+ }
+
+ per_cpu(collecting, cpu) = 1;
+ collect_counters(gator_get_time(), prev);
+ per_cpu(collecting, cpu) = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+ gator_trace_emit_link(next);
+#endif
+ marshal_sched_trace_switch(next->pid, state);
+
+ per_cpu(in_scheduler_context, cpu) = false;
+}
+
+GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p))
+{
+ marshal_sched_trace_exit(p->tgid, p->pid);
+}
+
+static void do_nothing(void *info)
+{
+ // Intentionally do nothing
+ (void)info;
+}
+
+static int register_scheduler_tracepoints(void)
+{
+ // register tracepoints
+ if (GATOR_REGISTER_TRACE(sched_process_fork))
+ goto fail_sched_process_fork;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+ if (GATOR_REGISTER_TRACE(sched_process_exec))
+ goto fail_sched_process_exec;
+#endif
+ if (GATOR_REGISTER_TRACE(sched_switch))
+ goto fail_sched_switch;
+ if (GATOR_REGISTER_TRACE(sched_process_free))
+ goto fail_sched_process_free;
+ pr_debug("gator: registered tracepoints\n");
+
+ // Now that the scheduler tracepoint is registered, force a context switch
+ // on all cpus to capture what is currently running.
+ on_each_cpu(do_nothing, NULL, 0);
+
+ return 0;
+
+ // unregister tracepoints on error
+fail_sched_process_free:
+ GATOR_UNREGISTER_TRACE(sched_switch);
+fail_sched_switch:
+ GATOR_UNREGISTER_TRACE(sched_process_fork);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+fail_sched_process_exec:
+ GATOR_UNREGISTER_TRACE(sched_process_exec);
+#endif
+fail_sched_process_fork:
+ pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+ return -1;
+}
+
+static void unregister_scheduler_tracepoints(void)
+{
+ GATOR_UNREGISTER_TRACE(sched_process_fork);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+ GATOR_UNREGISTER_TRACE(sched_process_exec);
+#endif
+ GATOR_UNREGISTER_TRACE(sched_switch);
+ GATOR_UNREGISTER_TRACE(sched_process_free);
+ pr_debug("gator: unregistered tracepoints\n");
+}
+
+static void gator_trace_sched_stop(void)
+{
+ int cpu;
+
+ unregister_scheduler_tracepoints();
+
+ for_each_present_cpu(cpu) {
+ kfree(per_cpu(taskname_keys, cpu));
+ }
+}
+
+static int gator_trace_sched_start(void)
+{
+ int cpu, size;
+ int ret;
+
+ for_each_present_cpu(cpu) {
+ size = TASK_MAP_ENTRIES * TASK_MAX_COLLISIONS * sizeof(uint64_t);
+ per_cpu(taskname_keys, cpu) = (uint64_t *)kmalloc(size, GFP_KERNEL);
+ if (!per_cpu(taskname_keys, cpu))
+ return -1;
+ memset(per_cpu(taskname_keys, cpu), 0, size);
+ }
+
+ ret = register_scheduler_tracepoints();
+
+ return ret;
+}
+
+static void gator_trace_sched_offline(void)
+{
+ trace_sched_insert_idle();
+}
+
+static void gator_trace_sched_init(void)
+{
+ int i;
+ for (i = 0; i < CPU_WAIT_TOTAL; i++) {
+ cpu_wait_enabled[i] = 0;
+ sched_cpu_key[i] = gator_events_get_key();
+ }
+}
diff --git a/drivers/gator/mali/mali_dd_gator_api.h b/drivers/gator/mali/mali_dd_gator_api.h
new file mode 100644
index 000000000000..104b34f2d72a
--- /dev/null
+++ b/drivers/gator/mali/mali_dd_gator_api.h
@@ -0,0 +1,40 @@
+#if !defined(MALI_DDK_GATOR_API_VERSION)
+ #define MALI_DDK_GATOR_API_VERSION 3
+#endif
+#if !defined(MALI_TRUE)
+ #define MALI_TRUE ((unsigned int)1)
+#endif
+
+#if !defined(MALI_FALSE)
+ #define MALI_FALSE ((unsigned int)0)
+#endif
+
+struct mali_dd_hwcnt_info {
+
+ /* Passed from Gator to kbase */
+ //u32 in_mali_dd_hwcnt_version;
+ unsigned short int bitmask[4];
+
+ /* Passed from kbase to Gator */
+
+ /* ptr to counter dump memory */
+ void *kernel_dump_buffer;
+
+ /* size of counter dump memory */
+ unsigned int size;
+
+ unsigned int gpu_id;
+
+ unsigned int nr_cores;
+
+ unsigned int nr_core_groups;
+
+ /* The cached present bitmaps - these are the same as the corresponding hardware registers*/
+ unsigned long int shader_present_bitmap;
+};
+
+struct mali_dd_hwcnt_handles;
+extern struct mali_dd_hwcnt_handles* mali_dd_hwcnt_init(struct mali_dd_hwcnt_info *in_out_info);
+extern void mali_dd_hwcnt_clear(struct mali_dd_hwcnt_info *in_out_info, struct mali_dd_hwcnt_handles *opaque_handles);
+extern unsigned int kbase_dd_instr_hwcnt_dump_complete(struct mali_dd_hwcnt_handles *opaque_handles, unsigned int * const success);
+extern unsigned int kbase_dd_instr_hwcnt_dump_irq(struct mali_dd_hwcnt_handles *opaque_handles);
diff --git a/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h
new file mode 100644
index 000000000000..ff00d90cee78
--- /dev/null
+++ b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h
@@ -0,0 +1,163 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 __MALI_MJOLLNIR_PROFILING_GATOR_API_H__
+#define __MALI_MJOLLNIR_PROFILING_GATOR_API_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+/*
+ * The number of processor cores. Update to suit your hardware implementation.
+ */
+#define MAX_NUM_FP_CORES (4)
+#define MAX_NUM_VP_CORES (1)
+#define MAX_NUM_L2_CACHE_CORES (1)
+
+enum counters
+{
+ /* Timeline activity */
+ ACTIVITY_VP_0 = 0,
+ ACTIVITY_FP_0,
+ ACTIVITY_FP_1,
+ ACTIVITY_FP_2,
+ ACTIVITY_FP_3,
+
+ /* L2 cache counters */
+ COUNTER_L2_0_C0,
+ COUNTER_L2_0_C1,
+
+ /* Vertex processor counters */
+ COUNTER_VP_0_C0,
+ COUNTER_VP_0_C1,
+
+ /* Fragment processor counters */
+ COUNTER_FP_0_C0,
+ COUNTER_FP_0_C1,
+ COUNTER_FP_1_C0,
+ COUNTER_FP_1_C1,
+ COUNTER_FP_2_C0,
+ COUNTER_FP_2_C1,
+ COUNTER_FP_3_C0,
+ COUNTER_FP_3_C1,
+
+ /* EGL Software Counters */
+ COUNTER_EGL_BLIT_TIME,
+
+ /* GLES Software Counters */
+ COUNTER_GLES_DRAW_ELEMENTS_CALLS,
+ COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES,
+ COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED,
+ COUNTER_GLES_DRAW_ARRAYS_CALLS,
+ COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED,
+ COUNTER_GLES_DRAW_POINTS,
+ COUNTER_GLES_DRAW_LINES,
+ COUNTER_GLES_DRAW_LINE_LOOP,
+ COUNTER_GLES_DRAW_LINE_STRIP,
+ COUNTER_GLES_DRAW_TRIANGLES,
+ COUNTER_GLES_DRAW_TRIANGLE_STRIP,
+ COUNTER_GLES_DRAW_TRIANGLE_FAN,
+ COUNTER_GLES_NON_VBO_DATA_COPY_TIME,
+ COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI,
+ COUNTER_GLES_UPLOAD_TEXTURE_TIME,
+ COUNTER_GLES_UPLOAD_VBO_TIME,
+ COUNTER_GLES_NUM_FLUSHES,
+ COUNTER_GLES_NUM_VSHADERS_GENERATED,
+ COUNTER_GLES_NUM_FSHADERS_GENERATED,
+ COUNTER_GLES_VSHADER_GEN_TIME,
+ COUNTER_GLES_FSHADER_GEN_TIME,
+ COUNTER_GLES_INPUT_TRIANGLES,
+ COUNTER_GLES_VXCACHE_HIT,
+ COUNTER_GLES_VXCACHE_MISS,
+ COUNTER_GLES_VXCACHE_COLLISION,
+ COUNTER_GLES_CULLED_TRIANGLES,
+ COUNTER_GLES_CULLED_LINES,
+ COUNTER_GLES_BACKFACE_TRIANGLES,
+ COUNTER_GLES_GBCLIP_TRIANGLES,
+ COUNTER_GLES_GBCLIP_LINES,
+ COUNTER_GLES_TRIANGLES_DRAWN,
+ COUNTER_GLES_DRAWCALL_TIME,
+ COUNTER_GLES_TRIANGLES_COUNT,
+ COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT,
+ COUNTER_GLES_STRIP_TRIANGLES_COUNT,
+ COUNTER_GLES_FAN_TRIANGLES_COUNT,
+ COUNTER_GLES_LINES_COUNT,
+ COUNTER_GLES_INDEPENDENT_LINES_COUNT,
+ COUNTER_GLES_STRIP_LINES_COUNT,
+ COUNTER_GLES_LOOP_LINES_COUNT,
+
+ COUNTER_FILMSTRIP,
+ COUNTER_FREQUENCY,
+ COUNTER_VOLTAGE,
+
+ NUMBER_OF_EVENTS
+};
+
+#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0
+#define LAST_ACTIVITY_EVENT ACTIVITY_FP_3
+
+#define FIRST_HW_COUNTER COUNTER_L2_0_C0
+#define LAST_HW_COUNTER COUNTER_FP_3_C1
+
+#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME
+#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT
+
+/* Signifies that the system is able to report voltage and frequency numbers. */
+#define DVFS_REPORTED_BY_DDK 1
+
+/**
+ * Structure to pass performance counter data of a Mali core
+ */
+typedef struct _mali_profiling_core_counters
+{
+ u32 source0;
+ u32 value0;
+ u32 source1;
+ u32 value1;
+} _mali_profiling_core_counters;
+
+/*
+ * For compatibility with utgard.
+ */
+typedef struct _mali_profiling_l2_counter_values
+{
+ struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES];
+} _mali_profiling_l2_counter_values;
+
+typedef struct _mali_profiling_mali_version
+{
+ u32 mali_product_id;
+ u32 mali_version_major;
+ u32 mali_version_minor;
+ u32 num_of_l2_cores;
+ u32 num_of_fp_cores;
+ u32 num_of_vp_cores;
+} _mali_profiling_mali_version;
+
+extern void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values);
+extern u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values);
+
+/*
+ * List of possible actions allowing DDK to be controlled by Streamline.
+ * The following numbers are used by DDK to control the frame buffer dumping.
+ */
+#define FBDUMP_CONTROL_ENABLE (1)
+#define FBDUMP_CONTROL_RATE (2)
+#define SW_COUNTER_ENABLE (3)
+#define FBDUMP_CONTROL_RESIZE_FACTOR (4)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MALI_MJOLLNIR_PROFILING_GATOR_API_H__ */
diff --git a/drivers/gator/mali/mali_utgard_profiling_gator_api.h b/drivers/gator/mali/mali_utgard_profiling_gator_api.h
new file mode 100644
index 000000000000..43c576042880
--- /dev/null
+++ b/drivers/gator/mali/mali_utgard_profiling_gator_api.h
@@ -0,0 +1,201 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 __MALI_UTGARD_PROFILING_GATOR_API_H__
+#define __MALI_UTGARD_PROFILING_GATOR_API_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define MALI_PROFILING_API_VERSION 4
+
+#define MAX_NUM_L2_CACHE_CORES 3
+#define MAX_NUM_FP_CORES 8
+#define MAX_NUM_VP_CORES 1
+
+/** The list of events supported by the Mali DDK. */
+typedef enum
+{
+ /* Vertex processor activity */
+ ACTIVITY_VP_0 = 0,
+
+ /* Fragment processor activity */
+ ACTIVITY_FP_0, /* 1 */
+ ACTIVITY_FP_1,
+ ACTIVITY_FP_2,
+ ACTIVITY_FP_3,
+ ACTIVITY_FP_4,
+ ACTIVITY_FP_5,
+ ACTIVITY_FP_6,
+ ACTIVITY_FP_7,
+
+ /* L2 cache counters */
+ COUNTER_L2_0_C0,
+ COUNTER_L2_0_C1,
+ COUNTER_L2_1_C0,
+ COUNTER_L2_1_C1,
+ COUNTER_L2_2_C0,
+ COUNTER_L2_2_C1,
+
+ /* Vertex processor counters */
+ COUNTER_VP_0_C0, /*15*/
+ COUNTER_VP_0_C1,
+
+ /* Fragment processor counters */
+ COUNTER_FP_0_C0,
+ COUNTER_FP_0_C1,
+ COUNTER_FP_1_C0,
+ COUNTER_FP_1_C1,
+ COUNTER_FP_2_C0,
+ COUNTER_FP_2_C1,
+ COUNTER_FP_3_C0,
+ COUNTER_FP_3_C1,
+ COUNTER_FP_4_C0,
+ COUNTER_FP_4_C1,
+ COUNTER_FP_5_C0,
+ COUNTER_FP_5_C1,
+ COUNTER_FP_6_C0,
+ COUNTER_FP_6_C1,
+ COUNTER_FP_7_C0,
+ COUNTER_FP_7_C1, /* 32 */
+
+ /*
+ * If more hardware counters are added, the _mali_osk_hw_counter_table
+ * below should also be updated.
+ */
+
+ /* EGL software counters */
+ COUNTER_EGL_BLIT_TIME,
+
+ /* GLES software counters */
+ COUNTER_GLES_DRAW_ELEMENTS_CALLS,
+ COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES,
+ COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED,
+ COUNTER_GLES_DRAW_ARRAYS_CALLS,
+ COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED,
+ COUNTER_GLES_DRAW_POINTS,
+ COUNTER_GLES_DRAW_LINES,
+ COUNTER_GLES_DRAW_LINE_LOOP,
+ COUNTER_GLES_DRAW_LINE_STRIP,
+ COUNTER_GLES_DRAW_TRIANGLES,
+ COUNTER_GLES_DRAW_TRIANGLE_STRIP,
+ COUNTER_GLES_DRAW_TRIANGLE_FAN,
+ COUNTER_GLES_NON_VBO_DATA_COPY_TIME,
+ COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI,
+ COUNTER_GLES_UPLOAD_TEXTURE_TIME,
+ COUNTER_GLES_UPLOAD_VBO_TIME,
+ COUNTER_GLES_NUM_FLUSHES,
+ COUNTER_GLES_NUM_VSHADERS_GENERATED,
+ COUNTER_GLES_NUM_FSHADERS_GENERATED,
+ COUNTER_GLES_VSHADER_GEN_TIME,
+ COUNTER_GLES_FSHADER_GEN_TIME,
+ COUNTER_GLES_INPUT_TRIANGLES,
+ COUNTER_GLES_VXCACHE_HIT,
+ COUNTER_GLES_VXCACHE_MISS,
+ COUNTER_GLES_VXCACHE_COLLISION,
+ COUNTER_GLES_CULLED_TRIANGLES,
+ COUNTER_GLES_CULLED_LINES,
+ COUNTER_GLES_BACKFACE_TRIANGLES,
+ COUNTER_GLES_GBCLIP_TRIANGLES,
+ COUNTER_GLES_GBCLIP_LINES,
+ COUNTER_GLES_TRIANGLES_DRAWN,
+ COUNTER_GLES_DRAWCALL_TIME,
+ COUNTER_GLES_TRIANGLES_COUNT,
+ COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT,
+ COUNTER_GLES_STRIP_TRIANGLES_COUNT,
+ COUNTER_GLES_FAN_TRIANGLES_COUNT,
+ COUNTER_GLES_LINES_COUNT,
+ COUNTER_GLES_INDEPENDENT_LINES_COUNT,
+ COUNTER_GLES_STRIP_LINES_COUNT,
+ COUNTER_GLES_LOOP_LINES_COUNT,
+
+ /* Framebuffer capture pseudo-counter */
+ COUNTER_FILMSTRIP,
+
+ NUMBER_OF_EVENTS
+} _mali_osk_counter_id;
+
+#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0
+#define LAST_ACTIVITY_EVENT ACTIVITY_FP_7
+
+#define FIRST_HW_COUNTER COUNTER_L2_0_C0
+#define LAST_HW_COUNTER COUNTER_FP_7_C1
+
+#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME
+#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT
+
+#define FIRST_SPECIAL_COUNTER COUNTER_FILMSTRIP
+#define LAST_SPECIAL_COUNTER COUNTER_FILMSTRIP
+
+/**
+ * Structure to pass performance counter data of a Mali core
+ */
+typedef struct _mali_profiling_core_counters
+{
+ u32 source0;
+ u32 value0;
+ u32 source1;
+ u32 value1;
+} _mali_profiling_core_counters;
+
+/**
+ * Structure to pass performance counter data of Mali L2 cache cores
+ */
+typedef struct _mali_profiling_l2_counter_values
+{
+ struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES];
+} _mali_profiling_l2_counter_values;
+
+/**
+ * Structure to pass data defining Mali instance in use:
+ *
+ * mali_product_id - Mali product id
+ * mali_version_major - Mali version major number
+ * mali_version_minor - Mali version minor number
+ * num_of_l2_cores - number of L2 cache cores
+ * num_of_fp_cores - number of fragment processor cores
+ * num_of_vp_cores - number of vertex processor cores
+ */
+typedef struct _mali_profiling_mali_version
+{
+ u32 mali_product_id;
+ u32 mali_version_major;
+ u32 mali_version_minor;
+ u32 num_of_l2_cores;
+ u32 num_of_fp_cores;
+ u32 num_of_vp_cores;
+} _mali_profiling_mali_version;
+
+/*
+ * List of possible actions to be controlled by Streamline.
+ * The following numbers are used by gator to control the frame buffer dumping and s/w counter reporting.
+ * We cannot use the enums in mali_uk_types.h because they are unknown inside gator.
+ */
+#define FBDUMP_CONTROL_ENABLE (1)
+#define FBDUMP_CONTROL_RATE (2)
+#define SW_COUNTER_ENABLE (3)
+#define FBDUMP_CONTROL_RESIZE_FACTOR (4)
+
+void _mali_profiling_control(u32 action, u32 value);
+
+u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values);
+
+int _mali_profiling_set_event(u32 counter_id, s32 event_id);
+
+u32 _mali_profiling_get_api_version(void);
+
+void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MALI_UTGARD_PROFILING_GATOR_API_H__ */
diff --git a/drivers/gator/mali_t6xx.mk b/drivers/gator/mali_t6xx.mk
new file mode 100644
index 000000000000..fa7571ded17b
--- /dev/null
+++ b/drivers/gator/mali_t6xx.mk
@@ -0,0 +1,39 @@
+# Defines for Mali-T6xx driver
+EXTRA_CFLAGS += -DMALI_USE_UMP=1 \
+ -DMALI_LICENSE_IS_GPL=1 \
+ -DMALI_BASE_TRACK_MEMLEAK=0 \
+ -DMALI_DEBUG=0 \
+ -DMALI_ERROR_INJECT_ON=0 \
+ -DMALI_CUSTOMER_RELEASE=1 \
+ -DMALI_UNIT_TEST=0 \
+ -DMALI_BACKEND_KERNEL=1 \
+ -DMALI_NO_MALI=0
+
+DDK_DIR ?= .
+ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/t6xx),)
+KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase
+OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase/osk
+endif
+
+ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/midgard),)
+KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard
+OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard/osk
+EXTRA_CFLAGS += -DMALI_DIR_MIDGARD=1
+endif
+
+ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/midgard/mali_dd_gator_api.h),)
+EXTRA_CFLAGS += -DMALI_SIMPLE_API=1
+endif
+
+UMP_DIR = $(DDK_DIR)/include/linux
+
+# Include directories in the DDK
+EXTRA_CFLAGS += -I$(KBASE_DIR)/ \
+ -I$(KBASE_DIR)/.. \
+ -I$(OSK_DIR)/.. \
+ -I$(UMP_DIR)/.. \
+ -I$(DDK_DIR)/include \
+ -I$(KBASE_DIR)/osk/src/linux/include \
+ -I$(KBASE_DIR)/platform_dummy \
+ -I$(KBASE_DIR)/src
+
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 84b49cfb81a8..556cbab64167 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -235,6 +235,18 @@ static struct irq_chip pl061_irqchip = {
.irq_set_type = pl061_irq_type,
};
+/* Parse gpio base from DT */
+static int pl061_parse_gpio_base(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ int ret, id;
+
+ id = of_alias_get_id(np, "gpio");
+ if (id < 0)
+ return id;
+ return (id * PL061_GPIO_NR);
+}
+
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
@@ -254,7 +266,7 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
return -ENODEV;
}
} else {
- chip->gc.base = -1;
+ chip->gc.base = pl061_parse_gpio_base(dev);
irq_base = 0;
}
@@ -264,8 +276,11 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
spin_lock_init(&chip->lock);
- chip->gc.request = pl061_gpio_request;
- chip->gc.free = pl061_gpio_free;
+ /* Hook the request()/free() for pinctrl operation */
+ if (of_get_property(dev->of_node, "gpio-ranges", NULL)) {
+ chip->gc.request = pl061_gpio_request;
+ chip->gc.free = pl061_gpio_free;
+ }
chip->gc.direction_input = pl061_direction_input;
chip->gc.direction_output = pl061_direction_output;
chip->gc.get = pl061_get_value;
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index 3810da47043f..73267713cabe 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -47,10 +47,10 @@ int samsung_gpio_setpull_updown(struct samsung_gpio_chip *chip,
int shift = off * 2;
u32 pup;
- pup = __raw_readl(reg);
+ pup = readl_relaxed(reg);
pup &= ~(3 << shift);
pup |= pull << shift;
- __raw_writel(pup, reg);
+ writel_relaxed(pup, reg);
return 0;
}
@@ -60,7 +60,7 @@ samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip,
{
void __iomem *reg = chip->base + 0x08;
int shift = off * 2;
- u32 pup = __raw_readl(reg);
+ u32 pup = readl_relaxed(reg);
pup >>= shift;
pup &= 0x3;
@@ -113,7 +113,7 @@ static int s3c24xx_gpio_setpull_1(struct samsung_gpio_chip *chip,
samsung_gpio_pull_t updown)
{
void __iomem *reg = chip->base + 0x08;
- u32 pup = __raw_readl(reg);
+ u32 pup = readl_relaxed(reg);
if (pull == updown)
pup &= ~(1 << off);
@@ -122,7 +122,7 @@ static int s3c24xx_gpio_setpull_1(struct samsung_gpio_chip *chip,
else
return -EINVAL;
- __raw_writel(pup, reg);
+ writel_relaxed(pup, reg);
return 0;
}
@@ -131,7 +131,7 @@ static samsung_gpio_pull_t s3c24xx_gpio_getpull_1(struct samsung_gpio_chip *chip
samsung_gpio_pull_t updown)
{
void __iomem *reg = chip->base + 0x08;
- u32 pup = __raw_readl(reg);
+ u32 pup = readl_relaxed(reg);
pup &= (1 << off);
return pup ? S3C_GPIO_PULL_NONE : updown;
@@ -190,10 +190,10 @@ static int samsung_gpio_setcfg_2bit(struct samsung_gpio_chip *chip,
cfg <<= shift;
}
- con = __raw_readl(reg);
+ con = readl_relaxed(reg);
con &= ~(0x3 << shift);
con |= cfg;
- __raw_writel(con, reg);
+ writel_relaxed(con, reg);
return 0;
}
@@ -213,7 +213,7 @@ static unsigned int samsung_gpio_getcfg_2bit(struct samsung_gpio_chip *chip,
{
u32 con;
- con = __raw_readl(chip->base);
+ con = readl_relaxed(chip->base);
con >>= off * 2;
con &= 3;
@@ -253,10 +253,10 @@ static int samsung_gpio_setcfg_4bit(struct samsung_gpio_chip *chip,
cfg <<= shift;
}
- con = __raw_readl(reg);
+ con = readl_relaxed(reg);
con &= ~(0xf << shift);
con |= cfg;
- __raw_writel(con, reg);
+ writel_relaxed(con, reg);
return 0;
}
@@ -283,7 +283,7 @@ static unsigned samsung_gpio_getcfg_4bit(struct samsung_gpio_chip *chip,
if (off < 8 && chip->chip.ngpio > 8)
reg -= 4;
- con = __raw_readl(reg);
+ con = readl_relaxed(reg);
con >>= shift;
con &= 0xf;
@@ -321,10 +321,10 @@ static int s3c24xx_gpio_setcfg_abank(struct samsung_gpio_chip *chip,
cfg <<= shift;
}
- con = __raw_readl(reg);
+ con = readl_relaxed(reg);
con &= ~(0x1 << shift);
con |= cfg;
- __raw_writel(con, reg);
+ writel_relaxed(con, reg);
return 0;
}
@@ -346,7 +346,7 @@ static unsigned s3c24xx_gpio_getcfg_abank(struct samsung_gpio_chip *chip,
{
u32 con;
- con = __raw_readl(chip->base);
+ con = readl_relaxed(chip->base);
con >>= off;
con &= 1;
con++;
@@ -437,10 +437,10 @@ static int samsung_gpiolib_2bit_input(struct gpio_chip *chip, unsigned offset)
samsung_gpio_lock(ourchip, flags);
- con = __raw_readl(base + 0x00);
+ con = readl_relaxed(base + 0x00);
con &= ~(3 << (offset * 2));
- __raw_writel(con, base + 0x00);
+ writel_relaxed(con, base + 0x00);
samsung_gpio_unlock(ourchip, flags);
return 0;
@@ -457,18 +457,18 @@ static int samsung_gpiolib_2bit_output(struct gpio_chip *chip,
samsung_gpio_lock(ourchip, flags);
- dat = __raw_readl(base + 0x04);
+ dat = readl_relaxed(base + 0x04);
dat &= ~(1 << offset);
if (value)
dat |= 1 << offset;
- __raw_writel(dat, base + 0x04);
+ writel_relaxed(dat, base + 0x04);
- con = __raw_readl(base + 0x00);
+ con = readl_relaxed(base + 0x00);
con &= ~(3 << (offset * 2));
con |= 1 << (offset * 2);
- __raw_writel(con, base + 0x00);
- __raw_writel(dat, base + 0x04);
+ writel_relaxed(con, base + 0x00);
+ writel_relaxed(dat, base + 0x04);
samsung_gpio_unlock(ourchip, flags);
return 0;
@@ -497,12 +497,12 @@ static int samsung_gpiolib_4bit_input(struct gpio_chip *chip,
void __iomem *base = ourchip->base;
unsigned long con;
- con = __raw_readl(base + GPIOCON_OFF);
+ con = readl_relaxed(base + GPIOCON_OFF);
if (ourchip->bitmap_gpio_int & BIT(offset))
con |= 0xf << con_4bit_shift(offset);
else
con &= ~(0xf << con_4bit_shift(offset));
- __raw_writel(con, base + GPIOCON_OFF);
+ writel_relaxed(con, base + GPIOCON_OFF);
pr_debug("%s: %p: CON now %08lx\n", __func__, base, con);
@@ -517,20 +517,20 @@ static int samsung_gpiolib_4bit_output(struct gpio_chip *chip,
unsigned long con;
unsigned long dat;
- con = __raw_readl(base + GPIOCON_OFF);
+ con = readl_relaxed(base + GPIOCON_OFF);
con &= ~(0xf << con_4bit_shift(offset));
con |= 0x1 << con_4bit_shift(offset);
- dat = __raw_readl(base + GPIODAT_OFF);
+ dat = readl_relaxed(base + GPIODAT_OFF);
if (value)
dat |= 1 << offset;
else
dat &= ~(1 << offset);
- __raw_writel(dat, base + GPIODAT_OFF);
- __raw_writel(con, base + GPIOCON_OFF);
- __raw_writel(dat, base + GPIODAT_OFF);
+ writel_relaxed(dat, base + GPIODAT_OFF);
+ writel_relaxed(con, base + GPIOCON_OFF);
+ writel_relaxed(dat, base + GPIODAT_OFF);
pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
@@ -572,9 +572,9 @@ static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip,
else
regcon -= 4;
- con = __raw_readl(regcon);
+ con = readl_relaxed(regcon);
con &= ~(0xf << con_4bit_shift(offset));
- __raw_writel(con, regcon);
+ writel_relaxed(con, regcon);
pr_debug("%s: %p: CON %08lx\n", __func__, base, con);
@@ -596,20 +596,20 @@ static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip,
else
regcon -= 4;
- con = __raw_readl(regcon);
+ con = readl_relaxed(regcon);
con &= ~(0xf << con_4bit_shift(con_offset));
con |= 0x1 << con_4bit_shift(con_offset);
- dat = __raw_readl(base + GPIODAT_OFF);
+ dat = readl_relaxed(base + GPIODAT_OFF);
if (value)
dat |= 1 << offset;
else
dat &= ~(1 << offset);
- __raw_writel(dat, base + GPIODAT_OFF);
- __raw_writel(con, regcon);
- __raw_writel(dat, base + GPIODAT_OFF);
+ writel_relaxed(dat, base + GPIODAT_OFF);
+ writel_relaxed(con, regcon);
+ writel_relaxed(dat, base + GPIODAT_OFF);
pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
@@ -635,19 +635,19 @@ static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip,
local_irq_save(flags);
- con = __raw_readl(base + 0x00);
- dat = __raw_readl(base + 0x04);
+ con = readl_relaxed(base + 0x00);
+ dat = readl_relaxed(base + 0x04);
dat &= ~(1 << offset);
if (value)
dat |= 1 << offset;
- __raw_writel(dat, base + 0x04);
+ writel_relaxed(dat, base + 0x04);
con &= ~(1 << offset);
- __raw_writel(con, base + 0x00);
- __raw_writel(dat, base + 0x04);
+ writel_relaxed(con, base + 0x00);
+ writel_relaxed(dat, base + 0x04);
local_irq_restore(flags);
return 0;
@@ -664,11 +664,11 @@ static void samsung_gpiolib_set(struct gpio_chip *chip,
samsung_gpio_lock(ourchip, flags);
- dat = __raw_readl(base + 0x04);
+ dat = readl_relaxed(base + 0x04);
dat &= ~(1 << offset);
if (value)
dat |= 1 << offset;
- __raw_writel(dat, base + 0x04);
+ writel_relaxed(dat, base + 0x04);
samsung_gpio_unlock(ourchip, flags);
}
@@ -678,7 +678,7 @@ static int samsung_gpiolib_get(struct gpio_chip *chip, unsigned offset)
struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
unsigned long val;
- val = __raw_readl(ourchip->base + 0x04);
+ val = readl_relaxed(ourchip->base + 0x04);
val >>= offset;
val &= 1;
@@ -1325,7 +1325,7 @@ s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin)
shift = off * 2;
reg = chip->base + 0x0C;
- drvstr = __raw_readl(reg);
+ drvstr = readl_relaxed(reg);
drvstr = drvstr >> shift;
drvstr &= 0x3;
@@ -1348,11 +1348,11 @@ int s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr)
shift = off * 2;
reg = chip->base + 0x0C;
- tmp = __raw_readl(reg);
+ tmp = readl_relaxed(reg);
tmp &= ~(0x3 << shift);
tmp |= drvstr << shift;
- __raw_writel(tmp, reg);
+ writel_relaxed(tmp, reg);
return 0;
}
@@ -1366,10 +1366,10 @@ unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change)
unsigned long misccr;
local_irq_save(flags);
- misccr = __raw_readl(S3C24XX_MISCCR);
+ misccr = readl_relaxed(S3C24XX_MISCCR);
misccr &= ~clear;
misccr ^= change;
- __raw_writel(misccr, S3C24XX_MISCCR);
+ writel_relaxed(misccr, S3C24XX_MISCCR);
local_irq_restore(flags);
return misccr;
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index 4f3c7eb2d37d..cac48cff1717 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -1055,9 +1055,9 @@ static void exynos_dp_phy_init(struct exynos_dp_device *dp)
} else if (dp->phy_addr) {
u32 reg;
- reg = __raw_readl(dp->phy_addr);
+ reg = readl_relaxed(dp->phy_addr);
reg |= dp->enable_mask;
- __raw_writel(reg, dp->phy_addr);
+ writel_relaxed(reg, dp->phy_addr);
}
}
@@ -1068,9 +1068,9 @@ static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
} else if (dp->phy_addr) {
u32 reg;
- reg = __raw_readl(dp->phy_addr);
+ reg = readl_relaxed(dp->phy_addr);
reg &= ~(dp->enable_mask);
- __raw_writel(reg, dp->phy_addr);
+ writel_relaxed(reg, dp->phy_addr);
}
}
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 2619f7f4517a..35341d6016e4 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1432,8 +1432,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
* UGCI) cram a lot of unrelated inputs into the
* same interface. */
hidinput->report = report;
- if (drv->input_configured)
- drv->input_configured(hid, hidinput);
+ if (drv->input_configured &&
+ drv->input_configured(hid, hidinput))
+ goto out_cleanup;
if (input_register_device(hidinput->input))
goto out_cleanup;
hidinput = NULL;
@@ -1454,8 +1455,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
if (hidinput) {
- if (drv->input_configured)
- drv->input_configured(hid, hidinput);
+ if (drv->input_configured &&
+ drv->input_configured(hid, hidinput))
+ goto out_cleanup;
if (input_register_device(hidinput->input))
goto out_cleanup;
}
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 51e25b9407f2..b2cb437e4340 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -372,6 +372,16 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
}
+ /* Only map fields from TouchScreen or TouchPad collections.
+ * We need to ignore fields that belong to other collections
+ * such as Mouse that might have the same GenericDesktop usages. */
+ if (field->application == HID_DG_TOUCHSCREEN)
+ set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
+ else if (field->application == HID_DG_TOUCHPAD)
+ set_bit(INPUT_PROP_POINTER, hi->input->propbit);
+ else
+ return 0;
+
if (usage->usage_index)
prev_usage = &field->usage[usage->usage_index - 1];
@@ -701,12 +711,13 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
mt_sync_frame(td, report->field[0]->hidinput->input);
}
-static void mt_touch_input_configured(struct hid_device *hdev,
+static int mt_touch_input_configured(struct hid_device *hdev,
struct hid_input *hi)
{
struct mt_device *td = hid_get_drvdata(hdev);
struct mt_class *cls = &td->mtclass;
struct input_dev *input = hi->input;
+ int ret;
if (!td->maxcontacts)
td->maxcontacts = MT_DEFAULT_MAXCONTACT;
@@ -721,9 +732,12 @@ static void mt_touch_input_configured(struct hid_device *hdev,
if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
td->mt_flags |= INPUT_MT_DROP_UNUSED;
- input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
+ ret = input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
+ if (ret)
+ return ret;
td->mt_flags = 0;
+ return 0;
}
static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -877,15 +891,16 @@ static void mt_post_parse(struct mt_device *td)
cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
}
-static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct mt_device *td = hid_get_drvdata(hdev);
char *name;
const char *suffix = NULL;
+ int ret = 0;
struct hid_field *field = hi->report->field[0];
if (hi->report->id == td->mt_report_id)
- mt_touch_input_configured(hdev, hi);
+ ret = mt_touch_input_configured(hdev, hi);
/*
* some egalax touchscreens have "application == HID_DG_TOUCHSCREEN"
@@ -936,6 +951,7 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
hi->input->name = name;
}
}
+ return ret;
}
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 2ac87fa3058d..fd5c460be4dc 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -77,6 +77,22 @@ config I2C_AMD8111
This driver can also be built as a module. If so, the module
will be called i2c-amd8111.
+config I2C_HIX5HD2
+ tristate "Hix5hd2 high-speed I2C driver"
+ help
+ Say Y here to include support for high-speed I2C controller in the
+ Hisilicon based 3716V200 SoCs. The IP module is private for Hisilicon,
+ It be use on the Hisilicon media chipset from 2006 and will continue to use
+ it.Specifically, the following versions of the chipset are supported:
+ Hi3716CV200 (support six I2C module)
+ Hi3719CV100 (support six I2C module)
+ Hi3718CV100 (support six I2C module)
+ Hi3719MV100 (support two I2C module)
+ Hi3718MV100 (support two I2C module)
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-hix5hd2.
+
config I2C_I801
tristate "Intel 82801 (ICH/PCH)"
depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 49bf07e5ef4d..9739938b1484 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
+obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
diff --git a/drivers/i2c/busses/i2c-hix5hd2.c b/drivers/i2c/busses/i2c-hix5hd2.c
new file mode 100644
index 000000000000..13f597ba5cc7
--- /dev/null
+++ b/drivers/i2c/busses/i2c-hix5hd2.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ *
+ * Now only support 7 bit address.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+/* Register Map */
+#define HIX5I2C_CTRL 0x00
+#define HIX5I2C_COM 0x04
+#define HIX5I2C_ICR 0x08
+#define HIX5I2C_SR 0x0c
+#define HIX5I2C_SCL_H 0x10
+#define HIX5I2C_SCL_L 0x14
+#define HIX5I2C_TXR 0x18
+#define HIX5I2C_RXR 0x1c
+
+/* I2C_CTRL_REG */
+#define I2C_ENABLE BIT(8)
+#define I2C_UNMASK_TOTAL BIT(7)
+#define I2C_UNMASK_START BIT(6)
+#define I2C_UNMASK_END BIT(5)
+#define I2C_UNMASK_SEND BIT(4)
+#define I2C_UNMASK_RECEIVE BIT(3)
+#define I2C_UNMASK_ACK BIT(2)
+#define I2C_UNMASK_ARBITRATE BIT(1)
+#define I2C_UNMASK_OVER BIT(0)
+#define I2C_UNMASK_ALL (I2C_UNMASK_ACK | I2C_UNMASK_OVER)
+
+/* I2C_COM_REG */
+#define I2C_NO_ACK BIT(4)
+#define I2C_START BIT(3)
+#define I2C_READ BIT(2)
+#define I2C_WRITE BIT(1)
+#define I2C_STOP BIT(0)
+
+/* I2C_ICR_REG */
+#define I2C_CLEAR_START BIT(6)
+#define I2C_CLEAR_END BIT(5)
+#define I2C_CLEAR_SEND BIT(4)
+#define I2C_CLEAR_RECEIVE BIT(3)
+#define I2C_CLEAR_ACK BIT(2)
+#define I2C_CLEAR_ARBITRATE BIT(1)
+#define I2C_CLEAR_OVER BIT(0)
+#define I2C_CLEAR_ALL (I2C_CLEAR_START | I2C_CLEAR_END | \
+ I2C_CLEAR_SEND | I2C_CLEAR_RECEIVE | \
+ I2C_CLEAR_ACK | I2C_CLEAR_ARBITRATE | \
+ I2C_CLEAR_OVER)
+
+/* I2C_SR_REG */
+#define I2C_BUSY BIT(7)
+#define I2C_START_INTR BIT(6)
+#define I2C_END_INTR BIT(5)
+#define I2C_SEND_INTR BIT(4)
+#define I2C_RECEIVE_INTR BIT(3)
+#define I2C_ACK_INTR BIT(2)
+#define I2C_ARBITRATE_INTR BIT(1)
+#define I2C_OVER_INTR BIT(0)
+
+/*
+ * Controller operating frequency, timing values for operation
+ * are calculated against this frequency
+ */
+#define HIX5I2C_NS_TX_CLOCK 100000 /* 100k */
+#define HIX5I2C_HS_TX_CLOCK 400000 /* 400k */
+
+#define HIX5I2C_READ_OPERATION 0x01
+#define HIX5I2C_WRITE_OPERATION 0xfe
+
+enum hix5hd2_i2c_state {
+ HIX5I2C_STAT_RW_ERR = -1,
+ HIX5I2C_STAT_INIT,
+ HIX5I2C_STAT_RW,
+ HIX5I2C_STAT_SND_STOP,
+ HIX5I2C_STAT_RW_SUCCESS,
+};
+
+enum hix5hd2_i2c_speed {
+ HIX5I2C_NORMAL_SPD, /* Fast speed upto 1Mbps */
+ HIX5I2C_HIG_SPD, /* High speed upto 3.4Mbps */
+};
+
+struct hix5hd2_i2c {
+ struct i2c_adapter adap;
+ struct i2c_msg *msg;
+ unsigned int msg_ptr; /* msg index */
+ unsigned int len; /* msg length */
+ int stop;
+ struct completion msg_complete;
+
+ unsigned int irq;
+ void __iomem *regs;
+ struct clk *clk;
+ struct device *dev;
+ spinlock_t lock; /* IRQ synchronization */
+
+ int err;
+ enum hix5hd2_i2c_state state;
+ enum hix5hd2_i2c_speed speed_mode;
+
+ /* Controller frequency */
+ unsigned int s_clock;
+};
+
+static u32 hix5hd2_i2c_clr_pend_irq(struct hix5hd2_i2c *i2c)
+{
+ u32 val = readl_relaxed(i2c->regs + HIX5I2C_SR);
+
+ writel_relaxed(val, i2c->regs + HIX5I2C_ICR);
+
+ return val;
+}
+
+static void hix5hd2_i2c_clr_all_irq(struct hix5hd2_i2c *i2c)
+{
+ writel_relaxed(I2C_CLEAR_ALL, i2c->regs + HIX5I2C_ICR);
+}
+
+static void hix5hd2_i2c_disable_irq(struct hix5hd2_i2c *i2c)
+{
+ writel_relaxed(0, i2c->regs + HIX5I2C_CTRL);
+}
+
+static void hix5hd2_i2c_enable_irq(struct hix5hd2_i2c *i2c)
+{
+ writel_relaxed(I2C_ENABLE | I2C_UNMASK_TOTAL | I2C_UNMASK_ALL,
+ i2c->regs + HIX5I2C_CTRL);
+}
+
+static void hix5hd2_i2c_drv_setrate(struct hix5hd2_i2c *i2c)
+{
+ u32 rate, val;
+ u32 sclh, scll, sysclock;
+
+ /* close all i2c interrupt */
+ val = readl_relaxed(i2c->regs + HIX5I2C_CTRL);
+ writel_relaxed(val & (~I2C_UNMASK_TOTAL), i2c->regs + HIX5I2C_CTRL);
+
+ rate = i2c->s_clock;
+ sysclock = clk_get_rate(i2c->clk);
+ sclh = (sysclock / (rate * 2)) / 2 - 1;
+ writel_relaxed(sclh, i2c->regs + HIX5I2C_SCL_H);
+ scll = (sysclock / (rate * 2)) / 2 - 1;
+ writel_relaxed(scll, i2c->regs + HIX5I2C_SCL_L);
+
+ /* restore original interrupt*/
+ writel_relaxed(val, i2c->regs + HIX5I2C_CTRL);
+
+ dev_dbg(i2c->dev, "%s: sysclock=%d, rate=%d, sclh=%d, scll=%d\n",
+ __func__, sysclock, rate, sclh, scll);
+}
+
+static void hix5hd2_i2c_init(struct hix5hd2_i2c *i2c)
+{
+ hix5hd2_i2c_disable_irq(i2c);
+ hix5hd2_i2c_drv_setrate(i2c);
+ hix5hd2_i2c_clr_all_irq(i2c);
+ hix5hd2_i2c_enable_irq(i2c);
+}
+
+static void hix5hd2_i2c_reset(struct hix5hd2_i2c *i2c)
+{
+ clk_disable_unprepare(i2c->clk);
+ msleep(20);
+ clk_prepare_enable(i2c->clk);
+ hix5hd2_i2c_init(i2c);
+}
+
+static int hix5hd2_i2c_wait_bus_idle(struct hix5hd2_i2c *i2c)
+{
+ unsigned long stop_time;
+ u32 int_status;
+
+ /* wait for 100 milli seconds for the bus to be idle */
+ stop_time = jiffies + msecs_to_jiffies(100);
+ do {
+ int_status = hix5hd2_i2c_clr_pend_irq(i2c);
+ if (!(int_status & I2C_BUSY))
+ return 0;
+
+ usleep_range(50, 200);
+ } while (time_before(jiffies, stop_time));
+
+ return -EBUSY;
+}
+
+static void hix5hd2_rw_over(struct hix5hd2_i2c *i2c)
+{
+ if (HIX5I2C_STAT_SND_STOP == i2c->state)
+ dev_dbg(i2c->dev, "%s: rw and send stop over\n", __func__);
+ else
+ dev_dbg(i2c->dev, "%s: have not data to send\n", __func__);
+
+ i2c->state = HIX5I2C_STAT_RW_SUCCESS;
+ i2c->err = 0;
+}
+
+static void hix5hd2_rw_handle_stop(struct hix5hd2_i2c *i2c)
+{
+ if (i2c->stop) {
+ i2c->state = HIX5I2C_STAT_SND_STOP;
+ writel_relaxed(I2C_STOP, i2c->regs + HIX5I2C_COM);
+ } else {
+ hix5hd2_rw_over(i2c);
+ }
+}
+
+static void hix5hd2_read_handle(struct hix5hd2_i2c *i2c)
+{
+ if (1 == i2c->len) {
+ /* the last byte don't need send ACK */
+ writel_relaxed(I2C_READ | I2C_NO_ACK, i2c->regs + HIX5I2C_COM);
+ } else if (i2c->len > 1) {
+ /* if i2c master receive data will send ACK */
+ writel_relaxed(I2C_READ, i2c->regs + HIX5I2C_COM);
+ } else {
+ hix5hd2_rw_handle_stop(i2c);
+ }
+}
+
+static void hix5hd2_write_handle(struct hix5hd2_i2c *i2c)
+{
+ u8 data;
+
+ if (i2c->len > 0) {
+ data = i2c->msg->buf[i2c->msg_ptr++];
+ writel_relaxed(data, i2c->regs + HIX5I2C_TXR);
+ writel_relaxed(I2C_WRITE, i2c->regs + HIX5I2C_COM);
+ } else {
+ hix5hd2_rw_handle_stop(i2c);
+ }
+}
+
+static int hix5hd2_rw_preprocess(struct hix5hd2_i2c *i2c)
+{
+ u8 data;
+
+ if (HIX5I2C_STAT_INIT == i2c->state) {
+ i2c->state = HIX5I2C_STAT_RW;
+ } else if (HIX5I2C_STAT_RW == i2c->state) {
+ if (i2c->msg->flags & I2C_M_RD) {
+ data = readl_relaxed(i2c->regs + HIX5I2C_RXR);
+ i2c->msg->buf[i2c->msg_ptr++] = data;
+ }
+ i2c->len--;
+ } else {
+ dev_dbg(i2c->dev, "%s: error: i2c->state = %d, len = %d\n",
+ __func__, i2c->state, i2c->len);
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static irqreturn_t hix5hd2_i2c_irq(int irqno, void *dev_id)
+{
+ struct hix5hd2_i2c *i2c = dev_id;
+ u32 int_status;
+ int ret;
+
+ spin_lock(&i2c->lock);
+
+ int_status = hix5hd2_i2c_clr_pend_irq(i2c);
+
+ /* handle error */
+ if (int_status & I2C_ARBITRATE_INTR) {
+ /*Bus error */
+ dev_dbg(i2c->dev, "ARB bus loss\n");
+ i2c->err = -ENXIO;
+ i2c->state = HIX5I2C_STAT_RW_ERR;
+ goto stop;
+ } else if (int_status & I2C_ACK_INTR) {
+ /* ack error */
+ dev_dbg(i2c->dev, "No ACK from device\n");
+ i2c->err = -ENXIO;
+ i2c->state = HIX5I2C_STAT_RW_ERR;
+ goto stop;
+ }
+
+ if (int_status & I2C_OVER_INTR) {
+ if (i2c->len > 0) {
+ ret = hix5hd2_rw_preprocess(i2c);
+ if (ret) {
+ i2c->err = ret;
+ i2c->state = HIX5I2C_STAT_RW_ERR;
+ goto stop;
+ }
+ if (i2c->msg->flags & I2C_M_RD)
+ hix5hd2_read_handle(i2c);
+ else
+ hix5hd2_write_handle(i2c);
+ } else {
+ hix5hd2_rw_over(i2c);
+ }
+ }
+
+stop:
+ if ((HIX5I2C_STAT_RW_SUCCESS == i2c->state &&
+ i2c->msg->len == i2c->msg_ptr) ||
+ (HIX5I2C_STAT_RW_ERR == i2c->state)) {
+ hix5hd2_i2c_disable_irq(i2c);
+ hix5hd2_i2c_clr_pend_irq(i2c);
+ complete(&i2c->msg_complete);
+ }
+
+ spin_unlock(&i2c->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void hix5hd2_i2c_message_start(struct hix5hd2_i2c *i2c, int stop)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c->lock, flags);
+ hix5hd2_i2c_clr_all_irq(i2c);
+ hix5hd2_i2c_enable_irq(i2c);
+
+ if (i2c->msg->flags & I2C_M_RD)
+ writel_relaxed(i2c->msg->addr | HIX5I2C_READ_OPERATION,
+ i2c->regs + HIX5I2C_TXR);
+ else
+ writel_relaxed(i2c->msg->addr & HIX5I2C_WRITE_OPERATION,
+ i2c->regs + HIX5I2C_TXR);
+
+ writel_relaxed(I2C_WRITE | I2C_START, i2c->regs + HIX5I2C_COM);
+ spin_unlock_irqrestore(&i2c->lock, flags);
+}
+
+static int hix5hd2_i2c_xfer_msg(struct hix5hd2_i2c *i2c,
+ struct i2c_msg *msgs, int stop)
+{
+ unsigned long timeout;
+ int ret;
+
+ i2c->msg = msgs;
+ i2c->msg_ptr = 0;
+ i2c->len = i2c->msg->len;
+ i2c->stop = stop;
+ i2c->err = 0;
+ i2c->state = HIX5I2C_STAT_INIT;
+
+ reinit_completion(&i2c->msg_complete);
+ hix5hd2_i2c_message_start(i2c, stop);
+
+ timeout = wait_for_completion_timeout(&i2c->msg_complete,
+ i2c->adap.timeout);
+ if (timeout == 0) {
+ i2c->state = HIX5I2C_STAT_RW_ERR;
+ i2c->err = -ETIMEDOUT;
+ dev_warn(i2c->dev, "%s timeout=%d\n",
+ msgs->flags & I2C_M_RD ? "rx" : "tx",
+ i2c->adap.timeout);
+ }
+ ret = i2c->state;
+
+ /*
+ * If this is the last message to be transfered (stop == 1)
+ * Then check if the bus can be brought back to idle.
+ */
+ if (i2c->state == HIX5I2C_STAT_RW_SUCCESS && stop)
+ ret = hix5hd2_i2c_wait_bus_idle(i2c);
+
+ if (ret < 0)
+ hix5hd2_i2c_reset(i2c);
+
+ return i2c->err;
+}
+
+static int hix5hd2_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct hix5hd2_i2c *i2c = i2c_get_adapdata(adap);
+ int i, ret, stop;
+
+ pm_runtime_get_sync(i2c->dev);
+
+ for (i = 0; i < num; i++, msgs++) {
+ stop = (i == num - 1);
+ ret = hix5hd2_i2c_xfer_msg(i2c, msgs, stop);
+ if (ret < 0)
+ goto out;
+ }
+
+ if (i == num) {
+ ret = num;
+ } else {
+ /* Only one message, cannot access the device */
+ if (i == 1)
+ ret = -EREMOTEIO;
+ else
+ ret = i;
+
+ dev_warn(i2c->dev, "xfer message failed\n");
+ }
+
+out:
+ pm_runtime_mark_last_busy(i2c->dev);
+ pm_runtime_put_autosuspend(i2c->dev);
+ return ret;
+}
+
+static u32 hix5hd2_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm hix5hd2_i2c_algorithm = {
+ .master_xfer = hix5hd2_i2c_xfer,
+ .functionality = hix5hd2_i2c_func,
+};
+
+static int hix5hd2_i2c_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct hix5hd2_i2c *i2c;
+ struct resource *mem;
+ unsigned int op_clock;
+ int ret;
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(struct hix5hd2_i2c), GFP_KERNEL);
+ if (!i2c) {
+ dev_err(&pdev->dev, "no memory for state\n");
+ return -ENOMEM;
+ }
+
+ if (of_property_read_u32(np, "clock-frequency", &op_clock)) {
+ /* use default value */
+ i2c->speed_mode = HIX5I2C_HIG_SPD;
+ i2c->s_clock = HIX5I2C_HS_TX_CLOCK;
+ } else {
+ if (op_clock >= HIX5I2C_HS_TX_CLOCK) {
+ i2c->speed_mode = HIX5I2C_HIG_SPD;
+ i2c->s_clock = HIX5I2C_HS_TX_CLOCK;
+ } else {
+ i2c->speed_mode = HIX5I2C_NORMAL_SPD;
+ i2c->s_clock = op_clock;
+ }
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(i2c->regs))
+ return PTR_ERR(i2c->regs);
+
+ i2c->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ return -ENOENT;
+ }
+ clk_prepare_enable(i2c->clk);
+
+ i2c->irq = platform_get_irq(pdev, 0);
+ if (i2c->irq <= 0) {
+ dev_err(&pdev->dev, "cannot find HS-I2C IRQ\n");
+ ret = -EINVAL;
+ goto err_clk;
+ }
+
+ strlcpy(i2c->adap.name, "hix5hd2-i2c", sizeof(i2c->adap.name));
+ i2c->dev = &pdev->dev;
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.algo = &hix5hd2_i2c_algorithm;
+ i2c->adap.retries = 3;
+ i2c->adap.dev.of_node = np;
+ i2c->adap.algo_data = i2c;
+ i2c->adap.dev.parent = &pdev->dev;
+ i2c_set_adapdata(&i2c->adap, i2c);
+ platform_set_drvdata(pdev, i2c);
+ spin_lock_init(&i2c->lock);
+ init_completion(&i2c->msg_complete);
+
+ hix5hd2_i2c_init(i2c);
+
+ ret = devm_request_irq(&pdev->dev, i2c->irq, hix5hd2_i2c_irq,
+ IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ dev_name(&pdev->dev), i2c);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n", i2c->irq);
+ goto err_clk;
+ }
+
+ ret = i2c_add_adapter(&i2c->adap);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add bus to i2c core\n");
+ goto err_clk;
+ }
+
+ pm_suspend_ignore_children(&pdev->dev, true);
+ pm_runtime_set_autosuspend_delay(i2c->dev, MSEC_PER_SEC);
+ pm_runtime_use_autosuspend(i2c->dev);
+ pm_runtime_set_active(i2c->dev);
+ pm_runtime_enable(i2c->dev);
+
+ return ret;
+
+err_clk:
+ clk_disable_unprepare(i2c->clk);
+ return ret;
+}
+
+static int hix5hd2_i2c_remove(struct platform_device *pdev)
+{
+ struct hix5hd2_i2c *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adap);
+ pm_runtime_disable(i2c->dev);
+ pm_runtime_set_suspended(i2c->dev);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int hix5hd2_i2c_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct hix5hd2_i2c *i2c = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(i2c->clk);
+
+ return 0;
+}
+
+static int hix5hd2_i2c_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct hix5hd2_i2c *i2c = platform_get_drvdata(pdev);
+
+ clk_prepare_enable(i2c->clk);
+ hix5hd2_i2c_init(i2c);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops hix5hd2_i2c_pm_ops = {
+ SET_PM_RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend,
+ hix5hd2_i2c_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id hix5hd2_i2c_match[] = {
+ { .compatible = "hisilicon,hix5hd2-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hix5hd2_i2c_match);
+
+static struct platform_driver hix5hd2_i2c_driver = {
+ .probe = hix5hd2_i2c_probe,
+ .remove = hix5hd2_i2c_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "hix5hd2-i2c",
+ .pm = &hix5hd2_i2c_pm_ops,
+ .of_match_table = hix5hd2_i2c_match,
+ },
+};
+
+module_platform_driver(hix5hd2_i2c_driver);
+
+MODULE_DESCRIPTION("Hix5hd2 I2C Bus driver");
+MODULE_AUTHOR("Wei Yan <sledge.yanwei@huawei.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:i2c-hix5hd2");
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index 0c1e37e3120a..6d4c2d36bbca 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -35,12 +35,12 @@
*/
struct iio_event_interface {
wait_queue_head_t wait;
+ struct mutex read_lock;
DECLARE_KFIFO(det_events, struct iio_event_data, 16);
struct list_head dev_attr_list;
unsigned long flags;
struct attribute_group group;
- struct mutex read_lock;
};
/**
@@ -492,6 +492,7 @@ int iio_device_register_eventset(struct iio_dev *indio_dev)
error_free_setup_event_lines:
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
+ mutex_destroy(&indio_dev->event_interface->read_lock);
kfree(indio_dev->event_interface);
return ret;
}
@@ -516,5 +517,6 @@ void iio_device_unregister_eventset(struct iio_dev *indio_dev)
return;
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
kfree(indio_dev->event_interface->group.attrs);
+ mutex_destroy(&indio_dev->event_interface->read_lock);
kfree(indio_dev->event_interface);
}
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a11ff74a5127..9c13b63ef915 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -174,6 +174,15 @@ config INPUT_APMPOWER
To compile this driver as a module, choose M here: the
module will be called apm-power.
+config INPUT_KEYRESET
+ tristate "Reset key"
+ depends on INPUT
+ ---help---
+ Say Y here if you want to reboot when some keys are pressed;
+
+ To compile this driver as a module, choose M here: the
+ module will be called keyreset.
+
comment "Input Device Drivers"
source "drivers/input/keyboard/Kconfig"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f631497f..191ea43d0800 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index de055451d1af..85c7534a39f4 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -26,6 +26,7 @@
#include <linux/major.h>
#include <linux/device.h>
#include <linux/cdev.h>
+#include <linux/wakelock.h>
#include "input-compat.h"
struct evdev {
@@ -46,6 +47,9 @@ struct evdev_client {
unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
+ struct wake_lock wake_lock;
+ bool use_wake_lock;
+ char name[28];
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
@@ -149,10 +153,14 @@ static void __pass_event(struct evdev_client *client,
client->buffer[client->tail].value = 0;
client->packet_head = client->tail;
+ if (client->use_wake_lock)
+ wake_unlock(&client->wake_lock);
}
if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;
+ if (client->use_wake_lock)
+ wake_lock(&client->wake_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
}
@@ -371,10 +379,14 @@ static int evdev_release(struct inode *inode, struct file *file)
evdev_detach_client(evdev, client);
+ if (client->use_wake_lock)
+ wake_lock_destroy(&client->wake_lock);
+
if (is_vmalloc_addr(client))
vfree(client);
else
kfree(client);
+ kfree(client);
evdev_close_device(evdev);
@@ -407,6 +419,8 @@ static int evdev_open(struct inode *inode, struct file *file)
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
+ snprintf(client->name, sizeof(client->name), "%s-%d",
+ dev_name(&evdev->dev), task_tgid_vnr(current));
client->evdev = evdev;
evdev_attach_client(evdev, client);
@@ -473,6 +487,9 @@ static int evdev_fetch_next_event(struct evdev_client *client,
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= client->bufsize - 1;
+ if (client->use_wake_lock &&
+ client->packet_head == client->tail)
+ wake_unlock(&client->wake_lock);
}
spin_unlock_irq(&client->buffer_lock);
@@ -792,6 +809,11 @@ static int evdev_handle_mt_request(struct input_dev *dev,
return 0;
}
+/*
+ * HACK: disable conflicting EVIOCREVOKE until Android userspace stops using
+ * EVIOCSSUSPENDBLOCK
+ */
+/*
static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
struct file *file)
{
@@ -802,6 +824,36 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
return 0;
}
+*/
+
+static int evdev_enable_suspend_block(struct evdev *evdev,
+ struct evdev_client *client)
+{
+ if (client->use_wake_lock)
+ return 0;
+
+ spin_lock_irq(&client->buffer_lock);
+ wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
+ client->use_wake_lock = true;
+ if (client->packet_head != client->tail)
+ wake_lock(&client->wake_lock);
+ spin_unlock_irq(&client->buffer_lock);
+ return 0;
+}
+
+static int evdev_disable_suspend_block(struct evdev *evdev,
+ struct evdev_client *client)
+{
+ if (!client->use_wake_lock)
+ return 0;
+
+ spin_lock_irq(&client->buffer_lock);
+ client->use_wake_lock = false;
+ wake_lock_destroy(&client->wake_lock);
+ spin_unlock_irq(&client->buffer_lock);
+
+ return 0;
+}
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
@@ -865,12 +917,17 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
else
return evdev_ungrab(evdev, client);
+ /*
+ * HACK: disable conflicting EVIOCREVOKE until Android userspace stops
+ * using EVIOCSSUSPENDBLOCK
+ */
+ /*
case EVIOCREVOKE:
if (p)
return -EINVAL;
else
return evdev_revoke(evdev, client, file);
-
+ */
case EVIOCSCLOCKID:
if (copy_from_user(&i, p, sizeof(unsigned int)))
return -EFAULT;
@@ -890,6 +947,15 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
case EVIOCSKEYCODE_V2:
return evdev_handle_set_keycode_v2(dev, p);
+
+ case EVIOCGSUSPENDBLOCK:
+ return put_user(client->use_wake_lock, ip);
+
+ case EVIOCSSUSPENDBLOCK:
+ if (p)
+ return evdev_enable_suspend_block(evdev, client);
+ else
+ return evdev_disable_suspend_block(evdev, client);
}
size = _IOC_SIZE(cmd);
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index 024b7bdffe5b..542d75a542d6 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -87,26 +87,26 @@ struct omap4_keypad {
static int kbd_readl(struct omap4_keypad *keypad_data, u32 offset)
{
- return __raw_readl(keypad_data->base +
+ return readl_relaxed(keypad_data->base +
keypad_data->reg_offset + offset);
}
static void kbd_writel(struct omap4_keypad *keypad_data, u32 offset, u32 value)
{
- __raw_writel(value,
+ writel_relaxed(value,
keypad_data->base + keypad_data->reg_offset + offset);
}
static int kbd_read_irqreg(struct omap4_keypad *keypad_data, u32 offset)
{
- return __raw_readl(keypad_data->base +
+ return readl_relaxed(keypad_data->base +
keypad_data->irqreg_offset + offset);
}
static void kbd_write_irqreg(struct omap4_keypad *keypad_data,
u32 offset, u32 value)
{
- __raw_writel(value,
+ writel_relaxed(value,
keypad_data->base + keypad_data->irqreg_offset + offset);
}
@@ -293,7 +293,7 @@ static int omap4_keypad_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
goto err_unmap;
}
- rev = __raw_readl(keypad_data->base + OMAP4_KBD_REVISION);
+ rev = readl_relaxed(keypad_data->base + OMAP4_KBD_REVISION);
rev &= 0x03 << 30;
rev >>= 30;
switch (rev) {
diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c
new file mode 100644
index 000000000000..36208fe0baae
--- /dev/null
+++ b/drivers/input/keyreset.c
@@ -0,0 +1,239 @@
+/* drivers/input/keyreset.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/input.h>
+#include <linux/keyreset.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+
+
+struct keyreset_state {
+ struct input_handler input_handler;
+ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
+ unsigned long upbit[BITS_TO_LONGS(KEY_CNT)];
+ unsigned long key[BITS_TO_LONGS(KEY_CNT)];
+ spinlock_t lock;
+ int key_down_target;
+ int key_down;
+ int key_up;
+ int restart_disabled;
+ int (*reset_fn)(void);
+};
+
+int restart_requested;
+static void deferred_restart(struct work_struct *dummy)
+{
+ restart_requested = 2;
+ sys_sync();
+ restart_requested = 3;
+ kernel_restart(NULL);
+}
+static DECLARE_WORK(restart_work, deferred_restart);
+
+static void keyreset_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ unsigned long flags;
+ struct keyreset_state *state = handle->private;
+
+ if (type != EV_KEY)
+ return;
+
+ if (code >= KEY_MAX)
+ return;
+
+ if (!test_bit(code, state->keybit))
+ return;
+
+ spin_lock_irqsave(&state->lock, flags);
+ if (!test_bit(code, state->key) == !value)
+ goto done;
+ __change_bit(code, state->key);
+ if (test_bit(code, state->upbit)) {
+ if (value) {
+ state->restart_disabled = 1;
+ state->key_up++;
+ } else
+ state->key_up--;
+ } else {
+ if (value)
+ state->key_down++;
+ else
+ state->key_down--;
+ }
+ if (state->key_down == 0 && state->key_up == 0)
+ state->restart_disabled = 0;
+
+ pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value,
+ state->key_down, state->key_up, state->restart_disabled);
+
+ if (value && !state->restart_disabled &&
+ state->key_down == state->key_down_target) {
+ state->restart_disabled = 1;
+ if (restart_requested)
+ panic("keyboard reset failed, %d", restart_requested);
+ if (state->reset_fn) {
+ restart_requested = state->reset_fn();
+ } else {
+ pr_info("keyboard reset\n");
+ schedule_work(&restart_work);
+ restart_requested = 1;
+ }
+ }
+done:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+static int keyreset_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ int i;
+ int ret;
+ struct input_handle *handle;
+ struct keyreset_state *state =
+ container_of(handler, struct keyreset_state, input_handler);
+
+ for (i = 0; i < KEY_MAX; i++) {
+ if (test_bit(i, state->keybit) && test_bit(i, dev->keybit))
+ break;
+ }
+ if (i == KEY_MAX)
+ return -ENODEV;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "keyreset";
+ handle->private = state;
+
+ ret = input_register_handle(handle);
+ if (ret)
+ goto err_input_register_handle;
+
+ ret = input_open_device(handle);
+ if (ret)
+ goto err_input_open_device;
+
+ pr_info("using input dev %s for key reset\n", dev->name);
+
+ return 0;
+
+err_input_open_device:
+ input_unregister_handle(handle);
+err_input_register_handle:
+ kfree(handle);
+ return ret;
+}
+
+static void keyreset_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id keyreset_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(input, keyreset_ids);
+
+static int keyreset_probe(struct platform_device *pdev)
+{
+ int ret;
+ int key, *keyp;
+ struct keyreset_state *state;
+ struct keyreset_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return -EINVAL;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ spin_lock_init(&state->lock);
+ keyp = pdata->keys_down;
+ while ((key = *keyp++)) {
+ if (key >= KEY_MAX)
+ continue;
+ state->key_down_target++;
+ __set_bit(key, state->keybit);
+ }
+ if (pdata->keys_up) {
+ keyp = pdata->keys_up;
+ while ((key = *keyp++)) {
+ if (key >= KEY_MAX)
+ continue;
+ __set_bit(key, state->keybit);
+ __set_bit(key, state->upbit);
+ }
+ }
+
+ if (pdata->reset_fn)
+ state->reset_fn = pdata->reset_fn;
+
+ state->input_handler.event = keyreset_event;
+ state->input_handler.connect = keyreset_connect;
+ state->input_handler.disconnect = keyreset_disconnect;
+ state->input_handler.name = KEYRESET_NAME;
+ state->input_handler.id_table = keyreset_ids;
+ ret = input_register_handler(&state->input_handler);
+ if (ret) {
+ kfree(state);
+ return ret;
+ }
+ platform_set_drvdata(pdev, state);
+ return 0;
+}
+
+int keyreset_remove(struct platform_device *pdev)
+{
+ struct keyreset_state *state = platform_get_drvdata(pdev);
+ input_unregister_handler(&state->input_handler);
+ kfree(state);
+ return 0;
+}
+
+
+struct platform_driver keyreset_driver = {
+ .driver.name = KEYRESET_NAME,
+ .probe = keyreset_probe,
+ .remove = keyreset_remove,
+};
+
+static int __init keyreset_init(void)
+{
+ return platform_driver_register(&keyreset_driver);
+}
+
+static void __exit keyreset_exit(void)
+{
+ return platform_driver_unregister(&keyreset_driver);
+}
+
+module_init(keyreset_init);
+module_exit(keyreset_exit);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 2ff4425a893b..749c8e6b7bfe 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -93,6 +93,15 @@ config INPUT_BMA150
To compile this driver as a module, choose M here: the
module will be called bma150.
+config INPUT_HI6421_ONKEY
+ tristate "Hisilicon Hi6421 PMIC ONKEY support"
+ depends on MFD_HI6421_PMIC
+ help
+ Say Y to enable support for Hi6421 PMIC ONKEY.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hi6421_pwrkey.
+
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM
@@ -308,6 +317,17 @@ config INPUT_ATI_REMOTE2
To compile this driver as a module, choose M here: the module will be
called ati_remote2.
+config INPUT_KEYCHORD
+ tristate "Key chord input driver support"
+ help
+ Say Y here if you want to enable the key chord driver
+ accessible at /dev/keychord. This driver can be used
+ for receiving notifications when client specified key
+ combinations are pressed.
+
+ To compile this driver as a module, choose M here: the
+ module will be called keychord.
+
config INPUT_KEYSPAN_REMOTE
tristate "Keyspan DMR USB remote control"
depends on USB_ARCH_HAS_HCD
@@ -443,6 +463,11 @@ config INPUT_SGI_BTNS
To compile this driver as a module, choose M here: the
module will be called sgi_btns.
+config INPUT_GPIO
+ tristate "GPIO driver support"
+ help
+ Say Y here if you want to support gpio based keys, wheels etc...
+
config HP_SDC_RTC
tristate "HP SDC Real Time Clock"
depends on (GSC || HP300) && SERIO
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 4955ad322a01..fc62fdff881b 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -29,9 +29,12 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
+obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o
+obj-$(CONFIG_INPUT_HI6421_ONKEY) += hi6421_pwrkey.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c
new file mode 100644
index 000000000000..0acf4a576f53
--- /dev/null
+++ b/drivers/input/misc/gpio_axis.c
@@ -0,0 +1,192 @@
+/* drivers/input/misc/gpio_axis.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+struct gpio_axis_state {
+ struct gpio_event_input_devs *input_devs;
+ struct gpio_event_axis_info *info;
+ uint32_t pos;
+};
+
+uint16_t gpio_axis_4bit_gray_map_table[] = {
+ [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */
+ [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */
+ [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */
+ [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */
+ [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */
+ [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */
+ [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */
+ [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */
+};
+uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_4bit_gray_map_table[in];
+}
+
+uint16_t gpio_axis_5bit_singletrack_map_table[] = {
+ [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */
+ [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */
+ [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */
+ [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */
+ [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */
+ [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */
+ [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */
+ [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */
+ [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */
+ [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */
+};
+uint16_t gpio_axis_5bit_singletrack_map(
+ struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_5bit_singletrack_map_table[in];
+}
+
+static void gpio_event_update_axis(struct gpio_axis_state *as, int report)
+{
+ struct gpio_event_axis_info *ai = as->info;
+ int i;
+ int change;
+ uint16_t state = 0;
+ uint16_t pos;
+ uint16_t old_pos = as->pos;
+ for (i = ai->count - 1; i >= 0; i--)
+ state = (state << 1) | gpio_get_value(ai->gpio[i]);
+ pos = ai->map(ai, state);
+ if (ai->flags & GPIOEAF_PRINT_RAW)
+ pr_info("axis %d-%d raw %x, pos %d -> %d\n",
+ ai->type, ai->code, state, old_pos, pos);
+ if (report && pos != old_pos) {
+ if (ai->type == EV_REL) {
+ change = (ai->decoded_size + pos - old_pos) %
+ ai->decoded_size;
+ if (change > ai->decoded_size / 2)
+ change -= ai->decoded_size;
+ if (change == ai->decoded_size / 2) {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d unknown direction, "
+ "pos %d -> %d\n", ai->type,
+ ai->code, old_pos, pos);
+ change = 0; /* no closest direction */
+ }
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d change %d\n",
+ ai->type, ai->code, change);
+ input_report_rel(as->input_devs->dev[ai->dev],
+ ai->code, change);
+ } else {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d now %d\n",
+ ai->type, ai->code, pos);
+ input_event(as->input_devs->dev[ai->dev],
+ ai->type, ai->code, pos);
+ }
+ input_sync(as->input_devs->dev[ai->dev]);
+ }
+ as->pos = pos;
+}
+
+static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_axis_state *as = dev_id;
+ gpio_event_update_axis(as, 1);
+ return IRQ_HANDLED;
+}
+
+int gpio_event_axis_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ int irq;
+ struct gpio_event_axis_info *ai;
+ struct gpio_axis_state *as;
+
+ ai = container_of(info, struct gpio_event_axis_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ for (i = 0; i < ai->count; i++)
+ disable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ for (i = 0; i < ai->count; i++)
+ enable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ *data = as = kmalloc(sizeof(*as), GFP_KERNEL);
+ if (as == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_axis_state_failed;
+ }
+ as->input_devs = input_devs;
+ as->info = ai;
+ if (ai->dev >= input_devs->count) {
+ pr_err("gpio_event_axis: bad device index %d >= %d "
+ "for %d:%d\n", ai->dev, input_devs->count,
+ ai->type, ai->code);
+ ret = -EINVAL;
+ goto err_bad_device_index;
+ }
+
+ input_set_capability(input_devs->dev[ai->dev],
+ ai->type, ai->code);
+ if (ai->type == EV_ABS) {
+ input_set_abs_params(input_devs->dev[ai->dev], ai->code,
+ 0, ai->decoded_size - 1, 0, 0);
+ }
+ for (i = 0; i < ai->count; i++) {
+ ret = gpio_request(ai->gpio[i], "gpio_event_axis");
+ if (ret < 0)
+ goto err_request_gpio_failed;
+ ret = gpio_direction_input(ai->gpio[i]);
+ if (ret < 0)
+ goto err_gpio_direction_input_failed;
+ ret = irq = gpio_to_irq(ai->gpio[i]);
+ if (ret < 0)
+ goto err_get_irq_num_failed;
+ ret = request_irq(irq, gpio_axis_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "gpio_event_axis", as);
+ if (ret < 0)
+ goto err_request_irq_failed;
+ }
+ gpio_event_update_axis(as, 0);
+ return 0;
+ }
+
+ ret = 0;
+ as = *data;
+ for (i = ai->count - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(ai->gpio[i]), as);
+err_request_irq_failed:
+err_get_irq_num_failed:
+err_gpio_direction_input_failed:
+ gpio_free(ai->gpio[i]);
+err_request_gpio_failed:
+ ;
+ }
+err_bad_device_index:
+ kfree(as);
+ *data = NULL;
+err_alloc_axis_state_failed:
+ return ret;
+}
diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c
new file mode 100644
index 000000000000..90f07eba3ce9
--- /dev/null
+++ b/drivers/input/misc/gpio_event.c
@@ -0,0 +1,228 @@
+/* drivers/input/misc/gpio_event.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/module.h>
+#include <linux/input.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct gpio_event {
+ struct gpio_event_input_devs *input_devs;
+ const struct gpio_event_platform_data *info;
+ void *state[0];
+};
+
+static int gpio_input_event(
+ struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ int i;
+ int devnr;
+ int ret = 0;
+ int tmp_ret;
+ struct gpio_event_info **ii;
+ struct gpio_event *ip = input_get_drvdata(dev);
+
+ for (devnr = 0; devnr < ip->input_devs->count; devnr++)
+ if (ip->input_devs->dev[devnr] == dev)
+ break;
+ if (devnr == ip->input_devs->count) {
+ pr_err("gpio_input_event: unknown device %p\n", dev);
+ return -EIO;
+ }
+
+ for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->event) {
+ tmp_ret = (*ii)->event(ip->input_devs, *ii,
+ &ip->state[i],
+ devnr, type, code, value);
+ if (tmp_ret)
+ ret = tmp_ret;
+ }
+ }
+ return ret;
+}
+
+static int gpio_event_call_all_func(struct gpio_event *ip, int func)
+{
+ int i;
+ int ret;
+ struct gpio_event_info **ii;
+
+ if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) {
+ ii = ip->info->info;
+ for (i = 0; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->func == NULL) {
+ ret = -ENODEV;
+ pr_err("gpio_event_probe: Incomplete pdata, "
+ "no function\n");
+ goto err_no_func;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend)
+ continue;
+ ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i],
+ func);
+ if (ret) {
+ pr_err("gpio_event_probe: function failed\n");
+ goto err_func_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ i = ip->info->info_count;
+ ii = ip->info->info + i;
+ while (i > 0) {
+ i--;
+ ii--;
+ if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend)
+ continue;
+ (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1);
+err_func_failed:
+err_no_func:
+ ;
+ }
+ return ret;
+}
+
+static void __maybe_unused gpio_event_suspend(struct gpio_event *ip)
+{
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND);
+ if (ip->info->power)
+ ip->info->power(ip->info, 0);
+}
+
+static void __maybe_unused gpio_event_resume(struct gpio_event *ip)
+{
+ if (ip->info->power)
+ ip->info->power(ip->info, 1);
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME);
+}
+
+static int gpio_event_probe(struct platform_device *pdev)
+{
+ int err;
+ struct gpio_event *ip;
+ struct gpio_event_platform_data *event_info;
+ int dev_count = 1;
+ int i;
+ int registered = 0;
+
+ event_info = pdev->dev.platform_data;
+ if (event_info == NULL) {
+ pr_err("gpio_event_probe: No pdata\n");
+ return -ENODEV;
+ }
+ if ((!event_info->name && !event_info->names[0]) ||
+ !event_info->info || !event_info->info_count) {
+ pr_err("gpio_event_probe: Incomplete pdata\n");
+ return -ENODEV;
+ }
+ if (!event_info->name)
+ while (event_info->names[dev_count])
+ dev_count++;
+ ip = kzalloc(sizeof(*ip) +
+ sizeof(ip->state[0]) * event_info->info_count +
+ sizeof(*ip->input_devs) +
+ sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL);
+ if (ip == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ ip->input_devs = (void*)&ip->state[event_info->info_count];
+ platform_set_drvdata(pdev, ip);
+
+ for (i = 0; i < dev_count; i++) {
+ struct input_dev *input_dev = input_allocate_device();
+ if (input_dev == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: "
+ "Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ input_set_drvdata(input_dev, ip);
+ input_dev->name = event_info->name ?
+ event_info->name : event_info->names[i];
+ input_dev->event = gpio_input_event;
+ ip->input_devs->dev[i] = input_dev;
+ }
+ ip->input_devs->count = dev_count;
+ ip->info = event_info;
+ if (event_info->power)
+ ip->info->power(ip->info, 1);
+
+ err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT);
+ if (err)
+ goto err_call_all_func_failed;
+
+ for (i = 0; i < dev_count; i++) {
+ err = input_register_device(ip->input_devs->dev[i]);
+ if (err) {
+ pr_err("gpio_event_probe: Unable to register %s "
+ "input device\n", ip->input_devs->dev[i]->name);
+ goto err_input_register_device_failed;
+ }
+ registered++;
+ }
+
+ return 0;
+
+err_input_register_device_failed:
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+err_call_all_func_failed:
+ if (event_info->power)
+ ip->info->power(ip->info, 0);
+ for (i = 0; i < registered; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
+ for (i = dev_count - 1; i >= registered; i--) {
+ input_free_device(ip->input_devs->dev[i]);
+err_input_dev_alloc_failed:
+ ;
+ }
+ kfree(ip);
+err_kp_alloc_failed:
+ return err;
+}
+
+static int gpio_event_remove(struct platform_device *pdev)
+{
+ struct gpio_event *ip = platform_get_drvdata(pdev);
+ int i;
+
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+ if (ip->info->power)
+ ip->info->power(ip->info, 0);
+ for (i = 0; i < ip->input_devs->count; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
+ kfree(ip);
+ return 0;
+}
+
+static struct platform_driver gpio_event_driver = {
+ .probe = gpio_event_probe,
+ .remove = gpio_event_remove,
+ .driver = {
+ .name = GPIO_EVENT_DEV_NAME,
+ },
+};
+
+module_platform_driver(gpio_event_driver);
+
+MODULE_DESCRIPTION("GPIO Event Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c
new file mode 100644
index 000000000000..eefd02725aff
--- /dev/null
+++ b/drivers/input/misc/gpio_input.c
@@ -0,0 +1,390 @@
+/* drivers/input/misc/gpio_input.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pm_wakeup.h>
+
+enum {
+ DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */
+ DEBOUNCE_PRESSED = BIT(1),
+ DEBOUNCE_NOTPRESSED = BIT(2),
+ DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */
+ DEBOUNCE_POLL = BIT(4), /* Stable polling state */
+
+ DEBOUNCE_UNKNOWN =
+ DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED,
+};
+
+struct gpio_key_state {
+ struct gpio_input_state *ds;
+ uint8_t debounce;
+};
+
+struct gpio_input_state {
+ struct gpio_event_input_devs *input_devs;
+ const struct gpio_event_input_info *info;
+ struct hrtimer timer;
+ int use_irq;
+ int debounce_count;
+ spinlock_t irq_lock;
+ struct wakeup_source *ws;
+ struct gpio_key_state key_state[0];
+};
+
+static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer)
+{
+ int i;
+ int pressed;
+ struct gpio_input_state *ds =
+ container_of(timer, struct gpio_input_state, timer);
+ unsigned gpio_flags = ds->info->flags;
+ unsigned npolarity;
+ int nkeys = ds->info->keymap_size;
+ const struct gpio_event_direct_entry *key_entry;
+ struct gpio_key_state *key_state;
+ unsigned long irqflags;
+ uint8_t debounce;
+ bool sync_needed;
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++)
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+#endif
+ key_entry = ds->info->keymap;
+ key_state = ds->key_state;
+ sync_needed = false;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ debounce = key_state->debounce;
+ if (debounce & DEBOUNCE_WAIT_IRQ)
+ continue;
+ if (key_state->debounce & DEBOUNCE_UNSTABLE) {
+ debounce = key_state->debounce = DEBOUNCE_UNKNOWN;
+ enable_irq(gpio_to_irq(key_entry->gpio));
+ if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) continue debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH);
+ pressed = gpio_get_value(key_entry->gpio) ^ npolarity;
+ if (debounce & DEBOUNCE_POLL) {
+ if (pressed == !(debounce & DEBOUNCE_PRESSED)) {
+ ds->debounce_count++;
+ key_state->debounce = DEBOUNCE_UNKNOWN;
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-"
+ "%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ continue;
+ }
+ if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 1\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_PRESSED;
+ continue;
+ }
+ if (!pressed && (debounce & DEBOUNCE_PRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 0\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_NOTPRESSED;
+ continue;
+ }
+ /* key is stable */
+ ds->debounce_count--;
+ if (ds->use_irq)
+ key_state->debounce |= DEBOUNCE_WAIT_IRQ;
+ else
+ key_state->debounce |= DEBOUNCE_POLL;
+ if (gpio_flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) "
+ "changed to %d\n", ds->info->type,
+ key_entry->code, i, key_entry->gpio, pressed);
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
+ key_entry->code, pressed);
+ sync_needed = true;
+ }
+ if (sync_needed) {
+ for (i = 0; i < ds->input_devs->count; i++)
+ input_sync(ds->input_devs->dev[i]);
+ }
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+ }
+#endif
+
+ if (ds->debounce_count)
+ hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL);
+ else if (!ds->use_irq)
+ hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL);
+ else
+ __pm_relax(ds->ws);
+
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_key_state *ks = dev_id;
+ struct gpio_input_state *ds = ks->ds;
+ int keymap_index = ks - ds->key_state;
+ const struct gpio_event_direct_entry *key_entry;
+ unsigned long irqflags;
+ int pressed;
+
+ if (!ds->use_irq)
+ return IRQ_HANDLED;
+
+ key_entry = &ds->info->keymap[keymap_index];
+
+ if (ds->info->debounce_time.tv64) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ks->debounce & DEBOUNCE_WAIT_IRQ) {
+ ks->debounce = DEBOUNCE_UNKNOWN;
+ if (ds->debounce_count++ == 0) {
+ __pm_stay_awake(ds->ws);
+ hrtimer_start(
+ &ds->timer, ds->info->debounce_time,
+ HRTIMER_MODE_REL);
+ }
+ if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_event_input_irq_handler: "
+ "key %x-%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ keymap_index, key_entry->gpio);
+ } else {
+ disable_irq_nosync(irq);
+ ks->debounce = DEBOUNCE_UNSTABLE;
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ } else {
+ pressed = gpio_get_value(key_entry->gpio) ^
+ !(ds->info->flags & GPIOEDF_ACTIVE_HIGH);
+ if (ds->info->flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_event_input_irq_handler: key %x-%x, %d "
+ "(%d) changed to %d\n",
+ ds->info->type, key_entry->code, keymap_index,
+ key_entry->gpio, pressed);
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
+ key_entry->code, pressed);
+ input_sync(ds->input_devs->dev[key_entry->dev]);
+ }
+ return IRQ_HANDLED;
+}
+
+static int gpio_event_input_request_irqs(struct gpio_input_state *ds)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+ for (i = 0; i < ds->info->keymap_size; i++) {
+ err = irq = gpio_to_irq(ds->info->keymap[i].gpio);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_event_input_irq_handler,
+ req_flags, "gpio_keys", &ds->key_state[i]);
+ if (err) {
+ pr_err("gpio_event_input_request_irqs: request_irq "
+ "failed for input %d, irq %d\n",
+ ds->info->keymap[i].gpio, irq);
+ goto err_request_irq_failed;
+ }
+ if (ds->info->info.no_suspend) {
+ err = enable_irq_wake(irq);
+ if (err) {
+ pr_err("gpio_event_input_request_irqs: "
+ "enable_irq_wake failed for input %d, "
+ "irq %d\n",
+ ds->info->keymap[i].gpio, irq);
+ goto err_enable_irq_wake_failed;
+ }
+ }
+ }
+ return 0;
+
+ for (i = ds->info->keymap_size - 1; i >= 0; i--) {
+ irq = gpio_to_irq(ds->info->keymap[i].gpio);
+ if (ds->info->info.no_suspend)
+ disable_irq_wake(irq);
+err_enable_irq_wake_failed:
+ free_irq(irq, &ds->key_state[i]);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_input_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ unsigned long irqflags;
+ struct gpio_event_input_info *di;
+ struct gpio_input_state *ds = *data;
+ char *wlname;
+
+ di = container_of(info, struct gpio_event_input_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ disable_irq(gpio_to_irq(di->keymap[i].gpio));
+ hrtimer_cancel(&ds->timer);
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ enable_irq(gpio_to_irq(di->keymap[i].gpio));
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (ktime_to_ns(di->poll_time) <= 0)
+ di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC);
+
+ *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) *
+ di->keymap_size, GFP_KERNEL);
+ if (ds == NULL) {
+ ret = -ENOMEM;
+ pr_err("gpio_event_input_func: "
+ "Failed to allocate private data\n");
+ goto err_ds_alloc_failed;
+ }
+ ds->debounce_count = di->keymap_size;
+ ds->input_devs = input_devs;
+ ds->info = di;
+ wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s",
+ input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "");
+
+ ds->ws = wakeup_source_register(wlname);
+ kfree(wlname);
+ if (!ds->ws) {
+ ret = -ENOMEM;
+ pr_err("gpio_event_input_func: "
+ "Failed to allocate wakeup source\n");
+ goto err_ws_failed;
+ }
+
+ spin_lock_init(&ds->irq_lock);
+
+ for (i = 0; i < di->keymap_size; i++) {
+ int dev = di->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_input_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ di->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], di->type,
+ di->keymap[i].code);
+ ds->key_state[i].ds = ds;
+ ds->key_state[i].debounce = DEBOUNCE_UNKNOWN;
+ }
+
+ for (i = 0; i < di->keymap_size; i++) {
+ ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in");
+ if (ret) {
+ pr_err("gpio_event_input_func: gpio_request "
+ "failed for %d\n", di->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_input(di->keymap[i].gpio);
+ if (ret) {
+ pr_err("gpio_event_input_func: "
+ "gpio_direction_input failed for %d\n",
+ di->keymap[i].gpio);
+ goto err_gpio_configure_failed;
+ }
+ }
+
+ ret = gpio_event_input_request_irqs(ds);
+
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ ds->use_irq = ret == 0;
+
+ pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s "
+ "mode\n", input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "",
+ ret == 0 ? "interrupt" : "polling");
+
+ hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ds->timer.function = gpio_event_input_timer_func;
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ ret = 0;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ hrtimer_cancel(&ds->timer);
+ if (ds->use_irq) {
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+ int irq = gpio_to_irq(di->keymap[i].gpio);
+ if (ds->info->info.no_suspend)
+ disable_irq_wake(irq);
+ free_irq(irq, &ds->key_state[i]);
+ }
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+err_gpio_configure_failed:
+ gpio_free(di->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+err_bad_keymap:
+ wakeup_source_unregister(ds->ws);
+err_ws_failed:
+ kfree(ds);
+err_ds_alloc_failed:
+ return ret;
+}
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
new file mode 100644
index 000000000000..eaa9e89d473a
--- /dev/null
+++ b/drivers/input/misc/gpio_matrix.c
@@ -0,0 +1,441 @@
+/* drivers/input/misc/gpio_matrix.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+
+struct gpio_kp {
+ struct gpio_event_input_devs *input_devs;
+ struct gpio_event_matrix_info *keypad_info;
+ struct hrtimer timer;
+ struct wake_lock wake_lock;
+ int current_output;
+ unsigned int use_irq:1;
+ unsigned int key_state_changed:1;
+ unsigned int last_key_state_changed:1;
+ unsigned int some_keys_pressed:2;
+ unsigned int disabled_irq:1;
+ unsigned long keys_pressed[0];
+};
+
+static void clear_phantom_key(struct gpio_kp *kp, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int key_index = out * mi->ninputs + in;
+ unsigned short keyentry = mi->keymap[key_index];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+
+ if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ __clear_bit(key_index, kp->keys_pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "not cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ }
+}
+
+static int restore_keys_for_input(struct gpio_kp *kp, int out, int in)
+{
+ int rv = 0;
+ int key_index;
+
+ key_index = out * kp->keypad_info->ninputs + in;
+ while (out < kp->keypad_info->noutputs) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ rv = 1;
+ clear_phantom_key(kp, out, in);
+ }
+ key_index += kp->keypad_info->ninputs;
+ out++;
+ }
+ return rv;
+}
+
+static void remove_phantom_keys(struct gpio_kp *kp)
+{
+ int out, in, inp;
+ int key_index;
+
+ if (kp->some_keys_pressed < 3)
+ return;
+
+ for (out = 0; out < kp->keypad_info->noutputs; out++) {
+ inp = -1;
+ key_index = out * kp->keypad_info->ninputs;
+ for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ if (inp == -1) {
+ inp = in;
+ continue;
+ }
+ if (inp >= 0) {
+ if (!restore_keys_for_input(kp, out + 1,
+ inp))
+ break;
+ clear_phantom_key(kp, out, inp);
+ inp = -2;
+ }
+ restore_keys_for_input(kp, out, in);
+ }
+ }
+ }
+}
+
+static void report_key(struct gpio_kp *kp, int key_index, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int pressed = test_bit(key_index, kp->keys_pressed);
+ unsigned short keyentry = mi->keymap[key_index];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+
+ if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) {
+ if (keycode == KEY_RESERVED) {
+ if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS)
+ pr_info("gpiomatrix: unmapped key, %d-%d "
+ "(%d-%d) changed to %d\n",
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS)
+ pr_info("gpiomatrix: key %x, %d-%d (%d-%d) "
+ "changed to %d\n", keycode,
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ input_report_key(kp->input_devs->dev[dev], keycode, pressed);
+ }
+ }
+}
+
+static void report_sync(struct gpio_kp *kp)
+{
+ int i;
+
+ for (i = 0; i < kp->input_devs->count; i++)
+ input_sync(kp->input_devs->dev[i]);
+}
+
+static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer)
+{
+ int out, in;
+ int key_index;
+ int gpio;
+ struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer);
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+ unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH);
+
+ out = kp->current_output;
+ if (out == mi->noutputs) {
+ out = 0;
+ kp->last_key_state_changed = kp->key_state_changed;
+ kp->key_state_changed = 0;
+ kp->some_keys_pressed = 0;
+ } else {
+ key_index = out * mi->ninputs;
+ for (in = 0; in < mi->ninputs; in++, key_index++) {
+ gpio = mi->input_gpios[in];
+ if (gpio_get_value(gpio) ^ !polarity) {
+ if (kp->some_keys_pressed < 3)
+ kp->some_keys_pressed++;
+ kp->key_state_changed |= !__test_and_set_bit(
+ key_index, kp->keys_pressed);
+ } else
+ kp->key_state_changed |= __test_and_clear_bit(
+ key_index, kp->keys_pressed);
+ }
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, !polarity);
+ else
+ gpio_direction_input(gpio);
+ out++;
+ }
+ kp->current_output = out;
+ if (out < mi->noutputs) {
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, polarity);
+ else
+ gpio_direction_output(gpio, polarity);
+ hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) {
+ if (kp->key_state_changed) {
+ hrtimer_start(&kp->timer, mi->debounce_delay,
+ HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ kp->key_state_changed = kp->last_key_state_changed;
+ }
+ if (kp->key_state_changed) {
+ if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS)
+ remove_phantom_keys(kp);
+ key_index = 0;
+ for (out = 0; out < mi->noutputs; out++)
+ for (in = 0; in < mi->ninputs; in++, key_index++)
+ report_key(kp, key_index, out, in);
+ report_sync(kp);
+ }
+ if (!kp->use_irq || kp->some_keys_pressed) {
+ hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+
+ /* No keys are pressed, reenable interrupt */
+ for (out = 0; out < mi->noutputs; out++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[out], polarity);
+ else
+ gpio_direction_output(mi->output_gpios[out], polarity);
+ }
+ for (in = 0; in < mi->ninputs; in++)
+ enable_irq(gpio_to_irq(mi->input_gpios[in]));
+ wake_unlock(&kp->wake_lock);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id)
+{
+ int i;
+ struct gpio_kp *kp = dev_id;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+
+ if (!kp->use_irq) {
+ /* ignore interrupt while registering the handler */
+ kp->disabled_irq = 1;
+ disable_irq_nosync(irq_in);
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < mi->ninputs; i++)
+ disable_irq_nosync(gpio_to_irq(mi->input_gpios[i]));
+ for (i = 0; i < mi->noutputs; i++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[i],
+ !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ gpio_direction_input(mi->output_gpios[i]);
+ }
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ return IRQ_HANDLED;
+}
+
+static int gpio_keypad_request_irqs(struct gpio_kp *kp)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long request_flags;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+
+ switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) {
+ default:
+ request_flags = IRQF_TRIGGER_FALLING;
+ break;
+ case GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_RISING;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ:
+ request_flags = IRQF_TRIGGER_LOW;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_HIGH;
+ break;
+ }
+
+ for (i = 0; i < mi->ninputs; i++) {
+ err = irq = gpio_to_irq(mi->input_gpios[i]);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_keypad_irq_handler, request_flags,
+ "gpio_kp", kp);
+ if (err) {
+ pr_err("gpiomatrix: request_irq failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ goto err_request_irq_failed;
+ }
+ err = enable_irq_wake(irq);
+ if (err) {
+ pr_err("gpiomatrix: set_irq_wake failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ }
+ disable_irq(irq);
+ if (kp->disabled_irq) {
+ kp->disabled_irq = 0;
+ enable_irq(irq);
+ }
+ }
+ return 0;
+
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int i;
+ int err;
+ int key_count;
+ struct gpio_kp *kp;
+ struct gpio_event_matrix_info *mi;
+
+ mi = container_of(info, struct gpio_event_matrix_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) {
+ /* TODO: disable scanning */
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (mi->keymap == NULL ||
+ mi->input_gpios == NULL ||
+ mi->output_gpios == NULL) {
+ err = -ENODEV;
+ pr_err("gpiomatrix: Incomplete pdata\n");
+ goto err_invalid_platform_data;
+ }
+ key_count = mi->ninputs * mi->noutputs;
+
+ *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) *
+ BITS_TO_LONGS(key_count), GFP_KERNEL);
+ if (kp == NULL) {
+ err = -ENOMEM;
+ pr_err("gpiomatrix: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ kp->input_devs = input_devs;
+ kp->keypad_info = mi;
+ for (i = 0; i < key_count; i++) {
+ unsigned short keyentry = mi->keymap[i];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+ if (dev >= input_devs->count) {
+ pr_err("gpiomatrix: bad device index %d >= "
+ "%d for key code %d\n",
+ dev, input_devs->count, keycode);
+ err = -EINVAL;
+ goto err_bad_keymap;
+ }
+ if (keycode && keycode <= KEY_MAX)
+ input_set_capability(input_devs->dev[dev],
+ EV_KEY, keycode);
+ }
+
+ for (i = 0; i < mi->noutputs; i++) {
+ err = gpio_request(mi->output_gpios[i], "gpio_kp_out");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_request_output_gpio_failed;
+ }
+ if (gpio_cansleep(mi->output_gpios[i])) {
+ pr_err("gpiomatrix: unsupported output gpio %d,"
+ " can sleep\n", mi->output_gpios[i]);
+ err = -EINVAL;
+ goto err_output_gpio_configure_failed;
+ }
+ if (mi->flags & GPIOKPF_DRIVE_INACTIVE)
+ err = gpio_direction_output(mi->output_gpios[i],
+ !(mi->flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ err = gpio_direction_input(mi->output_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_configure failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_output_gpio_configure_failed;
+ }
+ }
+ for (i = 0; i < mi->ninputs; i++) {
+ err = gpio_request(mi->input_gpios[i], "gpio_kp_in");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "input %d\n", mi->input_gpios[i]);
+ goto err_request_input_gpio_failed;
+ }
+ err = gpio_direction_input(mi->input_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_direction_input failed"
+ " for input %d\n", mi->input_gpios[i]);
+ goto err_gpio_direction_input_failed;
+ }
+ }
+ kp->current_output = mi->noutputs;
+ kp->key_state_changed = 1;
+
+ hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ kp->timer.function = gpio_keypad_timer_func;
+ wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp");
+ err = gpio_keypad_request_irqs(kp);
+ kp->use_irq = err == 0;
+
+ pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for "
+ "%s%s in %s mode\n", input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "",
+ kp->use_irq ? "interrupt" : "polling");
+
+ if (kp->use_irq)
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+
+ return 0;
+ }
+
+ err = 0;
+ kp = *data;
+
+ if (kp->use_irq)
+ for (i = mi->noutputs - 1; i >= 0; i--)
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+
+ hrtimer_cancel(&kp->timer);
+ wake_lock_destroy(&kp->wake_lock);
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_gpio_direction_input_failed:
+ gpio_free(mi->input_gpios[i]);
+err_request_input_gpio_failed:
+ ;
+ }
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_output_gpio_configure_failed:
+ gpio_free(mi->output_gpios[i]);
+err_request_output_gpio_failed:
+ ;
+ }
+err_bad_keymap:
+ kfree(kp);
+err_kp_alloc_failed:
+err_invalid_platform_data:
+ return err;
+}
diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c
new file mode 100644
index 000000000000..2aac2fad0a17
--- /dev/null
+++ b/drivers/input/misc/gpio_output.c
@@ -0,0 +1,97 @@
+/* drivers/input/misc/gpio_output.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+
+int gpio_event_output_event(
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, unsigned int dev, unsigned int type,
+ unsigned int code, int value)
+{
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+ if (type != oi->type)
+ return 0;
+ if (!(oi->flags & GPIOEDF_ACTIVE_HIGH))
+ value = !value;
+ for (i = 0; i < oi->keymap_size; i++)
+ if (dev == oi->keymap[i].dev && code == oi->keymap[i].code)
+ gpio_set_value(oi->keymap[i].gpio, value);
+ return 0;
+}
+
+int gpio_event_output_func(
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, int func)
+{
+ int ret;
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME)
+ return 0;
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH);
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ int dev = oi->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_output_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ oi->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], oi->type,
+ oi->keymap[i].code);
+ }
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ ret = gpio_request(oi->keymap[i].gpio,
+ "gpio_event_output");
+ if (ret) {
+ pr_err("gpio_event_output_func: gpio_request "
+ "failed for %d\n", oi->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_output(oi->keymap[i].gpio,
+ output_level);
+ if (ret) {
+ pr_err("gpio_event_output_func: "
+ "gpio_direction_output failed for %d\n",
+ oi->keymap[i].gpio);
+ goto err_gpio_direction_output_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ for (i = oi->keymap_size - 1; i >= 0; i--) {
+err_gpio_direction_output_failed:
+ gpio_free(oi->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+err_bad_keymap:
+ return ret;
+}
+
diff --git a/drivers/input/misc/hi6421_pwrkey.c b/drivers/input/misc/hi6421_pwrkey.c
new file mode 100644
index 000000000000..31d0145fc688
--- /dev/null
+++ b/drivers/input/misc/hi6421_pwrkey.c
@@ -0,0 +1,153 @@
+/*
+ * hi6421_pwrkey.c - Hisilicon Hi6421 PMIC ONKEY driver
+ *
+ * Copyright (C) 2013 Hisilicon Ltd.
+ * Copyright (C) 2013 Linaro Ltd.
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/hi6421-pmic.h>
+
+struct hi6421_onkey_info {
+ struct input_dev *idev;
+ int irq[4];
+};
+
+static irqreturn_t hi6421_onkey_handler(int irq, void *data)
+{
+ struct hi6421_onkey_info *info = (struct hi6421_onkey_info *)data;
+
+ /* only handle power down & power up event at here */
+ if (irq == info->irq[0]) {
+ input_report_key(info->idev, KEY_POWER, 1);
+ input_sync(info->idev);
+ } else if (irq == info->irq[1]) {
+ input_report_key(info->idev, KEY_POWER, 0);
+ input_sync(info->idev);
+ }
+ return IRQ_HANDLED;
+}
+
+static int hi6421_onkey_probe(struct platform_device *pdev)
+{
+ struct hi6421_onkey_info *info;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->idev = input_allocate_device();
+ if (!info->idev) {
+ dev_err(&pdev->dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+ info->idev->name = "hi6421_on";
+ info->idev->phys = "hi6421_on/input0";
+ info->idev->dev.parent = &pdev->dev;
+ info->idev->evbit[0] = BIT_MASK(EV_KEY);
+ __set_bit(KEY_POWER, info->idev->keybit);
+
+ info->irq[0] = platform_get_irq_byname(pdev, "down");
+ if (info->irq[0] < 0) {
+ ret = -ENOENT;
+ goto err_irq0;
+ }
+ ret = request_irq(info->irq[0], hi6421_onkey_handler,
+ IRQF_DISABLED, "down", info);
+ if (ret < 0)
+ goto err_irq0;
+ info->irq[1] = platform_get_irq_byname(pdev, "up");
+ if (info->irq[1] < 0) {
+ ret = -ENOENT;
+ goto err_irq1;
+ }
+ ret = request_irq(info->irq[1], hi6421_onkey_handler,
+ IRQF_DISABLED, "up", info);
+ if (ret < 0)
+ goto err_irq1;
+ info->irq[2] = platform_get_irq_byname(pdev, "hold 1s");
+ if (info->irq[2] < 0) {
+ ret = -ENOENT;
+ goto err_irq2;
+ }
+ ret = request_irq(info->irq[2], hi6421_onkey_handler,
+ IRQF_DISABLED, "hold 1s", info);
+ if (ret < 0)
+ goto err_irq2;
+ info->irq[3] = platform_get_irq_byname(pdev, "hold 10s");
+ if (info->irq[3] < 0) {
+ ret = -ENOENT;
+ goto err_irq3;
+ }
+ ret = request_irq(info->irq[3], hi6421_onkey_handler,
+ IRQF_DISABLED, "hold 10s", info);
+ if (ret < 0)
+ goto err_irq3;
+
+ ret = input_register_device(info->idev);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register input device: %d\n", ret);
+ goto err_reg;
+ }
+
+ platform_set_drvdata(pdev, info);
+ return ret;
+err_reg:
+ free_irq(info->irq[3], info);
+err_irq3:
+ free_irq(info->irq[2], info);
+err_irq2:
+ free_irq(info->irq[1], info);
+err_irq1:
+ free_irq(info->irq[0], info);
+err_irq0:
+ input_free_device(info->idev);
+ return ret;
+}
+
+static int hi6421_onkey_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct of_device_id hi6421_onkey_of_match[] = {
+ { .compatible = "hisilicon,hi6421-onkey", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, hi6421_onkey_of_match);
+
+static struct platform_driver hi6421_onkey_driver = {
+ .probe = hi6421_onkey_probe,
+ .remove = hi6421_onkey_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "hi6421-onkey",
+ .of_match_table = hi6421_onkey_of_match,
+ },
+};
+module_platform_driver(hi6421_onkey_driver);
+
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@linaro.org");
+MODULE_DESCRIPTION("Hi6421 PMIC Power key driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c
new file mode 100644
index 000000000000..a5ea27ad0e16
--- /dev/null
+++ b/drivers/input/misc/keychord.c
@@ -0,0 +1,391 @@
+/*
+ * drivers/input/misc/keychord.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/keychord.h>
+#include <linux/sched.h>
+
+#define KEYCHORD_NAME "keychord"
+#define BUFFER_SIZE 16
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("Key chord input driver");
+MODULE_SUPPORTED_DEVICE("keychord");
+MODULE_LICENSE("GPL");
+
+#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \
+ ((char *)kc + sizeof(struct input_keychord) + \
+ kc->count * sizeof(kc->keycodes[0])))
+
+struct keychord_device {
+ struct input_handler input_handler;
+ int registered;
+
+ /* list of keychords to monitor */
+ struct input_keychord *keychords;
+ int keychord_count;
+
+ /* bitmask of keys contained in our keychords */
+ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
+ /* current state of the keys */
+ unsigned long keystate[BITS_TO_LONGS(KEY_CNT)];
+ /* number of keys that are currently pressed */
+ int key_down;
+
+ /* second input_device_id is needed for null termination */
+ struct input_device_id device_ids[2];
+
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+ unsigned char head;
+ unsigned char tail;
+ __u16 buff[BUFFER_SIZE];
+};
+
+static int check_keychord(struct keychord_device *kdev,
+ struct input_keychord *keychord)
+{
+ int i;
+
+ if (keychord->count != kdev->key_down)
+ return 0;
+
+ for (i = 0; i < keychord->count; i++) {
+ if (!test_bit(keychord->keycodes[i], kdev->keystate))
+ return 0;
+ }
+
+ /* we have a match */
+ return 1;
+}
+
+static void keychord_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ struct keychord_device *kdev = handle->private;
+ struct input_keychord *keychord;
+ unsigned long flags;
+ int i, got_chord = 0;
+
+ if (type != EV_KEY || code >= KEY_MAX)
+ return;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* do nothing if key state did not change */
+ if (!test_bit(code, kdev->keystate) == !value)
+ goto done;
+ __change_bit(code, kdev->keystate);
+ if (value)
+ kdev->key_down++;
+ else
+ kdev->key_down--;
+
+ /* don't notify on key up */
+ if (!value)
+ goto done;
+ /* ignore this event if it is not one of the keys we are monitoring */
+ if (!test_bit(code, kdev->keybit))
+ goto done;
+
+ keychord = kdev->keychords;
+ if (!keychord)
+ goto done;
+
+ /* check to see if the keyboard state matches any keychords */
+ for (i = 0; i < kdev->keychord_count; i++) {
+ if (check_keychord(kdev, keychord)) {
+ kdev->buff[kdev->head] = keychord->id;
+ kdev->head = (kdev->head + 1) % BUFFER_SIZE;
+ got_chord = 1;
+ break;
+ }
+ /* skip to next keychord */
+ keychord = NEXT_KEYCHORD(keychord);
+ }
+
+done:
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ if (got_chord) {
+ pr_info("keychord: got keychord id %d. Any tasks: %d\n",
+ keychord->id,
+ !list_empty_careful(&kdev->waitq.task_list));
+ wake_up_interruptible(&kdev->waitq);
+ }
+}
+
+static int keychord_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ int i, ret;
+ struct input_handle *handle;
+ struct keychord_device *kdev =
+ container_of(handler, struct keychord_device, input_handler);
+
+ /*
+ * ignore this input device if it does not contain any keycodes
+ * that we are monitoring
+ */
+ for (i = 0; i < KEY_MAX; i++) {
+ if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit))
+ break;
+ }
+ if (i == KEY_MAX)
+ return -ENODEV;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = KEYCHORD_NAME;
+ handle->private = kdev;
+
+ ret = input_register_handle(handle);
+ if (ret)
+ goto err_input_register_handle;
+
+ ret = input_open_device(handle);
+ if (ret)
+ goto err_input_open_device;
+
+ pr_info("keychord: using input dev %s for fevent\n", dev->name);
+
+ return 0;
+
+err_input_open_device:
+ input_unregister_handle(handle);
+err_input_register_handle:
+ kfree(handle);
+ return ret;
+}
+
+static void keychord_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+/*
+ * keychord_read is used to read keychord events from the driver
+ */
+static ssize_t keychord_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct keychord_device *kdev = file->private_data;
+ __u16 id;
+ int retval;
+ unsigned long flags;
+
+ if (count < sizeof(id))
+ return -EINVAL;
+ count = sizeof(id);
+
+ if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(kdev->waitq,
+ kdev->head != kdev->tail);
+ if (retval)
+ return retval;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* pop a keychord ID off the queue */
+ id = kdev->buff[kdev->tail];
+ kdev->tail = (kdev->tail + 1) % BUFFER_SIZE;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ if (copy_to_user(buffer, &id, count))
+ return -EFAULT;
+
+ return count;
+}
+
+/*
+ * keychord_write is used to configure the driver
+ */
+static ssize_t keychord_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct keychord_device *kdev = file->private_data;
+ struct input_keychord *keychords = 0;
+ struct input_keychord *keychord, *next, *end;
+ int ret, i, key;
+ unsigned long flags;
+
+ if (count < sizeof(struct input_keychord))
+ return -EINVAL;
+ keychords = kzalloc(count, GFP_KERNEL);
+ if (!keychords)
+ return -ENOMEM;
+
+ /* read list of keychords from userspace */
+ if (copy_from_user(keychords, buffer, count)) {
+ kfree(keychords);
+ return -EFAULT;
+ }
+
+ /* unregister handler before changing configuration */
+ if (kdev->registered) {
+ input_unregister_handler(&kdev->input_handler);
+ kdev->registered = 0;
+ }
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* clear any existing configuration */
+ kfree(kdev->keychords);
+ kdev->keychords = 0;
+ kdev->keychord_count = 0;
+ kdev->key_down = 0;
+ memset(kdev->keybit, 0, sizeof(kdev->keybit));
+ memset(kdev->keystate, 0, sizeof(kdev->keystate));
+ kdev->head = kdev->tail = 0;
+
+ keychord = keychords;
+ end = (struct input_keychord *)((char *)keychord + count);
+
+ while (keychord < end) {
+ next = NEXT_KEYCHORD(keychord);
+ if (keychord->count <= 0 || next > end) {
+ pr_err("keychord: invalid keycode count %d\n",
+ keychord->count);
+ goto err_unlock_return;
+ }
+ if (keychord->version != KEYCHORD_VERSION) {
+ pr_err("keychord: unsupported version %d\n",
+ keychord->version);
+ goto err_unlock_return;
+ }
+
+ /* keep track of the keys we are monitoring in keybit */
+ for (i = 0; i < keychord->count; i++) {
+ key = keychord->keycodes[i];
+ if (key < 0 || key >= KEY_CNT) {
+ pr_err("keychord: keycode %d out of range\n",
+ key);
+ goto err_unlock_return;
+ }
+ __set_bit(key, kdev->keybit);
+ }
+
+ kdev->keychord_count++;
+ keychord = next;
+ }
+
+ kdev->keychords = keychords;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ ret = input_register_handler(&kdev->input_handler);
+ if (ret) {
+ kfree(keychords);
+ kdev->keychords = 0;
+ return ret;
+ }
+ kdev->registered = 1;
+
+ return count;
+
+err_unlock_return:
+ spin_unlock_irqrestore(&kdev->lock, flags);
+ kfree(keychords);
+ return -EINVAL;
+}
+
+static unsigned int keychord_poll(struct file *file, poll_table *wait)
+{
+ struct keychord_device *kdev = file->private_data;
+
+ poll_wait(file, &kdev->waitq, wait);
+
+ if (kdev->head != kdev->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int keychord_open(struct inode *inode, struct file *file)
+{
+ struct keychord_device *kdev;
+
+ kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL);
+ if (!kdev)
+ return -ENOMEM;
+
+ spin_lock_init(&kdev->lock);
+ init_waitqueue_head(&kdev->waitq);
+
+ kdev->input_handler.event = keychord_event;
+ kdev->input_handler.connect = keychord_connect;
+ kdev->input_handler.disconnect = keychord_disconnect;
+ kdev->input_handler.name = KEYCHORD_NAME;
+ kdev->input_handler.id_table = kdev->device_ids;
+
+ kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT;
+ __set_bit(EV_KEY, kdev->device_ids[0].evbit);
+
+ file->private_data = kdev;
+
+ return 0;
+}
+
+static int keychord_release(struct inode *inode, struct file *file)
+{
+ struct keychord_device *kdev = file->private_data;
+
+ if (kdev->registered)
+ input_unregister_handler(&kdev->input_handler);
+ kfree(kdev);
+
+ return 0;
+}
+
+static const struct file_operations keychord_fops = {
+ .owner = THIS_MODULE,
+ .open = keychord_open,
+ .release = keychord_release,
+ .read = keychord_read,
+ .write = keychord_write,
+ .poll = keychord_poll,
+};
+
+static struct miscdevice keychord_misc = {
+ .fops = &keychord_fops,
+ .name = KEYCHORD_NAME,
+ .minor = MISC_DYNAMIC_MINOR,
+};
+
+static int __init keychord_init(void)
+{
+ return misc_register(&keychord_misc);
+}
+
+static void __exit keychord_exit(void)
+{
+ misc_deregister(&keychord_misc);
+}
+
+module_init(keychord_init);
+module_exit(keychord_exit);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 6bb9a7dd23b6..fdb9aa486667 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -950,4 +950,15 @@ config TOUCHSCREEN_ZFORCE
To compile this driver as a module, choose M here: the
module will be called zforce_ts.
+config TOUCHSCREEN_MXT224E
+ tristate "Atmel mXT224E based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a TPS6507x based touchscreen
+ controller.
+
+ If unsure, say N.
+
+ module will be called atmel_mXT224E.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4be94fce41af..679135d921ee 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MXT224E) += atmel_mXT224E.o
diff --git a/drivers/input/touchscreen/atmel_mXT224E.c b/drivers/input/touchscreen/atmel_mXT224E.c
new file mode 100644
index 000000000000..0a7fab242d98
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_mXT224E.c
@@ -0,0 +1,1473 @@
+/* drivers/input/touchscreen/atmel_mXT224E.c - ATMEL Touch driver
+ *
+ * Copyright (C) 2008 ATMEL
+ * Copyright (C) 2011 Huawei Corporation.
+ *
+ * Based on touchscreen code from Atmel Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/bitops.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+
+#include <linux/kthread.h>
+
+#ifdef TS_ATMEL_DEBUG
+#define TS_DEBUG_ATMEL(fmt, args...) pr_info(fmt, ##args)
+#else
+#define TS_DEBUG_ATMEL(fmt, args...)
+#endif
+
+#define ATMEL_MXT224E_NAME "atmel_mxt224e"
+
+#define INFO_BLK_FID 0
+#define INFO_BLK_VID 1
+#define INFO_BLK_VER 2
+#define INFO_BLK_BUILD 3
+#define INFO_BLK_XSIZE 4
+#define INFO_BLK_YSIZE 5
+#define INFO_BLK_OBJS 6
+
+#define OBJ_TABLE_TYPE 0
+#define OBJ_TABLE_LSB 1
+#define OBJ_TABLE_MSB 2
+#define OBJ_TABLE_SIZE 3
+#define OBJ_TABLE_INSTANCES 4
+#define OBJ_TABLE_RIDS 5
+
+#define RESERVED_T0 0u
+#define RESERVED_T1 1u
+#define DEBUG_DELTAS_T2 2u
+#define DEBUG_REFERENCES_T3 3u
+#define DEBUG_SIGNALS_T4 4u
+#define GEN_MESSAGEPROCESSOR_T5 5u
+#define GEN_COMMANDPROCESSOR_T6 6u
+#define GEN_POWERCONFIG_T7 7u
+#define GEN_ACQUISITIONCONFIG_T8 8u
+#define TOUCH_MULTITOUCHSCREEN_T9 9u
+#define TOUCH_SINGLETOUCHSCREEN_T10 10u
+#define TOUCH_XSLIDER_T11 11u
+#define TOUCH_YSLIDER_T12 12u
+#define TOUCH_XWHEEL_T13 13u
+#define TOUCH_YWHEEL_T14 14u
+#define TOUCH_KEYARRAY_T15 15u
+#define PROCG_SIGNALFILTER_T16 16u
+#define PROCI_LINEARIZATIONTABLE_T17 17u
+#define SPT_COMCONFIG_T18 18u
+#define SPT_GPIOPWM_T19 19u
+#define PROCI_GRIPFACESUPPRESSION_T20 20u
+#define RESERVED_T21 21u
+#define PROCG_NOISESUPPRESSION_T22 22u
+#define TOUCH_PROXIMITY_T23 23u
+#define PROCI_ONETOUCHGESTUREPROCESSOR_T24 24u
+#define SPT_SELFTEST_T25 25u
+#define DEBUG_CTERANGE_T26 26u
+#define PROCI_TWOTOUCHGESTUREPROCESSOR_T27 27u
+#define SPT_CTECONFIG_T28 28u
+#define SPT_GPI_T29 29u
+#define SPT_GATE_T30 30u
+#define TOUCH_KEYSET_T31 31u
+#define TOUCH_XSLIDERSET_T32 32u
+#define DIAGNOSTIC_T37 37u
+#define PROCI_GRIPSUPPRESSION_T40 40u
+#define PROCI_TOUCHSUPPRESSION_T42 42u
+#define SPT_CTECONFIG_T46 46u
+#define PROCI_STYLUS_T47 47u
+#define PROCG_NOISESUPPRESSION_T48 48u
+
+#define T37_PAGE_SIZE 128
+
+#define T37_TCH_FLAG_SIZE 80
+#define T37_TCH_FLAG_IDX 0
+#define T37_ATCH_FLAG_IDX 40
+
+#define T37_MODE 0
+#define T37_PAGE 1
+#define T37_DATA 2 /* n bytes */
+
+#define T37_PAGE_NUM0 0
+#define T37_PAGE_NUM1 1
+#define T37_PAGE_NUM2 2
+#define T37_PAGE_NUM3 3
+
+#define MSG_RID 0
+
+#define T6_CFG_RESET 0
+#define T6_CFG_BACKUPNV 1
+#define T6_CFG_CALIBRATE 2
+#define T6_CFG_REPORTALL 3
+/* Reserved */
+#define T6_CFG_DIAG 5
+
+#define T6_CFG_DIAG_CMD_PAGEUP 0x01
+#define T6_CFG_DIAG_CMD_PAGEDOWN 0x02
+#define T6_CFG_DIAG_CMD_DELTAS 0x10
+#define T6_CFG_DIAG_CMD_REF 0x11
+#define T6_CFG_DIAG_CMD_CTE 0x31
+#define T6_CFG_DIAG_CMD_TCH 0xF3
+
+#define T6_MSG_STATUS 1
+#define T6_MSG_CHECKSUM 2 /* three bytes */
+
+#define T6_MSG_STATUS_COMSERR BIT(2)
+#define T6_MSG_STATUS_CFGERR BIT(3)
+#define T6_MSG_STATUS_CAL BIT(4)
+#define T6_MSG_STATUS_SIGERR BIT(5)
+#define T6_MSG_STATUS_OFL BIT(6)
+#define T6_MSG_STATUS_RESET BIT(7)
+
+#define T7_CFG_IDLEACQINT 0
+#define T7_CFG_ACTVACQINT 1
+#define T7_CFG_ACTV2IDLETO 2
+
+#define T8_CFG_CHRGTIME 0
+/* Reserved */
+#define T8_CFG_TCHDRIFT 2
+#define T8_CFG_DRIFTST 3
+#define T8_CFG_TCHAUTOCAL 4
+#define T8_CFG_SYNC 5
+#define T8_CFG_ATCHCALST 6
+#define T8_CFG_ATCHCALSTHR 7
+#define T8_CFG_ATCHFRCCALTHR 8 /* FW v2.x */
+#define T8_CFG_ATCHFRCCALRATIO 9 /* FW v2.x */
+
+#define T9_CFG_CTRL 0
+#define T9_CFG_XORIGIN 1
+#define T9_CFG_YORIGIN 2
+#define T9_CFG_XSIZE 3
+#define T9_CFG_YSIZE 4
+#define T9_CFG_AKSCFG 5
+#define T9_CFG_BLEN 6
+#define T9_CFG_TCHTHR 7
+#define T9_CFG_TCHDI 8
+#define T9_CFG_ORIENT 9
+#define T9_CFG_MRGTIMEOUT 10
+#define T9_CFG_MOVHYSTI 11
+#define T9_CFG_MOVHYSTN 12
+#define T9_CFG_MOVFILTER 13
+#define T9_CFG_NUMTOUCH 14
+#define T9_CFG_MRGHYST 15
+#define T9_CFG_MRGTHR 16
+#define T9_CFG_AMPHYST 17
+#define T9_CFG_XRANGE 18 /* two bytes */
+#define T9_CFG_YRANGE 20 /* two bytes */
+#define T9_CFG_XLOCLIP 22
+#define T9_CFG_XHICLIP 23
+#define T9_CFG_YLOCLIP 24
+#define T9_CFG_YHICLIP 25
+#define T9_CFG_XEDGECTRL 26
+#define T9_CFG_XEDGEDIST 27
+#define T9_CFG_YEDGECTRL 28
+#define T9_CFG_YEDGEDIST 29
+#define T9_CFG_JUMPLIMIT 30
+#define T9_CFG_TCHHYST 31 /* FW v2.x */
+
+#define T9_MSG_STATUS 1
+#define T9_MSG_XPOSMSB 2
+#define T9_MSG_YPOSMSB 3
+#define T9_MSG_XYPOSLSB 4
+#define T9_MSG_TCHAREA 5
+#define T9_MSG_TCHAMPLITUDE 6
+#define T9_MSG_TCHVECTOR 7
+
+#define T9_MSG_STATUS_UNGRIP BIT(0) /* FW v2.x */
+#define T9_MSG_STATUS_SUPPRESS BIT(1)
+#define T9_MSG_STATUS_AMP BIT(2)
+#define T9_MSG_STATUS_VECTOR BIT(3)
+#define T9_MSG_STATUS_MOVE BIT(4)
+#define T9_MSG_STATUS_RELEASE BIT(5)
+#define T9_MSG_STATUS_PRESS BIT(6)
+#define T9_MSG_STATUS_DETECT BIT(7)
+
+#define T20_CFG_CTRL 0
+#define T20_CFG_XLOGRIP 1
+#define T20_CFG_XHIGRIP 2
+#define T20_CFG_YLOGRIP 3
+#define T20_CFG_YHIGRIP 4
+#define T20_CFG_MAXTCHS 5
+/* Reserved */
+#define T20_CFG_SZTHR1 7
+#define T20_CFG_SZTHR2 8
+#define T20_CFG_SHPTHR1 9
+#define T20_CFG_SHPTHR2 10
+#define T20_CFG_SHPEXTTO 11
+
+#define T20_MSG_STATUS 1
+
+#define T20_MSG_STATUS_FACESUP BIT(0)
+
+#define T22_CFG_CTRL 0
+/* Reserved */
+#define T22_CFG_GCAFUL 3 /* two bytes */
+#define T22_CFG_GCAFLL 5 /* two bytes */
+#define T22_CFG_ACTVGCAFVALID 7
+#define T22_CFG_NOISETHR 8
+/* Reserved */
+#define T22_CFG_FREQHOPSCALE 10
+#define T22_CFG_FREQ 11 /* five bytes */
+#define T22_CFG_IDLEGCAFVAILD 16
+
+#define T22_MSG_STATUS 1
+#define T22_MSG_GCAFDEPTH 2
+#define T22_MSG_FREQINDEX 3
+
+#define T22_MSG_STATUS_FHCHG BIT(0)
+#define T22_MSG_STATUS_GCAFERR BIT(2)
+#define T22_MSG_STATUS_FHERR BIT(3)
+#define T22_MSG_STATUS_GCAFCHG BIT(4)
+
+#define T19_CFG_CTRL 0
+#define T19_CFG_REPORTMASK 1
+#define T19_CFG_DIR 2
+#define T19_CFG_INTPULLUP 3
+#define T19_CFG_OUT 4
+#define T19_CFG_WAKE 5
+#define T19_CFG_PWM 6
+#define T19_CFG_PERIOD 7
+#define T19_CFG_DUTY0 8
+#define T19_CFG_DUTY1 9
+#define T19_CFG_DUTY2 10
+#define T19_CFG_DUTY3 11
+#define T19_CFG_TRIGGER0 12
+#define T19_CFG_TRIGGER1 13
+#define T19_CFG_TRIGGER2 14
+#define T19_CFG_TRIGGER3 15
+
+#define T19_CFG_CTRL_ENABLE BIT(0)
+#define T19_CFG_CTRL_RPTEN BIT(1)
+#define T19_CFG_CTRL_FORCERPT BIT(2)
+
+#define T19_MSG_STATUS 1
+
+#define T25_CFG_CTRL 0
+#define T25_CFG_CMD 1
+
+#define T25_MSG_STATUS 1
+#define T25_MSG_INFO 2 /* five bytes */
+
+#define T28_CFG_CTRL 0
+#define T28_CFG_CMD 1
+#define T28_CFG_MODE 2
+#define T28_CFG_IDLEGCAFDEPTH 3
+#define T28_CFG_ACTVGCAFDEPTH 4
+#define T28_CFG_VOLTAGE 5
+
+#define T28_CFG_MODE0_X 16
+#define T28_CFG_MODE0_Y 14
+
+#define T28_MSG_STATUS 1
+
+#define T48_NOISESUPPRESSION_CFG 1
+
+/* cable_config[] of atmel_i2c_platform_data */
+/* config[] of atmel_config_data */
+#define CB_TCHTHR 0
+#define CB_NOISETHR 1
+#define CB_IDLEGCAFDEPTH 2
+#define CB_ACTVGCAFDEPTH 3
+
+#define NC_TCHTHR 0
+#define NC_TCHDI 1
+#define NC_NOISETHR 2
+
+/* filter_level */
+#define FL_XLOGRIPMIN 0
+#define FL_XLOGRIPMAX 1
+#define FL_XHIGRIPMIN 2
+#define FL_XHIGRIPMAX 3
+
+struct info_id_t {
+ uint8_t family_id;
+ uint8_t variant_id;
+ uint8_t version;
+ uint8_t build;
+ uint8_t matrix_x_size;
+ uint8_t matrix_y_size;
+ uint8_t num_declared_objects;
+};
+
+struct object_t {
+ uint8_t object_type;
+ uint16_t i2c_address;
+ uint8_t size;
+ uint8_t instances;
+ uint8_t num_report_ids;
+ uint8_t report_ids;
+};
+
+struct atmel_virtual_key {
+ int keycode;
+ int range_min;
+ int range_max;
+};
+
+struct atmel_finger_data {
+ int x;
+ int y;
+ int w;
+ int z;
+};
+
+struct atmel_i2c_platform_data {
+ uint16_t version;
+ uint16_t source;
+ uint16_t abs_x_min;
+ uint16_t abs_x_max;
+ uint16_t abs_y_min;
+ uint16_t abs_y_max;
+ uint8_t abs_pressure_min;
+ uint8_t abs_pressure_max;
+ uint8_t abs_width_min;
+ uint8_t abs_width_max;
+ uint8_t abs_area_min;
+ uint8_t abs_area_max;
+ int gpio_irq;
+ int gpio_reset;
+ int (*power)(int on);
+ u8 config_T6[6];
+ u8 config_T7[3];
+ u8 config_T8[10];
+ u8 config_T9[35];
+ u8 config_T15[11];
+ u8 config_T19[16];
+ u8 config_T20[12];
+ u8 config_T22[17];
+ u8 config_T23[15];
+ u8 config_T24[19];
+ u8 config_T25[14];
+ u8 config_T27[7];
+ u8 config_T28[6];
+ u8 config_T40[5];
+ u8 config_T42[8];
+ u8 config_T46[9];
+ u8 config_T47[10];
+ u8 config_T48[54];
+ u8 object_crc[3];
+ u8 cable_config[4];
+ u8 cable_config_T7[3];
+ u8 cable_config_T8[10];
+ u8 cable_config_T9[35];
+ u8 cable_config_T22[17];
+ u8 cable_config_T28[6];
+ u8 cable_config_T46[9];
+ u8 cable_config_T48[54];
+ u8 noise_config[3];
+ u16 filter_level[4];
+ u8 GCAF_level[5];
+ u8 ATCH_NOR[6];
+ u8 ATCH_NOR_20S[6];
+};
+
+struct atmel_config_data {
+ int8_t config[4];
+ int8_t *config_T7;
+ int8_t *config_T8;
+ int8_t *config_T9;
+ int8_t *config_T22;
+ int8_t *config_T28;
+ int8_t *config_T46;
+ int8_t *config_T48;
+};
+
+#define ATMEL_I2C_RETRY_TIMES 10
+
+/* config_setting */
+#define NONE 0
+#define CONNECTED 1
+struct atmel_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct atmel_i2c_platform_data *pdata;
+ struct workqueue_struct *atmel_wq;
+ struct work_struct work;
+ int (*power) (int on);
+ struct info_id_t *id;
+ struct object_t *object_table;
+ struct iomux_block *gpio_block;
+ struct block_config *gpio_block_config;
+ uint8_t finger_count;
+ uint16_t abs_x_min;
+ uint16_t abs_x_max;
+ uint16_t abs_y_min;
+ uint16_t abs_y_max;
+ uint8_t abs_area_min;
+ uint8_t abs_area_max;
+ uint8_t abs_width_min;
+ uint8_t abs_width_max;
+ uint8_t abs_pressure_min;
+ uint8_t abs_pressure_max;
+ uint8_t first_pressed;
+ struct atmel_finger_data finger_data[10];
+ uint8_t finger_type;
+ uint8_t finger_support;
+ uint16_t finger_pressed;
+ uint8_t face_suppression;
+ uint8_t grip_suppression;
+ uint8_t noise_status[2];
+ uint16_t *filter_level;
+ uint8_t calibration_confirm;
+ uint64_t timestamp;
+ struct atmel_config_data config_setting[2];
+ int8_t noise_config[3];
+ uint8_t status;
+ uint8_t GCAF_sample;
+ uint8_t *GCAF_level;
+ uint8_t noisethr;
+ uint8_t noisethr_config;
+ uint8_t diag_command;
+ uint8_t *ATCH_EXT;
+ int8_t *ATCH_NOR;
+ int8_t *ATCH_NOR_20S;
+ int pre_data[11];
+ /*unlock flag used to indicate calibration after unlock system*/
+ int unlock_flag;
+
+ /*For usb detect*/
+ struct work_struct usb_work;
+ struct notifier_block nb;
+ unsigned long usb_event;
+ struct mutex lock;
+};
+
+static struct atmel_ts_data *private_ts;
+
+#define LDO_POWR_VOLTAGE 2700000 /*2.7v*/
+static struct regulator *LDO;
+
+int i2c_atmel_read(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length)
+{
+ int retry, ret;
+ uint8_t addr[2];
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = addr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = data,
+ }
+ };
+ addr[0] = address & 0xFF;
+ addr[1] = (address >> 8) & 0xFF;
+
+ for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) {
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if ((ret == 2) || (ret == -ERESTARTSYS))
+ break;
+ mdelay(10);
+ }
+ if (retry == ATMEL_I2C_RETRY_TIMES) {
+ dev_err(&client->dev, "k3ts, %s: i2c_read_block retry over %d\n", __func__,
+ ATMEL_I2C_RETRY_TIMES);
+ return -EIO;
+ }
+ return 0;
+}
+
+int i2c_atmel_write(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length)
+{
+ int retry, loop_i, ret;
+ uint8_t buf[length + 2];
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = length + 2,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = address & 0xFF;
+ buf[1] = (address >> 8) & 0xFF;
+
+ for (loop_i = 0; loop_i < length; loop_i++)
+ buf[loop_i + 2] = data[loop_i];
+
+ for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) {
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if ((ret == 1) || (ret == -ERESTARTSYS))
+ break;
+ mdelay(10);
+ }
+
+ if (retry == ATMEL_I2C_RETRY_TIMES) {
+ dev_err(&client->dev, "k3ts, %s: i2c_write_block retry over %d\n", __func__,
+ ATMEL_I2C_RETRY_TIMES);
+ return -EIO;
+ }
+ return 0;
+
+}
+
+int i2c_atmel_write_byte_data(struct i2c_client *client, uint16_t address, uint8_t value)
+{
+ i2c_atmel_write(client, address, &value, 1);
+ return 0;
+}
+
+uint16_t get_object_address(struct atmel_ts_data *ts, uint8_t object_type)
+{
+ uint8_t loop_i;
+ for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) {
+ if (ts->object_table[loop_i].object_type == object_type)
+ return ts->object_table[loop_i].i2c_address;
+ }
+ return 0;
+}
+uint8_t get_object_size(struct atmel_ts_data *ts, uint8_t object_type)
+{
+ uint8_t loop_i;
+ for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) {
+ if (ts->object_table[loop_i].object_type == object_type)
+ return ts->object_table[loop_i].size;
+ }
+ return 0;
+}
+
+uint8_t get_object_size_from_address(struct atmel_ts_data *ts, int address)
+{
+ uint8_t loop_i;
+ for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) {
+ if (ts->object_table[loop_i].i2c_address == address)
+ return ts->object_table[loop_i].size;
+ }
+ return 0;
+}
+
+uint8_t get_rid(struct atmel_ts_data *ts, uint8_t object_type)
+{
+ uint8_t loop_i;
+ for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) {
+ if (ts->object_table[loop_i].object_type == object_type)
+ return ts->object_table[loop_i].report_ids;
+ }
+ return 0;
+}
+
+static void check_calibration(struct atmel_ts_data *ts)
+{
+ uint8_t data[T37_DATA + T37_TCH_FLAG_SIZE];
+ uint8_t loop_i, loop_j, x_limit = 0, check_mask, tch_ch = 0, atch_ch = 0;
+
+ memset(data, 0xFF, sizeof(data));
+ i2c_atmel_write_byte_data(ts->client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_DIAG, T6_CFG_DIAG_CMD_TCH);
+
+ for (loop_i = 0;
+ !(data[T37_MODE] == T6_CFG_DIAG_CMD_TCH && data[T37_PAGE] == T37_PAGE_NUM0) && loop_i < 10; loop_i++) {
+ msleep(5);
+ i2c_atmel_read(ts->client,
+ get_object_address(ts, DIAGNOSTIC_T37), data, 2);
+ }
+
+ if (loop_i == 10)
+ dev_err(&ts->client->dev, "k3ts, %s: Diag data not ready\n", __func__);
+
+ i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data,
+ T37_DATA + T37_TCH_FLAG_SIZE);
+ if (data[T37_MODE] == T6_CFG_DIAG_CMD_TCH &&
+ data[T37_PAGE] == T37_PAGE_NUM0) {
+ x_limit = T28_CFG_MODE0_X + ts->config_setting[NONE].config_T28[T28_CFG_MODE];
+ x_limit = x_limit << 1;
+ if (x_limit <= 40) {
+ for (loop_i = 0; loop_i < x_limit; loop_i += 2) {
+ for (loop_j = 0; loop_j < BITS_PER_BYTE; loop_j++) {
+ check_mask = BIT_MASK(loop_j);
+ if (data[T37_DATA + T37_TCH_FLAG_IDX + loop_i] &
+ check_mask)
+ tch_ch++;
+ if (data[T37_DATA + T37_TCH_FLAG_IDX + loop_i + 1] &
+ check_mask)
+ tch_ch++;
+ if (data[T37_DATA + T37_ATCH_FLAG_IDX + loop_i] &
+ check_mask)
+ atch_ch++;
+ if (data[T37_DATA + T37_ATCH_FLAG_IDX + loop_i + 1] &
+ check_mask)
+ atch_ch++;
+ }
+ }
+ }
+ }
+ i2c_atmel_write_byte_data(ts->client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_DIAG, T6_CFG_DIAG_CMD_PAGEUP);
+
+ if (tch_ch && (atch_ch == 0)) {
+ if (jiffies > (ts->timestamp + HZ/2) && (ts->calibration_confirm == 1)) {
+ ts->calibration_confirm = 2;
+ }
+ if (ts->calibration_confirm < 2)
+ ts->calibration_confirm = 1;
+ ts->timestamp = jiffies;
+ } else if (atch_ch > 1 || tch_ch > 8) {
+ ts->calibration_confirm = 0;
+ i2c_atmel_write_byte_data(ts->client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_CALIBRATE, 0x55);
+ }
+}
+
+static void confirm_calibration(struct atmel_ts_data *ts)
+{
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) +
+ T8_CFG_TCHAUTOCAL, ts->ATCH_NOR_20S, 6);
+ ts->pre_data[0] = 2;
+}
+
+static void msg_process_finger_data_x10y10bit(struct atmel_finger_data *fdata, uint8_t *data)
+{
+ fdata->x = data[T9_MSG_XPOSMSB] << 2 | data[T9_MSG_XYPOSLSB] >> 6;
+ fdata->y = data[T9_MSG_YPOSMSB] << 2 | (data[T9_MSG_XYPOSLSB] & 0x0C) >>2;
+ fdata->w = data[T9_MSG_TCHAREA];
+ fdata->z = data[T9_MSG_TCHAMPLITUDE];
+}
+static void msg_process_finger_data_x10y12bit(struct atmel_finger_data *fdata, uint8_t *data)
+{
+ fdata->x = data[T9_MSG_XPOSMSB] << 2 | data[T9_MSG_XYPOSLSB] >> 6;
+ fdata->y = data[T9_MSG_YPOSMSB] << 4 | (data[T9_MSG_XYPOSLSB] & 0x0F) ;
+ fdata->w = data[T9_MSG_TCHAREA];
+ fdata->z = data[T9_MSG_TCHAMPLITUDE];
+}
+
+static void msg_process_multitouch(struct atmel_ts_data *ts, uint8_t *data, uint8_t idx)
+{
+ if (ts->calibration_confirm < 2 && ts->id->version == 0x10)
+ check_calibration(ts);
+ if(ts->abs_y_max >= 1024) {
+ msg_process_finger_data_x10y12bit(&ts->finger_data[idx], data);
+ } else {
+ msg_process_finger_data_x10y10bit(&ts->finger_data[idx], data);
+ }
+ if (data[T9_MSG_STATUS] & T9_MSG_STATUS_RELEASE) {
+ if (ts->grip_suppression & BIT(idx))
+ ts->grip_suppression &= ~BIT(idx);
+ if (ts->finger_pressed & BIT(idx)) {
+ ts->finger_count--;
+ ts->finger_pressed &= ~BIT(idx);
+ if (!ts->first_pressed) {
+ if (!ts->finger_count)
+ ts->first_pressed = 1;
+ }
+ if (ts->pre_data[0] < 2 && ts->unlock_flag != 1) {
+
+ if (ts->finger_count) {
+ i2c_atmel_write_byte_data(ts->client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_CALIBRATE, 0x55);
+ } else if (!ts->finger_count && ts->pre_data[0] == 1)
+ ts->pre_data[0] = 0;
+ }
+ }
+ } else if ((data[T9_MSG_STATUS] & (T9_MSG_STATUS_DETECT | T9_MSG_STATUS_PRESS))
+ && !(ts->finger_pressed & BIT(idx))) {
+ if (ts->id->version >= 0x10 && ts->pre_data[0] < 2) {
+ if (jiffies > (ts->timestamp + 20 * HZ)) {
+ confirm_calibration(ts);
+ }
+ }
+ if (!(ts->grip_suppression & BIT(idx))) {
+ ts->finger_count++;
+ ts->finger_pressed |= BIT(idx);
+ if (ts->id->version >= 0x10 && ts->pre_data[0] < 2) {
+ ts->pre_data[idx + 1] = ts->finger_data[idx].x;
+ ts->pre_data[idx + 2] = ts->finger_data[idx].y;
+ if (ts->finger_count == ts->finger_support) {
+ i2c_atmel_write_byte_data(ts->client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_CALIBRATE, 0x55);
+ } else if (!ts->pre_data[0] && ts->finger_count == 1)
+ ts->pre_data[0] = 1;
+ }
+ }
+ } else if ((data[T9_MSG_STATUS] & (T9_MSG_STATUS_DETECT|T9_MSG_STATUS_PRESS))
+ && ts->pre_data[0] < 2 && ts->unlock_flag != 1) {
+ if (ts->finger_count == 1 && ts->pre_data[0] &&
+ (idx == 0 && ((abs(ts->finger_data[idx].y - ts->pre_data[idx + 2]) > 50)
+ || (abs(ts->finger_data[idx].x - ts->pre_data[idx + 1]) > 50))))
+ {
+ ts->unlock_flag = 1;
+ ts->calibration_confirm = 2;
+ }
+ }
+
+}
+
+static void compatible_input_report(struct input_dev *idev,
+ struct atmel_finger_data *fdata, uint8_t press, uint8_t last)
+{
+ if (!press) {
+ input_mt_sync(idev);
+ /*input_report_key(idev, BTN_TOUCH, 0);*/
+ input_report_key(idev, BTN_TOUCH, 1);
+
+ } else {
+ TS_DEBUG_ATMEL("k3ts, %s: Touch report_key x = %d, y = %d, z = %d, w = %d\n ", __func__,
+ fdata->x, fdata->y, fdata->z, fdata->w);
+ input_report_abs(idev, ABS_MT_TOUCH_MAJOR, fdata->z);
+ input_report_abs(idev, ABS_MT_WIDTH_MAJOR, fdata->w);
+ input_report_abs(idev, ABS_MT_POSITION_X, fdata->x);
+ input_report_abs(idev, ABS_MT_POSITION_Y, fdata->y);
+ input_mt_sync(idev);
+ }
+}
+
+
+
+static void multi_input_report(struct atmel_ts_data *ts)
+{
+ uint8_t loop_i, finger_report = 0;
+
+ for (loop_i = 0; loop_i < ts->finger_support; loop_i++) {
+ if (ts->finger_pressed & BIT(loop_i)) {
+ compatible_input_report(ts->input_dev, &ts->finger_data[loop_i],
+ 1, (ts->finger_count == ++finger_report));
+ }
+ }
+}
+static irqreturn_t atmel_interrupt_fun(int irq, void *dev_id)
+{
+ int ret;
+ struct atmel_ts_data *ts = dev_id;
+ uint8_t data[7];
+ int8_t report_type;
+ uint8_t msg_byte_num = 7;
+
+ memset(data, 0x0, sizeof(data));
+
+ mutex_lock(&ts->lock);
+ ret = i2c_atmel_read(ts->client, get_object_address(ts,
+ GEN_MESSAGEPROCESSOR_T5), data, 7);
+
+ report_type = data[MSG_RID] - ts->finger_type;
+ if (report_type >= 0 && report_type < ts->finger_support) {
+ msg_process_multitouch(ts, data, report_type);
+ } else {
+ if (data[MSG_RID] == get_rid(ts, GEN_COMMANDPROCESSOR_T6)) {
+ if (data[1] & 0x10) {
+ ts->timestamp = jiffies;
+ }
+ if (data[1] & 0x80) {
+ msleep(100);
+
+ i2c_atmel_write_byte_data(ts->client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_CALIBRATE, 0x55);
+ }
+ msg_byte_num = 5;
+ }
+ if (data[MSG_RID] == get_rid(ts, PROCI_TOUCHSUPPRESSION_T42)) {
+ if (ts->calibration_confirm < 2 && ts->id->version == 0x10) {
+ i2c_atmel_write_byte_data(ts->client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_CALIBRATE, 0x55);
+ }
+ ts->face_suppression = data[T20_MSG_STATUS];
+ printk(KERN_INFO "Touch Face suppression %s: ",
+ ts->face_suppression ? "Active" : "Inactive");
+ msg_byte_num = 2;
+ }
+ }
+ if (!ts->finger_count || ts->face_suppression) {
+ ts->finger_pressed = 0;
+ ts->finger_count = 0;
+ compatible_input_report(ts->input_dev, NULL, 0, 1);
+ } else {
+ multi_input_report(ts);
+ }
+ input_sync(ts->input_dev);
+ mutex_unlock(&ts->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int read_object_table(struct atmel_ts_data *ts)
+{
+ uint8_t i, type_count = 0;
+ uint8_t data[6];
+ memset(data, 0x0, sizeof(data));
+
+ ts->object_table = kzalloc(sizeof(struct object_t)*ts->id->num_declared_objects, GFP_KERNEL);
+ if (ts->object_table == NULL) {
+ dev_err(&ts->client->dev, "k3ts, %s: allocate object_table failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < ts->id->num_declared_objects; i++) {
+ i2c_atmel_read(ts->client, i * 6 + 0x07, data, 6);
+ ts->object_table[i].object_type = data[OBJ_TABLE_TYPE];
+ ts->object_table[i].i2c_address =
+ data[OBJ_TABLE_LSB] | data[OBJ_TABLE_MSB] << 8;
+ ts->object_table[i].size = data[OBJ_TABLE_SIZE] + 1;
+ ts->object_table[i].instances = data[OBJ_TABLE_INSTANCES];
+ ts->object_table[i].num_report_ids = data[OBJ_TABLE_RIDS];
+ if (data[OBJ_TABLE_RIDS]) {
+ ts->object_table[i].report_ids = type_count + 1;
+ type_count += data[OBJ_TABLE_RIDS];
+ }
+ if (data[OBJ_TABLE_TYPE] == TOUCH_MULTITOUCHSCREEN_T9)
+ ts->finger_type = ts->object_table[i].report_ids;
+ }
+
+ return 0;
+}
+
+struct atmel_i2c_platform_data *atmel_ts_get_pdata(struct i2c_client *client)
+{
+ struct device_node *node = client->dev.of_node;
+ struct atmel_i2c_platform_data *pdata = client->dev.platform_data;
+ u32 data[8];
+
+ if (pdata)
+ return pdata;
+
+ if (!node)
+ return NULL;
+
+ pdata = devm_kzalloc(&client->dev, sizeof(struct atmel_i2c_platform_data),
+ GFP_KERNEL);
+ pdata->gpio_irq = of_get_named_gpio(node, "atmel-ts,gpio-irq", 0);
+ pdata->gpio_reset = of_get_named_gpio(node, "atmel-ts,gpio-reset", 0);
+
+ of_property_read_u32_array(node, "atmel-ts,abs", &data[0], 8);
+ pdata->abs_x_min = data[0];
+ pdata->abs_x_max = data[1];
+ pdata->abs_y_min = data[2];
+ pdata->abs_y_max = data[3];
+ pdata->abs_pressure_min = data[4];
+ pdata->abs_pressure_max = data[5];
+ pdata->abs_width_min = data[6];
+ pdata->abs_width_max = data[7];
+
+ of_property_read_u8_array(node, "atmel-ts,cfg_t6", &pdata->config_T6[0], 6);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t7", &pdata->config_T7[0], 3);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t8", &pdata->config_T8[0], 10);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t9", &pdata->config_T9[0], 35);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t15", &pdata->config_T15[0], 11);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t19", &pdata->config_T19[0], 16);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t23", &pdata->config_T23[0], 15);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t25", &pdata->config_T25[0], 14);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t40", &pdata->config_T40[0], 5);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t42", &pdata->config_T42[0], 8);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t46", &pdata->config_T46[0], 9);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t47", &pdata->config_T47[0], 10);
+ of_property_read_u8_array(node, "atmel-ts,cfg_t48", &pdata->config_T48[0], 54);
+ of_property_read_u8_array(node, "atmel-ts,object_crc", &pdata->object_crc[0], 3);
+ of_property_read_u8_array(node, "atmel-ts,cable_config", &pdata->cable_config[0], 4);
+ of_property_read_u8_array(node, "atmel-ts,cable_config_t7", &pdata->cable_config_T7[0], 3);
+ of_property_read_u8_array(node, "atmel-ts,cable_config_t8", &pdata->cable_config_T8[0], 10);
+ of_property_read_u8_array(node, "atmel-ts,cable_config_t46", &pdata->cable_config_T46[0], 9);
+ of_property_read_u8_array(node, "atmel-ts,cable_config_t48", &pdata->cable_config_T48[0], 54);
+ of_property_read_u8_array(node, "atmel-ts,noise_config", &pdata->noise_config[0], 3);
+ of_property_read_u16_array(node, "atmel-ts,filter_level", &pdata->filter_level[0], 4);
+ of_property_read_u8_array(node, "atmel-ts,gcaf_level", &pdata->GCAF_level[0], 5);
+ of_property_read_u8_array(node, "atmel-ts,atch_nor", &pdata->ATCH_NOR[0], 6);
+ of_property_read_u8_array(node, "atmel-ts,atch_nor_20s", &pdata->ATCH_NOR_20S[0], 6);
+
+ return pdata;
+}
+
+static int atmel_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct atmel_ts_data *ts;
+ struct atmel_i2c_platform_data *pdata;
+ int ret = 0, intr = 0;
+ uint8_t loop_i;
+ struct i2c_msg msg[2];
+ uint8_t data[16];
+ uint8_t CRC_check = 0;
+
+ client->dev.init_name = "atmel-ts";
+ LDO = regulator_get(&client->dev, "ldo");
+ if (IS_ERR(LDO)) {
+ dev_err(&client->dev, "no regulator found\n");
+ LDO = NULL;
+ } else {
+ ret = regulator_enable(LDO);
+ if (!ret)
+ ret = regulator_set_voltage(LDO, LDO_POWR_VOLTAGE, LDO_POWR_VOLTAGE);
+ if (ret)
+ dev_err(&client->dev, "k3ts, %s: failed to set LDO\n", __func__);
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "k3ts, %s: need I2C_FUNC_I2C\n", __func__);
+ ret = -ENODEV;
+ goto err_check_functionality_failed;
+ }
+
+ ts = kzalloc(sizeof(struct atmel_ts_data), GFP_KERNEL);
+ if (ts == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_data_failed;
+ }
+
+ ts->unlock_flag = 0;
+ mutex_init(&ts->lock);
+
+ ts->atmel_wq = create_singlethread_workqueue("atmel_wq");
+ if (!ts->atmel_wq) {
+ dev_err(&client->dev, "k3ts, %s: create workqueue failed\n", __func__);
+ ret = -ENOMEM;
+ goto err_cread_wq_failed;
+ }
+
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+
+ pdata = atmel_ts_get_pdata(client);
+ ts->pdata = pdata;
+
+ if (pdata) {
+ ts->power = pdata->power;
+ intr = pdata->gpio_irq;
+ client->irq = gpio_to_irq(intr);
+ }
+ if (ts->power)
+ ret = ts->power(1);
+ ret = gpio_request(intr, "gpio_tp_intr");
+ if (ret) {
+ dev_err(&client->dev, "gpio_request %d failed\n", intr);
+ goto err_request_gpio_failed;
+ }
+ ret = gpio_direction_input(intr);
+ if (ret) {
+ dev_err(&client->dev, "k3ts, %s: gpio_direction_input failed %d\n", __func__, intr);
+ goto err_gpio_direction_failed;
+ }
+
+ ret = gpio_request(ts->pdata->gpio_reset, "gpio_tp_reset");
+ if (ret) {
+ dev_err(&client->dev, "k3ts, %s: gpio_request failed %d, ret = %d\n",
+ __func__, ts->pdata->gpio_reset, ret);
+ goto err_request_gpio_reset_failed;
+ }
+
+ gpio_direction_output(ts->pdata->gpio_reset, 1);
+ mdelay(5);
+ gpio_direction_output(ts->pdata->gpio_reset, 0);
+ mdelay(10);
+ gpio_direction_output(ts->pdata->gpio_reset, 1);
+ mdelay(50);
+
+ for (loop_i = 0; loop_i < 10; loop_i++) {
+ if (!gpio_get_value(intr))
+ break;
+ msleep(10);
+ }
+
+ if (loop_i == 10)
+ dev_err(&client->dev, "k3ts, %s: No Messages\n", __func__);
+
+ /* read message*/
+ msg[0].addr = ts->client->addr;
+ msg[0].flags = I2C_M_RD;
+ msg[0].len = 7;
+ msg[0].buf = data;
+ ret = i2c_transfer(client->adapter, msg, 1);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "k3ts, %s: No Atmel chip inside\n", __func__);
+ goto err_detect_failed;
+ }
+ if (ts->power)
+ ret = ts->power(2);
+
+ if (data[MSG_RID] == 0x01 &&
+ (data[T6_MSG_STATUS] & (T6_MSG_STATUS_SIGERR|T6_MSG_STATUS_COMSERR))) {
+ dev_err(&client->dev, "k3ts, %s: init err: %x\n", __func__, data[1]);
+ goto err_detect_failed;
+ } else {
+ for (loop_i = 0; loop_i < 10; loop_i++) {
+ if (gpio_get_value(intr)) {
+ dev_err(&client->dev, "k3ts, %s: No more message\n", __func__);
+ break;
+ }
+ ret = i2c_transfer(client->adapter, msg, 1);
+ msleep(10);
+ }
+ }
+
+ /* Read the info block data. */
+ ts->id = kzalloc(sizeof(struct info_id_t), GFP_KERNEL);
+ if (ts->id == NULL) {
+ dev_err(&client->dev, "k3ts, %s: allocate info_id_t failed\n", __func__);
+ goto err_alloc_failed;
+ }
+ ret = i2c_atmel_read(client, 0x00, data, 7);
+
+ ts->id->family_id = data[INFO_BLK_FID];
+ ts->id->variant_id = data[INFO_BLK_VID];
+ if (ts->id->family_id == 0x80 && ts->id->variant_id == 0x10)
+ ts->id->version = data[INFO_BLK_VER] + 6;
+ else
+ ts->id->version = data[INFO_BLK_VER];
+
+ ts->id->build = data[INFO_BLK_BUILD];
+ ts->id->matrix_x_size = data[INFO_BLK_XSIZE];
+ ts->id->matrix_y_size = data[INFO_BLK_YSIZE];
+ ts->id->num_declared_objects = data[INFO_BLK_OBJS];
+
+ /* Read object table. */
+ ret = read_object_table(ts);
+ if (ret < 0)
+ goto err_read_table_failed;
+
+
+ if (pdata) {
+ ts->finger_support = pdata->config_T9[T9_CFG_NUMTOUCH];
+
+ /* OBJECT CONFIG CRC check */
+ if (pdata->object_crc[0]) {
+ ret = i2c_atmel_write_byte_data(client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_CALIBRATE, 0x55);
+ for (loop_i = 0; loop_i < 10; loop_i++) {
+ if (!gpio_get_value(intr)) {
+ ret = i2c_atmel_read(ts->client, get_object_address(ts,
+ GEN_MESSAGEPROCESSOR_T5), data, 5);
+ if (data[MSG_RID] == get_rid(ts, GEN_COMMANDPROCESSOR_T6))
+ break;
+ }
+ msleep(10);
+ }
+ if (loop_i == 10)
+ dev_err(&client->dev, "k3ts, %s: No checksum read\n", __func__);
+ else {
+ dev_info(&client->dev, "k3ts, %s: CRC print : %x, %x, %x\n", __func__,
+ data[T6_MSG_CHECKSUM + 0], data[T6_MSG_CHECKSUM + 1], data[T6_MSG_CHECKSUM + 2]);
+ for (loop_i = 0; loop_i < 3; loop_i++) {
+ if (pdata->object_crc[loop_i] != data[T6_MSG_CHECKSUM + loop_i]) {
+ dev_err(&client->dev,
+ "k3ts, %s: CRC Error: %x, %x\n", __func__,
+ pdata->object_crc[loop_i],
+ data[T6_MSG_CHECKSUM + loop_i]);
+ break;
+ }
+ }
+ if (loop_i == 3) {
+ dev_info(&client->dev, "k3ts, %s: CRC passed: ", __func__);
+ for (loop_i = 0; loop_i < 3; loop_i++)
+ pr_info("0x%2.2X ", pdata->object_crc[loop_i]);
+ pr_info("\n");
+ CRC_check = 1;/*means CRC check OK*/
+ }
+ }
+ }
+ ts->abs_x_min = pdata->abs_x_min;
+ ts->abs_x_max = pdata->abs_x_max;
+ ts->abs_y_min = pdata->abs_y_min;
+ ts->abs_y_max = pdata->abs_y_max;
+ ts->abs_pressure_min = pdata->abs_pressure_min;
+ ts->abs_pressure_max = pdata->abs_pressure_max;
+ ts->abs_width_min = pdata->abs_width_min;
+ ts->abs_width_max = pdata->abs_width_max;
+
+ ts->GCAF_level = pdata->GCAF_level;
+ if (ts->id->version >= 0x10) {
+ ts->ATCH_EXT = &pdata->config_T8[6];
+ ts->timestamp = jiffies + 60 * HZ;
+ }
+ ts->ATCH_NOR = pdata->ATCH_NOR;
+ ts->ATCH_NOR_20S = pdata->ATCH_NOR_20S;
+ ts->filter_level = pdata->filter_level;
+
+ ts->config_setting[NONE].config_T7
+ = ts->config_setting[CONNECTED].config_T7
+ = pdata->config_T7;
+ ts->config_setting[NONE].config_T8 = pdata->config_T8;
+ ts->config_setting[CONNECTED].config_T8 = pdata->cable_config_T8;
+ ts->config_setting[NONE].config_T9 = pdata->config_T9;
+ ts->config_setting[NONE].config_T22 = pdata->config_T22;
+ ts->config_setting[NONE].config_T28 = pdata->config_T28;
+ ts->config_setting[NONE].config_T46 = pdata->config_T46;
+ ts->config_setting[NONE].config_T48 = pdata->config_T48;
+ ts->config_setting[CONNECTED].config_T46 = pdata->cable_config_T46;
+ ts->config_setting[CONNECTED].config_T48 = pdata->cable_config_T48;
+
+ if (pdata->noise_config[0])
+ for (loop_i = 0; loop_i < 3; loop_i++)
+ ts->noise_config[loop_i] = pdata->noise_config[loop_i];
+
+ if (pdata->cable_config[0]) {
+ ts->config_setting[NONE].config[CB_TCHTHR] =
+ pdata->config_T9[T9_CFG_TCHTHR];
+ ts->config_setting[NONE].config[CB_NOISETHR] =
+ pdata->config_T22[T22_CFG_NOISETHR];
+ ts->config_setting[NONE].config[CB_IDLEGCAFDEPTH] =
+ pdata->config_T28[T28_CFG_IDLEGCAFDEPTH];
+ ts->config_setting[NONE].config[CB_ACTVGCAFDEPTH] =
+ pdata->config_T28[T28_CFG_ACTVGCAFDEPTH];
+ for (loop_i = 0; loop_i < 4; loop_i++)
+ ts->config_setting[CONNECTED].config[loop_i] =
+ pdata->cable_config[loop_i];
+ ts->GCAF_sample =
+ ts->config_setting[CONNECTED].config[CB_ACTVGCAFDEPTH];
+ if (ts->id->version >= 0x20)
+ ts->noisethr = pdata->cable_config[CB_TCHTHR] -
+ pdata->config_T9[T9_CFG_TCHHYST];
+ else
+ ts->noisethr = pdata->cable_config[CB_TCHTHR];
+ ts->noisethr_config =
+ ts->config_setting[CONNECTED].config[CB_NOISETHR];
+ } else {
+ if (pdata->cable_config_T7[0])
+ ts->config_setting[CONNECTED].config_T7 =
+ pdata->cable_config_T7;
+ if (pdata->cable_config_T8[0])
+ ts->config_setting[CONNECTED].config_T8 =
+ pdata->cable_config_T8;
+ if (pdata->cable_config_T9[0]) {
+ ts->config_setting[CONNECTED].config_T9 =
+ pdata->cable_config_T9;
+ ts->config_setting[CONNECTED].config_T22 =
+ pdata->cable_config_T22;
+ ts->config_setting[CONNECTED].config_T28 =
+ pdata->cable_config_T28;
+ ts->GCAF_sample =
+ ts->config_setting[CONNECTED].config_T28[T28_CFG_ACTVGCAFDEPTH];
+ }
+ if (ts->status == CONNECTED)
+ ts->noisethr = (ts->id->version >= 0x20) ?
+ pdata->cable_config_T9[T9_CFG_TCHTHR] - pdata->cable_config_T9[T9_CFG_TCHHYST] :
+ pdata->cable_config_T9[T9_CFG_TCHTHR];
+ else
+ ts->noisethr = (ts->id->version >= 0x20) ?
+ pdata->config_T9[T9_CFG_TCHTHR] - pdata->config_T9[T9_CFG_TCHHYST] :
+ pdata->config_T9[T9_CFG_TCHTHR];
+ ts->noisethr_config = pdata->cable_config_T22[T22_CFG_NOISETHR];
+
+ }
+
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6),
+ pdata->config_T6,
+ get_object_size(ts, GEN_COMMANDPROCESSOR_T6));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, GEN_POWERCONFIG_T7),
+ pdata->config_T7,
+ get_object_size(ts, GEN_POWERCONFIG_T7));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, GEN_ACQUISITIONCONFIG_T8),
+ pdata->config_T8,
+ get_object_size(ts, GEN_ACQUISITIONCONFIG_T8));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9),
+ pdata->config_T9,
+ get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, TOUCH_KEYARRAY_T15),
+ pdata->config_T15,
+ get_object_size(ts, TOUCH_KEYARRAY_T15));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, SPT_GPIOPWM_T19),
+ pdata->config_T19,
+ get_object_size(ts, SPT_GPIOPWM_T19));
+
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, PROCI_GRIPSUPPRESSION_T40),
+ pdata->config_T40,
+ get_object_size(ts, PROCI_GRIPSUPPRESSION_T40));
+
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, PROCI_TOUCHSUPPRESSION_T42),
+ pdata->config_T42,
+ get_object_size(ts, PROCI_TOUCHSUPPRESSION_T42));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, PROCG_NOISESUPPRESSION_T48),
+ pdata->config_T48,
+ get_object_size(ts, PROCG_NOISESUPPRESSION_T48));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, TOUCH_PROXIMITY_T23),
+ pdata->config_T23,
+ get_object_size(ts, TOUCH_PROXIMITY_T23));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, SPT_SELFTEST_T25),
+ pdata->config_T25,
+ get_object_size(ts, SPT_SELFTEST_T25));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, SPT_CTECONFIG_T46),
+ pdata->config_T46,
+ get_object_size(ts, SPT_CTECONFIG_T46));
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, PROCI_STYLUS_T47),
+ pdata->config_T47,
+ get_object_size(ts, PROCI_STYLUS_T47));
+
+ ret = i2c_atmel_write_byte_data(client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_BACKUPNV, 0x55);
+
+ for (loop_i = 0; loop_i < 10; loop_i++) {
+ if (!gpio_get_value(intr))
+ break;
+ dev_err(&client->dev, "k3ts, %s: wait for Message(%d)\n", __func__, loop_i + 1);
+ msleep(10);
+ }
+
+ i2c_atmel_read(client,
+ get_object_address(ts, GEN_MESSAGEPROCESSOR_T5), data, 7);
+
+ ret = i2c_atmel_write_byte_data(client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_RESET, 0x11);/*reset*/
+ msleep(100);
+
+ if (ts->status == CONNECTED) {
+ if (ts->config_setting[CONNECTED].config_T8 != NULL)
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, GEN_ACQUISITIONCONFIG_T8),
+ ts->config_setting[CONNECTED].config_T8,
+ get_object_size(ts, GEN_ACQUISITIONCONFIG_T8));
+ if (ts->config_setting[CONNECTED].config_T46 != NULL)
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, SPT_CTECONFIG_T46),
+ ts->config_setting[CONNECTED].config_T46,
+ get_object_size(ts, SPT_CTECONFIG_T46));
+ if (ts->config_setting[CONNECTED].config_T48 != NULL) {
+ i2c_atmel_write(ts->client,
+ get_object_address(ts, PROCG_NOISESUPPRESSION_T48),
+ ts->config_setting[CONNECTED].config_T48,
+ get_object_size(ts, PROCG_NOISESUPPRESSION_T48));
+ }
+ }
+ }
+ ts->calibration_confirm = 0;
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "k3ts, %s: Failed to allocate input device\n", __func__);
+ goto err_input_dev_alloc_failed;
+ }
+ /*Modified by z181527 for Debug Only*/
+ ts->input_dev->name = "synaptics"/*"atmel-touchscreen"*/;
+ set_bit(EV_SYN, ts->input_dev->evbit);
+ set_bit(EV_KEY, ts->input_dev->evbit);
+ set_bit(BTN_TOUCH, ts->input_dev->keybit);
+ set_bit(BTN_2, ts->input_dev->keybit);
+ set_bit(EV_ABS, ts->input_dev->evbit);
+ set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
+ ts->abs_x_min, ts->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
+ ts->abs_y_min, ts->abs_y_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ ts->abs_pressure_min, ts->abs_pressure_max,
+ 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ ts->abs_width_min, ts->abs_width_max, 0, 0);
+
+ ret = input_register_device(ts->input_dev);
+ if (ret) {
+ dev_err(&client->dev,
+ "k3ts, %s: atmel_ts_probe: Unable to register %s input device\n", __func__,
+ ts->input_dev->name);
+ goto err_input_register_device_failed;
+ }
+
+ ret = request_threaded_irq(client->irq, NULL, atmel_interrupt_fun,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, ts);
+ if (ret)
+ dev_err(&client->dev, "k3ts, %s: request_irq failed\n", __func__);
+
+ private_ts = ts;
+
+ dev_info(&client->dev, "k3ts, %s: probe %s successfully\n", __func__,
+ ts->input_dev->name);
+
+ return 0;
+
+err_input_register_device_failed:
+ input_free_device(ts->input_dev);
+err_input_dev_alloc_failed:
+err_read_table_failed:
+ kfree(ts->id);
+err_alloc_failed:
+err_detect_failed:
+err_gpio_direction_failed:
+err_request_gpio_reset_failed:
+ gpio_free(ts->pdata->gpio_reset);
+ gpio_free(intr);
+err_request_gpio_failed:
+ destroy_workqueue(ts->atmel_wq);
+err_cread_wq_failed:
+ kfree(ts);
+err_alloc_data_failed:
+err_check_functionality_failed:
+ if (LDO != NULL) {
+ regulator_disable(LDO);
+ regulator_put(LDO);
+ }
+ return ret;
+}
+
+static int atmel_ts_remove(struct i2c_client *client)
+{
+ struct atmel_ts_data *ts = i2c_get_clientdata(client);
+
+ free_irq(client->irq, ts);
+
+ destroy_workqueue(ts->atmel_wq);
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+
+ regulator_disable(LDO);
+ regulator_put(LDO);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int atmel_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct atmel_ts_data *ts = i2c_get_clientdata(client);
+ struct atmel_i2c_platform_data *pdata = ts->pdata;
+ uint8_t data[7];
+ int ret = 0;
+
+ mutex_lock(&ts->lock);
+ ts->finger_pressed = 0;
+ ts->finger_count = 0;
+ ts->first_pressed = 0;
+
+ if (ts->id->version >= 0x10) {
+ ts->pre_data[0] = 0;
+ ret = i2c_atmel_write(ts->client,
+ get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + T8_CFG_ATCHCALST,
+ ts->ATCH_EXT, 4);
+ if (ret < 0)
+ pr_err("k3ts, %s: failed to write config T8\n", __func__);
+ }
+
+ ret = i2c_atmel_write_byte_data(client,
+ get_object_address(ts, GEN_POWERCONFIG_T7) + T7_CFG_IDLEACQINT, 0x0);
+ if (ret < 0)
+ pr_err("k3ts, %s: failed to write config T7\n", __func__);
+
+ ret = i2c_atmel_write_byte_data(client,
+ get_object_address(ts, GEN_POWERCONFIG_T7) + T7_CFG_ACTVACQINT, 0x0);
+ if (ret < 0)
+ pr_err("k3ts, %s: failed to write config T7\n", __func__);
+
+ /* Read T5 until gpio_irq is HIGH level */
+ if (!gpio_get_value(pdata->gpio_irq)) {
+ ret = i2c_atmel_read(ts->client, get_object_address(ts,
+ GEN_MESSAGEPROCESSOR_T5), data, 7);
+ if (ret < 0) {
+ pr_err("k3ts, %s: failed to read T5\n", __func__);
+ }
+ }
+
+ mutex_unlock(&ts->lock);
+
+ pr_info("[%s]: -\n", __func__);
+ return 0;
+}
+
+static int atmel_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct atmel_ts_data *ts = i2c_get_clientdata(client);
+ int ret = 0;
+
+ pr_info("[%s]: +\n", __func__);
+
+ mutex_lock(&ts->lock);
+ if (ts->id->version >= 0x10)
+ ts->timestamp = jiffies;
+
+ ts->unlock_flag = 0;
+
+ ret = i2c_atmel_write(ts->client,
+ get_object_address(ts, GEN_POWERCONFIG_T7),
+ ts->config_setting[ts->status].config_T7,
+ get_object_size(ts, GEN_POWERCONFIG_T7));
+ if (ret < 0)
+ pr_err("k3ts, %s: failed to write config T7\n", __func__);
+
+ ts->calibration_confirm = 0;
+ msleep(1);
+
+ ret = i2c_atmel_write(ts->client,
+ get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) +
+ T8_CFG_TCHAUTOCAL, ts->ATCH_NOR, 6);
+ if (ret < 0)
+ pr_err("k3ts, %s: failed to write config T8\n", __func__);
+
+ ret = i2c_atmel_write_byte_data(client,
+ get_object_address(ts, GEN_COMMANDPROCESSOR_T6) +
+ T6_CFG_CALIBRATE, 0x55);
+ if (ret < 0)
+ pr_err("k3ts, %s: failed to write config T6\n", __func__);
+
+ mutex_unlock(&ts->lock);
+
+ pr_info("[%s]: -\n", __func__);
+
+ return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(atmel_ts_pm_ops, atmel_ts_suspend, atmel_ts_resume);
+
+static const struct i2c_device_id atml_ts_i2c_id[] = {
+ { ATMEL_MXT224E_NAME, 0 },
+ { }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_ts_dt_ids[] = {
+ { .compatible = "atmel,ts-mxt224e", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, atmel_ts_dt_ids);
+#endif
+
+static struct i2c_driver atmel_ts_driver = {
+ .id_table = atml_ts_i2c_id,
+ .probe = atmel_ts_probe,
+ .remove = atmel_ts_remove,
+ .driver = {
+ .of_match_table = of_match_ptr(atmel_ts_dt_ids),
+ .name = ATMEL_MXT224E_NAME,
+ .pm = &atmel_ts_pm_ops,
+ },
+};
+module_i2c_driver(atmel_ts_driver);
+
+MODULE_DESCRIPTION("ATMEL Touch driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 73052ba9ca62..1dca5bc2cc43 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
+obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o
obj-$(CONFIG_ARCH_MMP) += irq-mmp.o
obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o
obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index f8636a650cf6..e3620d7b1f59 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -49,14 +49,14 @@ static void combiner_mask_irq(struct irq_data *data)
{
u32 mask = 1 << (data->hwirq % 32);
- __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR);
+ writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR);
}
static void combiner_unmask_irq(struct irq_data *data)
{
u32 mask = 1 << (data->hwirq % 32);
- __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET);
+ writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_SET);
}
static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
@@ -69,7 +69,7 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
chained_irq_enter(chip, desc);
spin_lock(&irq_controller_lock);
- status = __raw_readl(chip_data->base + COMBINER_INT_STATUS);
+ status = readl_relaxed(chip_data->base + COMBINER_INT_STATUS);
spin_unlock(&irq_controller_lock);
status &= chip_data->irq_mask;
@@ -130,7 +130,7 @@ static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
combiner_data->parent_irq = irq;
/* Disable all interrupts */
- __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
+ writel_relaxed(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
}
static int combiner_irq_domain_xlate(struct irq_domain *d,
diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c
new file mode 100644
index 000000000000..9c8f833522e6
--- /dev/null
+++ b/drivers/irqchip/irq-hip04.c
@@ -0,0 +1,424 @@
+/*
+ * Hisilicon HiP04 INTC
+ *
+ * Copyright (C) 2002-2014 ARM Limited.
+ * Copyright (c) 2013-2014 Hisilicon Ltd.
+ * Copyright (c) 2013-2014 Linaro Ltd.
+ *
+ * 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.
+ *
+ * Interrupt architecture for the HIP04 INTC:
+ *
+ * o There is one Interrupt Distributor, which receives interrupts
+ * from system devices and sends them to the Interrupt Controllers.
+ *
+ * o There is one CPU Interface per CPU, which sends interrupts sent
+ * by the Distributor, and interrupts generated locally, to the
+ * associated CPU. The base address of the CPU interface is usually
+ * aliased so that the same address points to different chips depending
+ * on the CPU it is accessed from.
+ *
+ * Note that IRQs 0-31 are special - they are local to each CPU.
+ * As such, the enable set/clear, pending set/clear and active bit
+ * registers are banked per-cpu for these sources.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
+#include <linux/cpumask.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/irqchip/arm-gic.h>
+
+#include <asm/irq.h>
+#include <asm/exception.h>
+#include <asm/smp_plat.h>
+
+#include "irq-gic-common.h"
+#include "irqchip.h"
+
+#define HIP04_MAX_IRQS 510
+
+struct hip04_irq_data {
+ void __iomem *dist_base;
+ void __iomem *cpu_base;
+ struct irq_domain *domain;
+ unsigned int nr_irqs;
+};
+
+static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+
+/*
+ * The GIC mapping of CPU interfaces does not necessarily match
+ * the logical CPU numbering. Let's use a mapping as returned
+ * by the GIC itself.
+ */
+#define NR_HIP04_CPU_IF 16
+static u16 hip04_cpu_map[NR_HIP04_CPU_IF] __read_mostly;
+
+static struct hip04_irq_data hip04_data __read_mostly;
+
+static inline void __iomem *hip04_dist_base(struct irq_data *d)
+{
+ struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d);
+ return hip04_data->dist_base;
+}
+
+static inline void __iomem *hip04_cpu_base(struct irq_data *d)
+{
+ struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d);
+ return hip04_data->cpu_base;
+}
+
+static inline unsigned int hip04_irq(struct irq_data *d)
+{
+ return d->hwirq;
+}
+
+/*
+ * Routines to acknowledge, disable and enable interrupts
+ */
+static void hip04_mask_irq(struct irq_data *d)
+{
+ u32 mask = 1 << (hip04_irq(d) % 32);
+
+ raw_spin_lock(&irq_controller_lock);
+ writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_CLEAR +
+ (hip04_irq(d) / 32) * 4);
+ raw_spin_unlock(&irq_controller_lock);
+}
+
+static void hip04_unmask_irq(struct irq_data *d)
+{
+ u32 mask = 1 << (hip04_irq(d) % 32);
+
+ raw_spin_lock(&irq_controller_lock);
+ writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_SET +
+ (hip04_irq(d) / 32) * 4);
+ raw_spin_unlock(&irq_controller_lock);
+}
+
+static void hip04_eoi_irq(struct irq_data *d)
+{
+ writel_relaxed(hip04_irq(d), hip04_cpu_base(d) + GIC_CPU_EOI);
+}
+
+static int hip04_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ void __iomem *base = hip04_dist_base(d);
+ unsigned int irq = hip04_irq(d);
+
+ /* Interrupt configuration for SGIs can't be changed */
+ if (irq < 16)
+ return -EINVAL;
+
+ if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
+ return -EINVAL;
+
+ raw_spin_lock(&irq_controller_lock);
+
+ gic_configure_irq(irq, type, base, NULL);
+
+ raw_spin_unlock(&irq_controller_lock);
+
+ return 0;
+}
+
+#ifdef CONFIG_SMP
+static int hip04_irq_set_affinity(struct irq_data *d,
+ const struct cpumask *mask_val,
+ bool force)
+{
+ void __iomem *reg;
+ unsigned int cpu, shift = (hip04_irq(d) % 2) * 16;
+ u32 val, mask, bit;
+
+ if (!force)
+ cpu = cpumask_any_and(mask_val, cpu_online_mask);
+ else
+ cpu = cpumask_first(mask_val);
+
+ if (cpu >= NR_HIP04_CPU_IF || cpu >= nr_cpu_ids)
+ return -EINVAL;
+
+ raw_spin_lock(&irq_controller_lock);
+ reg = hip04_dist_base(d) + GIC_DIST_TARGET + ((hip04_irq(d) * 2) & ~3);
+ mask = 0xffff << shift;
+ bit = hip04_cpu_map[cpu] << shift;
+ val = readl_relaxed(reg) & ~mask;
+ writel_relaxed(val | bit, reg);
+ raw_spin_unlock(&irq_controller_lock);
+
+ return IRQ_SET_MASK_OK;
+}
+#endif
+
+static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat, irqnr;
+ void __iomem *cpu_base = hip04_data.cpu_base;
+
+ do {
+ irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
+ irqnr = irqstat & GICC_IAR_INT_ID_MASK;
+
+ if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) {
+ irqnr = irq_find_mapping(hip04_data.domain, irqnr);
+ handle_IRQ(irqnr, regs);
+ continue;
+ }
+ if (irqnr < 16) {
+ writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
+#ifdef CONFIG_SMP
+ handle_IPI(irqnr, regs);
+#endif
+ continue;
+ }
+ break;
+ } while (1);
+}
+
+static struct irq_chip hip04_irq_chip = {
+ .name = "HIP04 INTC",
+ .irq_mask = hip04_mask_irq,
+ .irq_unmask = hip04_unmask_irq,
+ .irq_eoi = hip04_eoi_irq,
+ .irq_set_type = hip04_irq_set_type,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = hip04_irq_set_affinity,
+#endif
+};
+
+static u16 hip04_get_cpumask(struct hip04_irq_data *intc)
+{
+ void __iomem *base = intc->dist_base;
+ u32 mask, i;
+
+ for (i = mask = 0; i < 32; i += 2) {
+ mask = readl_relaxed(base + GIC_DIST_TARGET + i * 2);
+ mask |= mask >> 16;
+ if (mask)
+ break;
+ }
+
+ if (!mask)
+ pr_crit("GIC CPU mask not found - kernel will fail to boot.\n");
+
+ return mask;
+}
+
+static void __init hip04_irq_dist_init(struct hip04_irq_data *intc)
+{
+ unsigned int i;
+ u32 cpumask;
+ unsigned int nr_irqs = intc->nr_irqs;
+ void __iomem *base = intc->dist_base;
+
+ writel_relaxed(0, base + GIC_DIST_CTRL);
+
+ /*
+ * Set all global interrupts to this CPU only.
+ */
+ cpumask = hip04_get_cpumask(intc);
+ cpumask |= cpumask << 16;
+ for (i = 32; i < nr_irqs; i += 2)
+ writel_relaxed(cpumask, base + GIC_DIST_TARGET + ((i * 2) & ~3));
+
+ gic_dist_config(base, nr_irqs, NULL);
+
+ writel_relaxed(1, base + GIC_DIST_CTRL);
+}
+
+static void hip04_irq_cpu_init(struct hip04_irq_data *intc)
+{
+ void __iomem *dist_base = intc->dist_base;
+ void __iomem *base = intc->cpu_base;
+ unsigned int cpu_mask, cpu = smp_processor_id();
+ int i;
+
+ /*
+ * Get what the GIC says our CPU mask is.
+ */
+ BUG_ON(cpu >= NR_HIP04_CPU_IF);
+ cpu_mask = hip04_get_cpumask(intc);
+ hip04_cpu_map[cpu] = cpu_mask;
+
+ /*
+ * Clear our mask from the other map entries in case they're
+ * still undefined.
+ */
+ for (i = 0; i < NR_HIP04_CPU_IF; i++)
+ if (i != cpu)
+ hip04_cpu_map[i] &= ~cpu_mask;
+
+ gic_cpu_config(dist_base, NULL);
+
+ writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
+ writel_relaxed(1, base + GIC_CPU_CTRL);
+}
+
+#ifdef CONFIG_SMP
+static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq)
+{
+ int cpu;
+ unsigned long flags, map = 0;
+
+ raw_spin_lock_irqsave(&irq_controller_lock, flags);
+
+ /* Convert our logical CPU mask into a physical one. */
+ for_each_cpu(cpu, mask)
+ map |= hip04_cpu_map[cpu];
+
+ /*
+ * Ensure that stores to Normal memory are visible to the
+ * other CPUs before they observe us issuing the IPI.
+ */
+ dmb(ishst);
+
+ /* this always happens on GIC0 */
+ writel_relaxed(map << 8 | irq, hip04_data.dist_base + GIC_DIST_SOFTINT);
+
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
+}
+#endif
+
+static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ if (hw < 32) {
+ irq_set_percpu_devid(irq);
+ irq_set_chip_and_handler(irq, &hip04_irq_chip,
+ handle_percpu_devid_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+ } else {
+ irq_set_chip_and_handler(irq, &hip04_irq_chip,
+ handle_fasteoi_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+ irq_set_chip_data(irq, d->host_data);
+ return 0;
+}
+
+static int hip04_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *controller,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ unsigned long ret = 0;
+
+ if (d->of_node != controller)
+ return -EINVAL;
+ if (intsize < 3)
+ return -EINVAL;
+
+ /* Get the interrupt number and add 16 to skip over SGIs */
+ *out_hwirq = intspec[1] + 16;
+
+ /* For SPIs, we need to add 16 more to get the irq ID number */
+ if (!intspec[0])
+ *out_hwirq += 16;
+
+ *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+
+ return ret;
+}
+
+#ifdef CONFIG_SMP
+static int hip04_irq_secondary_init(struct notifier_block *nfb,
+ unsigned long action,
+ void *hcpu)
+{
+ if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
+ hip04_irq_cpu_init(&hip04_data);
+ return NOTIFY_OK;
+}
+
+/*
+ * Notifier for enabling the INTC CPU interface. Set an arbitrarily high
+ * priority because the GIC needs to be up before the ARM generic timers.
+ */
+static struct notifier_block hip04_irq_cpu_notifier = {
+ .notifier_call = hip04_irq_secondary_init,
+ .priority = 100,
+};
+#endif
+
+static const struct irq_domain_ops hip04_irq_domain_ops = {
+ .map = hip04_irq_domain_map,
+ .xlate = hip04_irq_domain_xlate,
+};
+
+static int __init
+hip04_of_init(struct device_node *node, struct device_node *parent)
+{
+ irq_hw_number_t hwirq_base = 16;
+ int nr_irqs, irq_base, i;
+
+ if (WARN_ON(!node))
+ return -ENODEV;
+
+ hip04_data.dist_base = of_iomap(node, 0);
+ WARN(!hip04_data.dist_base, "fail to map hip04 intc dist registers\n");
+
+ hip04_data.cpu_base = of_iomap(node, 1);
+ WARN(!hip04_data.cpu_base, "unable to map hip04 intc cpu registers\n");
+
+ /*
+ * Initialize the CPU interface map to all CPUs.
+ * It will be refined as each CPU probes its ID.
+ */
+ for (i = 0; i < NR_HIP04_CPU_IF; i++)
+ hip04_cpu_map[i] = 0xff;
+
+ /*
+ * Find out how many interrupts are supported.
+ * The HIP04 INTC only supports up to 510 interrupt sources.
+ */
+ nr_irqs = readl_relaxed(hip04_data.dist_base + GIC_DIST_CTR) & 0x1f;
+ nr_irqs = (nr_irqs + 1) * 32;
+ if (nr_irqs > HIP04_MAX_IRQS)
+ nr_irqs = HIP04_MAX_IRQS;
+ hip04_data.nr_irqs = nr_irqs;
+
+ nr_irqs -= hwirq_base; /* calculate # of irqs to allocate */
+
+ irq_base = irq_alloc_descs(-1, hwirq_base, nr_irqs, numa_node_id());
+ if (IS_ERR_VALUE(irq_base)) {
+ pr_err("failed to allocate IRQ numbers\n");
+ return -EINVAL;
+ }
+
+ hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base,
+ hwirq_base,
+ &hip04_irq_domain_ops,
+ &hip04_data);
+
+ if (WARN_ON(!hip04_data.domain))
+ return -EINVAL;
+
+#ifdef CONFIG_SMP
+ set_smp_cross_call(hip04_raise_softirq);
+ register_cpu_notifier(&hip04_irq_cpu_notifier);
+#endif
+ set_handle_irq(hip04_handle_irq);
+
+ hip04_irq_dist_init(&hip04_data);
+ hip04_irq_cpu_init(&hip04_data);
+
+ return 0;
+}
+IRQCHIP_DECLARE(hip04_intc, "hisilicon,hip04-intc", hip04_of_init);
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 9fd9c6717e0c..8e5e5b1becec 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -6,6 +6,33 @@ menuconfig MAILBOX
signals. Say Y if your platform supports hardware mailboxes.
if MAILBOX
+config ARM_MHU_MBOX
+ bool "ARM Message Handling Unit (MHU) Mailbox"
+ help
+ This driver provides support for inter-processor communication
+ between System Control Processor (SCP) with Cortex-M3 processor
+ and Application Processors (AP) on some ARM based systems with
+ MHU peripheral.
+
+ SCP controls most of the power managament on the Application
+ Processors. It offers control and management of: the core/cluster
+ power states, various power domain DVFS including the core/cluster,
+ certain system clocks configuration, thermal sensors and many
+ others.
+
+config ARM_SCPI_PROTOCOL
+ bool "ARM System Control and Power Interface (SCPI) Message Protocol"
+ select ARM_MHU_MBOX
+ help
+ System Control and Power Interface (SCPI) Message Protocol is
+ defined for the purpose of communication between the Application
+ Cores(AP) and the System Control Processor(SCP). The MHU peripheral
+ provides a mechanism for inter-processor communication between SCP
+ and AP.
+
+ This protocol library provides interface for all the client drivers
+ making use of the features offered by the SCP.
+
config PL320_MBOX
bool "ARM PL320 Mailbox"
depends on ARM_AMBA
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 6d184dbcaca8..e31c84b6bc40 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -1,3 +1,10 @@
+# Generic MAILBOX API
+
+obj-$(CONFIG_MAILBOX) += mailbox.o
+
+obj-$(CONFIG_ARM_MHU_MBOX) += arm_mhu.o
+obj-$(CONFIG_ARM_SCPI_PROTOCOL) += scpi_protocol.o
+
obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o
diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c
new file mode 100644
index 000000000000..28fb4f01b413
--- /dev/null
+++ b/drivers/mailbox/arm_mhu.c
@@ -0,0 +1,320 @@
+/*
+ * Driver for the Message Handling Unit (MHU) which is the peripheral in
+ * the Compute SubSystem (CSS) providing a mechanism for inter-processor
+ * communication between System Control Processor (SCP) with Cortex-M3
+ * processor and Application Processors (AP).
+ *
+ * The MHU peripheral provides a mechanism to assert interrupt signals to
+ * facilitate inter-processor message passing between the SCP and the AP.
+ * The message payload can be deposited into main memory or on-chip memories.
+ * The MHU supports three bi-directional channels - low priority, high
+ * priority and secure(can't be used in non-secure execution modes)
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * Author: Sudeep Holla <sudeep.holla@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "arm_mhu.h"
+
+struct device* the_scpi_device;
+
+#define DRIVER_NAME "arm_mhu"
+
+/*
+ * +--------------------+-------+---------------+
+ * | Hardware Register | Offset| Driver View |
+ * +--------------------+-------+---------------+
+ * | SCP_INTR_L_STAT | 0x000 | RX_STATUS(L) |
+ * | SCP_INTR_L_SET | 0x008 | RX_SET(L) |
+ * | SCP_INTR_L_CLEAR | 0x010 | RX_CLEAR(L) |
+ * +--------------------+-------+---------------+
+ * | SCP_INTR_H_STAT | 0x020 | RX_STATUS(H) |
+ * | SCP_INTR_H_SET | 0x028 | RX_SET(H) |
+ * | SCP_INTR_H_CLEAR | 0x030 | RX_CLEAR(H) |
+ * +--------------------+-------+---------------+
+ * | CPU_INTR_L_STAT | 0x100 | TX_STATUS(L) |
+ * | CPU_INTR_L_SET | 0x108 | TX_SET(L) |
+ * | CPU_INTR_L_CLEAR | 0x110 | TX_CLEAR(L) |
+ * +--------------------+-------+---------------+
+ * | CPU_INTR_H_STAT | 0x120 | TX_STATUS(H) |
+ * | CPU_INTR_H_SET | 0x128 | TX_SET(H) |
+ * | CPU_INTR_H_CLEAR | 0x130 | TX_CLEAR(H) |
+ * +--------------------+-------+---------------+
+*/
+#define RX_OFFSET(chan) ((idx) * 0x20)
+#define RX_STATUS(chan) RX_OFFSET(chan)
+#define RX_SET(chan) (RX_OFFSET(chan) + 0x8)
+#define RX_CLEAR(chan) (RX_OFFSET(chan) + 0x10)
+
+#define TX_OFFSET(chan) (0x100 + (idx) * 0x20)
+#define TX_STATUS(chan) TX_OFFSET(chan)
+#define TX_SET(chan) (TX_OFFSET(chan) + 0x8)
+#define TX_CLEAR(chan) (TX_OFFSET(chan) + 0x10)
+
+/*
+ * +---------------+-------+----------------+
+ * | Payload | Offset| Driver View |
+ * +---------------+-------+----------------+
+ * | SCP->AP Low | 0x000 | RX_PAYLOAD(L) |
+ * | SCP->AP High | 0x400 | RX_PAYLOAD(H) |
+ * +---------------+-------+----------------+
+ * | AP->SCP Low | 0x200 | TX_PAYLOAD(H) |
+ * | AP->SCP High | 0x600 | TX_PAYLOAD(H) |
+ * +---------------+-------+----------------+
+*/
+#define PAYLOAD_MAX_SIZE 0x200
+#define PAYLOAD_OFFSET 0x400
+#define RX_PAYLOAD(chan) ((chan) * PAYLOAD_OFFSET)
+#define TX_PAYLOAD(chan) ((chan) * PAYLOAD_OFFSET + PAYLOAD_MAX_SIZE)
+
+struct mhu_chan {
+ int index;
+ int rx_irq;
+ struct mhu_ctlr *ctlr;
+ struct mhu_data_buf *data;
+};
+
+struct mhu_ctlr {
+ struct device *dev;
+ void __iomem *mbox_base;
+ void __iomem *payload_base;
+ struct mbox_controller mbox_con;
+ struct mhu_chan channels[CHANNEL_MAX];
+};
+
+static irqreturn_t mbox_handler(int irq, void *p)
+{
+ struct mbox_chan *link = (struct mbox_chan *)p;
+ struct mhu_chan *chan = link->con_priv;
+ struct mhu_ctlr *ctlr = chan->ctlr;
+ void __iomem *mbox_base = ctlr->mbox_base;
+ void __iomem *payload = ctlr->payload_base;
+ int idx = chan->index;
+ u32 status = readl(mbox_base + RX_STATUS(idx));
+
+ if (status && irq == chan->rx_irq) {
+ struct mhu_data_buf *data = chan->data;
+ if (!data)
+ return IRQ_NONE; /* spurious */
+ if (data->rx_buf)
+ memcpy(data->rx_buf, payload + RX_PAYLOAD(idx),
+ data->rx_size);
+ chan->data = NULL;
+ mbox_chan_received_data(link, data);
+ writel(~0, mbox_base + RX_CLEAR(idx));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mhu_send_data(struct mbox_chan *link, void *msg)
+{
+ struct mhu_chan *chan = link->con_priv;
+ struct mhu_ctlr *ctlr = chan->ctlr;
+ void __iomem *mbox_base = ctlr->mbox_base;
+ void __iomem *payload = ctlr->payload_base;
+ struct mhu_data_buf *data = (struct mhu_data_buf *)msg;
+ int idx = chan->index;
+
+ if (!data)
+ return -EINVAL;
+
+ chan->data = data;
+ if (data->tx_buf)
+ memcpy(payload + TX_PAYLOAD(idx), data->tx_buf, data->tx_size);
+ writel(data->cmd, mbox_base + TX_SET(idx));
+
+ return 0;
+}
+
+static int mhu_startup(struct mbox_chan *link)
+{
+ struct mhu_chan *chan = link->con_priv;
+ int err, mbox_irq = chan->rx_irq;
+
+ err = request_threaded_irq(mbox_irq, NULL, mbox_handler, IRQF_ONESHOT,
+ DRIVER_NAME, link);
+ return err;
+}
+
+static void mhu_shutdown(struct mbox_chan *link)
+{
+ struct mhu_chan *chan = link->con_priv;
+
+ chan->data = NULL;
+ free_irq(chan->rx_irq, link);
+}
+
+static bool mhu_last_tx_done(struct mbox_chan *link)
+{
+ struct mhu_chan *chan = link->con_priv;
+ struct mhu_ctlr *ctlr = chan->ctlr;
+ void __iomem *mbox_base = ctlr->mbox_base;
+ int idx = chan->index;
+
+ return !readl(mbox_base + TX_STATUS(idx));
+}
+
+static struct mbox_chan_ops mhu_ops = {
+ .send_data = mhu_send_data,
+ .startup = mhu_startup,
+ .shutdown = mhu_shutdown,
+ .last_tx_done = mhu_last_tx_done,
+};
+
+static int mhu_probe(struct platform_device *pdev)
+{
+ struct mhu_ctlr *ctlr;
+ struct mhu_chan *chan;
+ struct device *dev = &pdev->dev;
+ struct mbox_chan *l;
+ struct resource *res;
+ int idx;
+ static const char * const channel_names[] = {
+ CHANNEL_LOW_PRIORITY,
+ CHANNEL_HIGH_PRIORITY
+ };
+
+ ctlr = devm_kzalloc(dev, sizeof(*ctlr), GFP_KERNEL);
+ if (!ctlr) {
+ dev_err(dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get mailbox memory resource\n");
+ return -ENXIO;
+ }
+
+ ctlr->mbox_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctlr->mbox_base)) {
+ dev_err(dev, "failed to request or ioremap mailbox control\n");
+ return PTR_ERR(ctlr->mbox_base);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(dev, "failed to get payload memory resource\n");
+ return -ENXIO;
+ }
+
+ ctlr->payload_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctlr->payload_base)) {
+ dev_err(dev, "failed to request or ioremap mailbox payload\n");
+ return PTR_ERR(ctlr->payload_base);
+ }
+
+ ctlr->dev = dev;
+ platform_set_drvdata(pdev, ctlr);
+
+ l = devm_kzalloc(dev, sizeof(*l) * CHANNEL_MAX, GFP_KERNEL);
+ if (!l) {
+ dev_err(dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ ctlr->mbox_con.chans = l;
+ ctlr->mbox_con.num_chans = CHANNEL_MAX;
+ ctlr->mbox_con.txdone_irq = true;
+ ctlr->mbox_con.ops = &mhu_ops;
+ ctlr->mbox_con.dev = dev;
+
+ for (idx = 0; idx < CHANNEL_MAX; idx++) {
+ chan = &ctlr->channels[idx];
+ chan->index = idx;
+ chan->ctlr = ctlr;
+ chan->rx_irq = platform_get_irq(pdev, idx);
+ if (chan->rx_irq < 0) {
+ dev_err(dev, "failed to get interrupt for %s\n",
+ channel_names[idx]);
+ return -ENXIO;
+ }
+ l[idx].con_priv = chan;
+ }
+
+ if (mbox_controller_register(&ctlr->mbox_con)) {
+ dev_err(dev, "failed to register mailbox controller\n");
+ return -ENOMEM;
+ }
+
+ the_scpi_device = dev;
+ return 0;
+}
+
+static int mhu_remove(struct platform_device *pdev)
+{
+ struct mhu_ctlr *ctlr = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ mbox_controller_unregister(&ctlr->mbox_con);
+ devm_kfree(dev, ctlr->mbox_con.chans);
+
+ devm_iounmap(dev, ctlr->payload_base);
+ devm_iounmap(dev, ctlr->mbox_base);
+
+ platform_set_drvdata(pdev, NULL);
+ devm_kfree(dev, ctlr);
+ return 0;
+}
+
+static struct of_device_id mhu_of_match[] = {
+ { .compatible = "arm,mhu" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mhu_of_match);
+
+static struct platform_driver mhu_driver = {
+ .probe = mhu_probe,
+ .remove = mhu_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = mhu_of_match,
+ },
+};
+
+static int __init mhu_init(void)
+{
+ return platform_driver_register(&mhu_driver);
+}
+core_initcall(mhu_init);
+
+static void __exit mhu_exit(void)
+{
+ platform_driver_unregister(&mhu_driver);
+}
+module_exit(mhu_exit);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM MHU mailbox driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mailbox/arm_mhu.h b/drivers/mailbox/arm_mhu.h
new file mode 100644
index 000000000000..f78c1fa369c8
--- /dev/null
+++ b/drivers/mailbox/arm_mhu.h
@@ -0,0 +1,33 @@
+/*
+ * ARM Message Handling Unit (MHU) driver header
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#define CONTROLLER_NAME "mhu_ctlr"
+
+#define CHANNEL_MAX 2
+#define CHANNEL_LOW_PRIORITY "cpu_to_scp_low"
+#define CHANNEL_HIGH_PRIORITY "cpu_to_scp_high"
+
+struct mhu_data_buf {
+ u32 cmd;
+ int tx_size;
+ void *tx_buf;
+ int rx_size;
+ void *rx_buf;
+ void *cl_data;
+};
+
+extern struct device* the_scpi_device;
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
new file mode 100644
index 000000000000..df09d3bf55e4
--- /dev/null
+++ b/drivers/mailbox/mailbox.c
@@ -0,0 +1,467 @@
+/*
+ * Mailbox: Common code for Mailbox controllers and users
+ *
+ * Copyright (C) 2013-2014 Linaro Ltd.
+ * Author: Jassi Brar <jassisinghbrar@gmail.com>
+ *
+ * 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 <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/bitops.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox_controller.h>
+
+#define TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */
+#define TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */
+#define TXDONE_BY_ACK BIT(2) /* S/W ACK recevied by Client ticks the TX */
+
+static LIST_HEAD(mbox_cons);
+static DEFINE_MUTEX(con_mutex);
+
+static int add_to_rbuf(struct mbox_chan *chan, void *mssg)
+{
+ int idx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ /* See if there is any space left */
+ if (chan->msg_count == MBOX_TX_QUEUE_LEN) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -ENOBUFS;
+ }
+
+ idx = chan->msg_free;
+ chan->msg_data[idx] = mssg;
+ chan->msg_count++;
+
+ if (idx == MBOX_TX_QUEUE_LEN - 1)
+ chan->msg_free = 0;
+ else
+ chan->msg_free++;
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ return idx;
+}
+
+static void msg_submit(struct mbox_chan *chan)
+{
+ unsigned count, idx;
+ unsigned long flags;
+ void *data;
+ int err;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ if (!chan->msg_count || chan->active_req)
+ goto exit;
+
+ count = chan->msg_count;
+ idx = chan->msg_free;
+ if (idx >= count)
+ idx -= count;
+ else
+ idx += MBOX_TX_QUEUE_LEN - count;
+
+ data = chan->msg_data[idx];
+
+ /* Try to submit a message to the MBOX controller */
+ err = chan->mbox->ops->send_data(chan, data);
+ if (!err) {
+ chan->active_req = data;
+ chan->msg_count--;
+ }
+exit:
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+static void tx_tick(struct mbox_chan *chan, int r)
+{
+ unsigned long flags;
+ void *mssg;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ mssg = chan->active_req;
+ chan->active_req = NULL;
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ /* Submit next message */
+ msg_submit(chan);
+
+ /* Notify the client */
+ if (mssg && chan->cl->tx_done)
+ chan->cl->tx_done(chan->cl, mssg, r);
+
+ if (chan->cl->tx_block)
+ complete(&chan->tx_complete);
+}
+
+static void poll_txdone(unsigned long data)
+{
+ struct mbox_controller *mbox = (struct mbox_controller *)data;
+ bool txdone, resched = false;
+ int i;
+
+ for (i = 0; i < mbox->num_chans; i++) {
+ struct mbox_chan *chan = &mbox->chans[i];
+
+ if (chan->active_req && chan->cl) {
+ resched = true;
+ txdone = chan->mbox->ops->last_tx_done(chan);
+ if (txdone)
+ tx_tick(chan, 0);
+ }
+ }
+
+ if (resched)
+ mod_timer(&mbox->poll, jiffies +
+ msecs_to_jiffies(mbox->period));
+}
+
+/**
+ * mbox_chan_received_data - A way for controller driver to push data
+ * received from remote to the upper layer.
+ * @chan: Pointer to the mailbox channel on which RX happened.
+ * @mssg: Client specific message typecasted as void *
+ *
+ * After startup and before shutdown any data received on the chan
+ * is passed on to the API via atomic mbox_chan_received_data().
+ * The controller should ACK the RX only after this call returns.
+ */
+void mbox_chan_received_data(struct mbox_chan *chan, void *mssg)
+{
+ /* No buffering the received data */
+ if (chan->cl->rx_callback)
+ chan->cl->rx_callback(chan->cl, mssg);
+}
+EXPORT_SYMBOL_GPL(mbox_chan_received_data);
+
+/**
+ * mbox_chan_txdone - A way for controller driver to notify the
+ * framework that the last TX has completed.
+ * @chan: Pointer to the mailbox chan on which TX happened.
+ * @r: Status of last TX - OK or ERROR
+ *
+ * The controller that has IRQ for TX ACK calls this atomic API
+ * to tick the TX state machine. It works only if txdone_irq
+ * is set by the controller.
+ */
+void mbox_chan_txdone(struct mbox_chan *chan, int r)
+{
+ if (unlikely(!(chan->txdone_method & TXDONE_BY_IRQ))) {
+ dev_err(chan->mbox->dev,
+ "Controller can't run the TX ticker\n");
+ return;
+ }
+
+ tx_tick(chan, r);
+}
+EXPORT_SYMBOL_GPL(mbox_chan_txdone);
+
+/**
+ * mbox_client_txdone - The way for a client to run the TX state machine.
+ * @chan: Mailbox channel assigned to this client.
+ * @r: Success status of last transmission.
+ *
+ * The client/protocol had received some 'ACK' packet and it notifies
+ * the API that the last packet was sent successfully. This only works
+ * if the controller can't sense TX-Done.
+ */
+void mbox_client_txdone(struct mbox_chan *chan, int r)
+{
+ if (unlikely(!(chan->txdone_method & TXDONE_BY_ACK))) {
+ dev_err(chan->mbox->dev, "Client can't run the TX ticker\n");
+ return;
+ }
+
+ tx_tick(chan, r);
+}
+EXPORT_SYMBOL_GPL(mbox_client_txdone);
+
+/**
+ * mbox_client_peek_data - A way for client driver to pull data
+ * received from remote by the controller.
+ * @chan: Mailbox channel assigned to this client.
+ *
+ * A poke to controller driver for any received data.
+ * The data is actually passed onto client via the
+ * mbox_chan_received_data()
+ * The call can be made from atomic context, so the controller's
+ * implementation of peek_data() must not sleep.
+ *
+ * Return: True, if controller has, and is going to push after this,
+ * some data.
+ * False, if controller doesn't have any data to be read.
+ */
+bool mbox_client_peek_data(struct mbox_chan *chan)
+{
+ if (chan->mbox->ops->peek_data)
+ return chan->mbox->ops->peek_data(chan);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(mbox_client_peek_data);
+
+/**
+ * mbox_send_message - For client to submit a message to be
+ * sent to the remote.
+ * @chan: Mailbox channel assigned to this client.
+ * @mssg: Client specific message typecasted.
+ *
+ * For client to submit data to the controller destined for a remote
+ * processor. If the client had set 'tx_block', the call will return
+ * either when the remote receives the data or when 'tx_tout' millisecs
+ * run out.
+ * In non-blocking mode, the requests are buffered by the API and a
+ * non-negative token is returned for each queued request. If the request
+ * is not queued, a negative token is returned. Upon failure or successful
+ * TX, the API calls 'tx_done' from atomic context, from which the client
+ * could submit yet another request.
+ * The pointer to message should be preserved until it is sent
+ * over the chan, i.e, tx_done() is made.
+ * This function could be called from atomic context as it simply
+ * queues the data and returns a token against the request.
+ *
+ * Return: Non-negative integer for successful submission (non-blocking mode)
+ * or transmission over chan (blocking mode).
+ * Negative value denotes failure.
+ */
+int mbox_send_message(struct mbox_chan *chan, void *mssg)
+{
+ int t;
+
+ if (!chan || !chan->cl)
+ return -EINVAL;
+
+ t = add_to_rbuf(chan, mssg);
+ if (t < 0) {
+ dev_err(chan->mbox->dev, "Try increasing MBOX_TX_QUEUE_LEN\n");
+ return t;
+ }
+
+ msg_submit(chan);
+
+ init_completion(&chan->tx_complete);
+
+ if (chan->txdone_method == TXDONE_BY_POLL)
+ poll_txdone((unsigned long)chan->mbox);
+
+ if (chan->cl->tx_block && chan->active_req) {
+ unsigned long wait;
+ int ret;
+
+ if (!chan->cl->tx_tout) /* wait forever */
+ wait = msecs_to_jiffies(3600000);
+ else
+ wait = msecs_to_jiffies(chan->cl->tx_tout);
+
+ ret = wait_for_completion_timeout(&chan->tx_complete, wait);
+ if (ret == 0) {
+ t = -EIO;
+ tx_tick(chan, -EIO);
+ }
+ }
+
+ return t;
+}
+EXPORT_SYMBOL_GPL(mbox_send_message);
+
+/**
+ * mbox_request_channel - Request a mailbox channel.
+ * @cl: Identity of the client requesting the channel.
+ * @index: Index of mailbox specifier in 'mboxes' property.
+ *
+ * The Client specifies its requirements and capabilities while asking for
+ * a mailbox channel. It can't be called from atomic context.
+ * The channel is exclusively allocated and can't be used by another
+ * client before the owner calls mbox_free_channel.
+ * After assignment, any packet received on this channel will be
+ * handed over to the client via the 'rx_callback'.
+ * The framework holds reference to the client, so the mbox_client
+ * structure shouldn't be modified until the mbox_free_channel returns.
+ *
+ * Return: Pointer to the channel assigned to the client if successful.
+ * ERR_PTR for request failure.
+ */
+struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
+{
+ struct device *dev = cl->dev;
+ struct mbox_controller *mbox;
+ struct of_phandle_args spec;
+ struct mbox_chan *chan;
+ unsigned long flags;
+ int ret;
+
+ if (!dev || !dev->of_node) {
+ pr_debug("%s: No owner device node\n", __func__);
+ return ERR_PTR(-ENODEV);
+ }
+
+ mutex_lock(&con_mutex);
+
+ if (of_parse_phandle_with_args(dev->of_node, "mboxes",
+ "#mbox-cells", index, &spec)) {
+ dev_dbg(dev, "%s: can't parse \"mboxes\" property\n", __func__);
+ mutex_unlock(&con_mutex);
+ return ERR_PTR(-ENODEV);
+ }
+
+ chan = NULL;
+ list_for_each_entry(mbox, &mbox_cons, node)
+ if (mbox->dev->of_node == spec.np) {
+ chan = mbox->of_xlate(mbox, &spec);
+ break;
+ }
+
+ of_node_put(spec.np);
+
+ if (!chan || chan->cl || !try_module_get(mbox->dev->driver->owner)) {
+ dev_dbg(dev, "%s: mailbox not free\n", __func__);
+ mutex_unlock(&con_mutex);
+ return ERR_PTR(-EBUSY);
+ }
+
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->msg_free = 0;
+ chan->msg_count = 0;
+ chan->active_req = NULL;
+ chan->cl = cl;
+ init_completion(&chan->tx_complete);
+
+ if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
+ chan->txdone_method |= TXDONE_BY_ACK;
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ ret = chan->mbox->ops->startup(chan);
+ if (ret) {
+ dev_err(dev, "Unable to startup the chan (%d)\n", ret);
+ mbox_free_channel(chan);
+ chan = ERR_PTR(ret);
+ }
+
+ mutex_unlock(&con_mutex);
+ return chan;
+}
+EXPORT_SYMBOL_GPL(mbox_request_channel);
+
+/**
+ * mbox_free_channel - The client relinquishes control of a mailbox
+ * channel by this call.
+ * @chan: The mailbox channel to be freed.
+ */
+void mbox_free_channel(struct mbox_chan *chan)
+{
+ unsigned long flags;
+
+ if (!chan || !chan->cl)
+ return;
+
+ chan->mbox->ops->shutdown(chan);
+
+ /* The queued TX requests are simply aborted, no callbacks are made */
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->cl = NULL;
+ chan->active_req = NULL;
+ if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK))
+ chan->txdone_method = TXDONE_BY_POLL;
+
+ module_put(chan->mbox->dev->driver->owner);
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+EXPORT_SYMBOL_GPL(mbox_free_channel);
+
+static struct mbox_chan *
+of_mbox_index_xlate(struct mbox_controller *mbox,
+ const struct of_phandle_args *sp)
+{
+ int ind = sp->args[0];
+
+ if (ind >= mbox->num_chans)
+ return NULL;
+
+ return &mbox->chans[ind];
+}
+
+/**
+ * mbox_controller_register - Register the mailbox controller
+ * @mbox: Pointer to the mailbox controller.
+ *
+ * The controller driver registers its communication channels
+ */
+int mbox_controller_register(struct mbox_controller *mbox)
+{
+ int i, txdone;
+
+ /* Sanity check */
+ if (!mbox || !mbox->dev || !mbox->ops || !mbox->num_chans)
+ return -EINVAL;
+
+ if (mbox->txdone_irq)
+ txdone = TXDONE_BY_IRQ;
+ else if (mbox->txdone_poll)
+ txdone = TXDONE_BY_POLL;
+ else /* It has to be ACK then */
+ txdone = TXDONE_BY_ACK;
+
+ if (txdone == TXDONE_BY_POLL) {
+ mbox->poll.function = &poll_txdone;
+ mbox->poll.data = (unsigned long)mbox;
+ init_timer(&mbox->poll);
+ }
+
+ for (i = 0; i < mbox->num_chans; i++) {
+ struct mbox_chan *chan = &mbox->chans[i];
+
+ chan->cl = NULL;
+ chan->mbox = mbox;
+ chan->txdone_method = txdone;
+ spin_lock_init(&chan->lock);
+ }
+
+ if (!mbox->of_xlate)
+ mbox->of_xlate = of_mbox_index_xlate;
+
+ mutex_lock(&con_mutex);
+ list_add_tail(&mbox->node, &mbox_cons);
+ mutex_unlock(&con_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mbox_controller_register);
+
+/**
+ * mbox_controller_unregister - Unregister the mailbox controller
+ * @mbox: Pointer to the mailbox controller.
+ */
+void mbox_controller_unregister(struct mbox_controller *mbox)
+{
+ int i;
+
+ if (!mbox)
+ return;
+
+ mutex_lock(&con_mutex);
+
+ list_del(&mbox->node);
+
+ for (i = 0; i < mbox->num_chans; i++)
+ mbox_free_channel(&mbox->chans[i]);
+
+ if (mbox->txdone_poll)
+ del_timer_sync(&mbox->poll);
+
+ mutex_unlock(&con_mutex);
+}
+EXPORT_SYMBOL_GPL(mbox_controller_unregister);
diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c
index d873cbae2fbb..f3755e0aa935 100644
--- a/drivers/mailbox/pl320-ipc.c
+++ b/drivers/mailbox/pl320-ipc.c
@@ -26,7 +26,7 @@
#include <linux/device.h>
#include <linux/amba/bus.h>
-#include <linux/mailbox.h>
+#include <linux/pl320-ipc.h>
#define IPCMxSOURCE(m) ((m) * 0x40)
#define IPCMxDSET(m) (((m) * 0x40) + 0x004)
diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c
new file mode 100644
index 000000000000..49b500cd87ef
--- /dev/null
+++ b/drivers/mailbox/scpi_protocol.c
@@ -0,0 +1,350 @@
+/*
+ * System Control and Power Interface (SCPI) Message Protocol driver
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/mailbox_client.h>
+#include <linux/scpi_protocol.h>
+#include <linux/slab.h>
+
+#include "arm_mhu.h"
+
+#define CMD_ID_SHIFT 0
+#define CMD_ID_MASK 0xff
+#define CMD_SENDER_ID_SHIFT 8
+#define CMD_SENDER_ID_MASK 0xff
+#define CMD_DATA_SIZE_SHIFT 20
+#define CMD_DATA_SIZE_MASK 0x1ff
+#define PACK_SCPI_CMD(cmd, sender, txsz) \
+ ((((cmd) & CMD_ID_MASK) << CMD_ID_SHIFT) | \
+ (((sender) & CMD_SENDER_ID_MASK) << CMD_SENDER_ID_SHIFT) | \
+ (((txsz) & CMD_DATA_SIZE_MASK) << CMD_DATA_SIZE_SHIFT))
+
+#define MAX_DVFS_DOMAINS 3
+#define MAX_DVFS_OPPS 4
+#define DVFS_LATENCY(hdr) ((hdr) >> 16)
+#define DVFS_OPP_COUNT(hdr) (((hdr) >> 8) & 0xff)
+
+enum scpi_error_codes {
+ SCPI_SUCCESS = 0, /* Success */
+ SCPI_ERR_PARAM = 1, /* Invalid parameter(s) */
+ SCPI_ERR_ALIGN = 2, /* Invalid alignment */
+ SCPI_ERR_SIZE = 3, /* Invalid size */
+ SCPI_ERR_HANDLER = 4, /* Invalid handler/callback */
+ SCPI_ERR_ACCESS = 5, /* Invalid access/permission denied */
+ SCPI_ERR_RANGE = 6, /* Value out of range */
+ SCPI_ERR_TIMEOUT = 7, /* Timeout has occurred */
+ SCPI_ERR_NOMEM = 8, /* Invalid memory area or pointer */
+ SCPI_ERR_PWRSTATE = 9, /* Invalid power state */
+ SCPI_ERR_SUPPORT = 10, /* Not supported or disabled */
+ SCPI_ERR_DEVICE = 11, /* Device error */
+ SCPI_ERR_MAX
+};
+
+enum scpi_client_id {
+ SCPI_CL_NONE,
+ SCPI_CL_CLOCKS,
+ SCPI_CL_DVFS,
+ SCPI_CL_POWER,
+ SCPI_MAX,
+};
+
+enum scpi_std_cmd {
+ SCPI_CMD_INVALID = 0x00,
+ SCPI_CMD_SCPI_READY = 0x01,
+ SCPI_CMD_SCPI_CAPABILITIES = 0x02,
+ SCPI_CMD_EVENT = 0x03,
+ SCPI_CMD_SET_CSS_PWR_STATE = 0x04,
+ SCPI_CMD_GET_CSS_PWR_STATE = 0x05,
+ SCPI_CMD_CFG_PWR_STATE_STAT = 0x06,
+ SCPI_CMD_GET_PWR_STATE_STAT = 0x07,
+ SCPI_CMD_SYS_PWR_STATE = 0x08,
+ SCPI_CMD_L2_READY = 0x09,
+ SCPI_CMD_SET_AP_TIMER = 0x0a,
+ SCPI_CMD_CANCEL_AP_TIME = 0x0b,
+ SCPI_CMD_DVFS_CAPABILITIES = 0x0c,
+ SCPI_CMD_GET_DVFS_INFO = 0x0d,
+ SCPI_CMD_SET_DVFS = 0x0e,
+ SCPI_CMD_GET_DVFS = 0x0f,
+ SCPI_CMD_GET_DVFS_STAT = 0x10,
+ SCPI_CMD_SET_RTC = 0x11,
+ SCPI_CMD_GET_RTC = 0x12,
+ SCPI_CMD_CLOCK_CAPABILITIES = 0x13,
+ SCPI_CMD_SET_CLOCK_INDEX = 0x14,
+ SCPI_CMD_SET_CLOCK_VALUE = 0x15,
+ SCPI_CMD_GET_CLOCK_VALUE = 0x16,
+ SCPI_CMD_PSU_CAPABILITIES = 0x17,
+ SCPI_CMD_SET_PSU = 0x18,
+ SCPI_CMD_GET_PSU = 0x19,
+ SCPI_CMD_SENSOR_CAPABILITIES = 0x1a,
+ SCPI_CMD_SENSOR_INFO = 0x1b,
+ SCPI_CMD_SENSOR_VALUE = 0x1c,
+ SCPI_CMD_SENSOR_CFG_PERIODIC = 0x1d,
+ SCPI_CMD_SENSOR_CFG_BOUNDS = 0x1e,
+ SCPI_CMD_SENSOR_ASYNC_VALUE = 0x1f,
+ SCPI_CMD_COUNT
+};
+
+struct scpi_data_buf {
+ int client_id;
+ struct mhu_data_buf *data;
+ struct completion complete;
+};
+
+static int high_priority_cmds[] = {
+ SCPI_CMD_GET_CSS_PWR_STATE,
+ SCPI_CMD_CFG_PWR_STATE_STAT,
+ SCPI_CMD_GET_PWR_STATE_STAT,
+ SCPI_CMD_SET_DVFS,
+ SCPI_CMD_GET_DVFS,
+ SCPI_CMD_SET_RTC,
+ SCPI_CMD_GET_RTC,
+ SCPI_CMD_SET_CLOCK_INDEX,
+ SCPI_CMD_SET_CLOCK_VALUE,
+ SCPI_CMD_GET_CLOCK_VALUE,
+ SCPI_CMD_SET_PSU,
+ SCPI_CMD_GET_PSU,
+ SCPI_CMD_SENSOR_VALUE,
+ SCPI_CMD_SENSOR_CFG_PERIODIC,
+ SCPI_CMD_SENSOR_CFG_BOUNDS,
+};
+
+static struct scpi_opp *scpi_opps[MAX_DVFS_DOMAINS];
+
+static int scpi_linux_errmap[SCPI_ERR_MAX] = {
+ 0, -EINVAL, -ENOEXEC, -EMSGSIZE,
+ -EINVAL, -EACCES, -ERANGE, -ETIMEDOUT,
+ -ENOMEM, -EINVAL, -EOPNOTSUPP, -EIO,
+};
+
+static inline int scpi_to_linux_errno(int errno)
+{
+ if (errno >= SCPI_SUCCESS && errno < SCPI_ERR_MAX)
+ return scpi_linux_errmap[errno];
+ return -EIO;
+}
+
+static bool high_priority_chan_supported(int cmd)
+{
+ int idx;
+ for (idx = 0; idx < ARRAY_SIZE(high_priority_cmds); idx++)
+ if (cmd == high_priority_cmds[idx])
+ return true;
+ return false;
+}
+
+static void scpi_rx_callback(struct mbox_client *cl, void *msg)
+{
+ struct mhu_data_buf *data = (struct mhu_data_buf *)msg;
+ struct scpi_data_buf *scpi_buf = data->cl_data;
+ complete(&scpi_buf->complete);
+}
+
+static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority)
+{
+ struct mbox_chan *chan;
+ struct mbox_client cl;
+ struct mhu_data_buf *data = scpi_buf->data;
+ u32 status;
+
+ cl.dev = the_scpi_device;
+ cl.rx_callback = scpi_rx_callback;
+ cl.tx_done = NULL;
+ cl.tx_block = false;
+ cl.knows_txdone = false;
+
+ chan = mbox_request_channel(&cl, high_priority);
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ init_completion(&scpi_buf->complete);
+ if (mbox_send_message(chan, (void *)data) < 0) {
+ status = SCPI_ERR_TIMEOUT;
+ goto free_channel;
+ }
+
+ wait_for_completion(&scpi_buf->complete);
+ status = *(u32 *)(data->rx_buf); /* read first word */
+
+free_channel:
+ mbox_free_channel(chan);
+
+ return scpi_to_linux_errno(status);
+}
+
+#define SCPI_SETUP_DBUF(scpi_buf, mhu_buf, _client_id,\
+ _cmd, _tx_buf, _rx_buf) \
+do { \
+ struct mhu_data_buf *pdata = &mhu_buf; \
+ pdata->cmd = _cmd; \
+ pdata->tx_buf = &_tx_buf; \
+ pdata->tx_size = sizeof(_tx_buf); \
+ pdata->rx_buf = &_rx_buf; \
+ pdata->rx_size = sizeof(_rx_buf); \
+ scpi_buf.client_id = _client_id; \
+ scpi_buf.data = pdata; \
+} while (0)
+
+static int scpi_execute_cmd(struct scpi_data_buf *scpi_buf)
+{
+ struct mhu_data_buf *data;
+ bool high_priority;
+
+ if (!scpi_buf || !scpi_buf->data)
+ return -EINVAL;
+
+ data = scpi_buf->data;
+ high_priority = high_priority_chan_supported(data->cmd);
+ data->cmd = PACK_SCPI_CMD(data->cmd, scpi_buf->client_id,
+ data->tx_size);
+ data->cl_data = scpi_buf;
+
+ return send_scpi_cmd(scpi_buf, high_priority);
+}
+
+unsigned long scpi_clk_get_val(u16 clk_id)
+{
+ struct scpi_data_buf sdata;
+ struct mhu_data_buf mdata;
+ struct __packed {
+ u32 status;
+ u32 clk_rate;
+ } buf;
+
+ SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_CLOCKS,
+ SCPI_CMD_GET_CLOCK_VALUE, clk_id, buf);
+ if (scpi_execute_cmd(&sdata))
+ return 0;
+
+ return buf.clk_rate;
+}
+EXPORT_SYMBOL_GPL(scpi_clk_get_val);
+
+int scpi_clk_set_val(u16 clk_id, unsigned long rate)
+{
+ struct scpi_data_buf sdata;
+ struct mhu_data_buf mdata;
+ int stat;
+ struct __packed {
+ u32 clk_rate;
+ u16 clk_id;
+ } buf;
+
+ buf.clk_rate = (u32)rate;
+ buf.clk_id = clk_id;
+
+ SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_CLOCKS,
+ SCPI_CMD_SET_CLOCK_VALUE, buf, stat);
+ return scpi_execute_cmd(&sdata);
+}
+EXPORT_SYMBOL_GPL(scpi_clk_set_val);
+
+struct scpi_opp *scpi_dvfs_get_opps(u8 domain)
+{
+ struct scpi_data_buf sdata;
+ struct mhu_data_buf mdata;
+ struct __packed {
+ u32 status;
+ u32 header;
+ u32 freqs[MAX_DVFS_OPPS];
+ } buf;
+ struct scpi_opp *opp;
+ size_t freqs_sz;
+ int count, ret;
+
+ if (domain >= MAX_DVFS_DOMAINS)
+ return ERR_PTR(-EINVAL);
+
+ if (scpi_opps[domain]) /* data already populated */
+ return scpi_opps[domain];
+
+ SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS,
+ SCPI_CMD_GET_DVFS_INFO, domain, buf);
+ ret = scpi_execute_cmd(&sdata);
+ if (ret)
+ return ERR_PTR(ret);
+
+ opp = kmalloc(sizeof(*opp), GFP_KERNEL);
+ if (!opp)
+ return ERR_PTR(-ENOMEM);
+
+ count = DVFS_OPP_COUNT(buf.header);
+ freqs_sz = count * sizeof(*(opp->freqs));
+
+ opp->count = count;
+ opp->latency = DVFS_LATENCY(buf.header);
+ opp->freqs = kmalloc(freqs_sz, GFP_KERNEL);
+ if (!opp->freqs) {
+ kfree(opp);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memcpy(opp->freqs, &buf.freqs[0], freqs_sz);
+ scpi_opps[domain] = opp;
+
+ return opp;
+}
+EXPORT_SYMBOL_GPL(scpi_dvfs_get_opps);
+
+int scpi_dvfs_get_idx(u8 domain)
+{
+ struct scpi_data_buf sdata;
+ struct mhu_data_buf mdata;
+ struct __packed {
+ u32 status;
+ u8 dvfs_idx;
+ } buf;
+ int ret;
+
+ if (domain >= MAX_DVFS_DOMAINS)
+ return -EINVAL;
+
+ SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS,
+ SCPI_CMD_GET_DVFS, domain, buf);
+ ret = scpi_execute_cmd(&sdata);
+
+ if (!ret)
+ ret = buf.dvfs_idx;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scpi_dvfs_get_idx);
+
+int scpi_dvfs_set_idx(u8 domain, u8 idx)
+{
+ struct scpi_data_buf sdata;
+ struct mhu_data_buf mdata;
+ struct __packed {
+ u8 dvfs_domain;
+ u8 dvfs_idx;
+ } buf;
+ int stat;
+
+ buf.dvfs_idx = idx;
+ buf.dvfs_domain = domain;
+
+ if (domain >= MAX_DVFS_DOMAINS)
+ return -EINVAL;
+
+ SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS,
+ SCPI_CMD_SET_DVFS, buf, stat);
+ return scpi_execute_cmd(&sdata);
+}
+EXPORT_SYMBOL_GPL(scpi_dvfs_set_idx);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index de5abf244746..7c7158715334 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -183,6 +183,14 @@ config MFD_DA9063
Additional drivers must be enabled in order to use the functionality
of the device.
+config MFD_HI6421_PMIC
+ tristate "HiSilicon Hi6421 PMU/Codec IC"
+ depends on OF
+ help
+ This driver supports HiSilicon Hi6421 power management and codec IC,
+ including regulators, codec, ADCs, Coulomb counter, etc. Memory
+ mapped I/O ports are the way of communication with it.
+
config MFD_MC13XXX
tristate
depends on (SPI_MASTER || I2C)
@@ -539,6 +547,21 @@ config MFD_PM8921_CORE
Say M here if you want to include support for PM8921 chip as a module.
This will build a module called "pm8921-core".
+config MFD_QCOM_RPM
+ tristate "Qualcomm Resource Power Manager (RPM)"
+ depends on ARCH_QCOM && OF
+ select MFD_SYSCON
+ help
+ If you say yes to this option, support will be included for the
+ Resource Power Manager system found in the Qualcomm 8660, 8960 and
+ 8064 based devices.
+
+ This is required to access many regulators, clocks and bus
+ frequencies controlled by the RPM on these devices.
+
+ Say M here if you want to include support for the Qualcomm RPM as a
+ module. This will build a module called "qcom_rpm".
+
config MFD_RDC321X
tristate "RDC R-321x southbridge"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f00148782d9b..a6662cc38ff6 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -153,6 +153,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
+obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
@@ -172,3 +173,4 @@ obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o
intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
+obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o
diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c
new file mode 100644
index 000000000000..1ef6d4e16172
--- /dev/null
+++ b/drivers/mfd/hi6421-pmic-core.c
@@ -0,0 +1,310 @@
+/*
+ * Device driver for regulators in Hi6421 IC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/mfd/hi6421-pmic.h>
+
+#include <asm/mach/irq.h>
+
+/* 8-bit register offset in PMIC */
+#define HI6421_REG_IRQ1 1
+#define HI6421_REG_IRQ2 2
+#define HI6421_REG_IRQ3 3
+#define HI6421_REG_IRQM1 4
+#define HI6421_REG_IRQM2 5
+#define HI6421_REG_IRQM3 6
+
+static struct of_device_id of_hi6421_pmic_child_match_tbl[] = {
+ /* regulators */
+ {
+ .compatible = "hisilicon,hi6421-ldo",
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck012",
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck345",
+ },
+ { /* end */ }
+};
+
+static struct of_device_id of_hi6421_pmic_match_tbl[] = {
+ {
+ .compatible = "hisilicon,hi6421-pmic",
+ },
+ { /* end */ }
+};
+
+/*
+ * The PMIC register is only 8-bit.
+ * Hisilicon SoC use hardware to map PMIC register into SoC mapping.
+ * At here, we are accessing SoC register with 32-bit.
+ */
+u32 hi6421_pmic_read(struct hi6421_pmic *pmic, int reg)
+{
+ unsigned long flags;
+ u32 ret;
+ spin_lock_irqsave(&pmic->lock, flags);
+ ret = readl_relaxed(pmic->regs + (reg << 2));
+ spin_unlock_irqrestore(&pmic->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(hi6421_pmic_read);
+
+void hi6421_pmic_write(struct hi6421_pmic *pmic, int reg, u32 val)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&pmic->lock, flags);
+ writel_relaxed(val, pmic->regs + (reg << 2));
+ spin_unlock_irqrestore(&pmic->lock, flags);
+}
+EXPORT_SYMBOL(hi6421_pmic_write);
+
+void hi6421_pmic_rmw(struct hi6421_pmic *pmic, int reg,
+ u32 mask, u32 bits)
+{
+ u32 data;
+
+ spin_lock(&pmic->lock);
+ data = readl_relaxed(pmic->regs + (reg << 2)) & ~mask;
+ data |= mask & bits;
+ writel_relaxed(data, pmic->regs + (reg << 2));
+ spin_unlock(&pmic->lock);
+}
+EXPORT_SYMBOL(hi6421_pmic_rmw);
+
+static int hi6421_to_irq(struct hi6421_pmic *pmic, unsigned offset)
+{
+ return irq_find_mapping(pmic->domain, offset);
+}
+
+static irqreturn_t hi6421_irq_handler(int irq, void *data)
+{
+ struct hi6421_pmic *pmic = (struct hi6421_pmic *)data;
+ unsigned long pending;
+ int i, offset, index;
+
+
+ for (i = HI6421_REG_IRQ1; i <= HI6421_REG_IRQ3; i++) {
+ spin_lock(&pmic->lock);
+ pending = readl_relaxed(pmic->regs + (i << 2));
+ pending &= HI6421_MASK_FIELD;
+ writel_relaxed(pending, pmic->regs + ((i + 3) << 2));
+ spin_unlock(&pmic->lock);
+
+ if (pending) {
+ for_each_set_bit(offset, &pending, HI6421_BITS) {
+ index = offset + (i - HI6421_REG_IRQ1) * HI6421_BITS;
+ generic_handle_irq(hi6421_to_irq(pmic, index));
+ }
+ }
+
+ spin_lock(&pmic->lock);
+ writel_relaxed(0, pmic->regs + ((i + 3) << 2));
+ writel_relaxed(pending, pmic->regs + (i << 2));
+ spin_unlock(&pmic->lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hi6421_irq_mask(struct irq_data *d)
+{
+ struct hi6421_pmic *pmic = irq_data_get_irq_chip_data(d);
+ u32 data, offset;
+
+ offset = ((irqd_to_hwirq(d) >> 3) + HI6421_REG_IRQM1) << 2;
+ spin_lock(&pmic->lock);
+ data = readl_relaxed(pmic->regs + offset);
+ data |= irqd_to_hwirq(d) % 8;
+ writel_relaxed(data, pmic->regs + offset);
+ spin_unlock(&pmic->lock);
+}
+
+static void hi6421_irq_unmask(struct irq_data *d)
+{
+ struct hi6421_pmic *pmic = irq_data_get_irq_chip_data(d);
+ u32 data, offset;
+
+ offset = ((irqd_to_hwirq(d) >> 3) + HI6421_REG_IRQM1) << 2;
+ spin_lock(&pmic->lock);
+ data = readl_relaxed(pmic->regs + offset);
+ data &= ~(irqd_to_hwirq(d) % 8);
+ writel_relaxed(data, pmic->regs + offset);
+ spin_unlock(&pmic->lock);
+}
+
+static struct irq_chip hi6421_irqchip = {
+ .name = "pmic",
+ .irq_mask = hi6421_irq_mask,
+ .irq_unmask = hi6421_irq_unmask,
+};
+
+static int hi6421_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct hi6421_pmic *pmic = d->host_data;
+
+ irq_set_chip_and_handler_name(virq, &hi6421_irqchip,
+ handle_simple_irq, "hi6421");
+ irq_set_chip_data(virq, pmic);
+ irq_set_irq_type(virq, IRQ_TYPE_NONE);
+
+ return 0;
+}
+
+static struct irq_domain_ops hi6421_domain_ops = {
+ .map = hi6421_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int hi6421_pmic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct hi6421_pmic *pmic = NULL;
+ enum of_gpio_flags flags;
+ int i, ret;
+
+ pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic) {
+ dev_err(dev, "cannot allocate hi6421_pmic device info\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&pmic->enable_mutex);
+ /* get resources */
+ pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pmic->res) {
+ dev_err(dev, "platform_get_resource err\n");
+ return -ENOENT;
+ }
+
+ if (!devm_request_mem_region(dev, pmic->res->start,
+ resource_size(pmic->res),
+ pdev->name)) {
+ dev_err(dev, "cannot claim register memory\n");
+ return -ENOMEM;
+ }
+
+ pmic->regs = devm_ioremap(dev, pmic->res->start,
+ resource_size(pmic->res));
+ if (!pmic->regs) {
+ dev_err(dev, "cannot map register memory\n");
+ return -ENOMEM;
+ }
+
+ /* TODO: get and enable clk request */
+
+ spin_lock_init(&pmic->lock);
+
+ pmic->gpio = of_get_gpio_flags(np, 0, &flags);
+ if (pmic->gpio < 0)
+ return pmic->gpio;
+ if (!gpio_is_valid(pmic->gpio))
+ return -EINVAL;
+ ret = gpio_request_one(pmic->gpio, GPIOF_IN, "pmic");
+ if (ret < 0) {
+ dev_err(dev, "failed to request gpio%d\n", pmic->gpio);
+ return ret;
+ }
+ pmic->irq = gpio_to_irq(pmic->gpio);
+ /* clear IRQ status */
+ spin_lock(&pmic->lock);
+ writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ1 << 2));
+ writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ2 << 2));
+ writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ3 << 2));
+ spin_unlock(&pmic->lock);
+
+ pmic->domain = irq_domain_add_simple(np, HI6421_NR_IRQ, 0,
+ &hi6421_domain_ops, pmic);
+ if (!pmic->domain)
+ return -ENODEV;
+
+ for (i = 0; i < HI6421_NR_IRQ; i++) {
+ ret = irq_create_mapping(pmic->domain, i);
+ if (ret == NO_IRQ) {
+ dev_err(dev, "failed mapping hwirq %d\n", i);
+ return -ENOMEM;
+ }
+ }
+
+ ret = request_threaded_irq(pmic->irq, hi6421_irq_handler, NULL,
+ IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND,
+ "pmic", pmic);
+
+ platform_set_drvdata(pdev, pmic);
+
+ /* set over-current protection debounce 8ms*/
+ hi6421_pmic_rmw(pmic, OCP_DEB_CTRL_REG, \
+ OCP_DEB_SEL_MASK | OCP_EN_DEBOUNCE_MASK | OCP_AUTO_STOP_MASK, \
+ OCP_DEB_SEL_8MS | OCP_EN_DEBOUNCE_ENABLE);
+
+ /* populate sub nodes */
+ of_platform_populate(np, of_hi6421_pmic_child_match_tbl, NULL, dev);
+
+ return 0;
+}
+
+static int hi6421_pmic_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hi6421_pmic *pmic = platform_get_drvdata(pdev);
+
+ free_irq(pmic->irq, pmic);
+ gpio_free(pmic->gpio);
+ devm_iounmap(dev, pmic->regs);
+ devm_release_mem_region(dev, pmic->res->start,
+ resource_size(pmic->res));
+ devm_kfree(dev, pmic);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver hi6421_pmic_driver = {
+ .driver = {
+ .name = "hi6421_pmic",
+ .owner = THIS_MODULE,
+ .of_match_table = of_hi6421_pmic_match_tbl,
+ },
+ .probe = hi6421_pmic_probe,
+ .remove = hi6421_pmic_remove,
+};
+module_platform_driver(hi6421_pmic_driver);
+
+MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
+MODULE_DESCRIPTION("Hi6421 PMIC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 39904f77c049..96b60db0dac9 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -26,6 +26,7 @@
#include <linux/regmap.h>
#include <linux/of_platform.h>
#include <linux/mfd/core.h>
+#include <linux/mfd/pm8921-core.h>
#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
@@ -65,6 +66,41 @@ struct pm_irq_chip {
u8 config[0];
};
+int pm8xxx_read_irq_status(int irq)
+{
+ struct irq_data *d = irq_get_irq_data(irq);
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ unsigned int bits;
+ int irq_bit;
+ u8 block;
+ int rc;
+
+ if (!chip) {
+ pr_err("Failed to resolve pm_irq_chip\n");
+ return -EINVAL;
+ }
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", block, rc);
+ goto bail;
+ }
+
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+ if (rc)
+ pr_err("Failed Reading Status rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+
+ return rc ? rc : !!(bits & BIT(irq_bit));
+}
+EXPORT_SYMBOL(pm8xxx_read_irq_status);
+
static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
unsigned int *ip)
{
diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c
new file mode 100644
index 000000000000..7cc0513d43e2
--- /dev/null
+++ b/drivers/mfd/qcom_rpm.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/qcom_rpm.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/mfd/qcom,rpm.h>
+
+struct qcom_rpm_resource {
+ unsigned target_id;
+ unsigned status_id;
+ unsigned select_id;
+ unsigned size;
+};
+
+struct qcom_rpm_data {
+ u32 version;
+ const struct qcom_rpm_resource *resource_table;
+ unsigned n_resources;
+};
+
+struct qcom_rpm {
+ struct device *dev;
+ struct regmap *ipc_regmap;
+ unsigned ipc_offset;
+ unsigned ipc_bit;
+
+ struct completion ack;
+ struct mutex lock;
+
+ void __iomem *status_regs;
+ void __iomem *ctrl_regs;
+ void __iomem *req_regs;
+
+ u32 ack_status;
+
+ const struct qcom_rpm_data *data;
+};
+
+#define RPM_STATUS_REG(rpm, i) ((rpm)->status_regs + (i) * 4)
+#define RPM_CTRL_REG(rpm, i) ((rpm)->ctrl_regs + (i) * 4)
+#define RPM_REQ_REG(rpm, i) ((rpm)->req_regs + (i) * 4)
+
+#define RPM_REQUEST_TIMEOUT (5 * HZ)
+
+#define RPM_REQUEST_CONTEXT 3
+#define RPM_REQ_SELECT 11
+#define RPM_ACK_CONTEXT 15
+#define RPM_ACK_SELECTOR 23
+#define RPM_SELECT_SIZE 7
+
+#define RPM_ACTIVE_STATE BIT(0)
+#define RPM_NOTIFICATION BIT(30)
+#define RPM_REJECTED BIT(31)
+
+#define RPM_SIGNAL BIT(2)
+
+static const struct qcom_rpm_resource apq8064_rpm_resource_table[] = {
+ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
+ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 },
+ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 },
+ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 },
+ [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 },
+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 },
+ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 },
+ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 },
+ [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 },
+ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 },
+ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 },
+ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 },
+ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 },
+ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 },
+ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 },
+ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 },
+ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 },
+ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 },
+ [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 1 },
+ [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 1 },
+ [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 },
+ [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 21 },
+ [QCOM_RPM_PM8921_S1] = { 116, 31, 30, 2 },
+ [QCOM_RPM_PM8921_S2] = { 118, 33, 31, 2 },
+ [QCOM_RPM_PM8921_S3] = { 120, 35, 32, 2 },
+ [QCOM_RPM_PM8921_S4] = { 122, 37, 33, 2 },
+ [QCOM_RPM_PM8921_S5] = { 124, 39, 34, 2 },
+ [QCOM_RPM_PM8921_S6] = { 126, 41, 35, 2 },
+ [QCOM_RPM_PM8921_S7] = { 128, 43, 36, 2 },
+ [QCOM_RPM_PM8921_S8] = { 130, 45, 37, 2 },
+ [QCOM_RPM_PM8921_L1] = { 132, 47, 38, 2 },
+ [QCOM_RPM_PM8921_L2] = { 134, 49, 39, 2 },
+ [QCOM_RPM_PM8921_L3] = { 136, 51, 40, 2 },
+ [QCOM_RPM_PM8921_L4] = { 138, 53, 41, 2 },
+ [QCOM_RPM_PM8921_L5] = { 140, 55, 42, 2 },
+ [QCOM_RPM_PM8921_L6] = { 142, 57, 43, 2 },
+ [QCOM_RPM_PM8921_L7] = { 144, 59, 44, 2 },
+ [QCOM_RPM_PM8921_L8] = { 146, 61, 45, 2 },
+ [QCOM_RPM_PM8921_L9] = { 148, 63, 46, 2 },
+ [QCOM_RPM_PM8921_L10] = { 150, 65, 47, 2 },
+ [QCOM_RPM_PM8921_L11] = { 152, 67, 48, 2 },
+ [QCOM_RPM_PM8921_L12] = { 154, 69, 49, 2 },
+ [QCOM_RPM_PM8921_L13] = { 156, 71, 50, 2 },
+ [QCOM_RPM_PM8921_L14] = { 158, 73, 51, 2 },
+ [QCOM_RPM_PM8921_L15] = { 160, 75, 52, 2 },
+ [QCOM_RPM_PM8921_L16] = { 162, 77, 53, 2 },
+ [QCOM_RPM_PM8921_L17] = { 164, 79, 54, 2 },
+ [QCOM_RPM_PM8921_L18] = { 166, 81, 55, 2 },
+ [QCOM_RPM_PM8921_L19] = { 168, 83, 56, 2 },
+ [QCOM_RPM_PM8921_L20] = { 170, 85, 57, 2 },
+ [QCOM_RPM_PM8921_L21] = { 172, 87, 58, 2 },
+ [QCOM_RPM_PM8921_L22] = { 174, 89, 59, 2 },
+ [QCOM_RPM_PM8921_L23] = { 176, 91, 60, 2 },
+ [QCOM_RPM_PM8921_L24] = { 178, 93, 61, 2 },
+ [QCOM_RPM_PM8921_L25] = { 180, 95, 62, 2 },
+ [QCOM_RPM_PM8921_L26] = { 182, 97, 63, 2 },
+ [QCOM_RPM_PM8921_L27] = { 184, 99, 64, 2 },
+ [QCOM_RPM_PM8921_L28] = { 186, 101, 65, 2 },
+ [QCOM_RPM_PM8921_L29] = { 188, 103, 66, 2 },
+ [QCOM_RPM_PM8921_CLK1] = { 190, 105, 67, 2 },
+ [QCOM_RPM_PM8921_CLK2] = { 192, 107, 68, 2 },
+ [QCOM_RPM_PM8921_LVS1] = { 194, 109, 69, 1 },
+ [QCOM_RPM_PM8921_LVS2] = { 195, 110, 70, 1 },
+ [QCOM_RPM_PM8921_LVS3] = { 196, 111, 71, 1 },
+ [QCOM_RPM_PM8921_LVS4] = { 197, 112, 72, 1 },
+ [QCOM_RPM_PM8921_LVS5] = { 198, 113, 73, 1 },
+ [QCOM_RPM_PM8921_LVS6] = { 199, 114, 74, 1 },
+ [QCOM_RPM_PM8921_LVS7] = { 200, 115, 75, 1 },
+ [QCOM_RPM_PM8821_S1] = { 201, 116, 76, 2 },
+ [QCOM_RPM_PM8821_S2] = { 203, 118, 77, 2 },
+ [QCOM_RPM_PM8821_L1] = { 205, 120, 78, 2 },
+ [QCOM_RPM_PM8921_NCP] = { 207, 122, 80, 2 },
+ [QCOM_RPM_CXO_BUFFERS] = { 209, 124, 81, 1 },
+ [QCOM_RPM_USB_OTG_SWITCH] = { 210, 125, 82, 1 },
+ [QCOM_RPM_HDMI_SWITCH] = { 211, 126, 83, 1 },
+ [QCOM_RPM_DDR_DMM] = { 212, 127, 84, 2 },
+ [QCOM_RPM_VDDMIN_GPIO] = { 215, 131, 89, 1 },
+};
+
+static const struct qcom_rpm_data apq8064_template = {
+ .version = 3,
+ .resource_table = apq8064_rpm_resource_table,
+ .n_resources = ARRAY_SIZE(apq8064_rpm_resource_table),
+};
+
+static const struct qcom_rpm_resource msm8660_rpm_resource_table[] = {
+ [QCOM_RPM_CXO_CLK] = { 32, 12, 5, 1 },
+ [QCOM_RPM_PXO_CLK] = { 33, 13, 6, 1 },
+ [QCOM_RPM_PLL_4] = { 34, 14, 7, 1 },
+ [QCOM_RPM_APPS_FABRIC_CLK] = { 35, 15, 8, 1 },
+ [QCOM_RPM_SYS_FABRIC_CLK] = { 36, 16, 9, 1 },
+ [QCOM_RPM_MM_FABRIC_CLK] = { 37, 17, 10, 1 },
+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 38, 18, 11, 1 },
+ [QCOM_RPM_SFPB_CLK] = { 39, 19, 12, 1 },
+ [QCOM_RPM_CFPB_CLK] = { 40, 20, 13, 1 },
+ [QCOM_RPM_MMFPB_CLK] = { 41, 21, 14, 1 },
+ [QCOM_RPM_SMI_CLK] = { 42, 22, 15, 1 },
+ [QCOM_RPM_EBI1_CLK] = { 43, 23, 16, 1 },
+ [QCOM_RPM_APPS_L2_CACHE_CTL] = { 44, 24, 17, 1 },
+ [QCOM_RPM_APPS_FABRIC_HALT] = { 45, 25, 18, 2 },
+ [QCOM_RPM_APPS_FABRIC_MODE] = { 47, 26, 19, 3 },
+ [QCOM_RPM_APPS_FABRIC_ARB] = { 51, 28, 21, 6 },
+ [QCOM_RPM_SYS_FABRIC_HALT] = { 63, 29, 22, 2 },
+ [QCOM_RPM_SYS_FABRIC_MODE] = { 65, 30, 23, 3 },
+ [QCOM_RPM_SYS_FABRIC_ARB] = { 69, 32, 25, 22 },
+ [QCOM_RPM_MM_FABRIC_HALT] = { 105, 33, 26, 2 },
+ [QCOM_RPM_MM_FABRIC_MODE] = { 107, 34, 27, 3 },
+ [QCOM_RPM_MM_FABRIC_ARB] = { 111, 36, 29, 23 },
+ [QCOM_RPM_PM8901_SMPS0] = { 134, 37, 30, 2 },
+ [QCOM_RPM_PM8901_SMPS1] = { 136, 39, 31, 2 },
+ [QCOM_RPM_PM8901_SMPS2] = { 138, 41, 32, 2 },
+ [QCOM_RPM_PM8901_SMPS3] = { 140, 43, 33, 2 },
+ [QCOM_RPM_PM8901_SMPS4] = { 142, 45, 34, 2 },
+ [QCOM_RPM_PM8901_LDO0] = { 144, 47, 35, 2 },
+ [QCOM_RPM_PM8901_LDO1] = { 146, 49, 36, 2 },
+ [QCOM_RPM_PM8901_LDO2] = { 148, 51, 37, 2 },
+ [QCOM_RPM_PM8901_LDO3] = { 150, 53, 38, 2 },
+ [QCOM_RPM_PM8901_LDO4] = { 152, 55, 39, 2 },
+ [QCOM_RPM_PM8901_LDO5] = { 154, 57, 40, 2 },
+ [QCOM_RPM_PM8901_LDO6] = { 156, 59, 41, 2 },
+ [QCOM_RPM_PM8901_LVS0] = { 158, 61, 42, 1 },
+ [QCOM_RPM_PM8901_LVS1] = { 159, 62, 43, 1 },
+ [QCOM_RPM_PM8901_LVS2] = { 160, 63, 44, 1 },
+ [QCOM_RPM_PM8901_LVS3] = { 161, 64, 45, 1 },
+ [QCOM_RPM_PM8901_MVS] = { 162, 65, 46, 1 },
+ [QCOM_RPM_PM8058_SMPS0] = { 163, 66, 47, 2 },
+ [QCOM_RPM_PM8058_SMPS1] = { 165, 68, 48, 2 },
+ [QCOM_RPM_PM8058_SMPS2] = { 167, 70, 49, 2 },
+ [QCOM_RPM_PM8058_SMPS3] = { 169, 72, 50, 2 },
+ [QCOM_RPM_PM8058_SMPS4] = { 171, 74, 51, 2 },
+ [QCOM_RPM_PM8058_LDO0] = { 173, 76, 52, 2 },
+ [QCOM_RPM_PM8058_LDO1] = { 175, 78, 53, 2 },
+ [QCOM_RPM_PM8058_LDO2] = { 177, 80, 54, 2 },
+ [QCOM_RPM_PM8058_LDO3] = { 179, 82, 55, 2 },
+ [QCOM_RPM_PM8058_LDO4] = { 181, 84, 56, 2 },
+ [QCOM_RPM_PM8058_LDO5] = { 183, 86, 57, 2 },
+ [QCOM_RPM_PM8058_LDO6] = { 185, 88, 58, 2 },
+ [QCOM_RPM_PM8058_LDO7] = { 187, 90, 59, 2 },
+ [QCOM_RPM_PM8058_LDO8] = { 189, 92, 60, 2 },
+ [QCOM_RPM_PM8058_LDO9] = { 191, 94, 61, 2 },
+ [QCOM_RPM_PM8058_LDO10] = { 193, 96, 62, 2 },
+ [QCOM_RPM_PM8058_LDO11] = { 195, 98, 63, 2 },
+ [QCOM_RPM_PM8058_LDO12] = { 197, 100, 64, 2 },
+ [QCOM_RPM_PM8058_LDO13] = { 199, 102, 65, 2 },
+ [QCOM_RPM_PM8058_LDO14] = { 201, 104, 66, 2 },
+ [QCOM_RPM_PM8058_LDO15] = { 203, 106, 67, 2 },
+ [QCOM_RPM_PM8058_LDO16] = { 205, 108, 68, 2 },
+ [QCOM_RPM_PM8058_LDO17] = { 207, 110, 69, 2 },
+ [QCOM_RPM_PM8058_LDO18] = { 209, 112, 70, 2 },
+ [QCOM_RPM_PM8058_LDO19] = { 211, 114, 71, 2 },
+ [QCOM_RPM_PM8058_LDO20] = { 213, 116, 72, 2 },
+ [QCOM_RPM_PM8058_LDO21] = { 215, 118, 73, 2 },
+ [QCOM_RPM_PM8058_LDO22] = { 217, 120, 74, 2 },
+ [QCOM_RPM_PM8058_LDO23] = { 219, 122, 75, 2 },
+ [QCOM_RPM_PM8058_LDO24] = { 221, 124, 76, 2 },
+ [QCOM_RPM_PM8058_LDO25] = { 223, 126, 77, 2 },
+ [QCOM_RPM_PM8058_LVS0] = { 225, 128, 78, 1 },
+ [QCOM_RPM_PM8058_LVS1] = { 226, 129, 79, 1 },
+ [QCOM_RPM_PM8058_NCP] = { 227, 130, 80, 2 },
+ [QCOM_RPM_CXO_BUFFERS] = { 229, 132, 81, 1 },
+};
+
+static const struct qcom_rpm_data msm8660_template = {
+ .version = -1,
+ .resource_table = msm8660_rpm_resource_table,
+ .n_resources = ARRAY_SIZE(msm8660_rpm_resource_table),
+};
+
+static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = {
+ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
+ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 },
+ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 },
+ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 },
+ [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 },
+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 },
+ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 },
+ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 },
+ [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 },
+ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 },
+ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 },
+ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 },
+ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 },
+ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 },
+ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 },
+ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 },
+ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 },
+ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 29 },
+ [QCOM_RPM_MM_FABRIC_HALT] = { 88, 27, 26, 1 },
+ [QCOM_RPM_MM_FABRIC_MODE] = { 90, 28, 27, 1 },
+ [QCOM_RPM_MM_FABRIC_IOCTL] = { 93, 29, 28, 1 },
+ [QCOM_RPM_MM_FABRIC_ARB] = { 94, 30, 29, 23 },
+ [QCOM_RPM_PM8921_S1] = { 117, 31, 30, 2 },
+ [QCOM_RPM_PM8921_S2] = { 119, 33, 31, 2 },
+ [QCOM_RPM_PM8921_S3] = { 121, 35, 32, 2 },
+ [QCOM_RPM_PM8921_S4] = { 123, 37, 33, 2 },
+ [QCOM_RPM_PM8921_S5] = { 125, 39, 34, 2 },
+ [QCOM_RPM_PM8921_S6] = { 127, 41, 35, 2 },
+ [QCOM_RPM_PM8921_S7] = { 129, 43, 36, 2 },
+ [QCOM_RPM_PM8921_S8] = { 131, 45, 37, 2 },
+ [QCOM_RPM_PM8921_L1] = { 133, 47, 38, 2 },
+ [QCOM_RPM_PM8921_L2] = { 135, 49, 39, 2 },
+ [QCOM_RPM_PM8921_L3] = { 137, 51, 40, 2 },
+ [QCOM_RPM_PM8921_L4] = { 139, 53, 41, 2 },
+ [QCOM_RPM_PM8921_L5] = { 141, 55, 42, 2 },
+ [QCOM_RPM_PM8921_L6] = { 143, 57, 43, 2 },
+ [QCOM_RPM_PM8921_L7] = { 145, 59, 44, 2 },
+ [QCOM_RPM_PM8921_L8] = { 147, 61, 45, 2 },
+ [QCOM_RPM_PM8921_L9] = { 149, 63, 46, 2 },
+ [QCOM_RPM_PM8921_L10] = { 151, 65, 47, 2 },
+ [QCOM_RPM_PM8921_L11] = { 153, 67, 48, 2 },
+ [QCOM_RPM_PM8921_L12] = { 155, 69, 49, 2 },
+ [QCOM_RPM_PM8921_L13] = { 157, 71, 50, 2 },
+ [QCOM_RPM_PM8921_L14] = { 159, 73, 51, 2 },
+ [QCOM_RPM_PM8921_L15] = { 161, 75, 52, 2 },
+ [QCOM_RPM_PM8921_L16] = { 163, 77, 53, 2 },
+ [QCOM_RPM_PM8921_L17] = { 165, 79, 54, 2 },
+ [QCOM_RPM_PM8921_L18] = { 167, 81, 55, 2 },
+ [QCOM_RPM_PM8921_L19] = { 169, 83, 56, 2 },
+ [QCOM_RPM_PM8921_L20] = { 171, 85, 57, 2 },
+ [QCOM_RPM_PM8921_L21] = { 173, 87, 58, 2 },
+ [QCOM_RPM_PM8921_L22] = { 175, 89, 59, 2 },
+ [QCOM_RPM_PM8921_L23] = { 177, 91, 60, 2 },
+ [QCOM_RPM_PM8921_L24] = { 179, 93, 61, 2 },
+ [QCOM_RPM_PM8921_L25] = { 181, 95, 62, 2 },
+ [QCOM_RPM_PM8921_L26] = { 183, 97, 63, 2 },
+ [QCOM_RPM_PM8921_L27] = { 185, 99, 64, 2 },
+ [QCOM_RPM_PM8921_L28] = { 187, 101, 65, 2 },
+ [QCOM_RPM_PM8921_L29] = { 189, 103, 66, 2 },
+ [QCOM_RPM_PM8921_CLK1] = { 191, 105, 67, 2 },
+ [QCOM_RPM_PM8921_CLK2] = { 193, 107, 68, 2 },
+ [QCOM_RPM_PM8921_LVS1] = { 195, 109, 69, 1 },
+ [QCOM_RPM_PM8921_LVS2] = { 196, 110, 70, 1 },
+ [QCOM_RPM_PM8921_LVS3] = { 197, 111, 71, 1 },
+ [QCOM_RPM_PM8921_LVS4] = { 198, 112, 72, 1 },
+ [QCOM_RPM_PM8921_LVS5] = { 199, 113, 73, 1 },
+ [QCOM_RPM_PM8921_LVS6] = { 200, 114, 74, 1 },
+ [QCOM_RPM_PM8921_LVS7] = { 201, 115, 75, 1 },
+ [QCOM_RPM_PM8921_NCP] = { 202, 116, 80, 2 },
+ [QCOM_RPM_CXO_BUFFERS] = { 204, 118, 81, 1 },
+ [QCOM_RPM_USB_OTG_SWITCH] = { 205, 119, 82, 1 },
+ [QCOM_RPM_HDMI_SWITCH] = { 206, 120, 83, 1 },
+ [QCOM_RPM_DDR_DMM] = { 207, 121, 84, 2 },
+};
+
+static const struct qcom_rpm_data msm8960_template = {
+ .version = 3,
+ .resource_table = msm8960_rpm_resource_table,
+ .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
+};
+
+static const struct of_device_id qcom_rpm_of_match[] = {
+ { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
+ { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
+ { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
+
+struct qcom_rpm *dev_get_qcom_rpm(struct device *dev)
+{
+ return dev_get_drvdata(dev);
+}
+EXPORT_SYMBOL(dev_get_qcom_rpm);
+
+int qcom_rpm_write(struct qcom_rpm *rpm, int resource, u32 *buf, size_t count)
+{
+ const struct qcom_rpm_resource *res;
+ const struct qcom_rpm_data *data = rpm->data;
+ u32 sel_mask[RPM_SELECT_SIZE] = { 0 };
+ int left;
+ int ret = 0;
+ int i;
+
+ if (WARN_ON(resource < 0 || resource >= data->n_resources))
+ return -EINVAL;
+
+ res = &data->resource_table[resource];
+ if (WARN_ON(res->size != count))
+ return -EINVAL;
+
+ mutex_lock(&rpm->lock);
+
+ for (i = 0; i < res->size; i++)
+ writel_relaxed(buf[i], RPM_REQ_REG(rpm, res->target_id + i));
+
+ bitmap_set((unsigned long *)sel_mask, res->select_id, 1);
+ for (i = 0; i < ARRAY_SIZE(sel_mask); i++) {
+ writel_relaxed(sel_mask[i],
+ RPM_CTRL_REG(rpm, RPM_REQ_SELECT + i));
+ }
+
+ writel_relaxed(RPM_ACTIVE_STATE,
+ RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT));
+
+ reinit_completion(&rpm->ack);
+ regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
+
+ left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT);
+ if (!left)
+ ret = -ETIMEDOUT;
+ else if (rpm->ack_status & RPM_REJECTED)
+ ret = -EIO;
+
+ mutex_unlock(&rpm->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(qcom_rpm_write);
+
+static irqreturn_t qcom_rpm_ack_interrupt(int irq, void *dev)
+{
+ struct qcom_rpm *rpm = dev;
+ u32 ack;
+ int i;
+
+ ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
+ for (i = 0; i < RPM_SELECT_SIZE; i++)
+ writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + i));
+ writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
+
+ if (ack & RPM_NOTIFICATION) {
+ dev_warn(rpm->dev, "ignoring notification!\n");
+ } else {
+ rpm->ack_status = ack;
+ complete(&rpm->ack);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qcom_rpm_err_interrupt(int irq, void *dev)
+{
+ struct qcom_rpm *rpm = dev;
+
+ regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
+ dev_err(rpm->dev, "RPM triggered fatal error\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qcom_rpm_wakeup_interrupt(int irq, void *dev)
+{
+ return IRQ_HANDLED;
+}
+
+static int qcom_rpm_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device_node *syscon_np;
+ struct resource *res;
+ struct qcom_rpm *rpm;
+ u32 fw_version[3];
+ int irq_wakeup;
+ int irq_ack;
+ int irq_err;
+ int ret;
+
+ rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL);
+ if (!rpm) {
+ dev_err(&pdev->dev, "Can't allocate qcom_rpm\n");
+ return -ENOMEM;
+ }
+ rpm->dev = &pdev->dev;
+ mutex_init(&rpm->lock);
+ init_completion(&rpm->ack);
+
+ irq_ack = platform_get_irq_byname(pdev, "ack");
+ if (irq_ack < 0) {
+ dev_err(&pdev->dev, "required ack interrupt missing\n");
+ return irq_ack;
+ }
+
+ irq_err = platform_get_irq_byname(pdev, "err");
+ if (irq_err < 0) {
+ dev_err(&pdev->dev, "required err interrupt missing\n");
+ return irq_err;
+ }
+
+ irq_wakeup = platform_get_irq_byname(pdev, "wakeup");
+ if (irq_wakeup < 0) {
+ dev_err(&pdev->dev, "required wakeup interrupt missing\n");
+ return irq_wakeup;
+ }
+
+ match = of_match_device(qcom_rpm_of_match, &pdev->dev);
+ rpm->data = match->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rpm->status_regs = devm_ioremap_resource(&pdev->dev, res);
+ rpm->ctrl_regs = rpm->status_regs + 0x400;
+ rpm->req_regs = rpm->status_regs + 0x600;
+ if (IS_ERR(rpm->status_regs))
+ return PTR_ERR(rpm->status_regs);
+
+ syscon_np = of_parse_phandle(pdev->dev.of_node, "qcom,ipc", 0);
+ if (!syscon_np) {
+ dev_err(&pdev->dev, "no qcom,ipc node\n");
+ return -ENODEV;
+ }
+
+ rpm->ipc_regmap = syscon_node_to_regmap(syscon_np);
+ if (IS_ERR(rpm->ipc_regmap))
+ return PTR_ERR(rpm->ipc_regmap);
+
+ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 1,
+ &rpm->ipc_offset);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "no offset in qcom,ipc\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 2,
+ &rpm->ipc_bit);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "no bit in qcom,ipc\n");
+ return -EINVAL;
+ }
+
+ dev_set_drvdata(&pdev->dev, rpm);
+
+ fw_version[0] = readl(RPM_STATUS_REG(rpm, 0));
+ fw_version[1] = readl(RPM_STATUS_REG(rpm, 1));
+ fw_version[2] = readl(RPM_STATUS_REG(rpm, 2));
+ if (fw_version[0] != rpm->data->version) {
+ dev_err(&pdev->dev,
+ "RPM version %u.%u.%u incompatible with driver version %u",
+ fw_version[0],
+ fw_version[1],
+ fw_version[2],
+ rpm->data->version);
+ return -EFAULT;
+ }
+
+ dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0],
+ fw_version[1],
+ fw_version[2]);
+
+ writel(fw_version[0], RPM_CTRL_REG(rpm, 0));
+ writel(fw_version[1], RPM_CTRL_REG(rpm, 1));
+ writel(fw_version[2], RPM_CTRL_REG(rpm, 2));
+
+ ret = devm_request_irq(&pdev->dev,
+ irq_ack,
+ qcom_rpm_ack_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
+ "qcom_rpm_ack",
+ rpm);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request ack interrupt\n");
+ return ret;
+ }
+
+ ret = irq_set_irq_wake(irq_ack, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to mark ack irq as wakeup\n");
+
+ ret = devm_request_irq(&pdev->dev,
+ irq_err,
+ qcom_rpm_err_interrupt,
+ IRQF_TRIGGER_RISING,
+ "qcom_rpm_err",
+ rpm);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request err interrupt\n");
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev,
+ irq_wakeup,
+ qcom_rpm_wakeup_interrupt,
+ IRQF_TRIGGER_RISING,
+ "qcom_rpm_wakeup",
+ rpm);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request wakeup interrupt\n");
+ return ret;
+ }
+
+ ret = irq_set_irq_wake(irq_wakeup, 1);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n");
+
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+}
+
+static int qcom_rpm_remove_child(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
+ return 0;
+}
+
+static int qcom_rpm_remove(struct platform_device *pdev)
+{
+ device_for_each_child(&pdev->dev, NULL, qcom_rpm_remove_child);
+ return 0;
+}
+
+static struct platform_driver qcom_rpm_driver = {
+ .probe = qcom_rpm_probe,
+ .remove = qcom_rpm_remove,
+ .driver = {
+ .name = "qcom_rpm",
+ .owner = THIS_MODULE,
+ .of_match_table = qcom_rpm_of_match,
+ },
+};
+
+static int __init qcom_rpm_init(void)
+{
+ return platform_driver_register(&qcom_rpm_driver);
+}
+arch_initcall(qcom_rpm_init);
+
+static void __exit qcom_rpm_exit(void)
+{
+ platform_driver_unregister(&qcom_rpm_driver);
+}
+module_exit(qcom_rpm_exit)
+
+MODULE_DESCRIPTION("Qualcomm Resource Power Manager driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ssbi.c b/drivers/mfd/ssbi.c
index b78942ed4c67..4fbe02ed74da 100644
--- a/drivers/mfd/ssbi.c
+++ b/drivers/mfd/ssbi.c
@@ -331,7 +331,12 @@ static struct platform_driver ssbi_driver = {
.of_match_table = ssbi_match_table,
},
};
-module_platform_driver(ssbi_driver);
+
+static int ssbi_init(void)
+{
+ return platform_driver_register(&ssbi_driver);
+}
+subsys_initcall(ssbi_init);
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b841180c7c74..45e271b26916 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -402,6 +402,10 @@ config TI_DAC7512
This driver can also be built as a module. If so, the module
will be called ti_dac7512.
+config UID_STAT
+ bool "UID based statistics tracking exported to /proc/uid_stat"
+ default n
+
config VMWARE_BALLOON
tristate "VMware Balloon Driver"
depends on X86 && HYPERVISOR_GUEST
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 5497d026e651..e82072cad154 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_ISL29020) += isl29020.o
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
obj-$(CONFIG_DS1682) += ds1682.o
obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
+obj-$(CONFIG_UID_STAT) += uid_stat.o
obj-$(CONFIG_C2PORT) += c2port/
obj-$(CONFIG_HMC6352) += hmc6352.o
obj-y += eeprom/
diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c
new file mode 100644
index 000000000000..4766c1f83b94
--- /dev/null
+++ b/drivers/misc/uid_stat.c
@@ -0,0 +1,152 @@
+/* drivers/misc/uid_stat.c
+ *
+ * Copyright (C) 2008 - 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <asm/atomic.h>
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/uid_stat.h>
+#include <net/activity_stats.h>
+
+static DEFINE_SPINLOCK(uid_lock);
+static LIST_HEAD(uid_list);
+static struct proc_dir_entry *parent;
+
+struct uid_stat {
+ struct list_head link;
+ uid_t uid;
+ atomic_t tcp_rcv;
+ atomic_t tcp_snd;
+};
+
+static struct uid_stat *find_uid_stat(uid_t uid) {
+ struct uid_stat *entry;
+
+ list_for_each_entry(entry, &uid_list, link) {
+ if (entry->uid == uid) {
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+static int uid_stat_atomic_int_show(struct seq_file *m, void *v)
+{
+ unsigned int bytes;
+ atomic_t *counter = m->private;
+
+ bytes = (unsigned int) (atomic_read(counter) + INT_MIN);
+ return seq_printf(m, "%u\n", bytes);
+}
+
+static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode));
+}
+
+static const struct file_operations uid_stat_read_atomic_int_fops = {
+ .open = uid_stat_read_atomic_int_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/* Create a new entry for tracking the specified uid. */
+static struct uid_stat *create_stat(uid_t uid) {
+ struct uid_stat *new_uid;
+ /* Create the uid stat struct and append it to the list. */
+ new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC);
+ if (!new_uid)
+ return NULL;
+
+ new_uid->uid = uid;
+ /* Counters start at INT_MIN, so we can track 4GB of network traffic. */
+ atomic_set(&new_uid->tcp_rcv, INT_MIN);
+ atomic_set(&new_uid->tcp_snd, INT_MIN);
+
+ list_add_tail(&new_uid->link, &uid_list);
+ return new_uid;
+}
+
+static void create_stat_proc(struct uid_stat *new_uid)
+{
+ char uid_s[32];
+ struct proc_dir_entry *entry;
+ sprintf(uid_s, "%d", new_uid->uid);
+ entry = proc_mkdir(uid_s, parent);
+
+ /* Keep reference to uid_stat so we know what uid to read stats from. */
+ proc_create_data("tcp_snd", S_IRUGO, entry,
+ &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd);
+
+ proc_create_data("tcp_rcv", S_IRUGO, entry,
+ &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv);
+}
+
+static struct uid_stat *find_or_create_uid_stat(uid_t uid)
+{
+ struct uid_stat *entry;
+ unsigned long flags;
+ spin_lock_irqsave(&uid_lock, flags);
+ entry = find_uid_stat(uid);
+ if (entry) {
+ spin_unlock_irqrestore(&uid_lock, flags);
+ return entry;
+ }
+ entry = create_stat(uid);
+ spin_unlock_irqrestore(&uid_lock, flags);
+ if (entry)
+ create_stat_proc(entry);
+ return entry;
+}
+
+int uid_stat_tcp_snd(uid_t uid, int size) {
+ struct uid_stat *entry;
+ activity_stats_update();
+ entry = find_or_create_uid_stat(uid);
+ if (!entry)
+ return -1;
+ atomic_add(size, &entry->tcp_snd);
+ return 0;
+}
+
+int uid_stat_tcp_rcv(uid_t uid, int size) {
+ struct uid_stat *entry;
+ activity_stats_update();
+ entry = find_or_create_uid_stat(uid);
+ if (!entry)
+ return -1;
+ atomic_add(size, &entry->tcp_rcv);
+ return 0;
+}
+
+static int __init uid_stat_init(void)
+{
+ parent = proc_mkdir("uid_stat", NULL);
+ if (!parent) {
+ pr_err("uid_stat: failed to create proc entry\n");
+ return -1;
+ }
+ return 0;
+}
+
+__initcall(uid_stat_init);
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 5562308699bc..79d82124413f 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -50,6 +50,15 @@ config MMC_BLOCK_BOUNCE
If unsure, say Y here.
+config MMC_BLOCK_DEFERRED_RESUME
+ bool "Deferr MMC layer resume until I/O is requested"
+ depends on MMC_BLOCK
+ default n
+ help
+ Say Y here to enable deferred MMC resume until I/O
+ is requested. This will reduce overall resume latency and
+ save power when theres an SD card inserted but not being used.
+
config SDIO_UART
tristate "SDIO UART/GPS class support"
depends on TTY
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index ede41f05c392..8c15fe478ab8 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -36,6 +36,9 @@
#include <linux/compat.h>
#include <linux/pm_runtime.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/mmc.h>
+
#include <linux/mmc/ioctl.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -166,11 +169,7 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
static inline int mmc_get_devidx(struct gendisk *disk)
{
- int devmaj = MAJOR(disk_devt(disk));
- int devidx = MINOR(disk_devt(disk)) / perdev_minors;
-
- if (!devmaj)
- devidx = disk->first_minor / perdev_minors;
+ int devidx = disk->first_minor / perdev_minors;
return devidx;
}
@@ -425,9 +424,11 @@ static int ioctl_do_sanitize(struct mmc_card *card)
pr_debug("%s: %s - SANITIZE IN PROGRESS...\n",
mmc_hostname(card->host), __func__);
+ trace_mmc_blk_erase_start(EXT_CSD_SANITIZE_START, 0, 0);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_SANITIZE_START, 1,
MMC_SANITIZE_REQ_TIMEOUT);
+ trace_mmc_blk_erase_end(EXT_CSD_SANITIZE_START, 0, 0);
if (err)
pr_err("%s: %s - EXT_CSD_SANITIZE_START failed. err=%d\n",
@@ -850,18 +851,22 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
req->rq_disk->disk_name, "timed out", name, status);
/* If the status cmd initially failed, retry the r/w cmd */
- if (!status_valid)
+ if (!status_valid) {
+ pr_err("%s: status not valid, retrying timeout\n", req->rq_disk->disk_name);
return ERR_RETRY;
-
+ }
/*
* If it was a r/w cmd crc error, or illegal command
* (eg, issued in wrong state) then retry - we should
* have corrected the state problem above.
*/
- if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND))
+ if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) {
+ pr_err("%s: command error, retrying timeout\n", req->rq_disk->disk_name);
return ERR_RETRY;
+ }
/* Otherwise abort the command */
+ pr_err("%s: not retrying timeout\n", req->rq_disk->disk_name);
return ERR_ABORT;
default:
@@ -2010,6 +2015,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
unsigned long flags;
unsigned int cmd_flags = req ? req->cmd_flags : 0;
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ if (mmc_bus_needs_resume(card->host))
+ mmc_resume_bus(card->host);
+#endif
+
if (req && !mq->mqrq_prev->req)
/* claim host only for the first request */
mmc_get_card(card);
@@ -2131,6 +2141,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
md->disk->queue = md->queue.queue;
md->disk->driverfs_dev = parent;
set_disk_ro(md->disk, md->read_only || default_ro);
+ md->disk->flags = GENHD_FL_EXT_DEVT;
if (area_type & MMC_BLK_DATA_AREA_RPMB)
md->disk->flags |= GENHD_FL_NO_PART_SCAN;
@@ -2448,6 +2459,9 @@ static int mmc_blk_probe(struct mmc_card *card)
mmc_set_drvdata(card, md);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ mmc_set_bus_resume_policy(card->host, 1);
+#endif
if (mmc_add_disk(md))
goto out;
@@ -2490,6 +2504,9 @@ static void mmc_blk_remove(struct mmc_card *card)
pm_runtime_put_noidle(&card->dev);
mmc_blk_remove_req(md);
mmc_set_drvdata(card, NULL);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ mmc_set_bus_resume_policy(card->host, 0);
+#endif
}
static int _mmc_blk_suspend(struct mmc_card *card)
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index 9ebee72d9c3f..f771bc3496a4 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -11,3 +11,18 @@ config MMC_CLKGATE
support handling this in order for it to be of any use.
If unsure, say N.
+
+config MMC_EMBEDDED_SDIO
+ boolean "MMC embedded SDIO device support (EXPERIMENTAL)"
+ help
+ If you say Y here, support will be added for embedded SDIO
+ devices which do not contain the necessary enumeration
+ support in hardware to be properly detected.
+
+config MMC_PARANOID_SD_INIT
+ bool "Enable paranoid SD card initialization (EXPERIMENTAL)"
+ help
+ If you say Y here, the MMC layer will be extra paranoid
+ about re-trying SD init requests. This can be a useful
+ work-around for buggy controllers and hardware. Enable
+ if you are experiencing issues with SD detection.
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index d03a080fb9cd..ac839236a2c9 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -29,6 +29,9 @@
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/wakelock.h>
+
+#include <trace/events/mmc.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -55,6 +58,7 @@
#define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */
static struct workqueue_struct *workqueue;
+static struct wake_lock mmc_delayed_work_wake_lock;
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
/*
@@ -71,6 +75,7 @@ module_param(use_spi_crc, bool, 0);
static int mmc_schedule_delayed_work(struct delayed_work *work,
unsigned long delay)
{
+ wake_lock(&mmc_delayed_work_wake_lock);
return queue_delayed_work(workqueue, work, delay);
}
@@ -158,6 +163,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
pr_debug("%s: %d bytes transferred: %d\n",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
+ trace_mmc_blk_rw_end(cmd->opcode, cmd->arg, mrq->data);
}
if (mrq->stop) {
@@ -542,8 +548,12 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
mmc_start_bkops(host->card, true);
}
- if (!err && areq)
+ if (!err && areq) {
+ trace_mmc_blk_rw_start(areq->mrq->cmd->opcode,
+ areq->mrq->cmd->arg,
+ areq->mrq->data);
start_err = __mmc_start_data_req(host, areq->mrq);
+ }
if (host->areq)
mmc_post_req(host, host->areq->mrq, 0);
@@ -1886,8 +1896,13 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
struct mmc_command cmd = {0};
unsigned int qty = 0;
unsigned long timeout;
+ unsigned int fr, nr;
int err;
+ fr = from;
+ nr = to - from + 1;
+ trace_mmc_blk_erase_start(arg, fr, nr);
+
/*
* qty is used to calculate the erase timeout which depends on how many
* erase groups (or allocation units in SD terminology) are affected.
@@ -1991,6 +2006,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
(R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
out:
+
+ trace_mmc_blk_erase_end(arg, fr, nr);
return err;
}
@@ -2418,6 +2435,7 @@ void mmc_rescan(struct work_struct *work)
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
int i;
+ bool extend_wakelock = false;
if (host->trigger_card_event && host->ops->card_event) {
host->ops->card_event(host);
@@ -2444,6 +2462,12 @@ void mmc_rescan(struct work_struct *work)
host->detect_change = 0;
+ /* If the card was removed the bus will be marked
+ * as dead - extend the wakelock so userspace
+ * can respond */
+ if (host->bus_dead)
+ extend_wakelock = 1;
+
/*
* Let mmc_bus_put() free the bus/bus_ops if we've found that
* the card is no longer present.
@@ -2473,14 +2497,20 @@ void mmc_rescan(struct work_struct *work)
mmc_claim_host(host);
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
- if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
+ if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
+ extend_wakelock = true;
break;
+ }
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);
out:
+ if (extend_wakelock)
+ wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
+ else
+ wake_unlock(&mmc_delayed_work_wake_lock);
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
}
@@ -2676,6 +2706,22 @@ void mmc_init_context_info(struct mmc_host *host)
init_waitqueue_head(&host->context_info.wait);
}
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+void mmc_set_embedded_sdio_data(struct mmc_host *host,
+ struct sdio_cis *cis,
+ struct sdio_cccr *cccr,
+ struct sdio_embedded_func *funcs,
+ int num_funcs)
+{
+ host->embedded_sdio_data.cis = cis;
+ host->embedded_sdio_data.cccr = cccr;
+ host->embedded_sdio_data.funcs = funcs;
+ host->embedded_sdio_data.num_funcs = num_funcs;
+}
+
+EXPORT_SYMBOL(mmc_set_embedded_sdio_data);
+#endif
+
static int __init mmc_init(void)
{
int ret;
@@ -2684,6 +2730,9 @@ static int __init mmc_init(void)
if (!workqueue)
return -ENOMEM;
+ wake_lock_init(&mmc_delayed_work_wake_lock, WAKE_LOCK_SUSPEND,
+ "mmc_delayed_work");
+
ret = mmc_register_bus();
if (ret)
goto destroy_workqueue;
@@ -2704,6 +2753,7 @@ unregister_bus:
mmc_unregister_bus();
destroy_workqueue:
destroy_workqueue(workqueue);
+ wake_lock_destroy(&mmc_delayed_work_wake_lock);
return ret;
}
@@ -2714,6 +2764,7 @@ static void __exit mmc_exit(void)
mmc_unregister_host_class();
mmc_unregister_bus();
destroy_workqueue(workqueue);
+ wake_lock_destroy(&mmc_delayed_work_wake_lock);
}
subsys_initcall(mmc_init);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 95cceae96944..a99b6a774f9d 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -555,7 +555,8 @@ int mmc_add_host(struct mmc_host *host)
mmc_host_clk_sysfs_init(host);
mmc_start_host(host);
- register_pm_notifier(&host->pm_notify);
+ if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
+ register_pm_notifier(&host->pm_notify);
return 0;
}
@@ -572,7 +573,9 @@ EXPORT_SYMBOL(mmc_add_host);
*/
void mmc_remove_host(struct mmc_host *host)
{
- unregister_pm_notifier(&host->pm_notify);
+ if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
+ unregister_pm_notifier(&host->pm_notify);
+
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 0c44510bf717..dc5f4ac304f5 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -815,6 +815,9 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
bool reinit)
{
int err;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries;
+#endif
if (!reinit) {
/*
@@ -841,7 +844,26 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
/*
* Fetch switch information from card.
*/
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ for (retries = 1; retries <= 3; retries++) {
+ err = mmc_read_switch(card);
+ if (!err) {
+ if (retries > 1) {
+ printk(KERN_WARNING
+ "%s: recovered\n",
+ mmc_hostname(host));
+ }
+ break;
+ } else {
+ printk(KERN_WARNING
+ "%s: read switch failed (attempt %d)\n",
+ mmc_hostname(host), retries);
+ }
+ }
+#else
err = mmc_read_switch(card);
+#endif
+
if (err)
return err;
}
@@ -1034,7 +1056,10 @@ static int mmc_sd_alive(struct mmc_host *host)
*/
static void mmc_sd_detect(struct mmc_host *host)
{
- int err;
+ int err = 0;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries = 5;
+#endif
BUG_ON(!host);
BUG_ON(!host->card);
@@ -1044,7 +1069,23 @@ static void mmc_sd_detect(struct mmc_host *host)
/*
* Just check if our card has been removed.
*/
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ while(retries) {
+ err = mmc_send_status(host->card, NULL);
+ if (err) {
+ retries--;
+ udelay(5);
+ continue;
+ }
+ break;
+ }
+ if (!retries) {
+ printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n",
+ __func__, mmc_hostname(host), err);
+ }
+#else
err = _mmc_detect_card_removed(host);
+#endif
mmc_put_card(host->card);
@@ -1106,6 +1147,9 @@ static int mmc_sd_suspend(struct mmc_host *host)
static int _mmc_sd_resume(struct mmc_host *host)
{
int err = 0;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries;
+#endif
BUG_ON(!host);
BUG_ON(!host->card);
@@ -1116,7 +1160,23 @@ static int _mmc_sd_resume(struct mmc_host *host)
goto out;
mmc_power_up(host, host->card->ocr);
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ retries = 5;
+ while (retries) {
+ err = mmc_sd_init_card(host, host->card->ocr, host->card);
+
+ if (err) {
+ printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n",
+ mmc_hostname(host), err, retries);
+ mdelay(5);
+ retries--;
+ continue;
+ }
+ break;
+ }
+#else
err = mmc_sd_init_card(host, host->card->ocr, host->card);
+#endif
mmc_card_clr_suspended(host->card);
out:
@@ -1207,6 +1267,9 @@ int mmc_attach_sd(struct mmc_host *host)
{
int err;
u32 ocr, rocr;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries;
+#endif
BUG_ON(!host);
WARN_ON(!host->claimed);
@@ -1243,9 +1306,27 @@ int mmc_attach_sd(struct mmc_host *host)
/*
* Detect and init the card.
*/
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ retries = 5;
+ while (retries) {
+ err = mmc_sd_init_card(host, rocr, NULL);
+ if (err) {
+ retries--;
+ continue;
+ }
+ break;
+ }
+
+ if (!retries) {
+ printk(KERN_ERR "%s: mmc_sd_init_card() failure (err = %d)\n",
+ mmc_hostname(host), err);
+ goto err;
+ }
+#else
err = mmc_sd_init_card(host, rocr, NULL);
if (err)
goto err;
+#endif
mmc_release_host(host);
err = mmc_add_card(host->card);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index e636d9e99e4a..b7ad2ad38524 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -10,6 +10,7 @@
*/
#include <linux/err.h>
+#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/host.h>
@@ -28,6 +29,10 @@
#include "sdio_ops.h"
#include "sdio_cis.h"
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <linux/mmc/sdio_ids.h>
+#endif
+
static int sdio_read_fbr(struct sdio_func *func)
{
int ret;
@@ -739,19 +744,35 @@ try_again:
goto finish;
}
- /*
- * Read the common registers.
- */
- err = sdio_read_cccr(card, ocr);
- if (err)
- goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.cccr)
+ memcpy(&card->cccr, host->embedded_sdio_data.cccr, sizeof(struct sdio_cccr));
+ else {
+#endif
+ /*
+ * Read the common registers.
+ */
+ err = sdio_read_cccr(card, ocr);
+ if (err)
+ goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
- /*
- * Read the common CIS tuples.
- */
- err = sdio_read_common_cis(card);
- if (err)
- goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.cis)
+ memcpy(&card->cis, host->embedded_sdio_data.cis, sizeof(struct sdio_cis));
+ else {
+#endif
+ /*
+ * Read the common CIS tuples.
+ */
+ err = sdio_read_common_cis(card);
+ if (err)
+ goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
if (oldcard) {
int same = (card->cis.vendor == oldcard->cis.vendor &&
@@ -1136,14 +1157,36 @@ int mmc_attach_sdio(struct mmc_host *host)
funcs = (ocr & 0x70000000) >> 28;
card->sdio_funcs = 0;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.funcs)
+ card->sdio_funcs = funcs = host->embedded_sdio_data.num_funcs;
+#endif
+
/*
* Initialize (but don't add) all present functions.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
- err = sdio_init_func(host->card, i + 1);
- if (err)
- goto remove;
-
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.funcs) {
+ struct sdio_func *tmp;
+
+ tmp = sdio_alloc_func(host->card);
+ if (IS_ERR(tmp))
+ goto remove;
+ tmp->num = (i + 1);
+ card->sdio_func[i] = tmp;
+ tmp->class = host->embedded_sdio_data.funcs[i].f_class;
+ tmp->max_blksize = host->embedded_sdio_data.funcs[i].f_maxblksize;
+ tmp->vendor = card->cis.vendor;
+ tmp->device = card->cis.device;
+ } else {
+#endif
+ err = sdio_init_func(host->card, i + 1);
+ if (err)
+ goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
/*
* Enable Runtime PM for this func (if supported)
*/
@@ -1191,3 +1234,40 @@ err:
return err;
}
+int sdio_reset_comm(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ u32 ocr;
+ u32 rocr;
+ int err;
+
+ printk("%s():\n", __func__);
+ mmc_claim_host(host);
+
+ mmc_go_idle(host);
+
+ mmc_set_clock(host, host->f_min);
+
+ err = mmc_send_io_op_cond(host, 0, &ocr);
+ if (err)
+ goto err;
+
+ rocr = mmc_select_voltage(host, ocr);
+ if (!rocr) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = mmc_sdio_init_card(host, rocr, card, 0);
+ if (err)
+ goto err;
+
+ mmc_release_host(host);
+ return 0;
+err:
+ printk("%s: Error resetting SDIO communications (%d)\n",
+ mmc_hostname(host), err);
+ mmc_release_host(host);
+ return err;
+}
+EXPORT_SYMBOL(sdio_reset_comm);
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 4fa8fef9147f..afd07b61bd47 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -25,6 +25,10 @@
#include "sdio_cis.h"
#include "sdio_bus.h"
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <linux/mmc/host.h>
+#endif
+
/* show configuration fields */
#define sdio_config_attr(field, format_string) \
static ssize_t \
@@ -261,7 +265,14 @@ static void sdio_release_func(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
- sdio_free_func_cis(func);
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ /*
+ * If this device is embedded then we never allocated
+ * cis tables for this func
+ */
+ if (!func->card->host->embedded_sdio_data.funcs)
+#endif
+ sdio_free_func_cis(func);
kfree(func->info);
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 78cb4d5d9d58..8fdeb07723a6 100644..100755
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -384,6 +384,39 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)
EXPORT_SYMBOL_GPL(sdio_readb);
/**
+ * sdio_readb_ext - read a single byte from a SDIO function
+ * @func: SDIO function to access
+ * @addr: address to read
+ * @err_ret: optional status value from transfer
+ * @in: value to add to argument
+ *
+ * Reads a single byte from the address space of a given SDIO
+ * function. If there is a problem reading the address, 0xff
+ * is returned and @err_ret will contain the error code.
+ */
+unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr,
+ int *err_ret, unsigned in)
+{
+ int ret;
+ unsigned char val;
+
+ BUG_ON(!func);
+
+ if (err_ret)
+ *err_ret = 0;
+
+ ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val);
+ if (ret) {
+ if (err_ret)
+ *err_ret = ret;
+ return 0xFF;
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(sdio_readb_ext);
+
+/**
* sdio_writeb - write a single byte to a SDIO function
* @func: SDIO function to access
* @b: byte to write
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 451135822464..58aedcc900d1 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -14,6 +14,17 @@ config MMC_ARMMMCI
If unsure, say N.
+config MMC_QCOM_DML
+ tristate "Qualcomm Data Mover for SD Card Controller"
+ depends on MMC_ARMMMCI && QCOM_BAM_DMA
+ default y
+ help
+ This selects the Qualcomm Data Mover lite/local on SD Card controller.
+ This option will enable the dma to work correctly, if you are using
+ Qcom SOCs and MMC, you would probably need this option to get DMA working.
+
+ if unsure, say N.
+
config MMC_PXA
tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
depends on ARCH_PXA
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index f211eede8db5..2d223463e6c5 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
+obj-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 8f216edbdf08..bffc2a6db033 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1429,7 +1429,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
buf += len;
cnt -= len;
if (host->part_buf_count == 2) {
- mci_writew(host, DATA(host->data_offset),
+ mci_writew_data(host, DATA(host->data_offset),
host->part_buf16);
host->part_buf_count = 0;
}
@@ -1447,7 +1447,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
cnt -= len;
/* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i)
- mci_writew(host, DATA(host->data_offset),
+ mci_writew_data(host, DATA(host->data_offset),
aligned_buf[i]);
}
} else
@@ -1455,7 +1455,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
{
u16 *pdata = buf;
for (; cnt >= 2; cnt -= 2)
- mci_writew(host, DATA(host->data_offset), *pdata++);
+ mci_writew_data(host, DATA(host->data_offset), *pdata++);
buf = pdata;
}
/* put anything remaining in the part_buf */
@@ -1464,7 +1464,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
/* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
- mci_writew(host, DATA(host->data_offset),
+ mci_writew_data(host, DATA(host->data_offset),
host->part_buf16);
}
}
@@ -1480,7 +1480,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
int items = len >> 1;
int i;
for (i = 0; i < items; ++i)
- aligned_buf[i] = mci_readw(host,
+ aligned_buf[i] = mci_readw_data(host,
DATA(host->data_offset));
/* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len);
@@ -1492,11 +1492,11 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
{
u16 *pdata = buf;
for (; cnt >= 2; cnt -= 2)
- *pdata++ = mci_readw(host, DATA(host->data_offset));
+ *pdata++ = mci_readw_data(host, DATA(host->data_offset));
buf = pdata;
}
if (cnt) {
- host->part_buf16 = mci_readw(host, DATA(host->data_offset));
+ host->part_buf16 = mci_readw_data(host, DATA(host->data_offset));
dw_mci_pull_final_bytes(host, buf, cnt);
}
}
@@ -1512,7 +1512,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
buf += len;
cnt -= len;
if (host->part_buf_count == 4) {
- mci_writel(host, DATA(host->data_offset),
+ mci_writel_data(host, DATA(host->data_offset),
host->part_buf32);
host->part_buf_count = 0;
}
@@ -1530,7 +1530,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
cnt -= len;
/* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i)
- mci_writel(host, DATA(host->data_offset),
+ mci_writel_data(host, DATA(host->data_offset),
aligned_buf[i]);
}
} else
@@ -1538,7 +1538,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
{
u32 *pdata = buf;
for (; cnt >= 4; cnt -= 4)
- mci_writel(host, DATA(host->data_offset), *pdata++);
+ mci_writel_data(host, DATA(host->data_offset), *pdata++);
buf = pdata;
}
/* put anything remaining in the part_buf */
@@ -1547,7 +1547,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
/* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
- mci_writel(host, DATA(host->data_offset),
+ mci_writel_data(host, DATA(host->data_offset),
host->part_buf32);
}
}
@@ -1563,7 +1563,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
int items = len >> 2;
int i;
for (i = 0; i < items; ++i)
- aligned_buf[i] = mci_readl(host,
+ aligned_buf[i] = mci_readl_data(host,
DATA(host->data_offset));
/* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len);
@@ -1575,11 +1575,11 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
{
u32 *pdata = buf;
for (; cnt >= 4; cnt -= 4)
- *pdata++ = mci_readl(host, DATA(host->data_offset));
+ *pdata++ = mci_readl_data(host, DATA(host->data_offset));
buf = pdata;
}
if (cnt) {
- host->part_buf32 = mci_readl(host, DATA(host->data_offset));
+ host->part_buf32 = mci_readl_data(host, DATA(host->data_offset));
dw_mci_pull_final_bytes(host, buf, cnt);
}
}
@@ -1596,7 +1596,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
cnt -= len;
if (host->part_buf_count == 8) {
- mci_writeq(host, DATA(host->data_offset),
+ mci_writeq_data(host, DATA(host->data_offset),
host->part_buf);
host->part_buf_count = 0;
}
@@ -1614,7 +1614,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
cnt -= len;
/* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i)
- mci_writeq(host, DATA(host->data_offset),
+ mci_writeq_data(host, DATA(host->data_offset),
aligned_buf[i]);
}
} else
@@ -1622,7 +1622,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
{
u64 *pdata = buf;
for (; cnt >= 8; cnt -= 8)
- mci_writeq(host, DATA(host->data_offset), *pdata++);
+ mci_writeq_data(host, DATA(host->data_offset), *pdata++);
buf = pdata;
}
/* put anything remaining in the part_buf */
@@ -1631,7 +1631,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
/* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
- mci_writeq(host, DATA(host->data_offset),
+ mci_writeq_data(host, DATA(host->data_offset),
host->part_buf);
}
}
@@ -1647,7 +1647,7 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
int items = len >> 3;
int i;
for (i = 0; i < items; ++i)
- aligned_buf[i] = mci_readq(host,
+ aligned_buf[i] = mci_readq_data(host,
DATA(host->data_offset));
/* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len);
@@ -1659,11 +1659,11 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
{
u64 *pdata = buf;
for (; cnt >= 8; cnt -= 8)
- *pdata++ = mci_readq(host, DATA(host->data_offset));
+ *pdata++ = mci_readq_data(host, DATA(host->data_offset));
buf = pdata;
}
if (cnt) {
- host->part_buf = mci_readq(host, DATA(host->data_offset));
+ host->part_buf = mci_readq_data(host, DATA(host->data_offset));
dw_mci_pull_final_bytes(host, buf, cnt);
}
}
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 08fd956d81f3..e54aafecde5d 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -157,21 +157,27 @@
/* Register access macros */
#define mci_readl(dev, reg) \
- __raw_readl((dev)->regs + SDMMC_##reg)
+ readl_relaxed((dev)->regs + SDMMC_##reg)
#define mci_writel(dev, reg, value) \
- __raw_writel((value), (dev)->regs + SDMMC_##reg)
+ writel_relaxed((value), (dev)->regs + SDMMC_##reg)
/* 16-bit FIFO access macros */
-#define mci_readw(dev, reg) \
+#define mci_readw_data(dev, reg) \
__raw_readw((dev)->regs + SDMMC_##reg)
-#define mci_writew(dev, reg, value) \
+#define mci_writew_data(dev, reg, value) \
__raw_writew((value), (dev)->regs + SDMMC_##reg)
+/* 32-bit FIFO access macros */
+#define mci_readl_data(dev, reg) \
+ __raw_readl((dev)->regs + SDMMC_##reg)
+#define mci_writel_data(dev, reg, value) \
+ __raw_writel((value), (dev)->regs + SDMMC_##reg)
+
/* 64-bit FIFO access macros */
#ifdef readq
-#define mci_readq(dev, reg) \
+#define mci_readq_data(dev, reg) \
__raw_readq((dev)->regs + SDMMC_##reg)
-#define mci_writeq(dev, reg, value) \
+#define mci_writeq_data(dev, reg, value) \
__raw_writeq((value), (dev)->regs + SDMMC_##reg)
#else
/*
@@ -182,10 +188,10 @@
* executed on those machines. Defining these macros here keeps the
* rest of the code free from ifdefs.
*/
-#define mci_readq(dev, reg) \
- (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg))
-#define mci_writeq(dev, reg, value) \
- (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value))
+#define mci_readq_data(dev, reg) \
+ ({ u64 __r = le64_to_cpu(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg)); __r; })
+#define mci_writeq_data(dev, reg, value) \
+ (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (cpu_to_le64(value)))
#endif
extern int dw_mci_probe(struct dw_mci *host);
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index e4d470704150..264c947c1c19 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -43,6 +43,7 @@
#include <asm/sizes.h>
#include "mmci.h"
+#include "mmci_qcom_dml.h"
#define DRIVER_NAME "mmci-pl18x"
@@ -60,12 +61,13 @@ static unsigned int fmax = 515633;
* @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
* is asserted (likewise for RX)
* @data_cmd_enable: enable value for data commands.
- * @sdio: variant supports SDIO
+ * @st_sdio: enable ST specific SDIO logic
* @st_clkdiv: true if using a ST-specific clock divider algorithm
* @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
* @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
* register
+ * @datactrl_mask_sdio: SDIO enable mask in datactrl register
* @pwrreg_powerup: power up value for MMCIPOWER register
* @f_max: maximum clk frequency supported by the controller.
* @signal_direction: input/out direction of bus signals can be indicated
@@ -75,6 +77,8 @@ static unsigned int fmax = 515633;
* @explicit_mclk_control: enable explicit mclk control in driver.
* @qcom_fifo: enables qcom specific fifo pio read logic.
* @reversed_irq_handling: handle data irq before cmd irq.
+ * @qcom_dml: enables qcom specific dma glue for dma transfers.
+ * @any_blksize: true if block any sizes are supported
*/
struct variant_data {
unsigned int clkreg;
@@ -86,7 +90,8 @@ struct variant_data {
unsigned int fifohalfsize;
unsigned int data_cmd_enable;
unsigned int datactrl_mask_ddrmode;
- bool sdio;
+ unsigned int datactrl_mask_sdio;
+ bool st_sdio;
bool st_clkdiv;
bool blksz_datactrl16;
bool blksz_datactrl4;
@@ -99,6 +104,8 @@ struct variant_data {
bool explicit_mclk_control;
bool qcom_fifo;
bool reversed_irq_handling;
+ bool qcom_dml;
+ bool any_blksize;
};
static struct variant_data variant_arm = {
@@ -133,7 +140,8 @@ static struct variant_data variant_u300 = {
.clkreg_enable = MCI_ST_U300_HWFCEN,
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.datalength_bits = 16,
- .sdio = true,
+ .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
+ .st_sdio = true,
.pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000,
.signal_direction = true,
@@ -146,7 +154,8 @@ static struct variant_data variant_nomadik = {
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
.datalength_bits = 24,
- .sdio = true,
+ .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
+ .st_sdio = true,
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000,
@@ -163,7 +172,8 @@ static struct variant_data variant_ux500 = {
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
.datalength_bits = 24,
- .sdio = true,
+ .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
+ .st_sdio = true,
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000,
@@ -182,7 +192,8 @@ static struct variant_data variant_ux500v2 = {
.clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
.datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE,
.datalength_bits = 24,
- .sdio = true,
+ .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
+ .st_sdio = true,
.st_clkdiv = true,
.blksz_datactrl16 = true,
.pwrreg_powerup = MCI_PWR_ON,
@@ -191,6 +202,7 @@ static struct variant_data variant_ux500v2 = {
.pwrreg_clkgate = true,
.busy_detect = true,
.pwrreg_nopower = true,
+ .any_blksize = true,
};
static struct variant_data variant_qcom = {
@@ -208,6 +220,8 @@ static struct variant_data variant_qcom = {
.f_max = 208000000,
.explicit_mclk_control = true,
.qcom_fifo = true,
+ .qcom_dml = true,
+ .any_blksize = true,
};
static int mmci_card_busy(struct mmc_host *mmc)
@@ -235,10 +249,11 @@ static int mmci_card_busy(struct mmc_host *mmc)
static int mmci_validate_data(struct mmci_host *host,
struct mmc_data *data)
{
+ struct variant_data *variant = host->variant;
+
if (!data)
return 0;
-
- if (!is_power_of_2(data->blksz)) {
+ if (!is_power_of_2(data->blksz) && !variant->any_blksize) {
dev_err(mmc_dev(host->mmc),
"unsupported block size (%d bytes)\n", data->blksz);
return -EINVAL;
@@ -421,6 +436,7 @@ static void mmci_dma_setup(struct mmci_host *host)
{
const char *rxname, *txname;
dma_cap_mask_t mask;
+ struct variant_data *variant = host->variant;
host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
@@ -471,6 +487,10 @@ static void mmci_dma_setup(struct mmci_host *host)
if (max_seg_size < host->mmc->max_seg_size)
host->mmc->max_seg_size = max_seg_size;
}
+
+ if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel)
+ if (dml_hw_init(host, host->mmc->parent->of_node))
+ variant->qcom_dml = false;
}
/*
@@ -572,6 +592,7 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
struct dma_async_tx_descriptor *desc;
enum dma_data_direction buffer_dirn;
int nr_sg;
+ unsigned long flags = DMA_CTRL_ACK;
if (data->flags & MMC_DATA_READ) {
conf.direction = DMA_DEV_TO_MEM;
@@ -596,9 +617,12 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
if (nr_sg == 0)
return -EINVAL;
+ if (host->variant->qcom_dml)
+ flags |= DMA_PREP_INTERRUPT;
+
dmaengine_slave_config(chan, &conf);
desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
- conf.direction, DMA_CTRL_ACK);
+ conf.direction, flags);
if (!desc)
goto unmap_exit;
@@ -647,6 +671,9 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
dmaengine_submit(host->dma_desc_current);
dma_async_issue_pending(host->dma_current);
+ if (host->variant->qcom_dml)
+ dml_start_xfer(host, data);
+
datactrl |= MCI_DPSM_DMAENABLE;
/* Trigger the DMA transfer */
@@ -780,7 +807,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
writel(host->size, base + MMCIDATALENGTH);
blksz_bits = ffs(data->blksz) - 1;
- BUG_ON(1 << blksz_bits != data->blksz);
if (variant->blksz_datactrl16)
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
@@ -792,32 +818,26 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
if (data->flags & MMC_DATA_READ)
datactrl |= MCI_DPSM_DIRECTION;
- /* The ST Micro variants has a special bit to enable SDIO */
- if (variant->sdio && host->mmc->card)
- if (mmc_card_sdio(host->mmc->card)) {
- /*
- * The ST Micro variants has a special bit
- * to enable SDIO.
- */
- u32 clk;
+ if (host->mmc->card && mmc_card_sdio(host->mmc->card)) {
+ u32 clk;
- datactrl |= MCI_ST_DPSM_SDIOEN;
+ datactrl |= variant->datactrl_mask_sdio;
- /*
- * The ST Micro variant for SDIO small write transfers
- * needs to have clock H/W flow control disabled,
- * otherwise the transfer will not start. The threshold
- * depends on the rate of MCLK.
- */
- if (data->flags & MMC_DATA_WRITE &&
- (host->size < 8 ||
- (host->size <= 8 && host->mclk > 50000000)))
- clk = host->clk_reg & ~variant->clkreg_enable;
- else
- clk = host->clk_reg | variant->clkreg_enable;
+ /*
+ * The ST Micro variant for SDIO small write transfers
+ * needs to have clock H/W flow control disabled,
+ * otherwise the transfer will not start. The threshold
+ * depends on the rate of MCLK.
+ */
+ if (variant->st_sdio && data->flags & MMC_DATA_WRITE &&
+ (host->size < 8 ||
+ (host->size <= 8 && host->mclk > 50000000)))
+ clk = host->clk_reg & ~variant->clkreg_enable;
+ else
+ clk = host->clk_reg | variant->clkreg_enable;
- mmci_write_clkreg(host, clk);
- }
+ mmci_write_clkreg(host, clk);
+ }
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 ||
host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)
diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c
new file mode 100644
index 000000000000..300ef50636cb
--- /dev/null
+++ b/drivers/mmc/host/mmci_qcom_dml.c
@@ -0,0 +1,177 @@
+/*
+ *
+ * Copyright (c) 2011, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/bitops.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include "mmci.h"
+
+/* Registers */
+#define DML_CONFIG 0x00
+#define PRODUCER_CRCI_MSK GENMASK(1, 0)
+#define PRODUCER_CRCI_DISABLE 0
+#define PRODUCER_CRCI_X_SEL BIT(0)
+#define PRODUCER_CRCI_Y_SEL BIT(1)
+#define CONSUMER_CRCI_MSK GENMASK(3, 2)
+#define CONSUMER_CRCI_DISABLE 0
+#define CONSUMER_CRCI_X_SEL BIT(2)
+#define CONSUMER_CRCI_Y_SEL BIT(3)
+#define PRODUCER_TRANS_END_EN BIT(4)
+#define BYPASS BIT(16)
+#define DIRECT_MODE BIT(17)
+#define INFINITE_CONS_TRANS BIT(18)
+
+#define DML_SW_RESET 0x08
+#define DML_PRODUCER_START 0x0c
+#define DML_CONSUMER_START 0x10
+#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14
+#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18
+#define DML_PIPE_ID 0x1c
+#define PRODUCER_PIPE_ID_SHFT 0
+#define PRODUCER_PIPE_ID_MSK GENMASK(4, 0)
+#define CONSUMER_PIPE_ID_SHFT 16
+#define CONSUMER_PIPE_ID_MSK GENMASK(20, 16)
+
+#define DML_PRODUCER_BAM_BLOCK_SIZE 0x24
+#define DML_PRODUCER_BAM_TRANS_SIZE 0x28
+
+/* other definitions */
+#define PRODUCER_PIPE_LOGICAL_SIZE 4096
+#define CONSUMER_PIPE_LOGICAL_SIZE 4096
+
+#define DML_OFFSET 0x800
+
+void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
+{
+ u32 config;
+ void __iomem *base = host->base + DML_OFFSET;
+
+ if (data->flags & MMC_DATA_READ) {
+ /* Read operation: configure DML for producer operation */
+ /* Set producer CRCI-x and disable consumer CRCI */
+ config = readl_relaxed(base + DML_CONFIG);
+ config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
+ config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE;
+ writel_relaxed(config, base + DML_CONFIG);
+
+ /* Set the Producer BAM block size */
+ writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE);
+
+ /* Set Producer BAM Transaction size */
+ writel_relaxed(data->blocks * data->blksz,
+ base + DML_PRODUCER_BAM_TRANS_SIZE);
+ /* Set Producer Transaction End bit */
+ config = readl_relaxed(base + DML_CONFIG);
+ config |= PRODUCER_TRANS_END_EN;
+ writel_relaxed(config, base + DML_CONFIG);
+ /* Trigger producer */
+ writel_relaxed(1, base + DML_PRODUCER_START);
+ } else {
+ /* Write operation: configure DML for consumer operation */
+ /* Set consumer CRCI-x and disable producer CRCI*/
+ config = readl_relaxed(base + DML_CONFIG);
+ config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
+ config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE;
+ writel_relaxed(config, base + DML_CONFIG);
+ /* Clear Producer Transaction End bit */
+ config = readl_relaxed(base + DML_CONFIG);
+ config &= ~PRODUCER_TRANS_END_EN;
+ writel_relaxed(config, base + DML_CONFIG);
+ /* Trigger consumer */
+ writel_relaxed(1, base + DML_CONSUMER_START);
+ }
+
+ /* make sure the dml is configured before dma is triggered */
+ wmb();
+}
+
+static int of_get_dml_pipe_index(struct device_node *np, const char *name)
+{
+ int index;
+ struct of_phandle_args dma_spec;
+
+ index = of_property_match_string(np, "dma-names", name);
+
+ if (index < 0)
+ return -ENODEV;
+
+ if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
+ &dma_spec))
+ return -ENODEV;
+
+ if (dma_spec.args_count)
+ return dma_spec.args[0];
+
+ return -ENODEV;
+}
+
+/* Initialize the dml hardware connected to SD Card controller */
+int dml_hw_init(struct mmci_host *host, struct device_node *np)
+{
+ u32 config;
+ void __iomem *base;
+ u32 consumer_id, producer_id;
+
+ consumer_id = of_get_dml_pipe_index(np, "tx");
+ producer_id = of_get_dml_pipe_index(np, "rx");
+
+ if (producer_id < 0 || consumer_id < 0)
+ return -ENODEV;
+
+ base = host->base + DML_OFFSET;
+
+ /* Reset the DML block */
+ writel_relaxed(1, base + DML_SW_RESET);
+
+ /* Disable the producer and consumer CRCI */
+ config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE);
+ /*
+ * Disable the bypass mode. Bypass mode will only be used
+ * if data transfer is to happen in PIO mode and don't
+ * want the BAM interface to connect with SDCC-DML.
+ */
+ config &= ~BYPASS;
+ /*
+ * Disable direct mode as we don't DML to MASTER the AHB bus.
+ * BAM connected with DML should MASTER the AHB bus.
+ */
+ config &= ~DIRECT_MODE;
+ /*
+ * Disable infinite mode transfer as we won't be doing any
+ * infinite size data transfers. All data transfer will be
+ * of finite data size.
+ */
+ config &= ~INFINITE_CONS_TRANS;
+ writel_relaxed(config, base + DML_CONFIG);
+
+ /*
+ * Initialize the logical BAM pipe size for producer
+ * and consumer.
+ */
+ writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
+ base + DML_PRODUCER_PIPE_LOGICAL_SIZE);
+ writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
+ base + DML_CONSUMER_PIPE_LOGICAL_SIZE);
+
+ /* Initialize Producer/consumer pipe id */
+ writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT),
+ base + DML_PIPE_ID);
+
+ /* Make sure dml intialization is finished */
+ mb();
+
+ return 0;
+}
diff --git a/drivers/mmc/host/mmci_qcom_dml.h b/drivers/mmc/host/mmci_qcom_dml.h
new file mode 100644
index 000000000000..6e405d09d534
--- /dev/null
+++ b/drivers/mmc/host/mmci_qcom_dml.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright (c) 2011, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#ifndef __MMC_QCOM_DML_H__
+#define __MMC_QCOM_DML_H__
+
+#ifdef CONFIG_MMC_QCOM_DML
+int dml_hw_init(struct mmci_host *host, struct device_node *np);
+void dml_start_xfer(struct mmci_host *host, struct mmc_data *data);
+#else
+static inline int dml_hw_init(struct mmci_host *host, struct device_node *np)
+{
+ return -ENOSYS;
+}
+static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
+{
+}
+#endif /* CONFIG_MMC_QCOM_DML */
+
+#endif /* __MMC_QCOM_DML_H__ */
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 81974ecdfcbc..761ff00a65f3 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -81,8 +81,8 @@
#define mmc_omap2() (!mmc_omap1())
#define OMAP_MMC_REG(host, reg) (OMAP_MMC_REG_##reg << (host)->reg_shift)
-#define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG(host, reg))
-#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG(host, reg))
+#define OMAP_MMC_READ(host, reg) readw_relaxed((host)->virt_base + OMAP_MMC_REG(host, reg))
+#define OMAP_MMC_WRITE(host, reg, val) writew_relaxed((val), (host)->virt_base + OMAP_MMC_REG(host, reg))
/*
* Command types
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 965672663ef0..37db60c54b02 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -161,10 +161,10 @@
* MMC Host controller read/write API's
*/
#define OMAP_HSMMC_READ(base, reg) \
- __raw_readl((base) + OMAP_HSMMC_##reg)
+ readl_relaxed((base) + OMAP_HSMMC_##reg)
#define OMAP_HSMMC_WRITE(base, reg, val) \
- __raw_writel((val), (base) + OMAP_HSMMC_##reg)
+ writel_relaxed((val), (base) + OMAP_HSMMC_##reg)
struct omap_hsmmc_next {
unsigned int dma_len;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f1cf503517fd..5f9c951fce67 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,3 +1,10 @@
+config MTD_NAND_IDS
+ tristate "Include chip ids for known NAND devices."
+ depends on MTD
+ help
+ Useful for NAND drivers that do not use the NAND subsystem but
+ still like to take advantage of the known chip information.
+
config MTD_NAND_ECC
tristate
@@ -106,9 +113,6 @@ config MTD_NAND_OMAP_BCH
legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
so they should not enable this config symbol.
-config MTD_NAND_IDS
- tristate
-
config MTD_NAND_RICOH
tristate "Ricoh xD card reader"
default n
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index f0ed92e210a1..5967b385141b 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -931,7 +931,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u32 val;
val = readl(info->reg.gpmc_ecc_config);
- if (((val >> ECC_CONFIG_CS_SHIFT) & ~CS_MASK) != info->gpmc_cs)
+ if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs)
return -EINVAL;
/* read ecc result */
@@ -1794,9 +1794,12 @@ static int omap_nand_probe(struct platform_device *pdev)
}
/* populate MTD interface based on ECC scheme */
- nand_chip->ecc.layout = &omap_oobinfo;
ecclayout = &omap_oobinfo;
switch (info->ecc_opt) {
+ case OMAP_ECC_HAM1_CODE_SW:
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ break;
+
case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
nand_chip->ecc.mode = NAND_ECC_HW;
@@ -1848,7 +1851,7 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.priv = nand_bch_init(mtd,
nand_chip->ecc.size,
nand_chip->ecc.bytes,
- &nand_chip->ecc.layout);
+ &ecclayout);
if (!nand_chip->ecc.priv) {
pr_err("nand: error: unable to use s/w BCH library\n");
err = -EINVAL;
@@ -1923,7 +1926,7 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.priv = nand_bch_init(mtd,
nand_chip->ecc.size,
nand_chip->ecc.bytes,
- &nand_chip->ecc.layout);
+ &ecclayout);
if (!nand_chip->ecc.priv) {
pr_err("nand: error: unable to use s/w BCH library\n");
err = -EINVAL;
@@ -2012,6 +2015,9 @@ static int omap_nand_probe(struct platform_device *pdev)
goto return_error;
}
+ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW)
+ goto scan_tail;
+
/* all OOB bytes from oobfree->offset till end off OOB are free */
ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
/* check if NAND device's OOB is enough to store ECC signatures */
@@ -2021,7 +2027,9 @@ static int omap_nand_probe(struct platform_device *pdev)
err = -EINVAL;
goto return_error;
}
+ nand_chip->ecc.layout = ecclayout;
+scan_tail:
/* second phase scan */
if (nand_scan_tail(mtd)) {
err = -ENXIO;
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
index e9421731b05e..a54d89791311 100644
--- a/drivers/net/ethernet/hisilicon/Kconfig
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -24,4 +24,13 @@ config HIX5HD2_GMAC
help
This selects the hix5hd2 mac family network device.
+config HIP04_ETH
+ tristate "HISILICON P04 Ethernet support"
+ select PHYLIB
+ select MARVELL_PHY
+ select MFD_SYSCON
+ ---help---
+ If you wish to compile a kernel for a hardware with hisilicon p04 SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
index 9175e84622d4..6c14540a4dc5 100644
--- a/drivers/net/ethernet/hisilicon/Makefile
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o
+obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o hip04_eth.o
diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
new file mode 100644
index 000000000000..7f01b910349e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
@@ -0,0 +1,783 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * 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 <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/of_address.h>
+#include <linux/phy.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#define PPE_CFG_RX_ADDR 0x100
+#define PPE_CFG_POOL_GRP 0x300
+#define PPE_CFG_RX_BUF_SIZE 0x400
+#define PPE_CFG_RX_FIFO_SIZE 0x500
+#define PPE_CURR_BUF_CNT 0xa200
+
+#define GE_DUPLEX_TYPE 0x8
+#define GE_MAX_FRM_SIZE_REG 0x3c
+#define GE_PORT_MODE 0x40
+#define GE_PORT_EN 0x44
+#define GE_SHORT_RUNTS_THR_REG 0x50
+#define GE_TX_LOCAL_PAGE_REG 0x5c
+#define GE_TRANSMIT_CONTROL_REG 0x60
+#define GE_CF_CRC_STRIP_REG 0x1b0
+#define GE_MODE_CHANGE_EN 0x1b4
+#define GE_RECV_CONTROL_REG 0x1e0
+#define GE_STATION_MAC_ADDRESS 0x210
+#define PPE_CFG_TX_PKT_BD_ADDR 0x420
+#define PPE_CFG_MAX_FRAME_LEN_REG 0x408
+#define PPE_CFG_BUS_CTRL_REG 0x424
+#define PPE_CFG_RX_CTRL_REG 0x428
+#define PPE_CFG_RX_PKT_MODE_REG 0x438
+#define PPE_CFG_QOS_VMID_GEN 0x500
+#define PPE_CFG_RX_PKT_INT 0x538
+#define PPE_INTEN 0x600
+#define PPE_INTSTS 0x608
+#define PPE_RINT 0x604
+#define PPE_CFG_STS_MODE 0x700
+#define PPE_HIS_RX_PKT_CNT 0x804
+
+/* REG_INTERRUPT */
+#define RCV_INT BIT(10)
+#define RCV_NOBUF BIT(8)
+#define DEF_INT_MASK (RCV_INT | RCV_NOBUF)
+
+#define RX_DESC_NUM 64
+#define TX_DESC_NUM 64
+#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM-1))
+#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM-1))
+
+#define GMAC_PPE_RX_PKT_MAX_LEN 379
+#define GMAC_MAX_PKT_LEN 1516
+#define DESC_DEF_CFG 0x14
+#define RX_BUF_SIZE 1600
+#define RX_PKT_ERR 0x3
+#define TX_TIMEOUT (6 * HZ)
+#define RECLAIM_PERIOD HZ
+
+#define DRV_NAME "hip04-ether"
+
+struct tx_desc {
+ u32 send_addr;
+ u16 reserved_16;
+ u16 send_size;
+ u32 reserved_32;
+ u32 cfg;
+ u32 wb_addr;
+} ____cacheline_aligned;
+
+struct rx_desc {
+ u16 reserved_16;
+ u16 pkt_len;
+ u32 reserve1[3];
+ u32 pkt_err;
+ u32 reserve2[4];
+};
+
+struct hip04_priv {
+ void __iomem *base;
+ int phy_mode;
+ int chan;
+ unsigned int port;
+ unsigned int speed;
+ unsigned int duplex;
+ unsigned int reg_inten;
+
+ struct napi_struct napi;
+ struct net_device *ndev;
+
+ struct tx_desc *tx_desc;
+ dma_addr_t tx_desc_dma;
+ struct sk_buff *tx_skb[TX_DESC_NUM];
+ dma_addr_t tx_phys[TX_DESC_NUM];
+ spinlock_t lock;
+ unsigned int tx_head;
+ unsigned int tx_tail;
+ unsigned int tx_count;
+
+ unsigned char *rx_buf[RX_DESC_NUM];
+ dma_addr_t rx_phys[RX_DESC_NUM];
+ unsigned int rx_head;
+ unsigned int rx_buf_size;
+
+ struct device_node *phy_node;
+ struct phy_device *phy;
+ struct regmap *map;
+ struct timer_list txtimer;
+ struct work_struct tx_timeout_task;
+};
+
+static void hip04_config_port(struct net_device *ndev, u32 speed, u32 duplex)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ u32 val;
+
+ priv->speed = speed;
+ priv->duplex = duplex;
+
+ switch (priv->phy_mode) {
+ case PHY_INTERFACE_MODE_SGMII:
+ if (speed == SPEED_1000)
+ val = 8;
+ else if (speed == SPEED_100)
+ val = 7;
+ else
+ val = 6; /* SPEED_10 */
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ if (speed == SPEED_100)
+ val = 1;
+ else
+ val = 0; /* SPEED_10 */
+ break;
+ default:
+ netdev_warn(ndev, "not supported mode\n");
+ val = 0;
+ break;
+ }
+ writel_relaxed(val, priv->base + GE_PORT_MODE);
+
+ val = (duplex) ? BIT(0) : 0;
+ writel_relaxed(val, priv->base + GE_DUPLEX_TYPE);
+
+ val = BIT(0);
+ writel_relaxed(val, priv->base + GE_MODE_CHANGE_EN);
+}
+
+static void hip04_reset_ppe(struct hip04_priv *priv)
+{
+ u32 val, tmp;
+
+ do {
+ regmap_read(priv->map, priv->port * 4 + PPE_CURR_BUF_CNT, &val);
+ regmap_read(priv->map, priv->port * 4 + PPE_CFG_RX_ADDR, &tmp);
+ } while (val & 0xfff);
+}
+
+static void hip04_config_fifo(struct hip04_priv *priv)
+{
+ u32 val;
+
+ val = readl_relaxed(priv->base + PPE_CFG_STS_MODE);
+ val |= BIT(12); /* PPE_HIS_RX_PKT_CNT read clear */
+ writel_relaxed(val, priv->base + PPE_CFG_STS_MODE);
+
+ val = BIT(priv->port);
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_POOL_GRP, val);
+
+ val = priv->port << 8;
+ val |= BIT(14);
+ writel_relaxed(val, priv->base + PPE_CFG_QOS_VMID_GEN);
+
+ val = RX_BUF_SIZE;
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_BUF_SIZE, val);
+
+ val = RX_DESC_NUM << 16; /* depth */
+ val |= BIT(11); /* seq: first set first ues */
+ val |= RX_DESC_NUM * priv->chan; /* start_addr */
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_FIFO_SIZE, val);
+
+ /* pkt store format */
+ val = NET_IP_ALIGN << 11; /* align */
+ writel_relaxed(val, priv->base + PPE_CFG_RX_CTRL_REG);
+
+ /* following cfg required for 1000M */
+ /* pkt mode */
+ val = BIT(18); /* align */
+ writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_MODE_REG);
+
+ /* set bus ctrl */
+ val = BIT(14); /* buffer locally release */
+ val |= BIT(0); /* big endian */
+ writel_relaxed(val, priv->base + PPE_CFG_BUS_CTRL_REG);
+
+ /* set max pkt len, curtail if exceed */
+ val = GMAC_PPE_RX_PKT_MAX_LEN; /* max pkt len */
+ writel_relaxed(val, priv->base + PPE_CFG_MAX_FRAME_LEN_REG);
+
+ /* set max len of each pkt */
+ val = GMAC_MAX_PKT_LEN; /* max buffer len */
+ writel_relaxed(val, priv->base + GE_MAX_FRM_SIZE_REG);
+
+ /* set min len of each pkt */
+ val = 31; /* min buffer len */
+ writel_relaxed(val, priv->base + GE_SHORT_RUNTS_THR_REG);
+
+ /* tx */
+ val = readl_relaxed(priv->base + GE_TRANSMIT_CONTROL_REG);
+ val |= BIT(5); /* tx auto neg */
+ val |= BIT(6); /* tx add crc */
+ val |= BIT(7); /* tx short pad through */
+ writel_relaxed(val, priv->base + GE_TRANSMIT_CONTROL_REG);
+
+ /* rx crc */
+ val = BIT(0); /* rx strip crc */
+ writel_relaxed(val, priv->base + GE_CF_CRC_STRIP_REG);
+
+ /* rx */
+ val = readl_relaxed(priv->base + GE_RECV_CONTROL_REG);
+ val |= BIT(3); /* rx strip pad */
+ val |= BIT(4); /* run pkt en */
+ writel_relaxed(val, priv->base + GE_RECV_CONTROL_REG);
+
+ /* auto neg control */
+ val = BIT(0);
+ writel_relaxed(val, priv->base + GE_TX_LOCAL_PAGE_REG);
+}
+
+static void hip04_mac_enable(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ u32 val;
+
+ /* enable tx & rx */
+ val = readl_relaxed(priv->base + GE_PORT_EN);
+ val |= BIT(1); /* rx */
+ val |= BIT(2); /* tx */
+ writel_relaxed(val, priv->base + GE_PORT_EN);
+
+ /* clear rx int */
+ val = RCV_INT;
+ writel_relaxed(val, priv->base + PPE_RINT);
+
+ /* config recv int */
+ val = BIT(6); /* int threshold 1 package */
+ val |= 0x4; /* recv timeout */
+ writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_INT);
+
+ /* enable interrupt */
+ priv->reg_inten = DEF_INT_MASK;
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+}
+
+static void hip04_mac_disable(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ u32 val;
+
+ /* disable int */
+ priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+
+ /* disable tx & rx */
+ val = readl_relaxed(priv->base + GE_PORT_EN);
+ val &= ~(BIT(1)); /* rx */
+ val &= ~(BIT(2)); /* tx */
+ writel_relaxed(val, priv->base + GE_PORT_EN);
+}
+
+static void hip04_set_xmit_desc(struct hip04_priv *priv, dma_addr_t phys)
+{
+ writel(phys, priv->base + PPE_CFG_TX_PKT_BD_ADDR);
+}
+
+static void hip04_set_recv_desc(struct hip04_priv *priv, dma_addr_t phys)
+{
+ regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_ADDR, phys);
+}
+
+static u32 hip04_recv_cnt(struct hip04_priv *priv)
+{
+ return readl(priv->base + PPE_HIS_RX_PKT_CNT);
+}
+
+static void hip04_update_mac_address(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+
+ writel_relaxed(((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1])),
+ priv->base + GE_STATION_MAC_ADDRESS);
+ writel_relaxed(((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5])),
+ priv->base + GE_STATION_MAC_ADDRESS + 4);
+}
+
+static int hip04_set_mac_address(struct net_device *ndev, void *addr)
+{
+ eth_mac_addr(ndev, addr);
+ hip04_update_mac_address(ndev);
+ return 0;
+}
+
+static void hip04_tx_reclaim(struct net_device *ndev, bool force)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ unsigned tx_head = priv->tx_head;
+ unsigned tx_tail = priv->tx_tail;
+ struct tx_desc *desc;
+
+ spin_lock_bh(&priv->lock);
+ while ((tx_tail != tx_head) || (priv->tx_count == TX_DESC_NUM)) {
+ desc = &priv->tx_desc[priv->tx_tail];
+ if (desc->send_addr != 0) {
+ if (force)
+ desc->send_addr = 0;
+ else
+ break;
+ }
+ if (priv->tx_phys[tx_tail]) {
+ dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail],
+ priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE);
+ priv->tx_phys[tx_tail] = 0;
+ }
+ dev_kfree_skb(priv->tx_skb[tx_tail]);
+ priv->tx_skb[tx_tail] = NULL;
+ tx_tail = TX_NEXT(tx_tail);
+ priv->tx_count--;
+ }
+ priv->tx_tail = tx_tail;
+ spin_unlock_bh(&priv->lock);
+
+ if (priv->tx_count)
+ mod_timer(&priv->txtimer, jiffies + RECLAIM_PERIOD);
+
+ if (unlikely(netif_queue_stopped(ndev)) &&
+ (priv->tx_count < TX_DESC_NUM))
+ netif_wake_queue(ndev);
+}
+
+static void hip04_xmit_timer(unsigned long data)
+{
+ struct net_device *ndev = (void *) data;
+
+ hip04_tx_reclaim(ndev, false);
+}
+
+static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ unsigned int tx_head = priv->tx_head;
+ struct tx_desc *desc = &priv->tx_desc[tx_head];
+ dma_addr_t phys;
+
+ hip04_tx_reclaim(ndev, false);
+ mod_timer(&priv->txtimer, jiffies + RECLAIM_PERIOD);
+
+ if (priv->tx_count >= TX_DESC_NUM) {
+ netif_stop_queue(ndev);
+ return NETDEV_TX_BUSY;
+ }
+
+ phys = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&ndev->dev, phys)) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ priv->tx_skb[tx_head] = skb;
+ priv->tx_phys[tx_head] = phys;
+ desc->send_addr = cpu_to_be32(phys);
+ desc->send_size = cpu_to_be16(skb->len);
+ desc->cfg = cpu_to_be32(DESC_DEF_CFG);
+ phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc);
+ desc->wb_addr = cpu_to_be32(phys);
+ skb_tx_timestamp(skb);
+
+ /* Don't wait up for transmitted skbs to be freed. */
+ skb_orphan(skb);
+
+ hip04_set_xmit_desc(priv, phys);
+ priv->tx_head = TX_NEXT(tx_head);
+
+ stats->tx_bytes += skb->len;
+ stats->tx_packets++;
+ priv->tx_count++;
+
+ return NETDEV_TX_OK;
+}
+
+static int hip04_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct hip04_priv *priv = container_of(napi, struct hip04_priv, napi);
+ struct net_device *ndev = priv->ndev;
+ struct net_device_stats *stats = &ndev->stats;
+ unsigned int cnt = hip04_recv_cnt(priv);
+ struct rx_desc *desc;
+ struct sk_buff *skb;
+ unsigned char *buf;
+ bool last = false;
+ dma_addr_t phys;
+ int rx = 0;
+ u16 len;
+ u32 err;
+
+ while (cnt && !last) {
+ buf = priv->rx_buf[priv->rx_head];
+ skb = build_skb(buf, priv->rx_buf_size);
+ if (unlikely(!skb))
+ net_dbg_ratelimited("build_skb failed\n");
+
+ dma_unmap_single(&ndev->dev, priv->rx_phys[priv->rx_head],
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_phys[priv->rx_head] = 0;
+
+ desc = (struct rx_desc *)skb->data;
+ len = be16_to_cpu(desc->pkt_len);
+ err = be32_to_cpu(desc->pkt_err);
+
+ if (0 == len) {
+ dev_kfree_skb_any(skb);
+ last = true;
+ } else if ((err & RX_PKT_ERR) || (len >= GMAC_MAX_PKT_LEN)) {
+ dev_kfree_skb_any(skb);
+ stats->rx_dropped++;
+ stats->rx_errors++;
+ } else {
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+ skb_put(skb, len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ napi_gro_receive(&priv->napi, skb);
+ stats->rx_packets++;
+ stats->rx_bytes += len;
+ rx++;
+ }
+
+ buf = netdev_alloc_frag(priv->rx_buf_size);
+ if (!buf)
+ return -ENOMEM;
+ phys = dma_map_single(&ndev->dev, buf,
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&ndev->dev, phys))
+ return -EIO;
+ priv->rx_buf[priv->rx_head] = buf;
+ priv->rx_phys[priv->rx_head] = phys;
+ hip04_set_recv_desc(priv, phys);
+
+ priv->rx_head = RX_NEXT(priv->rx_head);
+ if (rx >= budget)
+ break;
+
+ if (--cnt == 0)
+ cnt = hip04_recv_cnt(priv);
+ }
+
+ if (rx < budget) {
+ napi_complete(napi);
+
+ /* enable rx interrupt */
+ priv->reg_inten |= RCV_INT | RCV_NOBUF;
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+ }
+
+ return rx;
+}
+
+static irqreturn_t hip04_mac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *) dev_id;
+ struct hip04_priv *priv = netdev_priv(ndev);
+ u32 ists = readl_relaxed(priv->base + PPE_INTSTS);
+
+ writel_relaxed(DEF_INT_MASK, priv->base + PPE_RINT);
+
+ if (ists & (RCV_INT | RCV_NOBUF)) {
+ /* disable rx interrupt */
+ priv->reg_inten &= ~(RCV_INT | RCV_NOBUF);
+ writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
+ napi_schedule(&priv->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hip04_adjust_link(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ struct phy_device *phy = priv->phy;
+
+ if ((priv->speed != phy->speed) || (priv->duplex != phy->duplex)) {
+ hip04_config_port(ndev, phy->speed, phy->duplex);
+ phy_print_status(phy);
+ }
+}
+
+static int hip04_mac_open(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ priv->rx_head = 0;
+ priv->tx_head = 0;
+ priv->tx_tail = 0;
+ priv->tx_count = 0;
+ hip04_reset_ppe(priv);
+
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ dma_addr_t phys;
+
+ phys = dma_map_single(&ndev->dev, priv->rx_buf[i],
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&ndev->dev, phys))
+ return -EIO;
+
+ priv->rx_phys[i] = phys;
+ hip04_set_recv_desc(priv, phys);
+ }
+
+ if (priv->phy)
+ phy_start(priv->phy);
+
+ netif_start_queue(ndev);
+ hip04_mac_enable(ndev);
+ napi_enable(&priv->napi);
+
+ return 0;
+}
+
+static int hip04_mac_stop(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ napi_disable(&priv->napi);
+ netif_stop_queue(ndev);
+ del_timer_sync(&priv->txtimer);
+ hip04_mac_disable(ndev);
+ hip04_tx_reclaim(ndev, true);
+ hip04_reset_ppe(priv);
+
+ if (priv->phy)
+ phy_stop(priv->phy);
+
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ if (priv->rx_phys[i]) {
+ dma_unmap_single(&ndev->dev, priv->rx_phys[i],
+ RX_BUF_SIZE, DMA_FROM_DEVICE);
+ priv->rx_phys[i] = 0;
+ }
+ }
+
+ return 0;
+}
+
+static void hip04_timeout(struct net_device *ndev)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+
+ schedule_work(&priv->tx_timeout_task);
+}
+
+static void hip04_tx_timeout_task(struct work_struct *work)
+{
+ struct hip04_priv *priv;
+
+ priv = container_of(work, struct hip04_priv, tx_timeout_task);
+ hip04_mac_stop(priv->ndev);
+ hip04_mac_open(priv->ndev);
+ return;
+}
+
+static struct net_device_ops hip04_netdev_ops = {
+ .ndo_open = hip04_mac_open,
+ .ndo_stop = hip04_mac_stop,
+ .ndo_start_xmit = hip04_mac_start_xmit,
+ .ndo_set_mac_address = hip04_set_mac_address,
+ .ndo_tx_timeout = hip04_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int hip04_alloc_ring(struct net_device *ndev, struct device *d)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ priv->tx_desc = dma_alloc_coherent(d,
+ TX_DESC_NUM * sizeof(struct tx_desc),
+ &priv->tx_desc_dma, GFP_KERNEL);
+ if (!priv->tx_desc)
+ return -ENOMEM;
+
+ priv->rx_buf_size = RX_BUF_SIZE +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ priv->rx_buf[i] = netdev_alloc_frag(priv->rx_buf_size);
+ if (!priv->rx_buf[i])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void hip04_free_ring(struct net_device *ndev, struct device *d)
+{
+ struct hip04_priv *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < RX_DESC_NUM; i++)
+ if (priv->rx_buf[i])
+ put_page(virt_to_head_page(priv->rx_buf[i]));
+
+ for (i = 0; i < TX_DESC_NUM; i++)
+ if (priv->tx_skb[i])
+ dev_kfree_skb_any(priv->tx_skb[i]);
+
+ dma_free_coherent(d, TX_DESC_NUM * sizeof(struct tx_desc),
+ priv->tx_desc, priv->tx_desc_dma);
+}
+
+static int hip04_mac_probe(struct platform_device *pdev)
+{
+ struct device *d = &pdev->dev;
+ struct device_node *node = d->of_node;
+ struct of_phandle_args arg;
+ struct net_device *ndev;
+ struct hip04_priv *priv;
+ struct resource *res;
+ unsigned int irq;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(struct hip04_priv));
+ if (!ndev)
+ return -ENOMEM;
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+ spin_lock_init(&priv->lock);
+ platform_set_drvdata(pdev, ndev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(d, res);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto init_fail;
+ }
+
+ ret = of_parse_phandle_with_fixed_args(node, "port-handle", 2, 0, &arg);
+ if (ret < 0) {
+ dev_warn(d, "no port-handle\n");
+ goto init_fail;
+ }
+
+ priv->port = arg.args[0];
+ priv->chan = arg.args[1];
+
+ priv->map = syscon_node_to_regmap(arg.np);
+ if (IS_ERR(priv->map)) {
+ dev_warn(d, "no syscon hisilicon,hip04-ppe\n");
+ ret = PTR_ERR(priv->map);
+ goto init_fail;
+ }
+
+ priv->phy_mode = of_get_phy_mode(node);
+ if (priv->phy_mode < 0) {
+ dev_warn(d, "not find phy-mode\n");
+ ret = -EINVAL;
+ goto init_fail;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ ret = -EINVAL;
+ goto init_fail;
+ }
+
+ ret = devm_request_irq(d, irq, hip04_mac_interrupt,
+ 0, pdev->name, ndev);
+ if (ret) {
+ netdev_err(ndev, "devm_request_irq failed\n");
+ goto init_fail;
+ }
+
+ priv->phy_node = of_parse_phandle(node, "phy-handle", 0);
+ if (priv->phy_node) {
+ priv->phy = of_phy_connect(ndev, priv->phy_node,
+ &hip04_adjust_link, 0, priv->phy_mode);
+ if (!priv->phy) {
+ ret = -EPROBE_DEFER;
+ goto init_fail;
+ }
+ }
+
+ INIT_WORK(&priv->tx_timeout_task, hip04_tx_timeout_task);
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &hip04_netdev_ops;
+ ndev->watchdog_timeo = TX_TIMEOUT;
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+ ndev->irq = irq;
+ netif_napi_add(ndev, &priv->napi, hip04_rx_poll, RX_DESC_NUM);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ hip04_reset_ppe(priv);
+ if (priv->phy_mode == PHY_INTERFACE_MODE_MII)
+ hip04_config_port(ndev, SPEED_100, DUPLEX_FULL);
+
+ hip04_config_fifo(priv);
+ random_ether_addr(ndev->dev_addr);
+ hip04_update_mac_address(ndev);
+
+ ret = hip04_alloc_ring(ndev, d);
+ if (ret) {
+ netdev_err(ndev, "alloc ring fail\n");
+ goto alloc_fail;
+ }
+
+ setup_timer(&priv->txtimer, hip04_xmit_timer, (unsigned long) ndev);
+ ret = register_netdev(ndev);
+ if (ret) {
+ free_netdev(ndev);
+ goto alloc_fail;
+ }
+
+ return 0;
+
+alloc_fail:
+ hip04_free_ring(ndev, d);
+init_fail:
+ of_node_put(priv->phy_node);
+ free_netdev(ndev);
+ return ret;
+}
+
+static int hip04_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct hip04_priv *priv = netdev_priv(ndev);
+ struct device *d = &pdev->dev;
+
+ if (priv->phy)
+ phy_disconnect(priv->phy);
+
+ hip04_free_ring(ndev, d);
+ unregister_netdev(ndev);
+ free_irq(ndev->irq, ndev);
+ of_node_put(priv->phy_node);
+ cancel_work_sync(&priv->tx_timeout_task);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mac_match[] = {
+ { .compatible = "hisilicon,hip04-mac" },
+ { }
+};
+
+static struct platform_driver hip04_mac_driver = {
+ .probe = hip04_mac_probe,
+ .remove = hip04_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mac_match,
+ },
+};
+module_platform_driver(hip04_mac_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 Ethernet driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-ether");
diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c
new file mode 100644
index 000000000000..e4f67caf4b5a
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c
@@ -0,0 +1,186 @@
+
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+
+#define MDIO_CMD_REG 0x0
+#define MDIO_ADDR_REG 0x4
+#define MDIO_WDATA_REG 0x8
+#define MDIO_RDATA_REG 0xc
+#define MDIO_STA_REG 0x10
+
+#define MDIO_START BIT(14)
+#define MDIO_R_VALID BIT(1)
+#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START)
+#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START)
+
+struct hip04_mdio_priv {
+ void __iomem *base;
+};
+
+#define WAIT_TIMEOUT 10
+static int hip04_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ int i;
+
+ for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) {
+ if (i == WAIT_TIMEOUT)
+ return -ETIMEDOUT;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = regnum | (mii_id << 5) | MDIO_READ;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ val = readl_relaxed(priv->base + MDIO_STA_REG);
+ if (val & MDIO_R_VALID) {
+ dev_err(bus->parent, "SMI bus read not valid\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ val = readl_relaxed(priv->base + MDIO_RDATA_REG);
+ ret = val & 0xFFFF;
+out:
+ return ret;
+}
+
+static int hip04_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct hip04_mdio_priv *priv = bus->priv;
+ u32 val;
+ int ret;
+
+ ret = hip04_mdio_wait_ready(bus);
+ if (ret < 0)
+ goto out;
+
+ writel_relaxed(value, priv->base + MDIO_WDATA_REG);
+ val = regnum | (mii_id << 5) | MDIO_WRITE;
+ writel_relaxed(val, priv->base + MDIO_CMD_REG);
+out:
+ return ret;
+}
+
+
+static int hip04_mdio_reset(struct mii_bus *bus)
+{
+ int temp, err, i;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ hip04_mdio_write(bus, i, 22, 0);
+ temp = hip04_mdio_read(bus, i, MII_BMCR);
+ temp |= BMCR_RESET;
+ err = hip04_mdio_write(bus, i, MII_BMCR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ mdelay(500);
+ return 0;
+}
+
+static int hip04_mdio_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct mii_bus *bus;
+ struct hip04_mdio_priv *priv;
+ int ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "hip04_mdio_bus";
+ bus->read = hip04_mdio_read;
+ bus->write = hip04_mdio_write;
+ bus->reset = hip04_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+ priv = bus->priv;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto out_mdio;
+ }
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ goto out_mdio;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+
+out_mdio:
+ mdiobus_free(bus);
+ return ret;
+}
+
+static int hip04_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+
+ mdiobus_unregister(bus);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static const struct of_device_id hip04_mdio_match[] = {
+ { .compatible = "hisilicon,hip04-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hip04_mdio_match);
+
+static struct platform_driver hip04_mdio_driver = {
+ .probe = hip04_mdio_probe,
+ .remove = hip04_mdio_remove,
+ .driver = {
+ .name = "hip04-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = hip04_mdio_match,
+ },
+};
+
+module_platform_driver(hip04_mdio_driver);
+
+MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hip04-mdio");
diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig
index 1373c6d7278d..282aec4860eb 100644
--- a/drivers/net/ppp/Kconfig
+++ b/drivers/net/ppp/Kconfig
@@ -149,6 +149,23 @@ config PPPOL2TP
tunnels. L2TP is replacing PPTP for VPN uses.
if TTY
+config PPPOLAC
+ tristate "PPP on L2TP Access Concentrator"
+ depends on PPP && INET
+ help
+ L2TP (RFC 2661) is a tunneling protocol widely used in virtual private
+ networks. This driver handles L2TP data packets between a UDP socket
+ and a PPP channel, but only permits one session per socket. Thus it is
+ fairly simple and suited for clients.
+
+config PPPOPNS
+ tristate "PPP on PPTP Network Server"
+ depends on PPP && INET
+ help
+ PPTP (RFC 2637) is a tunneling protocol widely used in virtual private
+ networks. This driver handles PPTP data packets between a RAW socket
+ and a PPP channel. It is fairly simple and easy to use.
+
config PPP_ASYNC
tristate "PPP support for async serial ports"
depends on PPP
diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile
index a6b6297b0066..d283d03c4683 100644
--- a/drivers/net/ppp/Makefile
+++ b/drivers/net/ppp/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o
obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
obj-$(CONFIG_PPPOL2TP) += pppox.o
obj-$(CONFIG_PPTP) += pppox.o pptp.o
+obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o
+obj-$(CONFIG_PPPOPNS) += pppox.o pppopns.o
diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c
new file mode 100644
index 000000000000..a5d3d634fd9a
--- /dev/null
+++ b/drivers/net/ppp/pppolac.c
@@ -0,0 +1,449 @@
+/* drivers/net/pppolac.c
+ *
+ * Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661)
+ *
+ * Copyright (C) 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+/* This driver handles L2TP data packets between a UDP socket and a PPP channel.
+ * The socket must keep connected, and only one session per socket is permitted.
+ * Sequencing of outgoing packets is controlled by LNS. Incoming packets with
+ * sequences are reordered within a sliding window of one second. Currently
+ * reordering only happens when a packet is received. It is done for simplicity
+ * since no additional locks or threads are required. This driver only works on
+ * IPv4 due to the lack of UDP encapsulation support in IPv6. */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/file.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/udp.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_channel.h>
+#include <net/tcp_states.h>
+#include <asm/uaccess.h>
+
+#define L2TP_CONTROL_BIT 0x80
+#define L2TP_LENGTH_BIT 0x40
+#define L2TP_SEQUENCE_BIT 0x08
+#define L2TP_OFFSET_BIT 0x02
+#define L2TP_VERSION 0x02
+#define L2TP_VERSION_MASK 0x0F
+
+#define PPP_ADDR 0xFF
+#define PPP_CTRL 0x03
+
+union unaligned {
+ __u32 u32;
+} __attribute__((packed));
+
+static inline union unaligned *unaligned(void *ptr)
+{
+ return (union unaligned *)ptr;
+}
+
+struct meta {
+ __u32 sequence;
+ __u32 timestamp;
+};
+
+static inline struct meta *skb_meta(struct sk_buff *skb)
+{
+ return (struct meta *)skb->cb;
+}
+
+/******************************************************************************/
+
+static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
+{
+ struct sock *sk = (struct sock *)sk_udp->sk_user_data;
+ struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac;
+ struct meta *meta = skb_meta(skb);
+ __u32 now = jiffies;
+ __u8 bits;
+ __u8 *ptr;
+
+ /* Drop the packet if L2TP header is missing. */
+ if (skb->len < sizeof(struct udphdr) + 6)
+ goto drop;
+
+ /* Put it back if it is a control packet. */
+ if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT)
+ return opt->backlog_rcv(sk_udp, skb);
+
+ /* Skip UDP header. */
+ skb_pull(skb, sizeof(struct udphdr));
+
+ /* Check the version. */
+ if ((skb->data[1] & L2TP_VERSION_MASK) != L2TP_VERSION)
+ goto drop;
+ bits = skb->data[0];
+ ptr = &skb->data[2];
+
+ /* Check the length if it is present. */
+ if (bits & L2TP_LENGTH_BIT) {
+ if ((ptr[0] << 8 | ptr[1]) != skb->len)
+ goto drop;
+ ptr += 2;
+ }
+
+ /* Skip all fields including optional ones. */
+ if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) +
+ (bits & L2TP_LENGTH_BIT ? 2 : 0) +
+ (bits & L2TP_OFFSET_BIT ? 2 : 0)))
+ goto drop;
+
+ /* Skip the offset padding if it is present. */
+ if (bits & L2TP_OFFSET_BIT &&
+ !skb_pull(skb, skb->data[-2] << 8 | skb->data[-1]))
+ goto drop;
+
+ /* Check the tunnel and the session. */
+ if (unaligned(ptr)->u32 != opt->local)
+ goto drop;
+
+ /* Check the sequence if it is present. */
+ if (bits & L2TP_SEQUENCE_BIT) {
+ meta->sequence = ptr[4] << 8 | ptr[5];
+ if ((__s16)(meta->sequence - opt->recv_sequence) < 0)
+ goto drop;
+ }
+
+ /* Skip PPP address and control if they are present. */
+ if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
+ skb->data[1] == PPP_CTRL)
+ skb_pull(skb, 2);
+
+ /* Fix PPP protocol if it is compressed. */
+ if (skb->len >= 1 && skb->data[0] & 1)
+ skb_push(skb, 1)[0] = 0;
+
+ /* Drop the packet if PPP protocol is missing. */
+ if (skb->len < 2)
+ goto drop;
+
+ /* Perform reordering if sequencing is enabled. */
+ atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT);
+ if (bits & L2TP_SEQUENCE_BIT) {
+ struct sk_buff *skb1;
+
+ /* Insert the packet into receive queue in order. */
+ skb_set_owner_r(skb, sk);
+ skb_queue_walk(&sk->sk_receive_queue, skb1) {
+ struct meta *meta1 = skb_meta(skb1);
+ __s16 order = meta->sequence - meta1->sequence;
+ if (order == 0)
+ goto drop;
+ if (order < 0) {
+ meta->timestamp = meta1->timestamp;
+ skb_insert(skb1, skb, &sk->sk_receive_queue);
+ skb = NULL;
+ break;
+ }
+ }
+ if (skb) {
+ meta->timestamp = now;
+ skb_queue_tail(&sk->sk_receive_queue, skb);
+ }
+
+ /* Remove packets from receive queue as long as
+ * 1. the receive buffer is full,
+ * 2. they are queued longer than one second, or
+ * 3. there are no missing packets before them. */
+ skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) {
+ meta = skb_meta(skb);
+ if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
+ now - meta->timestamp < HZ &&
+ meta->sequence != opt->recv_sequence)
+ break;
+ skb_unlink(skb, &sk->sk_receive_queue);
+ opt->recv_sequence = (__u16)(meta->sequence + 1);
+ skb_orphan(skb);
+ ppp_input(&pppox_sk(sk)->chan, skb);
+ }
+ return NET_RX_SUCCESS;
+ }
+
+ /* Flush receive queue if sequencing is disabled. */
+ skb_queue_purge(&sk->sk_receive_queue);
+ skb_orphan(skb);
+ ppp_input(&pppox_sk(sk)->chan, skb);
+ return NET_RX_SUCCESS;
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb)
+{
+ sock_hold(sk_udp);
+ sk_receive_skb(sk_udp, skb, 0);
+ return 0;
+}
+
+static struct sk_buff_head delivery_queue;
+
+static void pppolac_xmit_core(struct work_struct *delivery_work)
+{
+ mm_segment_t old_fs = get_fs();
+ struct sk_buff *skb;
+
+ set_fs(KERNEL_DS);
+ while ((skb = skb_dequeue(&delivery_queue))) {
+ struct sock *sk_udp = skb->sk;
+ struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
+ struct msghdr msg = {
+ .msg_iov = (struct iovec *)&iov,
+ .msg_iovlen = 1,
+ .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
+ };
+ sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len);
+ kfree_skb(skb);
+ }
+ set_fs(old_fs);
+}
+
+static DECLARE_WORK(delivery_work, pppolac_xmit_core);
+
+static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+ struct sock *sk_udp = (struct sock *)chan->private;
+ struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac;
+
+ /* Install PPP address and control. */
+ skb_push(skb, 2);
+ skb->data[0] = PPP_ADDR;
+ skb->data[1] = PPP_CTRL;
+
+ /* Install L2TP header. */
+ if (atomic_read(&opt->sequencing)) {
+ skb_push(skb, 10);
+ skb->data[0] = L2TP_SEQUENCE_BIT;
+ skb->data[6] = opt->xmit_sequence >> 8;
+ skb->data[7] = opt->xmit_sequence;
+ skb->data[8] = 0;
+ skb->data[9] = 0;
+ opt->xmit_sequence++;
+ } else {
+ skb_push(skb, 6);
+ skb->data[0] = 0;
+ }
+ skb->data[1] = L2TP_VERSION;
+ unaligned(&skb->data[2])->u32 = opt->remote;
+
+ /* Now send the packet via the delivery queue. */
+ skb_set_owner_w(skb, sk_udp);
+ skb_queue_tail(&delivery_queue, skb);
+ schedule_work(&delivery_work);
+ return 1;
+}
+
+/******************************************************************************/
+
+static struct ppp_channel_ops pppolac_channel_ops = {
+ .start_xmit = pppolac_xmit,
+};
+
+static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr,
+ int addrlen, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct pppox_sock *po = pppox_sk(sk);
+ struct sockaddr_pppolac *addr = (struct sockaddr_pppolac *)useraddr;
+ struct socket *sock_udp = NULL;
+ struct sock *sk_udp;
+ int error;
+
+ if (addrlen != sizeof(struct sockaddr_pppolac) ||
+ !addr->local.tunnel || !addr->local.session ||
+ !addr->remote.tunnel || !addr->remote.session) {
+ return -EINVAL;
+ }
+
+ lock_sock(sk);
+ error = -EALREADY;
+ if (sk->sk_state != PPPOX_NONE)
+ goto out;
+
+ sock_udp = sockfd_lookup(addr->udp_socket, &error);
+ if (!sock_udp)
+ goto out;
+ sk_udp = sock_udp->sk;
+ lock_sock(sk_udp);
+
+ /* Remove this check when IPv6 supports UDP encapsulation. */
+ error = -EAFNOSUPPORT;
+ if (sk_udp->sk_family != AF_INET)
+ goto out;
+ error = -EPROTONOSUPPORT;
+ if (sk_udp->sk_protocol != IPPROTO_UDP)
+ goto out;
+ error = -EDESTADDRREQ;
+ if (sk_udp->sk_state != TCP_ESTABLISHED)
+ goto out;
+ error = -EBUSY;
+ if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data)
+ goto out;
+ if (!sk_udp->sk_bound_dev_if) {
+ struct dst_entry *dst = sk_dst_get(sk_udp);
+ error = -ENODEV;
+ if (!dst)
+ goto out;
+ sk_udp->sk_bound_dev_if = dst->dev->ifindex;
+ dst_release(dst);
+ }
+
+ po->chan.hdrlen = 12;
+ po->chan.private = sk_udp;
+ po->chan.ops = &pppolac_channel_ops;
+ po->chan.mtu = PPP_MRU - 80;
+ po->proto.lac.local = unaligned(&addr->local)->u32;
+ po->proto.lac.remote = unaligned(&addr->remote)->u32;
+ atomic_set(&po->proto.lac.sequencing, 1);
+ po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv;
+
+ error = ppp_register_channel(&po->chan);
+ if (error)
+ goto out;
+
+ sk->sk_state = PPPOX_CONNECTED;
+ udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP;
+ udp_sk(sk_udp)->encap_rcv = pppolac_recv;
+ sk_udp->sk_backlog_rcv = pppolac_recv_core;
+ sk_udp->sk_user_data = sk;
+out:
+ if (sock_udp) {
+ release_sock(sk_udp);
+ if (error)
+ sockfd_put(sock_udp);
+ }
+ release_sock(sk);
+ return error;
+}
+
+static int pppolac_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (!sk)
+ return 0;
+
+ lock_sock(sk);
+ if (sock_flag(sk, SOCK_DEAD)) {
+ release_sock(sk);
+ return -EBADF;
+ }
+
+ if (sk->sk_state != PPPOX_NONE) {
+ struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private;
+ lock_sock(sk_udp);
+ skb_queue_purge(&sk->sk_receive_queue);
+ pppox_unbind_sock(sk);
+ udp_sk(sk_udp)->encap_type = 0;
+ udp_sk(sk_udp)->encap_rcv = NULL;
+ sk_udp->sk_backlog_rcv = pppox_sk(sk)->proto.lac.backlog_rcv;
+ sk_udp->sk_user_data = NULL;
+ release_sock(sk_udp);
+ sockfd_put(sk_udp->sk_socket);
+ }
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+ release_sock(sk);
+ sock_put(sk);
+ return 0;
+}
+
+/******************************************************************************/
+
+static struct proto pppolac_proto = {
+ .name = "PPPOLAC",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct pppox_sock),
+};
+
+static struct proto_ops pppolac_proto_ops = {
+ .family = PF_PPPOX,
+ .owner = THIS_MODULE,
+ .release = pppolac_release,
+ .bind = sock_no_bind,
+ .connect = pppolac_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = sock_no_poll,
+ .ioctl = pppox_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static int pppolac_create(struct net *net, struct socket *sock)
+{
+ struct sock *sk;
+
+ sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sock->state = SS_UNCONNECTED;
+ sock->ops = &pppolac_proto_ops;
+ sk->sk_protocol = PX_PROTO_OLAC;
+ sk->sk_state = PPPOX_NONE;
+ return 0;
+}
+
+/******************************************************************************/
+
+static struct pppox_proto pppolac_pppox_proto = {
+ .create = pppolac_create,
+ .owner = THIS_MODULE,
+};
+
+static int __init pppolac_init(void)
+{
+ int error;
+
+ error = proto_register(&pppolac_proto, 0);
+ if (error)
+ return error;
+
+ error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto);
+ if (error)
+ proto_unregister(&pppolac_proto);
+ else
+ skb_queue_head_init(&delivery_queue);
+ return error;
+}
+
+static void __exit pppolac_exit(void)
+{
+ unregister_pppox_proto(PX_PROTO_OLAC);
+ proto_unregister(&pppolac_proto);
+}
+
+module_init(pppolac_init);
+module_exit(pppolac_exit);
+
+MODULE_DESCRIPTION("PPP on L2TP Access Concentrator (PPPoLAC)");
+MODULE_AUTHOR("Chia-chi Yeh <chiachi@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c
new file mode 100644
index 000000000000..6016d29c0660
--- /dev/null
+++ b/drivers/net/ppp/pppopns.c
@@ -0,0 +1,428 @@
+/* drivers/net/pppopns.c
+ *
+ * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637)
+ *
+ * Copyright (C) 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+/* This driver handles PPTP data packets between a RAW socket and a PPP channel.
+ * The socket is created in the kernel space and connected to the same address
+ * of the control socket. Outgoing packets are always sent with sequences but
+ * without acknowledgements. Incoming packets with sequences are reordered
+ * within a sliding window of one second. Currently reordering only happens when
+ * a packet is received. It is done for simplicity since no additional locks or
+ * threads are required. This driver should work on both IPv4 and IPv6. */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/file.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/ppp_defs.h>
+#include <linux/if.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_channel.h>
+#include <asm/uaccess.h>
+
+#define GRE_HEADER_SIZE 8
+
+#define PPTP_GRE_BITS htons(0x2001)
+#define PPTP_GRE_BITS_MASK htons(0xEF7F)
+#define PPTP_GRE_SEQ_BIT htons(0x1000)
+#define PPTP_GRE_ACK_BIT htons(0x0080)
+#define PPTP_GRE_TYPE htons(0x880B)
+
+#define PPP_ADDR 0xFF
+#define PPP_CTRL 0x03
+
+struct header {
+ __u16 bits;
+ __u16 type;
+ __u16 length;
+ __u16 call;
+ __u32 sequence;
+} __attribute__((packed));
+
+struct meta {
+ __u32 sequence;
+ __u32 timestamp;
+};
+
+static inline struct meta *skb_meta(struct sk_buff *skb)
+{
+ return (struct meta *)skb->cb;
+}
+
+/******************************************************************************/
+
+static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb)
+{
+ struct sock *sk = (struct sock *)sk_raw->sk_user_data;
+ struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns;
+ struct meta *meta = skb_meta(skb);
+ __u32 now = jiffies;
+ struct header *hdr;
+
+ /* Skip transport header */
+ skb_pull(skb, skb_transport_header(skb) - skb->data);
+
+ /* Drop the packet if GRE header is missing. */
+ if (skb->len < GRE_HEADER_SIZE)
+ goto drop;
+ hdr = (struct header *)skb->data;
+
+ /* Check the header. */
+ if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local ||
+ (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS)
+ goto drop;
+
+ /* Skip all fields including optional ones. */
+ if (!skb_pull(skb, GRE_HEADER_SIZE +
+ (hdr->bits & PPTP_GRE_SEQ_BIT ? 4 : 0) +
+ (hdr->bits & PPTP_GRE_ACK_BIT ? 4 : 0)))
+ goto drop;
+
+ /* Check the length. */
+ if (skb->len != ntohs(hdr->length))
+ goto drop;
+
+ /* Check the sequence if it is present. */
+ if (hdr->bits & PPTP_GRE_SEQ_BIT) {
+ meta->sequence = ntohl(hdr->sequence);
+ if ((__s32)(meta->sequence - opt->recv_sequence) < 0)
+ goto drop;
+ }
+
+ /* Skip PPP address and control if they are present. */
+ if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
+ skb->data[1] == PPP_CTRL)
+ skb_pull(skb, 2);
+
+ /* Fix PPP protocol if it is compressed. */
+ if (skb->len >= 1 && skb->data[0] & 1)
+ skb_push(skb, 1)[0] = 0;
+
+ /* Drop the packet if PPP protocol is missing. */
+ if (skb->len < 2)
+ goto drop;
+
+ /* Perform reordering if sequencing is enabled. */
+ if (hdr->bits & PPTP_GRE_SEQ_BIT) {
+ struct sk_buff *skb1;
+
+ /* Insert the packet into receive queue in order. */
+ skb_set_owner_r(skb, sk);
+ skb_queue_walk(&sk->sk_receive_queue, skb1) {
+ struct meta *meta1 = skb_meta(skb1);
+ __s32 order = meta->sequence - meta1->sequence;
+ if (order == 0)
+ goto drop;
+ if (order < 0) {
+ meta->timestamp = meta1->timestamp;
+ skb_insert(skb1, skb, &sk->sk_receive_queue);
+ skb = NULL;
+ break;
+ }
+ }
+ if (skb) {
+ meta->timestamp = now;
+ skb_queue_tail(&sk->sk_receive_queue, skb);
+ }
+
+ /* Remove packets from receive queue as long as
+ * 1. the receive buffer is full,
+ * 2. they are queued longer than one second, or
+ * 3. there are no missing packets before them. */
+ skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) {
+ meta = skb_meta(skb);
+ if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
+ now - meta->timestamp < HZ &&
+ meta->sequence != opt->recv_sequence)
+ break;
+ skb_unlink(skb, &sk->sk_receive_queue);
+ opt->recv_sequence = meta->sequence + 1;
+ skb_orphan(skb);
+ ppp_input(&pppox_sk(sk)->chan, skb);
+ }
+ return NET_RX_SUCCESS;
+ }
+
+ /* Flush receive queue if sequencing is disabled. */
+ skb_queue_purge(&sk->sk_receive_queue);
+ skb_orphan(skb);
+ ppp_input(&pppox_sk(sk)->chan, skb);
+ return NET_RX_SUCCESS;
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static void pppopns_recv(struct sock *sk_raw, int length)
+{
+ struct sk_buff *skb;
+ while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) {
+ sock_hold(sk_raw);
+ sk_receive_skb(sk_raw, skb, 0);
+ }
+}
+
+static struct sk_buff_head delivery_queue;
+
+static void pppopns_xmit_core(struct work_struct *delivery_work)
+{
+ mm_segment_t old_fs = get_fs();
+ struct sk_buff *skb;
+
+ set_fs(KERNEL_DS);
+ while ((skb = skb_dequeue(&delivery_queue))) {
+ struct sock *sk_raw = skb->sk;
+ struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
+ struct msghdr msg = {
+ .msg_iov = (struct iovec *)&iov,
+ .msg_iovlen = 1,
+ .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
+ };
+ sk_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len);
+ kfree_skb(skb);
+ }
+ set_fs(old_fs);
+}
+
+static DECLARE_WORK(delivery_work, pppopns_xmit_core);
+
+static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+ struct sock *sk_raw = (struct sock *)chan->private;
+ struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns;
+ struct header *hdr;
+ __u16 length;
+
+ /* Install PPP address and control. */
+ skb_push(skb, 2);
+ skb->data[0] = PPP_ADDR;
+ skb->data[1] = PPP_CTRL;
+ length = skb->len;
+
+ /* Install PPTP GRE header. */
+ hdr = (struct header *)skb_push(skb, 12);
+ hdr->bits = PPTP_GRE_BITS | PPTP_GRE_SEQ_BIT;
+ hdr->type = PPTP_GRE_TYPE;
+ hdr->length = htons(length);
+ hdr->call = opt->remote;
+ hdr->sequence = htonl(opt->xmit_sequence);
+ opt->xmit_sequence++;
+
+ /* Now send the packet via the delivery queue. */
+ skb_set_owner_w(skb, sk_raw);
+ skb_queue_tail(&delivery_queue, skb);
+ schedule_work(&delivery_work);
+ return 1;
+}
+
+/******************************************************************************/
+
+static struct ppp_channel_ops pppopns_channel_ops = {
+ .start_xmit = pppopns_xmit,
+};
+
+static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr,
+ int addrlen, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct pppox_sock *po = pppox_sk(sk);
+ struct sockaddr_pppopns *addr = (struct sockaddr_pppopns *)useraddr;
+ struct sockaddr_storage ss;
+ struct socket *sock_tcp = NULL;
+ struct socket *sock_raw = NULL;
+ struct sock *sk_tcp;
+ struct sock *sk_raw;
+ int error;
+
+ if (addrlen != sizeof(struct sockaddr_pppopns))
+ return -EINVAL;
+
+ lock_sock(sk);
+ error = -EALREADY;
+ if (sk->sk_state != PPPOX_NONE)
+ goto out;
+
+ sock_tcp = sockfd_lookup(addr->tcp_socket, &error);
+ if (!sock_tcp)
+ goto out;
+ sk_tcp = sock_tcp->sk;
+ error = -EPROTONOSUPPORT;
+ if (sk_tcp->sk_protocol != IPPROTO_TCP)
+ goto out;
+ addrlen = sizeof(struct sockaddr_storage);
+ error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen);
+ if (error)
+ goto out;
+ if (!sk_tcp->sk_bound_dev_if) {
+ struct dst_entry *dst = sk_dst_get(sk_tcp);
+ error = -ENODEV;
+ if (!dst)
+ goto out;
+ sk_tcp->sk_bound_dev_if = dst->dev->ifindex;
+ dst_release(dst);
+ }
+
+ error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw);
+ if (error)
+ goto out;
+ sk_raw = sock_raw->sk;
+ sk_raw->sk_bound_dev_if = sk_tcp->sk_bound_dev_if;
+ error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0);
+ if (error)
+ goto out;
+
+ po->chan.hdrlen = 14;
+ po->chan.private = sk_raw;
+ po->chan.ops = &pppopns_channel_ops;
+ po->chan.mtu = PPP_MRU - 80;
+ po->proto.pns.local = addr->local;
+ po->proto.pns.remote = addr->remote;
+ po->proto.pns.data_ready = sk_raw->sk_data_ready;
+ po->proto.pns.backlog_rcv = sk_raw->sk_backlog_rcv;
+
+ error = ppp_register_channel(&po->chan);
+ if (error)
+ goto out;
+
+ sk->sk_state = PPPOX_CONNECTED;
+ lock_sock(sk_raw);
+ sk_raw->sk_data_ready = pppopns_recv;
+ sk_raw->sk_backlog_rcv = pppopns_recv_core;
+ sk_raw->sk_user_data = sk;
+ release_sock(sk_raw);
+out:
+ if (sock_tcp)
+ sockfd_put(sock_tcp);
+ if (error && sock_raw)
+ sock_release(sock_raw);
+ release_sock(sk);
+ return error;
+}
+
+static int pppopns_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (!sk)
+ return 0;
+
+ lock_sock(sk);
+ if (sock_flag(sk, SOCK_DEAD)) {
+ release_sock(sk);
+ return -EBADF;
+ }
+
+ if (sk->sk_state != PPPOX_NONE) {
+ struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private;
+ lock_sock(sk_raw);
+ skb_queue_purge(&sk->sk_receive_queue);
+ pppox_unbind_sock(sk);
+ sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready;
+ sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv;
+ sk_raw->sk_user_data = NULL;
+ release_sock(sk_raw);
+ sock_release(sk_raw->sk_socket);
+ }
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+ release_sock(sk);
+ sock_put(sk);
+ return 0;
+}
+
+/******************************************************************************/
+
+static struct proto pppopns_proto = {
+ .name = "PPPOPNS",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct pppox_sock),
+};
+
+static struct proto_ops pppopns_proto_ops = {
+ .family = PF_PPPOX,
+ .owner = THIS_MODULE,
+ .release = pppopns_release,
+ .bind = sock_no_bind,
+ .connect = pppopns_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = sock_no_poll,
+ .ioctl = pppox_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static int pppopns_create(struct net *net, struct socket *sock)
+{
+ struct sock *sk;
+
+ sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sock->state = SS_UNCONNECTED;
+ sock->ops = &pppopns_proto_ops;
+ sk->sk_protocol = PX_PROTO_OPNS;
+ sk->sk_state = PPPOX_NONE;
+ return 0;
+}
+
+/******************************************************************************/
+
+static struct pppox_proto pppopns_pppox_proto = {
+ .create = pppopns_create,
+ .owner = THIS_MODULE,
+};
+
+static int __init pppopns_init(void)
+{
+ int error;
+
+ error = proto_register(&pppopns_proto, 0);
+ if (error)
+ return error;
+
+ error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto);
+ if (error)
+ proto_unregister(&pppopns_proto);
+ else
+ skb_queue_head_init(&delivery_queue);
+ return error;
+}
+
+static void __exit pppopns_exit(void)
+{
+ unregister_pppox_proto(PX_PROTO_OPNS);
+ proto_unregister(&pppopns_proto);
+}
+
+module_init(pppopns_init);
+module_exit(pppopns_exit);
+
+MODULE_DESCRIPTION("PPP on PPTP Network Server (PPPoPNS)");
+MODULE_AUTHOR("Chia-chi Yeh <chiachi@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index acaaf6784179..1d95695b760f 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1868,6 +1868,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
unsigned int ifindex;
int ret;
+#ifdef CONFIG_ANDROID_PARANOID_NETWORK
+ if (cmd != TUNGETIFF && !capable(CAP_NET_ADMIN)) {
+ return -EPERM;
+ }
+#endif
+
if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == 0x89) {
if (copy_from_user(&ifr, argp, ifreq_len))
return -EFAULT;
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 16604bdf5197..710733f034fa 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -265,6 +265,11 @@ config MWL8K
To compile this driver as a module, choose M here: the module
will be called mwl8k. If unsure, say N.
+config WIFI_CONTROL_FUNC
+ bool "Enable WiFi control function abstraction"
+ help
+ Enables Power/Reset/Carddetect function abstraction
+
source "drivers/net/wireless/ath/Kconfig"
source "drivers/net/wireless/b43/Kconfig"
source "drivers/net/wireless/b43legacy/Kconfig"
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c
index 339d89f14d32..eab0ab976af2 100644
--- a/drivers/net/wireless/ath/ath6kl/sdio.c
+++ b/drivers/net/wireless/ath/ath6kl/sdio.c
@@ -1400,6 +1400,7 @@ static const struct sdio_device_id ath6kl_sdio_devices[] = {
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
+ {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x2))},
{},
};
diff --git a/drivers/of/address.c b/drivers/of/address.c
index e3718250d66e..5eaadaec9a23 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -5,6 +5,7 @@
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/pci_regs.h>
+#include <linux/slab.h>
#include <linux/string.h>
/* Max address size we deal with */
@@ -601,12 +602,72 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
}
EXPORT_SYMBOL(of_get_address);
+struct io_range {
+ struct list_head list;
+ phys_addr_t start;
+ resource_size_t size;
+};
+
+static LIST_HEAD(io_range_list);
+
+/*
+ * Record the PCI IO range (expressed as CPU physical address + size).
+ * Return a negative value if an error has occured, zero otherwise
+ */
+int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
+{
+#ifdef PCI_IOBASE
+ struct io_range *res;
+ resource_size_t allocated_size = 0;
+
+ /* check if the range hasn't been previously recorded */
+ list_for_each_entry(res, &io_range_list, list) {
+ if (addr >= res->start && addr + size <= res->start + size)
+ return 0;
+ allocated_size += res->size;
+ }
+
+ /* range not registed yet, check for available space */
+ if (allocated_size + size - 1 > IO_SPACE_LIMIT)
+ return -E2BIG;
+
+ /* add the range to the list */
+ res = kzalloc(sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+
+ res->start = addr;
+ res->size = size;
+
+ list_add_tail(&res->list, &io_range_list);
+
+ return 0;
+#else
+ return -EINVAL;
+#endif
+}
+
unsigned long __weak pci_address_to_pio(phys_addr_t address)
{
+#ifdef PCI_IOBASE
+ struct io_range *res;
+ resource_size_t offset = 0;
+
+ list_for_each_entry(res, &io_range_list, list) {
+ if (address >= res->start &&
+ address < res->start + res->size) {
+ return res->start - address + offset;
+ }
+ offset += res->size;
+ }
+
+ return (unsigned long)-1;
+#else
if (address > IO_SPACE_LIMIT)
return (unsigned long)-1;
return (unsigned long) address;
+#endif
}
static int __of_address_to_resource(struct device_node *dev,
@@ -847,3 +908,50 @@ bool of_dma_is_coherent(struct device_node *np)
return false;
}
EXPORT_SYMBOL_GPL(of_dma_is_coherent);
+
+/*
+ * of_pci_range_to_resource - Create a resource from an of_pci_range
+ * @range: the PCI range that describes the resource
+ * @np: device node where the range belongs to
+ * @res: pointer to a valid resource that will be updated to
+ * reflect the values contained in the range.
+ *
+ * Returns EINVAL if the range cannot be converted to resource.
+ *
+ * Note that if the range is an IO range, the resource will be converted
+ * using pci_address_to_pio() which can fail if it is called too early or
+ * if the range cannot be matched to any host bridge IO space (our case here).
+ * To guard against that we try to register the IO range first.
+ * If that fails we know that pci_address_to_pio() will do too.
+ */
+int of_pci_range_to_resource(struct of_pci_range *range,
+ struct device_node *np, struct resource *res)
+{
+ int err;
+ res->flags = range->flags;
+ res->parent = res->child = res->sibling = NULL;
+ res->name = np->full_name;
+
+ if (res->flags & IORESOURCE_IO) {
+ unsigned long port = -1;
+ err = pci_register_io_range(range->cpu_addr, range->size);
+ if (err)
+ goto invalid_range;
+ port = pci_address_to_pio(range->cpu_addr);
+ if (port == (unsigned long)-1) {
+ err = -EINVAL;
+ goto invalid_range;
+ }
+ res->start = port;
+ } else {
+ res->start = range->cpu_addr;
+ }
+ res->end = res->start + range->size - 1;
+ return 0;
+
+invalid_range:
+ res->start = (resource_size_t)OF_BAD_ADDR;
+ res->end = (resource_size_t)OF_BAD_ADDR;
+ return err;
+}
+
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index d0e88f114ff9..d45e9a30dd2e 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
+obj-$(CONFIG_ARCH_QCOM) += pci-qcom.o
diff --git a/drivers/pci/host/pci-qcom.c b/drivers/pci/host/pci-qcom.c
new file mode 100644
index 000000000000..67d3a497a13a
--- /dev/null
+++ b/drivers/pci/host/pci-qcom.c
@@ -0,0 +1,968 @@
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+/*
+ * QCOM MSM PCIe controller driver.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_gpio.h>
+#include <linux/msi.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/delay.h>
+
+#define INT_PCI_MSI_NR (8 * 32)
+#define MSM_PCIE_MSI_PHY 0xa0000000
+
+#define PCIE20_MSI_CTRL_ADDR (0x820)
+#define PCIE20_MSI_CTRL_UPPER_ADDR (0x824)
+#define PCIE20_MSI_CTRL_INTR_EN (0x828)
+#define PCIE20_MSI_CTRL_INTR_MASK (0x82C)
+#define PCIE20_MSI_CTRL_INTR_STATUS (0x830)
+
+#define PCIE20_MSI_CTRL_MAX 8
+/* Root Complex Port vendor/device IDs */
+#define PCIE_VENDOR_ID_RCP 0x17cb
+#define PCIE_DEVICE_ID_RCP 0x0101
+
+#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
+
+#define PCIE20_PARF_PCS_DEEMPH 0x34
+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(x) __set(x, 21, 16)
+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) __set(x, 13, 8)
+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) __set(x, 5, 0)
+
+#define PCIE20_PARF_PCS_SWING 0x38
+#define PCIE20_PARF_PCS_SWING_TX_SWING_FULL(x) __set(x, 14, 8)
+#define PCIE20_PARF_PCS_SWING_TX_SWING_LOW(x) __set(x, 6, 0)
+
+#define PCIE20_PARF_PHY_CTRL 0x40
+#define PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(x) __set(x, 20, 16)
+#define PCIE20_PARF_PHY_CTRL_PHY_LOS_LEVEL(x) __set(x, 12, 8)
+#define PCIE20_PARF_PHY_CTRL_PHY_RTUNE_REQ (1 << 4)
+#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BURNIN (1 << 2)
+#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BYPASS (1 << 1)
+#define PCIE20_PARF_PHY_CTRL_PHY_TEST_PWR_DOWN (1 << 0)
+
+#define PCIE20_PARF_PHY_REFCLK 0x4C
+#define PCIE20_PARF_CONFIG_BITS 0x50
+
+#define PCIE20_ELBI_SYS_CTRL 0x04
+#define PCIE20_ELBI_SYS_CTRL_LTSSM_EN 0x01
+
+#define PCIE20_CAP 0x70
+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10)
+
+#define PCIE20_COMMAND_STATUS 0x04
+#define PCIE20_BUSNUMBERS 0x18
+#define PCIE20_MEMORY_BASE_LIMIT 0x20
+
+#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818
+#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c
+#define PCIE20_PLR_IATU_VIEWPORT 0x900
+#define PCIE20_PLR_IATU_CTRL1 0x904
+#define PCIE20_PLR_IATU_CTRL2 0x908
+#define PCIE20_PLR_IATU_LBAR 0x90C
+#define PCIE20_PLR_IATU_UBAR 0x910
+#define PCIE20_PLR_IATU_LAR 0x914
+#define PCIE20_PLR_IATU_LTAR 0x918
+#define PCIE20_PLR_IATU_UTAR 0x91c
+
+#define MSM_PCIE_DEV_CFG_ADDR 0x01000000
+
+#define RD 0
+#define WR 1
+
+#define MAX_RC_NUM 3
+#define PCIE_BUS_PRIV_DATA(pdev) \
+ (((struct pci_sys_data *)pdev->bus->sysdata)->private_data)
+
+/* PCIe TLP types that we are interested in */
+#define PCI_CFG0_RDWR 0x4
+#define PCI_CFG1_RDWR 0x5
+
+#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
+({ \
+ unsigned long timeout = jiffies + usecs_to_jiffies(timeout_us); \
+ might_sleep_if(timeout_us); \
+ for (;;) { \
+ (val) = readl(addr); \
+ if (cond) \
+ break; \
+ if (timeout_us && time_after(jiffies, timeout)) { \
+ (val) = readl(addr); \
+ break; \
+ } \
+ if (sleep_us) \
+ usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \
+ } \
+ (cond) ? 0 : -ETIMEDOUT; \
+})
+
+struct qcom_msi {
+ struct msi_chip chip;
+ DECLARE_BITMAP(used, INT_PCI_MSI_NR);
+ struct irq_domain *domain;
+ unsigned long pages;
+ struct mutex lock;
+ int irq;
+};
+
+struct qcom_pcie {
+ void __iomem *elbi_base;
+ void __iomem *parf_base;
+ void __iomem *dwc_base;
+ void __iomem *cfg_base;
+ struct device *dev;
+ int reset_gpio;
+ bool ext_phy_ref_clk;
+ struct clk *iface_clk;
+ struct clk *bus_clk;
+ struct clk *phy_clk;
+ int irq_int[4];
+ struct reset_control *axi_reset;
+ struct reset_control *ahb_reset;
+ struct reset_control *por_reset;
+ struct reset_control *pci_reset;
+ struct reset_control *phy_reset;
+
+ struct resource conf;
+ struct resource io;
+ struct resource mem;
+
+ struct regulator *vdd_supply;
+ struct regulator *avdd_supply;
+ struct regulator *pcie_clk_supply;
+
+ struct qcom_msi msi;
+};
+
+static int nr_controllers;
+static DEFINE_SPINLOCK(qcom_hw_pci_lock);
+
+static inline struct qcom_pcie *sys_to_pcie(struct pci_sys_data *sys)
+{
+ return sys->private_data;
+}
+
+static void qcom_pcie_add_bus(struct pci_bus *bus)
+{
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ struct qcom_pcie *pcie = sys_to_pcie(bus->sysdata);
+
+ bus->msi = &pcie->msi.chip;
+ }
+}
+
+inline int is_msm_pcie_rc(struct pci_bus *bus)
+{
+ return (bus->number == 0);
+}
+
+static int qcom_pcie_is_link_up(struct qcom_pcie *dev)
+{
+ return readl_relaxed(dev->dwc_base + PCIE20_CAP_LINKCTRLSTATUS) &
+ BIT(29);
+}
+
+inline int msm_pcie_get_cfgtype(struct pci_bus *bus)
+{
+ /*
+ * http://www.tldp.org/LDP/tlk/dd/pci.html
+ * Pass it onto the secondary bus interface unchanged if the
+ * bus number specified is greater than the secondary bus
+ * number and less than or equal to the subordinate bus
+ * number.
+ *
+ * Read/Write to the RC and Device/Switch connected to the RC
+ * are CFG0 type transactions. Rest have to be forwarded
+ * down stream as CFG1 transactions.
+ *
+ */
+ if (bus->number == 0)
+ return PCI_CFG0_RDWR;
+
+ return PCI_CFG0_RDWR;
+}
+
+void msm_pcie_config_cfgtype(struct pci_bus *bus, u32 devfn)
+{
+ uint32_t bdf, cfgtype;
+ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata);
+
+ cfgtype = msm_pcie_get_cfgtype(bus);
+
+ if (cfgtype == PCI_CFG0_RDWR) {
+ bdf = MSM_PCIE_DEV_CFG_ADDR;
+ } else {
+ /*
+ * iATU Lower Target Address Register
+ * Bits Description
+ * *-1:0 Forms bits [*:0] of the
+ * start address of the new
+ * address of the translated
+ * region. The start address
+ * must be aligned to a
+ * CX_ATU_MIN_REGION_SIZE kB
+ * boundary, so these bits are
+ * always 0. A write to this
+ * location is ignored by the
+ * PCIe core.
+ * 31:*1 Forms bits [31:*] of the of
+ * the new address of the
+ * translated region.
+ *
+ * * is log2(CX_ATU_MIN_REGION_SIZE)
+ */
+ bdf = (((bus->number & 0xff) << 24) & 0xff000000) |
+ (((devfn & 0xff) << 16) & 0x00ff0000);
+ }
+
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT);
+ wmb();
+
+ /* Program Bdf Address */
+ writel_relaxed(bdf, dev->dwc_base + PCIE20_PLR_IATU_LTAR);
+ wmb();
+
+ /* Write Config Request Type */
+ writel_relaxed(cfgtype, dev->dwc_base + PCIE20_PLR_IATU_CTRL1);
+ wmb();
+}
+
+static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper,
+ int where, int size, u32 *val)
+{
+ uint32_t word_offset, byte_offset, mask;
+ uint32_t rd_val, wr_val;
+ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata);
+ void __iomem *config_base;
+ int rc;
+
+ rc = is_msm_pcie_rc(bus);
+
+ /*
+ * For downstream bus, make sure link is up
+ */
+ if (rc && (devfn != 0)) {
+ *val = ~0;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ } else if ((!rc) && (!qcom_pcie_is_link_up(dev))) {
+ *val = ~0;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ msm_pcie_config_cfgtype(bus, devfn);
+
+ word_offset = where & ~0x3;
+ byte_offset = where & 0x3;
+ mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset);
+
+ config_base = (rc) ? dev->dwc_base : dev->cfg_base;
+ rd_val = readl_relaxed(config_base + word_offset);
+
+ if (oper == RD) {
+ *val = ((rd_val & mask) >> (8 * byte_offset));
+ } else {
+ wr_val = (rd_val & ~mask) |
+ ((*val << (8 * byte_offset)) & mask);
+ writel_relaxed(wr_val, config_base + word_offset);
+ wmb(); /* ensure config data is written to hardware register */
+ }
+
+ return 0;
+}
+
+static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+ int size, u32 *val)
+{
+ return msm_pcie_oper_conf(bus, devfn, RD, where, size, val);
+}
+
+static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+ int where, int size, u32 val)
+{
+ /*
+ *Attempt to reset secondary bus is causing PCIE core to reset.
+ *Disable secondary bus reset functionality.
+ */
+ if ((bus->number == 0) && (where == PCI_BRIDGE_CONTROL) &&
+ (val & PCI_BRIDGE_CTL_BUS_RESET)) {
+ pr_info("PCIE secondary bus reset not supported\n");
+ val &= ~PCI_BRIDGE_CTL_BUS_RESET;
+ }
+
+ return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val);
+}
+
+static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct qcom_pcie *pcie_dev = PCIE_BUS_PRIV_DATA(dev);
+
+ return pcie_dev->irq_int[pin-1];
+}
+
+static int qcom_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct qcom_pcie *qcom_pcie = sys->private_data;
+
+ /*
+ * specify linux PCI framework to allocate device memory (BARs)
+ * from msm_pcie_dev.dev_mem_res resource.
+ */
+ sys->mem_offset = 0;
+ sys->io_offset = 0;
+
+ pci_add_resource(&sys->resources, &qcom_pcie->mem);
+ pci_add_resource(&sys->resources, &qcom_pcie->io);
+
+ return 1;
+}
+
+static struct pci_ops qcom_pcie_ops = {
+ .read = msm_pcie_rd_conf,
+ .write = msm_pcie_wr_conf,
+};
+
+static struct hw_pci qcom_hw_pci[MAX_RC_NUM] = {
+ {
+#ifdef CONFIG_PCI_DOMAINS
+ .domain = 0,
+#endif
+ .ops = &qcom_pcie_ops,
+ .nr_controllers = 1,
+ .swizzle = pci_common_swizzle,
+ .setup = qcom_pcie_setup,
+ .map_irq = qcom_pcie_map_irq,
+ .add_bus = qcom_pcie_add_bus,
+ },
+ {
+#ifdef CONFIG_PCI_DOMAINS
+ .domain = 1,
+#endif
+ .ops = &qcom_pcie_ops,
+ .nr_controllers = 1,
+ .swizzle = pci_common_swizzle,
+ .setup = qcom_pcie_setup,
+ .map_irq = qcom_pcie_map_irq,
+ },
+ {
+#ifdef CONFIG_PCI_DOMAINS
+ .domain = 2,
+#endif
+ .ops = &qcom_pcie_ops,
+ .nr_controllers = 1,
+ .swizzle = pci_common_swizzle,
+ .setup = qcom_pcie_setup,
+ .map_irq = qcom_pcie_map_irq,
+ },
+};
+
+static inline void qcom_elbi_writel_relaxed(struct qcom_pcie *pcie,
+ u32 val, u32 reg)
+{
+ writel_relaxed(val, pcie->elbi_base + reg);
+}
+
+static inline u32 qcom_elbi_readl_relaxed(struct qcom_pcie *pcie, u32 reg)
+{
+ return readl_relaxed(pcie->elbi_base + reg);
+}
+
+static inline void qcom_parf_writel_relaxed(struct qcom_pcie *pcie,
+ u32 val, u32 reg)
+{
+ writel_relaxed(val, pcie->parf_base + reg);
+}
+
+static inline u32 qcom_parf_readl_relaxed(struct qcom_pcie *pcie, u32 reg)
+{
+ return readl_relaxed(pcie->parf_base + reg);
+}
+
+static void msm_pcie_write_mask(void __iomem *addr,
+ uint32_t clear_mask, uint32_t set_mask)
+{
+ uint32_t val;
+
+ val = (readl_relaxed(addr) & ~clear_mask) | set_mask;
+ writel_relaxed(val, addr);
+ wmb(); /* ensure data is written to hardware register */
+}
+
+static void qcom_pcie_config_controller(struct qcom_pcie *dev)
+{
+ /*
+ * program and enable address translation region 0 (device config
+ * address space); region type config;
+ * axi config address range to device config address range
+ */
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT);
+ /* ensure that hardware locks the region before programming it */
+ wmb();
+
+ writel_relaxed(4, dev->dwc_base + PCIE20_PLR_IATU_CTRL1);
+ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2);
+ writel_relaxed(dev->conf.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR);
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR);
+ writel_relaxed(dev->conf.end, dev->dwc_base + PCIE20_PLR_IATU_LAR);
+ writel_relaxed(MSM_PCIE_DEV_CFG_ADDR,
+ dev->dwc_base + PCIE20_PLR_IATU_LTAR);
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR);
+ /* ensure that hardware registers the configuration */
+ wmb();
+
+ /*
+ * program and enable address translation region 2 (device resource
+ * address space); region type memory;
+ * axi device bar address range to device bar address range
+ */
+ writel_relaxed(2, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT);
+ /* ensure that hardware locks the region before programming it */
+ wmb();
+
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_CTRL1);
+ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2);
+ writel_relaxed(dev->mem.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR);
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR);
+ writel_relaxed(dev->mem.end, dev->dwc_base + PCIE20_PLR_IATU_LAR);
+ writel_relaxed(dev->mem.start,
+ dev->dwc_base + PCIE20_PLR_IATU_LTAR);
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR);
+ /* ensure that hardware registers the configuration */
+ wmb();
+
+ /* 1K PCIE buffer setting */
+ writel_relaxed(0x3, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0);
+ writel_relaxed(0x1, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1);
+ /* ensure that hardware registers the configuration */
+ wmb();
+}
+
+static int qcom_msi_alloc(struct qcom_msi *chip)
+{
+ int msi;
+
+ mutex_lock(&chip->lock);
+
+ msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR);
+ if (msi < INT_PCI_MSI_NR)
+ set_bit(msi, chip->used);
+ else
+ msi = -ENOSPC;
+
+ mutex_unlock(&chip->lock);
+
+ return msi;
+}
+
+static void qcom_msi_free(struct qcom_msi *chip, unsigned long irq)
+{
+ struct device *dev = chip->chip.dev;
+
+ mutex_lock(&chip->lock);
+
+ if (!test_bit(irq, chip->used))
+ dev_err(dev, "trying to free unused MSI#%lu\n", irq);
+ else
+ clear_bit(irq, chip->used);
+
+ mutex_unlock(&chip->lock);
+}
+
+
+static irqreturn_t handle_msi_irq(int irq, void *data)
+{
+ int i, j, index;
+ unsigned long val;
+ struct qcom_pcie *dev = data;
+ void __iomem *ctrl_status;
+ struct qcom_msi *msi = &dev->msi;
+
+ /* check for set bits, clear it by setting that bit
+ and trigger corresponding irq */
+ for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) {
+ ctrl_status = dev->dwc_base +
+ PCIE20_MSI_CTRL_INTR_STATUS + (i * 12);
+
+ val = readl_relaxed(ctrl_status);
+ while (val) {
+ j = find_first_bit(&val, 32);
+ index = j + (32 * i);
+ writel_relaxed(BIT(j), ctrl_status);
+ /* ensure that interrupt is cleared (acked) */
+ wmb();
+
+ irq = irq_find_mapping(msi->domain, index);
+ if (irq) {
+ if (test_bit(index, msi->used))
+ generic_handle_irq(irq);
+ else
+ dev_info(dev->dev, "unhandled MSI\n");
+ }
+ val = readl_relaxed(ctrl_status);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static inline struct qcom_msi *to_qcom_msi(struct msi_chip *chip)
+{
+ return container_of(chip, struct qcom_msi, chip);
+}
+
+static int qcom_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
+ struct msi_desc *desc)
+{
+ struct qcom_msi *msi = to_qcom_msi(chip);
+ struct msi_msg msg;
+ unsigned int irq;
+ int hwirq;
+
+ hwirq = qcom_msi_alloc(msi);
+ if (hwirq < 0)
+ return hwirq;
+
+ irq = irq_create_mapping(msi->domain, hwirq);
+ if (!irq)
+ return -EINVAL;
+
+ irq_set_msi_desc(irq, desc);
+
+ msg.address_lo = MSM_PCIE_MSI_PHY;
+ /* 32 bit address only */
+ msg.address_hi = 0;
+ msg.data = hwirq;
+
+ write_msi_msg(irq, &msg);
+
+ return 0;
+}
+
+static void qcom_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
+{
+ struct qcom_msi *msi = to_qcom_msi(chip);
+ struct irq_data *d = irq_get_irq_data(irq);
+
+ qcom_msi_free(msi, d->hwirq);
+}
+
+static struct irq_chip qcom_msi_irq_chip = {
+ .name = "PCI-MSI",
+ .irq_enable = unmask_msi_irq,
+ .irq_disable = mask_msi_irq,
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+};
+
+
+static int qcom_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &qcom_msi_irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, domain->host_data);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+
+static const struct irq_domain_ops msi_domain_ops = {
+ .map = qcom_pcie_msi_map,
+};
+uint32_t msm_pcie_msi_init(struct qcom_pcie *pcie, struct platform_device *pdev)
+{
+ int i, rc;
+ struct qcom_msi *msi = &pcie->msi;
+ int err;
+
+ mutex_init(&msi->lock);
+
+ msi->chip.dev = pcie->dev;
+ msi->chip.setup_irq = qcom_msi_setup_irq;
+ msi->chip.teardown_irq = qcom_msi_teardown_irq;
+ msi->domain = irq_domain_add_linear(pdev->dev.of_node, INT_PCI_MSI_NR,
+ &msi_domain_ops, &msi->chip);
+ if (!msi->domain) {
+ dev_err(&pdev->dev, "failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+
+
+ err = platform_get_irq_byname(pdev, "msi");
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
+ return err;
+ }
+
+ msi->irq = err;
+
+ /* program MSI controller and enable all interrupts */
+ writel_relaxed(MSM_PCIE_MSI_PHY, pcie->dwc_base + PCIE20_MSI_CTRL_ADDR);
+ writel_relaxed(0, pcie->dwc_base + PCIE20_MSI_CTRL_UPPER_ADDR);
+
+ for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++)
+ writel_relaxed(~0, pcie->dwc_base +
+ PCIE20_MSI_CTRL_INTR_EN + (i * 12));
+
+ /* ensure that hardware is configured before proceeding */
+ wmb();
+
+ /* register handler for physical MSI interrupt line */
+ rc = request_irq(msi->irq, handle_msi_irq, IRQF_TRIGGER_RISING,
+ "msm_pcie_msi", pcie);
+ if (rc) {
+ pr_err("Unable to allocate msi interrupt\n");
+ return rc;
+ }
+
+ return rc;
+}
+
+static int qcom_pcie_vreg_on(struct qcom_pcie *qcom_pcie)
+{
+ int err;
+ /* enable regulators */
+ err = regulator_enable(qcom_pcie->vdd_supply);
+ if (err < 0) {
+ dev_err(qcom_pcie->dev, "failed to enable VDD regulator\n");
+ return err;
+ }
+
+ err = regulator_enable(qcom_pcie->pcie_clk_supply);
+ if (err < 0) {
+ dev_err(qcom_pcie->dev, "failed to enable pcie-clk regulator\n");
+ return err;
+ }
+
+ err = regulator_enable(qcom_pcie->avdd_supply);
+ if (err < 0) {
+ dev_err(qcom_pcie->dev, "failed to enable AVDD regulator\n");
+ return err;
+ }
+
+ return err;
+
+}
+
+static int qcom_pcie_parse_dt(struct qcom_pcie *qcom_pcie,
+ struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *elbi_base, *parf_base, *dwc_base;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ int ret, i;
+
+ qcom_pcie->ext_phy_ref_clk = of_property_read_bool(np,
+ "qcom,external-phy-refclk");
+
+ elbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
+ qcom_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base);
+ if (IS_ERR(qcom_pcie->elbi_base)) {
+ dev_err(&pdev->dev, "Failed to ioremap elbi space\n");
+ return PTR_ERR(qcom_pcie->elbi_base);
+ }
+
+ parf_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
+ qcom_pcie->parf_base = devm_ioremap_resource(&pdev->dev, parf_base);
+ if (IS_ERR(qcom_pcie->parf_base)) {
+ dev_err(&pdev->dev, "Failed to ioremap parf space\n");
+ return PTR_ERR(qcom_pcie->parf_base);
+ }
+
+ dwc_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
+ qcom_pcie->dwc_base = devm_ioremap_resource(&pdev->dev, dwc_base);
+ if (IS_ERR(qcom_pcie->dwc_base)) {
+ dev_err(&pdev->dev, "Failed to ioremap dwc_base space\n");
+ return PTR_ERR(qcom_pcie->dwc_base);
+ }
+
+ if (of_pci_range_parser_init(&parser, np)) {
+ dev_err(&pdev->dev, "missing ranges property\n");
+ return -EINVAL;
+ }
+
+ /* Get the I/O and memory ranges from DT */
+ for_each_of_pci_range(&parser, &range) {
+ switch (range.pci_space & 0x3) {
+ case 0: /* cfg */
+ of_pci_range_to_resource(&range, np, &qcom_pcie->conf);
+ qcom_pcie->conf.flags = IORESOURCE_MEM;
+ break;
+ case 1: /* io */
+ of_pci_range_to_resource(&range, np, &qcom_pcie->io);
+ break;
+ default: /* mem */
+ of_pci_range_to_resource(&range, np, &qcom_pcie->mem);
+ break;
+ }
+ }
+
+ qcom_pcie->vdd_supply = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(qcom_pcie->vdd_supply)) {
+ dev_err(&pdev->dev, "Failed to get vdd supply\n");
+ return PTR_ERR(qcom_pcie->vdd_supply);
+ }
+
+ qcom_pcie->pcie_clk_supply = devm_regulator_get(&pdev->dev, "pcie-clk");
+ if (IS_ERR(qcom_pcie->pcie_clk_supply)) {
+ dev_err(&pdev->dev, "Failed to get pcie clk supply\n");
+ return PTR_ERR(qcom_pcie->pcie_clk_supply);
+ }
+ qcom_pcie->avdd_supply = devm_regulator_get(&pdev->dev, "avdd");
+ if (IS_ERR(qcom_pcie->avdd_supply)) {
+ dev_err(&pdev->dev, "Failed to get avdd supply\n");
+ return PTR_ERR(qcom_pcie->avdd_supply);
+ }
+
+
+ qcom_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+ if (!gpio_is_valid(qcom_pcie->reset_gpio)) {
+ dev_err(&pdev->dev, "pcie reset gpio is not valid\n");
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request_one(&pdev->dev, qcom_pcie->reset_gpio,
+ GPIOF_DIR_OUT, "pcie_reset");
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request pcie reset gpio\n");
+ return ret;
+ }
+
+ qcom_pcie->iface_clk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(qcom_pcie->iface_clk)) {
+ dev_err(&pdev->dev, "Failed to get pcie iface clock\n");
+ return PTR_ERR(qcom_pcie->iface_clk);
+ }
+
+ qcom_pcie->phy_clk = devm_clk_get(&pdev->dev, "phy");
+ if (IS_ERR(qcom_pcie->phy_clk)) {
+ dev_err(&pdev->dev, "Failed to get pcie phy clock\n");
+ return PTR_ERR(qcom_pcie->phy_clk);
+ }
+
+ qcom_pcie->bus_clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(qcom_pcie->bus_clk)) {
+ dev_err(&pdev->dev, "Failed to get pcie core clock\n");
+ return PTR_ERR(qcom_pcie->bus_clk);
+ }
+
+ qcom_pcie->axi_reset = devm_reset_control_get(&pdev->dev, "axi");
+ if (IS_ERR(qcom_pcie->axi_reset)) {
+ dev_err(&pdev->dev, "Failed to get axi reset\n");
+ return PTR_ERR(qcom_pcie->axi_reset);
+ }
+
+ qcom_pcie->ahb_reset = devm_reset_control_get(&pdev->dev, "ahb");
+ if (IS_ERR(qcom_pcie->ahb_reset)) {
+ dev_err(&pdev->dev, "Failed to get ahb reset\n");
+ return PTR_ERR(qcom_pcie->ahb_reset);
+ }
+
+ qcom_pcie->por_reset = devm_reset_control_get(&pdev->dev, "por");
+ if (IS_ERR(qcom_pcie->por_reset)) {
+ dev_err(&pdev->dev, "Failed to get por reset\n");
+ return PTR_ERR(qcom_pcie->por_reset);
+ }
+
+ qcom_pcie->pci_reset = devm_reset_control_get(&pdev->dev, "pci");
+ if (IS_ERR(qcom_pcie->pci_reset)) {
+ dev_err(&pdev->dev, "Failed to get pci reset\n");
+ return PTR_ERR(qcom_pcie->pci_reset);
+ }
+
+ qcom_pcie->phy_reset = devm_reset_control_get(&pdev->dev, "phy");
+ if (IS_ERR(qcom_pcie->phy_reset)) {
+ dev_err(&pdev->dev, "Failed to get phy reset\n");
+ return PTR_ERR(qcom_pcie->phy_reset);
+ }
+
+ for (i = 0; i < 4; i++) {
+ qcom_pcie->irq_int[i] = platform_get_irq(pdev, i+1);
+ if (qcom_pcie->irq_int[i] < 0) {
+ dev_err(&pdev->dev, "failed to get irq resource\n");
+ return qcom_pcie->irq_int[i];
+ }
+ }
+
+ return 0;
+}
+
+static int qcom_pcie_probe(struct platform_device *pdev)
+{
+ unsigned long flags;
+ struct qcom_pcie *qcom_pcie;
+ struct hw_pci *hw;
+ int ret;
+ u32 val;
+
+ qcom_pcie = devm_kzalloc(&pdev->dev, sizeof(*qcom_pcie), GFP_KERNEL);
+ if (!qcom_pcie) {
+ dev_err(&pdev->dev, "no memory for qcom_pcie\n");
+ return -ENOMEM;
+ }
+ qcom_pcie->dev = &pdev->dev;
+
+ ret = qcom_pcie_parse_dt(qcom_pcie, pdev);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+
+ qcom_pcie->cfg_base = devm_ioremap_resource(&pdev->dev,
+ &qcom_pcie->conf);
+ if (IS_ERR(qcom_pcie->cfg_base)) {
+ dev_err(&pdev->dev, "Failed to ioremap PCIe cfg space\n");
+ return PTR_ERR(qcom_pcie->cfg_base);
+ }
+
+ gpio_set_value(qcom_pcie->reset_gpio, 0);
+ usleep_range(10000, 15000);
+
+ /* enable power */
+ qcom_pcie_vreg_on(qcom_pcie);
+ /* assert PCIe PARF reset while powering the core */
+ reset_control_assert(qcom_pcie->ahb_reset);
+
+ /* enable clocks */
+ ret = clk_prepare_enable(qcom_pcie->iface_clk);
+ if (ret)
+ return ret;
+ ret = clk_prepare_enable(qcom_pcie->phy_clk);
+ if (ret)
+ return ret;
+ ret = clk_prepare_enable(qcom_pcie->bus_clk);
+ if (ret)
+ return ret;
+
+ /*
+ * de-assert PCIe PARF reset;
+ * wait 1us before accessing PARF registers
+ */
+ reset_control_deassert(qcom_pcie->ahb_reset);
+ udelay(1);
+
+ /* enable PCIe clocks and resets */
+ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_CTRL,
+ BIT(0), 0);
+
+ /* Set Tx Termination Offset */
+ val = qcom_parf_readl_relaxed(qcom_pcie, PCIE20_PARF_PHY_CTRL);
+ val |= PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(7);
+ qcom_parf_writel_relaxed(qcom_pcie, val, PCIE20_PARF_PHY_CTRL);
+
+ /* PARF programming */
+ qcom_parf_writel_relaxed(qcom_pcie,
+ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) |
+ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) |
+ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22),
+ PCIE20_PARF_PCS_DEEMPH);
+ qcom_parf_writel_relaxed(qcom_pcie,
+ PCIE20_PARF_PCS_SWING_TX_SWING_FULL(0x78) |
+ PCIE20_PARF_PCS_SWING_TX_SWING_LOW(0x78),
+ PCIE20_PARF_PCS_SWING);
+ qcom_parf_writel_relaxed(qcom_pcie, (4<<24), PCIE20_PARF_CONFIG_BITS);
+ /* ensure that hardware registers the PARF configuration */
+ wmb();
+
+ /* enable reference clock */
+ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_REFCLK,
+ qcom_pcie->ext_phy_ref_clk ? 0 : BIT(12),
+ BIT(16));
+
+ /* ensure that access is enabled before proceeding */
+ wmb();
+
+ /* de-assert PICe PHY, Core, POR and AXI clk domain resets */
+ reset_control_deassert(qcom_pcie->phy_reset);
+ reset_control_deassert(qcom_pcie->pci_reset);
+ reset_control_deassert(qcom_pcie->por_reset);
+ reset_control_deassert(qcom_pcie->axi_reset);
+
+ /* wait 150ms for clock acquisition */
+ usleep_range(10000, 15000);
+
+ /* de-assert PCIe reset link to bring EP out of reset */
+ gpio_set_value(qcom_pcie->reset_gpio, 1);
+ usleep_range(10000, 15000);
+
+ /* enable link training */
+ val = qcom_elbi_readl_relaxed(qcom_pcie, PCIE20_ELBI_SYS_CTRL);
+ val |= PCIE20_ELBI_SYS_CTRL_LTSSM_EN;
+ qcom_elbi_writel_relaxed(qcom_pcie, val, PCIE20_ELBI_SYS_CTRL);
+ wmb();
+
+ /* poll for link to come up for upto 100ms */
+ ret = readl_poll_timeout(
+ (qcom_pcie->dwc_base + PCIE20_CAP_LINKCTRLSTATUS),
+ val, (val & BIT(29)), 10000, 100000);
+
+ dev_info(&pdev->dev, "link initialized %d\n", ret);
+
+ qcom_pcie_config_controller(qcom_pcie);
+
+ platform_set_drvdata(pdev, qcom_pcie);
+
+ spin_lock_irqsave(&qcom_hw_pci_lock, flags);
+ qcom_hw_pci[nr_controllers].private_data = (void **)&qcom_pcie;
+ hw = &qcom_hw_pci[nr_controllers];
+ nr_controllers++;
+ spin_unlock_irqrestore(&qcom_hw_pci_lock, flags);
+
+ pci_common_init(hw);
+
+ msm_pcie_msi_init(qcom_pcie, pdev);
+ return 0;
+}
+
+static int __exit qcom_pcie_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct of_device_id qcom_pcie_match[] = {
+ { .compatible = "qcom,pcie-ipq8064", },
+ {}
+};
+
+static struct platform_driver qcom_pcie_driver = {
+ .probe = qcom_pcie_probe,
+ .remove = qcom_pcie_remove,
+ .driver = {
+ .name = "qcom_pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = qcom_pcie_match,
+ },
+};
+
+static int qcom_pcie_init(void)
+{
+ return platform_driver_register(&qcom_pcie_driver);
+}
+subsys_initcall_sync(qcom_pcie_init);
+
+/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */
+static void msm_pcie_fixup_early(struct pci_dev *dev)
+{
+ if (dev->hdr_type == 1)
+ dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8);
+}
+DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, msm_pcie_fixup_early);
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index d160a710d704..8a24faeba419 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -39,4 +39,27 @@ config PINCTRL_MSM8X74
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm TLMM block found in the Qualcomm 8974 platform.
+config PINCTRL_SPMI_PMIC
+ tristate "Qualcomm SPMI PMIC pin controller driver"
+ depends on GPIOLIB && OF
+ select PINMUX
+ select PINCONF
+ select GENERIC_PINCONF
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips,
+ which are using SPMI for communication with SoC. Example PMIC's
+ devices are pm8841, pm8941 and pma8084.
+
+config PINCTRL_SSBI_PMIC
+ tristate "Qualcomm SSBI PMIC pin controller driver"
+ depends on GPIOLIB && OF
+ select PINCONF
+ select PINMUX
+ select GENERIC_PINCONF
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm GPIO blocks found in the pm8018, pm8038, pm8058, pm8917 and
+ pm8921 pmics from Qualcomm.
+
endif
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 2a02602d715c..d09805847541 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -4,3 +4,5 @@ obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o
obj-$(CONFIG_PINCTRL_IPQ8064) += pinctrl-ipq8064.o
obj-$(CONFIG_PINCTRL_MSM8960) += pinctrl-msm8960.o
obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o
+obj-$(CONFIG_PINCTRL_SPMI_PMIC) += pinctrl-spmi-pmic.o
+obj-$(CONFIG_PINCTRL_SSBI_PMIC) += pinctrl-ssbi-pmic.o
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-pmic.c b/drivers/pinctrl/qcom/pinctrl-spmi-pmic.c
new file mode 100644
index 000000000000..ba1997947ff2
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-pmic.c
@@ -0,0 +1,1532 @@
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+
+/*
+ * Mode select - indicates whether the pin should be input, output, or both
+ * for GPIOs. MPP pins also support bidirectional, analog input, analog output
+ * and current sink.
+ */
+#define QPNP_PIN_MODE_DIG_IN 0
+#define QPNP_PIN_MODE_DIG_OUT 1
+#define QPNP_PIN_MODE_DIG_IN_OUT 2
+#define QPNP_PIN_MODE_BIDIR 3
+#define QPNP_PIN_MODE_AIN 4
+#define QPNP_PIN_MODE_AOUT 5
+#define QPNP_PIN_MODE_SINK 6
+
+/*
+ * Voltage select (GPIO, MPP) - specifies the voltage level when the output
+ * is set to 1. For an input GPIO specifies the voltage level at which
+ * the input is interpreted as a logical 1
+ * To be used with "power-func = <>"
+ */
+#define QPNP_PIN_VIN_4CH_INVALID 5
+#define QPNP_PIN_VIN_8CH_INVALID 8
+
+/*
+ * Analog Output - Set the analog output reference.
+ * See PM8XXX_MPP_AOUT_XXX. To be used with "qcom,aout = <>"
+ */
+#define QPNP_MPP_AOUT_INVALID 8
+
+/*
+ * Analog Input - Set the func for analog input.
+ * See PM8XXX_MPP_AIN_XXX. To be used with "qcom,ain = <>"
+ */
+#define QPNP_MPP_AIN_INVALID 8
+
+/*
+ * Output type - indicates pin should be configured as CMOS or
+ * open drain.
+ */
+#define QPNP_GPIO_OUT_BUF_CMOS 0
+#define QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS 1
+#define QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS 2
+
+/*
+ * Pull Up Values - it indicates whether a pull up or pull down
+ * should be applied. If a pull-up is required the current strength needs
+ * to be specified. Current values of 30uA, 1.5uA, 31.5uA, 1.5uA with 30uA
+ * boost are supported.
+ * Note that the hardware ignores this configuration if the GPIO is not set
+ * to input or output open-drain mode.
+ */
+#define QPNP_GPIO_PULL_UP_30 0
+#define QPNP_GPIO_PULL_UP_1P5 1
+#define QPNP_GPIO_PULL_UP_31P5 2
+#define QPNP_GPIO_PULL_UP_1P5_30 3
+#define QPNP_GPIO_PULL_DN 4
+#define QPNP_GPIO_PULL_NO 5
+
+/*
+ * Pull Up Values - it indicates whether a pull-up should be
+ * applied for bidirectional mode only. The hardware ignores the
+ * configuration when operating in other modes.
+ */
+#define QPNP_MPP_PULL_UP_0P6KOHM 0
+#define QPNP_MPP_PULL_UP_10KOHM 1
+#define QPNP_MPP_PULL_UP_30KOHM 2
+#define QPNP_MPP_PULL_UP_OPEN 3
+
+/* Out Strength (GPIO) - the amount of current supplied for an output GPIO */
+#define QPNP_GPIO_STRENGTH_LOW 1
+#define QPNP_GPIO_STRENGTH_MED 2
+#define QPNP_GPIO_STRENGTH_HIGH 3
+
+/*
+ * Master enable (GPIO, MPP) - Enable features within the pin block based on
+ * configurations. QPNP_PIN_MASTER_DISABLE = Completely disable the pin
+ * lock and let the pin float with high impedance regardless of other settings.
+ */
+#define QPNP_PIN_MASTER_DISABLE 0
+#define QPNP_PIN_MASTER_ENABLE 1
+
+/* revision registers base address offsets */
+#define QPNP_REG_DIG_MINOR_REV 0x0
+#define QPNP_REG_DIG_MAJOR_REV 0x1
+#define QPNP_REG_ANA_MINOR_REV 0x2
+
+/* type registers base address offsets */
+#define QPNP_REG_TYPE 0x4
+#define QPNP_REG_SUBTYPE 0x5
+
+/* GPIO peripheral type and subtype values */
+#define QPNP_GPIO_TYPE 0x10
+#define QPNP_GPIO_SUBTYPE_GPIO_4CH 0x1
+#define QPNP_GPIO_SUBTYPE_GPIOC_4CH 0x5
+#define QPNP_GPIO_SUBTYPE_GPIO_8CH 0x9
+#define QPNP_GPIO_SUBTYPE_GPIOC_8CH 0xd
+
+/* mpp peripheral type and subtype values */
+#define QPNP_MPP_TYPE 0x11
+#define QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT 0x3
+#define QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT 0x4
+#define QPNP_MPP_SUBTYPE_4CH_NO_SINK 0x5
+#define QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK 0x6
+#define QPNP_MPP_SUBTYPE_4CH_FULL_FUNC 0x7
+#define QPNP_MPP_SUBTYPE_8CH_FULL_FUNC 0xf
+
+#define QPNP_REG_STATUS1 0x8
+#define QPNP_REG_STATUS1_VAL_MASK 0x1
+#define QPNP_REG_STATUS1_GPIO_EN_REV0_MASK 0x2
+#define QPNP_REG_STATUS1_GPIO_EN_MASK 0x80
+#define QPNP_REG_STATUS1_MPP_EN_MASK 0x80
+
+/* control register base address offsets */
+#define QPNP_REG_MODE_CTL 0x40
+#define QPNP_REG_DIG_VIN_CTL 0x41
+#define QPNP_REG_DIG_PULL_CTL 0x42
+#define QPNP_REG_DIG_IN_CTL 0x43
+#define QPNP_REG_DIG_OUT_CTL 0x45
+#define QPNP_REG_EN_CTL 0x46
+#define QPNP_REG_AOUT_CTL 0x4b
+#define QPNP_REG_AIN_CTL 0x4a
+#define QPNP_REG_SINK_CTL 0x4c
+
+/* QPNP_REG_MODE_CTL */
+#define QPNP_REG_MODE_INVERT_SHIFT 0
+#define QPNP_REG_MODE_INVERT_MASK 0x1
+#define QPNP_REG_MODE_FUNCTION_SHIFT 1
+#define QPNP_REG_MODE_FUNCTION_MASK 0x6
+#define QPNP_REG_MODE_SEL_SHIFT 4
+#define QPNP_REG_MODE_SEL_MASK 0x70
+
+/* QPNP_REG_DIG_VIN_CTL */
+#define QPNP_REG_VIN_SHIFT 0
+#define QPNP_REG_VIN_MASK 0x7
+
+/* QPNP_REG_DIG_PULL_CTL */
+#define QPNP_REG_PULL_SHIFT 0
+#define QPNP_REG_PULL_MASK 0x7
+
+/* QPNP_REG_DIG_OUT_CTL */
+#define QPNP_REG_OUT_STRENGTH_SHIFT 0
+#define QPNP_REG_OUT_STRENGTH_MASK 0x3
+#define QPNP_REG_OUT_TYPE_SHIFT 4
+#define QPNP_REG_OUT_TYPE_MASK 0x30
+
+/* QPNP_REG_EN_CTL */
+#define QPNP_REG_MASTER_EN_SHIFT 7
+#define QPNP_REG_MASTER_EN_MASK 0x80
+
+/* QPNP_REG_AOUT_CTL */
+#define QPNP_REG_AOUT_REF_SHIFT 0
+#define QPNP_REG_AOUT_REF_MASK 0x7
+
+/* QPNP_REG_AIN_CTL */
+#define QPNP_REG_AIN_ROUTE_SHIFT 0
+#define QPNP_REG_AIN_ROUTE_MASK 0x7
+
+/* QPNP_REG_SINK_CTL */
+#define QPNP_REG_CS_OUT_SHIFT 0
+#define QPNP_REG_CS_OUT_MASK 0x7
+
+#define QPNP_PIN_PHYSICAL_OFFSET 1
+
+/* Qualcomm specific pin configurations */
+#define QPNP_PINCONF_PULL_UP (PIN_CONFIG_END + 1)
+#define QPNP_PINCONF_STRENGTH (PIN_CONFIG_END + 2)
+#define QPNP_PINCONF_AMUX_ROUTE (PIN_CONFIG_END + 3)
+#define QPNP_PINCONF_VREFENCE (PIN_CONFIG_END + 4)
+#define QPNP_PINCONF_MODE (PIN_CONFIG_END + 5)
+
+struct qpnp_chipinfo {
+ unsigned npins;
+ unsigned base;
+ unsigned type;
+};
+
+struct qpnp_padinfo {
+ u16 offset; /* address offset in SPMI device */
+ int irq;
+ const char *name; /* pin name */
+ unsigned int modes; /* supported modes: DI, DO, DIO, AI, AO... */
+ unsigned int type; /* peripheral hardware type */
+ unsigned int subtype; /* peripheral hardware subtype */
+ unsigned int major; /* digital major version */
+};
+
+#define QPNP_REG_ADDR(pad, reg) ((pad)->offset + reg)
+#define QPNP_GET(buff, shift, mask) ((buff & mask) >> shift)
+
+struct qpnp_pinctrl {
+ struct device *dev;
+ struct regmap *map;
+ struct pinctrl_dev *ctrl;
+ struct pinctrl_desc desc;
+ struct gpio_chip chip;
+
+ struct qpnp_padinfo *pads;
+ const struct qpnp_chipinfo *info;
+ const char *const *groups;
+ const char *const *functions;
+};
+
+static inline struct qpnp_pinctrl *to_qpnp_pinctrl(struct gpio_chip *chip)
+{
+ return container_of(chip, struct qpnp_pinctrl, chip);
+};
+
+struct qpnp_pinbindings {
+ const char *property;
+ unsigned param;
+ u32 default_value;
+};
+
+struct qpnp_pinattrib {
+ unsigned addr;
+ unsigned shift;
+ unsigned mask;
+ unsigned val;
+};
+
+static struct qpnp_pinbindings qpnp_pinbindings[] = {
+ /* QCOM_BIAS_PULL_UP_30... */
+ {"qcom,pull-up-strength", QPNP_PINCONF_PULL_UP, 0},
+ /* QCOM_DRIVE_STRENGTH_NO... */
+ {"qcom,drive-strength", QPNP_PINCONF_STRENGTH, 0},
+ /* PMIC_MPP_AMUX_ROUTE_CH5 ... */
+ {"qcom,amux-route", QPNP_PINCONF_AMUX_ROUTE, 0},
+ /* PMIC_MPP_VREFERENCE_1V25 ... */
+ {"qcom,vrefence", QPNP_PINCONF_VREFENCE, 0},
+ /* PMIC_MPP_MODE.. */
+ {"qcom,mode", QPNP_PINCONF_MODE, 0},
+};
+
+static const char * const qpnp_gpio_groups[] = {
+ "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8",
+ "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15",
+ "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
+ "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29",
+ "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
+};
+
+static const char * const qpnp_mpp_groups[] = {
+ "mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8",
+};
+
+static const char * const qpnp_gpio_functions[] = {
+ PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED,
+ PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2,
+ PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2,
+ PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4,
+};
+
+static const char * const qpnp_mpp_functions[] = {
+ PMIC_MPP_FUNC_NORMAL, PMIC_MPP_FUNC_PAIRED,
+ "reserved1", "reserved2",
+ PMIC_MPP_FUNC_DTEST1, PMIC_MPP_FUNC_DTEST2,
+ PMIC_MPP_FUNC_DTEST3, PMIC_MPP_FUNC_DTEST4,
+};
+
+static inline struct qpnp_padinfo *qpnp_get_desc(struct qpnp_pinctrl *qctrl,
+ unsigned pin)
+{
+ if (pin >= qctrl->desc.npins) {
+ dev_warn(qctrl->dev, "invalid pin number %d", pin);
+ return NULL;
+ }
+
+ return &qctrl->pads[pin];
+}
+
+static int qpnp_conv_to_pin(struct qpnp_pinctrl *qctrl,
+ struct qpnp_padinfo *pad, unsigned param,
+ unsigned val)
+{
+ struct qpnp_pinattrib attr[3];
+ unsigned int type, subtype;
+ int nattrs = 1, idx, ret;
+
+ type = pad->type;
+ subtype = pad->subtype;
+
+ switch (param) {
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ if (type != QPNP_GPIO_TYPE)
+ return -ENXIO;
+ attr[0].addr = QPNP_REG_DIG_OUT_CTL;
+ attr[0].shift = QPNP_REG_OUT_TYPE_SHIFT;
+ attr[0].mask = QPNP_REG_OUT_TYPE_MASK;
+ attr[0].val = QPNP_GPIO_OUT_BUF_CMOS;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ if (type != QPNP_GPIO_TYPE)
+ return -ENXIO;
+ if (subtype == QPNP_GPIO_SUBTYPE_GPIOC_4CH ||
+ subtype == QPNP_GPIO_SUBTYPE_GPIOC_8CH)
+ return -EINVAL;
+ attr[0].addr = QPNP_REG_DIG_OUT_CTL;
+ attr[0].shift = QPNP_REG_OUT_TYPE_SHIFT;
+ attr[0].mask = QPNP_REG_OUT_TYPE_MASK;
+ attr[0].val = QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+ if (type != QPNP_GPIO_TYPE)
+ return -ENXIO;
+ if (subtype == QPNP_GPIO_SUBTYPE_GPIOC_4CH ||
+ subtype == QPNP_GPIO_SUBTYPE_GPIOC_8CH)
+ return -EINVAL;
+ attr[0].addr = QPNP_REG_DIG_OUT_CTL;
+ attr[0].shift = QPNP_REG_OUT_TYPE_SHIFT;
+ attr[0].mask = QPNP_REG_OUT_TYPE_MASK;
+ attr[0].val = QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ attr[0].addr = QPNP_REG_DIG_PULL_CTL;
+ attr[0].shift = QPNP_REG_PULL_SHIFT;
+ attr[0].mask = QPNP_REG_PULL_MASK;
+ if (type == QPNP_GPIO_TYPE)
+ attr[0].val = QPNP_GPIO_PULL_NO;
+ else
+ attr[0].val = QPNP_MPP_PULL_UP_OPEN;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (type != QPNP_MPP_TYPE)
+ return -EINVAL;
+ switch (val) {
+ case 0:
+ val = QPNP_MPP_PULL_UP_OPEN;
+ break;
+ case 600:
+ val = QPNP_MPP_PULL_UP_0P6KOHM;
+ break;
+ case 10000:
+ val = QPNP_MPP_PULL_UP_10KOHM;
+ break;
+ case 30000:
+ val = QPNP_MPP_PULL_UP_30KOHM;
+ break;
+ default:
+ return -EINVAL;
+ }
+ attr[0].addr = QPNP_REG_DIG_PULL_CTL;
+ attr[0].shift = QPNP_REG_PULL_SHIFT;
+ attr[0].mask = QPNP_REG_PULL_MASK;
+ attr[0].val = val;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (type != QPNP_GPIO_TYPE)
+ return -EINVAL;
+ attr[0].addr = QPNP_REG_DIG_PULL_CTL;
+ attr[0].shift = QPNP_REG_PULL_SHIFT;
+ attr[0].mask = QPNP_REG_PULL_MASK;
+ if (val)
+ attr[0].val = QPNP_GPIO_PULL_DN;
+ else
+ attr[0].val = QPNP_GPIO_PULL_NO;
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ if (val >= QPNP_PIN_VIN_8CH_INVALID)
+ return -EINVAL;
+ if (val >= QPNP_PIN_VIN_4CH_INVALID) {
+ if (type == QPNP_GPIO_TYPE &&
+ (subtype == QPNP_GPIO_SUBTYPE_GPIO_4CH ||
+ subtype == QPNP_GPIO_SUBTYPE_GPIOC_4CH))
+ return -EINVAL;
+ if (type == QPNP_MPP_TYPE &&
+ (subtype == QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+ subtype == QPNP_MPP_SUBTYPE_4CH_NO_SINK ||
+ subtype == QPNP_MPP_SUBTYPE_4CH_FULL_FUNC ||
+ subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
+ subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK))
+ return -EINVAL;
+ }
+ attr[0].addr = QPNP_REG_DIG_VIN_CTL;
+ attr[0].shift = QPNP_REG_VIN_SHIFT;
+ attr[0].mask = QPNP_REG_VIN_MASK;
+ attr[0].val = val;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ if (type != QPNP_MPP_TYPE)
+ return -EINVAL;
+ if (subtype == QPNP_MPP_SUBTYPE_4CH_NO_SINK ||
+ subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK)
+ return -ENXIO;
+ if (val > 50) /* mA */
+ return -EINVAL;
+ attr[0].addr = QPNP_REG_SINK_CTL;
+ attr[0].shift = QPNP_REG_CS_OUT_SHIFT;
+ attr[0].mask = QPNP_REG_CS_OUT_MASK;
+ attr[0].val = (val / 5) - 1;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ nattrs = 2;
+ attr[0].addr = QPNP_REG_MODE_CTL;
+ attr[0].shift = QPNP_REG_MODE_SEL_SHIFT;
+ attr[0].mask = QPNP_REG_MODE_SEL_MASK;
+ attr[0].val = QPNP_PIN_MODE_DIG_IN;
+ attr[1].addr = QPNP_REG_EN_CTL;
+ attr[1].shift = QPNP_REG_MASTER_EN_SHIFT;
+ attr[1].mask = QPNP_REG_MASTER_EN_MASK;
+ attr[1].val = 1;
+ if (val)
+ break;
+ /* Fallthrough */
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ attr[1].addr = QPNP_REG_EN_CTL;
+ attr[1].shift = QPNP_REG_MASTER_EN_SHIFT;
+ attr[1].mask = QPNP_REG_MASTER_EN_MASK;
+ attr[1].val = 0;
+ break;
+ case PIN_CONFIG_OUTPUT:
+ nattrs = 3;
+ attr[0].addr = QPNP_REG_MODE_CTL;
+ attr[0].shift = QPNP_REG_MODE_INVERT_SHIFT;
+ attr[0].mask = QPNP_REG_MODE_INVERT_MASK;
+ attr[0].val = !!val;
+ attr[1].addr = QPNP_REG_MODE_CTL;
+ attr[1].shift = QPNP_REG_MODE_SEL_SHIFT;
+ attr[1].mask = QPNP_REG_MODE_SEL_MASK;
+ attr[1].val = QPNP_PIN_MODE_DIG_OUT;
+ attr[2].addr = QPNP_REG_EN_CTL;
+ attr[2].shift = QPNP_REG_MASTER_EN_SHIFT;
+ attr[2].mask = QPNP_REG_MASTER_EN_MASK;
+ attr[2].val = 1;
+ break;
+ case QPNP_PINCONF_PULL_UP:
+ if (type != QPNP_GPIO_TYPE)
+ return -EINVAL;
+ switch (val) {
+ default:
+ return -EINVAL;
+ case 0:
+ val = QPNP_GPIO_PULL_NO;
+ break;
+ case PMIC_GPIO_PULL_UP_30:
+ val = QPNP_GPIO_PULL_UP_30;
+ break;
+ case PMIC_GPIO_PULL_UP_1P5:
+ val = QPNP_GPIO_PULL_UP_1P5;
+ break;
+ case PMIC_GPIO_PULL_UP_31P5:
+ val = QPNP_GPIO_PULL_UP_31P5;
+ break;
+ case PMIC_GPIO_PULL_UP_1P5_30:
+ val = QPNP_GPIO_PULL_UP_1P5_30;
+ break;
+ }
+ attr[0].addr = QPNP_REG_DIG_PULL_CTL;
+ attr[0].shift = QPNP_REG_PULL_SHIFT;
+ attr[0].mask = QPNP_REG_PULL_MASK;
+ attr[0].val = val;
+ break;
+ case QPNP_PINCONF_STRENGTH:
+ if (type != QPNP_GPIO_TYPE)
+ return -EINVAL;
+ switch (val) {
+ default:
+ case PMIC_GPIO_STRENGTH_NO:
+ return -EINVAL;
+ case PMIC_GPIO_STRENGTH_LOW:
+ attr[0].val = QPNP_GPIO_STRENGTH_LOW;
+ break;
+ case PMIC_GPIO_STRENGTH_MED:
+ attr[0].val = QPNP_GPIO_STRENGTH_MED;
+ break;
+ case PMIC_GPIO_STRENGTH_HIGH:
+ attr[0].val = QPNP_GPIO_STRENGTH_HIGH;
+ break;
+ }
+ attr[0].addr = QPNP_REG_DIG_OUT_CTL;
+ attr[0].shift = QPNP_REG_OUT_STRENGTH_SHIFT;
+ attr[0].mask = QPNP_REG_OUT_STRENGTH_MASK;
+ break;
+ case QPNP_PINCONF_VREFENCE:
+ if (type != QPNP_MPP_TYPE)
+ return -ENXIO;
+ if (val >= QPNP_MPP_AOUT_INVALID)
+ return -EINVAL;
+ if (subtype == QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+ subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT)
+ return -ENXIO;
+ attr[0].addr = QPNP_REG_AOUT_CTL;
+ attr[0].shift = QPNP_REG_AOUT_REF_SHIFT;
+ attr[0].mask = QPNP_REG_AOUT_REF_MASK;
+ attr[0].val = val;
+ break;
+ case QPNP_PINCONF_AMUX_ROUTE:
+ if (type != QPNP_MPP_TYPE)
+ return -ENXIO;
+ if (val >= QPNP_MPP_AIN_INVALID)
+ return -EINVAL;
+ attr[0].addr = QPNP_REG_AIN_CTL;
+ attr[0].shift = QPNP_REG_AIN_ROUTE_SHIFT;
+ attr[0].mask = QPNP_REG_AIN_ROUTE_MASK;
+ attr[0].val = val;
+ break;
+ case QPNP_PINCONF_MODE:
+ if ((pad->modes & BIT(val)) == 0)
+ return -ENXIO;
+ attr[0].addr = QPNP_REG_MODE_CTL;
+ attr[0].shift = QPNP_REG_MODE_SEL_SHIFT;
+ attr[0].mask = QPNP_REG_MODE_SEL_MASK;
+ attr[0].val = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (idx = 0; idx < nattrs; idx++) {
+ /* add base offset */
+ attr[idx].addr = QPNP_REG_ADDR(pad, attr[idx].addr);
+ ret = regmap_update_bits(qctrl->map, attr[idx].addr,
+ attr[idx].mask,
+ attr[idx].val << attr[idx].shift);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int qpnp_conv_from_pin(struct qpnp_pinctrl *qctrl,
+ struct qpnp_padinfo *pad,
+ unsigned param, unsigned *val)
+{
+ struct qpnp_pinattrib attr;
+ unsigned int type, subtype, field;
+ unsigned int addr, buff;
+ int ret;
+
+ *val = 0;
+ type = pad->type;
+ subtype = pad->subtype;
+
+ switch (param) {
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+ if (type != QPNP_GPIO_TYPE)
+ return -ENXIO;
+ attr.addr = QPNP_REG_DIG_OUT_CTL;
+ attr.shift = QPNP_REG_OUT_TYPE_SHIFT;
+ attr.mask = QPNP_REG_OUT_TYPE_MASK;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (type != QPNP_GPIO_TYPE)
+ return -ENXIO;
+ /* Fallthrough */
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ attr.addr = QPNP_REG_DIG_PULL_CTL;
+ attr.shift = QPNP_REG_PULL_SHIFT;
+ attr.mask = QPNP_REG_PULL_MASK;
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ attr.addr = QPNP_REG_EN_CTL;
+ attr.shift = QPNP_REG_MASTER_EN_SHIFT;
+ attr.mask = QPNP_REG_MASTER_EN_MASK;
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ attr.addr = QPNP_REG_DIG_VIN_CTL;
+ attr.shift = QPNP_REG_VIN_SHIFT;
+ attr.mask = QPNP_REG_VIN_MASK;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ if (type != QPNP_MPP_TYPE)
+ return -ENXIO;
+ attr.addr = QPNP_REG_SINK_CTL;
+ attr.shift = QPNP_REG_CS_OUT_SHIFT;
+ attr.mask = QPNP_REG_CS_OUT_MASK;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ attr.addr = QPNP_REG_EN_CTL;
+ attr.shift = QPNP_REG_MASTER_EN_SHIFT;
+ attr.mask = QPNP_REG_MASTER_EN_MASK;
+ break;
+ case PIN_CONFIG_OUTPUT:
+ attr.addr = QPNP_REG_MODE_CTL;
+ attr.shift = QPNP_REG_MODE_INVERT_SHIFT;
+ attr.mask = QPNP_REG_MODE_INVERT_MASK;
+ break;
+ case QPNP_PINCONF_PULL_UP:
+ if (type != QPNP_GPIO_TYPE)
+ return -ENXIO;
+ attr.addr = QPNP_REG_DIG_PULL_CTL;
+ attr.shift = QPNP_REG_PULL_SHIFT;
+ attr.mask = QPNP_REG_PULL_MASK;
+ break;
+ case QPNP_PINCONF_STRENGTH:
+ if (type != QPNP_GPIO_TYPE)
+ return -ENXIO;
+ attr.addr = QPNP_REG_DIG_OUT_CTL;
+ attr.shift = QPNP_REG_OUT_STRENGTH_SHIFT;
+ attr.mask = QPNP_REG_OUT_STRENGTH_MASK;
+ break;
+ case QPNP_PINCONF_VREFENCE:
+ if (type != QPNP_MPP_TYPE)
+ return -ENXIO;
+ if (subtype == QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+ subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT)
+ return -ENXIO;
+ attr.addr = QPNP_REG_AOUT_CTL;
+ attr.shift = QPNP_REG_AOUT_REF_SHIFT;
+ attr.mask = QPNP_REG_AOUT_REF_MASK;
+ break;
+ case QPNP_PINCONF_AMUX_ROUTE:
+ if (type != QPNP_MPP_TYPE)
+ return -ENXIO;
+ attr.addr = QPNP_REG_AIN_CTL;
+ attr.shift = QPNP_REG_AIN_ROUTE_SHIFT;
+ attr.mask = QPNP_REG_AIN_ROUTE_MASK;
+ break;
+ case QPNP_PINCONF_MODE:
+ attr.addr = QPNP_REG_MODE_CTL;
+ attr.shift = QPNP_REG_MODE_SEL_SHIFT;
+ attr.mask = QPNP_REG_MODE_SEL_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ addr = QPNP_REG_ADDR(pad, attr.addr);
+ ret = regmap_read(qctrl->map, addr, &buff);
+ if (ret < 0)
+ return ret;
+
+ field = QPNP_GET(buff, attr.shift, attr.mask);
+
+ switch (param) {
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ if (field == QPNP_GPIO_OUT_BUF_CMOS)
+ *val = 1;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ if (field == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS)
+ *val = 1;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+ if (field == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS)
+ *val = 1;
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ if (type == QPNP_GPIO_TYPE) {
+ if (field == QPNP_GPIO_PULL_NO)
+ *val = 1;
+ } else {
+ if (field == QPNP_MPP_PULL_UP_OPEN)
+ *val = 1;
+ }
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ switch (field) {
+ default:
+ case QPNP_MPP_PULL_UP_OPEN:
+ *val = 0;
+ break;
+ case QPNP_MPP_PULL_UP_0P6KOHM:
+ *val = 600;
+ break;
+ case QPNP_MPP_PULL_UP_10KOHM:
+ *val = 10000;
+ break;
+ case QPNP_MPP_PULL_UP_30KOHM:
+ *val = 30000;
+ break;
+ }
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (field == QPNP_GPIO_PULL_DN)
+ *val = 1;
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ *val = field;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ *val = (field + 1) * 5;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ *val = field;
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ if (field == QPNP_PIN_MASTER_DISABLE)
+ *val = 1;
+ break;
+ case PIN_CONFIG_OUTPUT:
+ *val = field;
+ break;
+ case QPNP_PINCONF_PULL_UP:
+ switch (field) {
+ case QPNP_GPIO_PULL_NO:
+ field = 0;
+ break;
+ case QPNP_GPIO_PULL_UP_30:
+ field = PMIC_GPIO_PULL_UP_30;
+ break;
+ case QPNP_GPIO_PULL_UP_1P5:
+ field = PMIC_GPIO_PULL_UP_1P5;
+ break;
+ case QPNP_GPIO_PULL_UP_31P5:
+ field = PMIC_GPIO_PULL_UP_31P5;
+ break;
+
+ case QPNP_GPIO_PULL_UP_1P5_30:
+ field = PMIC_GPIO_PULL_UP_1P5_30;
+ break;
+ }
+ *val = field;
+ break;
+ case QPNP_PINCONF_STRENGTH:
+ switch (field) {
+ case QPNP_GPIO_STRENGTH_HIGH:
+ field = PMIC_GPIO_STRENGTH_HIGH;
+ break;
+ case QPNP_GPIO_STRENGTH_MED:
+ field = PMIC_GPIO_STRENGTH_MED;
+ break;
+ case QPNP_GPIO_STRENGTH_LOW:
+ field = PMIC_GPIO_STRENGTH_LOW;
+ break;
+ }
+ *val = field;
+ break;
+ case QPNP_PINCONF_MODE:
+ case QPNP_PINCONF_VREFENCE:
+ case QPNP_PINCONF_AMUX_ROUTE:
+ *val = field;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct qpnp_pinctrl *qpctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ /* Every PIN is a group */
+ return qpctrl->desc.npins;
+}
+
+static const char *qpnp_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned pin)
+{
+ struct qpnp_pinctrl *qpctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ /* Every PIN is a group */
+ return qpctrl->desc.pins[pin].name;
+}
+
+static int qpnp_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct qpnp_pinctrl *qpctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ /* Every PIN is a group */
+ *pins = &qpctrl->desc.pins[pin].number;
+ *num_pins = 1;
+ return 0;
+}
+
+static int qpnp_parse_dt_config(struct device_node *np,
+ struct pinctrl_dev *pctldev,
+ unsigned long **configs, unsigned int *nconfs)
+{
+ struct qpnp_pinbindings *par;
+ unsigned long cfg;
+ int ret, idx;
+ u32 val;
+
+ for (idx = 0; idx < ARRAY_SIZE(qpnp_pinbindings); idx++) {
+
+ par = &qpnp_pinbindings[idx];
+ ret = of_property_read_u32(np, par->property, &val);
+
+ /* property not found */
+ if (ret == -EINVAL)
+ continue;
+
+ /* use default value, when no value is specified */
+ if (ret)
+ val = par->default_value;
+
+ dev_dbg(pctldev->dev, "found %s with value %u\n",
+ par->property, val);
+
+ cfg = pinconf_to_config_packed(par->param, val);
+
+ ret = pinctrl_utils_add_config(pctldev, configs, nconfs, cfg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qpnp_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map,
+ unsigned *reserv, unsigned *nmaps,
+ enum pinctrl_map_type type)
+{
+ unsigned long *configs = NULL;
+ unsigned nconfs = 0;
+ struct property *prop;
+ const char *group;
+ int ret;
+
+ ret = qpnp_parse_dt_config(np, pctldev, &configs, &nconfs);
+ if (ret < 0)
+ return ret;
+
+ if (!nconfs)
+ return 0;
+
+ ret = of_property_count_strings(np, "pins");
+ if (ret < 0)
+ goto exit;
+
+ ret = pinctrl_utils_reserve_map(pctldev, map, reserv,
+ nmaps, ret);
+ if (ret < 0)
+ goto exit;
+
+ of_property_for_each_string(np, "pins", prop, group) {
+ ret = pinctrl_utils_add_map_configs(pctldev, map,
+ reserv, nmaps, group, configs, nconfs, type);
+ if (ret < 0)
+ break;
+ }
+exit:
+ kfree(configs);
+ return ret;
+}
+
+static int qpnp_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map,
+ unsigned *nmaps)
+{
+ struct device_node *np;
+ enum pinctrl_map_type type;
+ unsigned reserv;
+ int ret;
+
+ ret = 0;
+ *map = NULL;
+ *nmaps = 0;
+ reserv = 0;
+ type = PIN_MAP_TYPE_CONFIGS_GROUP;
+
+ for_each_child_of_node(np_config, np) {
+
+ ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map,
+ &reserv, nmaps, type);
+ if (ret)
+ break;
+
+ ret = qpnp_dt_subnode_to_map(pctldev, np, map, &reserv,
+ nmaps, type);
+ if (ret)
+ break;
+ }
+
+ if (ret < 0)
+ pinctrl_utils_dt_free_map(pctldev, *map, *nmaps);
+
+ return ret;
+}
+
+static const struct pinctrl_ops qpnp_pinctrl_ops = {
+ .get_groups_count = qpnp_get_groups_count,
+ .get_group_name = qpnp_get_group_name,
+ .get_group_pins = qpnp_get_group_pins,
+ .dt_node_to_map = qpnp_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int qpnp_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(qpnp_gpio_functions);
+}
+
+static const char *qpnp_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned function)
+{
+ struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ return qctrl->functions[function];
+}
+
+static int qpnp_get_function_groups(struct pinctrl_dev *pctldev,
+ unsigned function,
+ const char *const **groups,
+ unsigned *const num_qgroups)
+{
+ struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = qctrl->groups;
+ *num_qgroups = qctrl->desc.npins;
+ return 0;
+}
+
+static int qpnp_pinmux_enable(struct pinctrl_dev *pctldev,
+ unsigned function,
+ unsigned pin)
+{
+ struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct qpnp_padinfo *pad;
+ unsigned int addr, val, mask;
+ int ret;
+
+ pad = qpnp_get_desc(qctrl, pin);
+ if (!pad)
+ return -EINVAL;
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_MODE_CTL);
+ val = function << QPNP_REG_MODE_FUNCTION_SHIFT;
+ mask = QPNP_REG_MODE_FUNCTION_MASK;
+ ret = regmap_update_bits(qctrl->map, addr, mask, val);
+ if (ret)
+ return ret;
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_EN_CTL);
+ val = BIT(QPNP_REG_MASTER_EN_SHIFT);
+ mask = QPNP_REG_MASTER_EN_MASK;
+ ret = regmap_update_bits(qctrl->map, addr, mask, val);
+
+ return ret;
+}
+
+static const struct pinmux_ops qpnp_pinmux_ops = {
+ .get_functions_count = qpnp_get_functions_count,
+ .get_function_name = qpnp_get_function_name,
+ .get_function_groups = qpnp_get_function_groups,
+ .enable = qpnp_pinmux_enable,
+};
+
+static int qpnp_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+ struct qpnp_padinfo *pad;
+ unsigned int val, en_mask, buff, addr;
+ int ret;
+
+ pad = qpnp_get_desc(qctrl, offset);
+ if (!pad)
+ return -EINVAL;
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_MODE_CTL);
+ ret = regmap_read(qctrl->map, addr, &buff);
+ if (ret < 0)
+ return ret;
+
+ /* GPIO val is from RT status if input is enabled */
+ if ((buff & QPNP_REG_MODE_SEL_MASK) ==
+ (QPNP_PIN_MODE_DIG_IN << QPNP_REG_MODE_SEL_SHIFT)) {
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_STATUS1);
+ ret = regmap_read(qctrl->map, addr, &val);
+ if (ret < 0)
+ return ret;
+
+ if (pad->type == QPNP_GPIO_TYPE && pad->major == 0)
+ en_mask = QPNP_REG_STATUS1_GPIO_EN_REV0_MASK;
+ else if (pad->type == QPNP_GPIO_TYPE &&
+ pad->major > 0)
+ en_mask = QPNP_REG_STATUS1_GPIO_EN_MASK;
+ else /* MPP */
+ en_mask = QPNP_REG_STATUS1_MPP_EN_MASK;
+
+ if (!(val & en_mask))
+ return -EPERM;
+
+ ret = val & QPNP_REG_STATUS1_VAL_MASK;
+
+ } else {
+ ret = buff & QPNP_REG_MODE_INVERT_MASK;
+ ret = ret >> QPNP_REG_MODE_INVERT_SHIFT;
+ }
+
+ return !!ret;
+}
+
+static void qpnp_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+ struct qpnp_padinfo *pad;
+ unsigned int addr, buff;
+
+ pad = qpnp_get_desc(qctrl, offset);
+ if (!pad)
+ return;
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_MODE_CTL);
+ buff = !!value << QPNP_REG_MODE_INVERT_SHIFT;
+
+ regmap_update_bits(qctrl->map, addr, QPNP_REG_MODE_INVERT_MASK, buff);
+}
+
+static int qpnp_config_get(struct pinctrl_dev *pctldev,
+ unsigned int pin,
+ unsigned long *config)
+{
+ struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned param = pinconf_to_config_param(*config);
+ struct qpnp_padinfo *pad;
+ unsigned arg;
+ int ret;
+
+ pad = qpnp_get_desc(qctrl, pin);
+ if (!pad)
+ return -EINVAL;
+
+ /* Convert pinconf values to register values */
+ ret = qpnp_conv_from_pin(qctrl, pad, param, &arg);
+ if (ret)
+ return ret;
+
+ /* Convert register value to pinconf value */
+ *config = pinconf_to_config_packed(param, arg);
+ return 0;
+}
+
+static int qpnp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *configs, unsigned nconfs)
+{
+ struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct qpnp_padinfo *pad;
+ unsigned param;
+ unsigned arg;
+ int idx, ret;
+
+ pad = qpnp_get_desc(qctrl, pin);
+ if (!pad)
+ return -EINVAL;
+
+ for (idx = 0; idx < nconfs; idx++) {
+ param = pinconf_to_config_param(configs[idx]);
+ arg = pinconf_to_config_argument(configs[idx]);
+
+ /* Convert pinconf values to register values */
+ ret = qpnp_conv_to_pin(qctrl, pad, param, arg);
+ if (ret < 0) {
+ dev_err(pctldev->dev, "config %u = %u failed\n",
+ param, arg);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void qpnp_config_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *seq, unsigned pin)
+{
+ struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int en, buff, pull, out, drive, addr, mode, func, inv;
+ struct qpnp_padinfo *pad;
+ const char *name;
+ int ret;
+
+ static const char *const modes[] = {
+ "di", "do", "dio", "ai", "ao", "aio", "cs"
+ };
+ static const char * const biases[] = {
+ "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
+ "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull"
+ };
+ static const char * const buffer_types[] = {
+ "push-pull", "open-drain", "open-func"
+ };
+ static const char * const strengths[] = {
+ "no", "low", "medium", "high"
+ };
+
+ pad = qpnp_get_desc(qctrl, pin);
+ if (!pad)
+ return;
+
+ name = pad->name;
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_MODE_CTL);
+ ret = regmap_read(qctrl->map, addr, &buff);
+ if (ret < 0) {
+ seq_printf(seq, " %-8s: read error %d", name, ret);
+ return;
+ }
+
+ mode = QPNP_GET(buff, QPNP_REG_MODE_SEL_SHIFT,
+ QPNP_REG_MODE_SEL_MASK);
+ func = QPNP_GET(buff, QPNP_REG_MODE_FUNCTION_SHIFT,
+ QPNP_REG_MODE_FUNCTION_MASK);
+ inv = QPNP_GET(buff, QPNP_REG_MODE_INVERT_SHIFT,
+ QPNP_REG_MODE_INVERT_MASK);
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_DIG_PULL_CTL);
+ ret = regmap_read(qctrl->map, addr, &buff);
+ if (ret < 0) {
+ seq_printf(seq, " %-8s: read error %d", name, ret);
+ return;
+ }
+ pull = QPNP_GET(buff, QPNP_REG_PULL_SHIFT, QPNP_REG_PULL_MASK);
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_DIG_OUT_CTL);
+ ret = regmap_read(qctrl->map, addr, &buff);
+ if (ret < 0) {
+ seq_printf(seq, " %-8s: read error %d", name, ret);
+ return;
+ }
+ out = QPNP_GET(buff, QPNP_REG_OUT_TYPE_SHIFT, QPNP_REG_OUT_TYPE_MASK);
+ drive = QPNP_GET(buff, QPNP_REG_OUT_STRENGTH_SHIFT,
+ QPNP_REG_OUT_STRENGTH_MASK);
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_EN_CTL);
+ ret = regmap_read(qctrl->map, addr, &buff);
+ if (ret < 0) {
+ seq_printf(seq, " %-8s: read error %d", name, ret);
+ return;
+ }
+ en = QPNP_GET(buff, QPNP_REG_MASTER_EN_SHIFT, QPNP_REG_MASTER_EN_MASK);
+
+ seq_printf(seq, " %-8s: %-4s", name, modes[mode]);
+ seq_printf(seq, " %-7s", qctrl->functions[func]);
+ seq_printf(seq, " %-24s %-12s", biases[pull], buffer_types[out]);
+ seq_printf(seq, " %-6s %-4s", strengths[drive], inv ? "inv" : "");
+ seq_printf(seq, " %s", !en ? "high-Z" : "");
+
+}
+
+static const struct pinconf_ops qpnp_pinconf_ops = {
+ .pin_config_group_get = qpnp_config_get,
+ .pin_config_group_set = qpnp_config_set,
+ .pin_config_group_dbg_show = qpnp_config_dbg_show,
+};
+
+static int qpnp_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+ unsigned long config;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
+
+ return qpnp_config_set(qctrl->ctrl, offset, &config, 1);
+}
+
+static int qpnp_direction_output(struct gpio_chip *chip,
+ unsigned offset, int val)
+{
+ struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+ unsigned long config;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
+ return qpnp_config_set(qctrl->ctrl, offset, &config, 1);
+}
+
+static int qpnp_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void qpnp_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int qpnp_of_xlate(struct gpio_chip *chip,
+ const struct of_phandle_args *gpio_desc, u32 *flags)
+{
+ struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+ struct qpnp_padinfo *pad;
+ unsigned pin = gpio_desc->args[0] - QPNP_PIN_PHYSICAL_OFFSET;
+
+ if (chip->of_gpio_n_cells < 2)
+ return -EINVAL;
+
+ pad = qpnp_get_desc(qctrl, pin);
+ if (!pad)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpio_desc->args[1];
+
+ return pin;
+}
+
+static int qpnp_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+ struct qpnp_padinfo *pad;
+
+ pad = qpnp_get_desc(qctrl, offset);
+ if (!pad)
+ return -EINVAL;
+
+ return pad->irq;
+}
+
+static void qpnp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+ unsigned idx;
+
+ for (idx = 0; idx < chip->ngpio; idx++) {
+ qpnp_config_dbg_show(qctrl->ctrl, s, idx);
+ seq_puts(s, "\n");
+ }
+}
+
+static const struct gpio_chip qpnp_gpio_template = {
+ .direction_input = qpnp_direction_input,
+ .direction_output = qpnp_direction_output,
+ .get = qpnp_get,
+ .set = qpnp_set,
+ .request = qpnp_request,
+ .free = qpnp_free,
+ .of_xlate = qpnp_of_xlate,
+ .to_irq = qpnp_to_irq,
+ .dbg_show = qpnp_dbg_show,
+};
+
+static int qpnp_control_init(struct qpnp_pinctrl *qctrl,
+ struct qpnp_padinfo *pad)
+{
+ pad->modes = 0;
+
+ if (pad->type == QPNP_GPIO_TYPE) {
+ switch (pad->subtype) {
+ case QPNP_GPIO_SUBTYPE_GPIO_4CH:
+ case QPNP_GPIO_SUBTYPE_GPIOC_4CH:
+ case QPNP_GPIO_SUBTYPE_GPIO_8CH:
+ case QPNP_GPIO_SUBTYPE_GPIOC_8CH:
+
+ /* only GPIO is supported*/
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
+ break;
+ default:
+ dev_err(qctrl->dev, "invalid GPIO subtype 0x%x\n",
+ pad->subtype);
+ return -EINVAL;
+ }
+
+ } else if (pad->type == QPNP_MPP_TYPE) {
+
+ switch (pad->subtype) {
+ case QPNP_MPP_SUBTYPE_4CH_NO_SINK:
+ case QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK:
+
+ /* Current sink not supported*/
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
+ pad->modes |= BIT(QPNP_PIN_MODE_BIDIR);
+ pad->modes |= BIT(QPNP_PIN_MODE_AIN);
+ pad->modes |= BIT(QPNP_PIN_MODE_AOUT);
+ break;
+ case QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT:
+ case QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
+
+ /* Analog output not supported */
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
+ pad->modes |= BIT(QPNP_PIN_MODE_BIDIR);
+ pad->modes |= BIT(QPNP_PIN_MODE_AIN);
+ pad->modes |= BIT(QPNP_PIN_MODE_SINK);
+ break;
+ case QPNP_MPP_SUBTYPE_4CH_FULL_FUNC:
+ case QPNP_MPP_SUBTYPE_8CH_FULL_FUNC:
+
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
+ pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
+ pad->modes |= BIT(QPNP_PIN_MODE_BIDIR);
+ pad->modes |= BIT(QPNP_PIN_MODE_AIN);
+ pad->modes |= BIT(QPNP_PIN_MODE_AOUT);
+ pad->modes |= BIT(QPNP_PIN_MODE_SINK);
+ break;
+ default:
+ dev_err(qctrl->dev, "invalid MPP subtype 0x%x\n",
+ pad->subtype);
+ return -EINVAL;
+ }
+ } else {
+ dev_err(qctrl->dev, "invalid type 0x%x\n", pad->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_discover(struct platform_device *pdev,
+ struct qpnp_pinctrl *qctrl)
+{
+ struct device *dev = qctrl->dev;
+ struct pinctrl_pin_desc *desc, *descs;
+ struct qpnp_padinfo *pad, *pads;
+ unsigned int addr, npins;
+ int idx, ret;
+
+ npins = qctrl->info->npins;
+ pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
+ if (!pads)
+ return -ENOMEM;
+
+ descs = devm_kcalloc(dev, npins, sizeof(*descs), GFP_KERNEL);
+ if (!descs)
+ return -ENOMEM;
+
+ for (idx = 0; idx < npins; idx++) {
+
+ pad = &pads[idx];
+ desc = &descs[idx];
+
+ pad->irq = platform_get_irq(pdev, idx);
+ if (pad->irq < 0)
+ return pad->irq;
+
+ pad->offset = qctrl->info->base + (idx * 0x100);
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_DIG_MAJOR_REV);
+ ret = regmap_read(qctrl->map, addr, &pad->major);
+ if (ret < 0)
+ return ret;
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_TYPE);
+ ret = regmap_read(qctrl->map, addr, &pad->type);
+ if (ret < 0)
+ return ret;
+
+ if (pad->type != qctrl->info->type) {
+ dev_err(dev, "Expected %x, found %x\n",
+ qctrl->info->type, pad->type);
+ return -EINVAL;
+ }
+
+ addr = QPNP_REG_ADDR(pad, QPNP_REG_SUBTYPE);
+ ret = regmap_read(qctrl->map, addr, &pad->subtype);
+ if (ret < 0)
+ return ret;
+
+ ret = qpnp_control_init(qctrl, pad);
+ if (ret)
+ return ret;
+
+ pad->name = qctrl->groups[idx];
+ desc->number = idx;
+ desc->name = pad->name;
+ }
+
+ qctrl->pads = pads;
+
+ qctrl->chip = qpnp_gpio_template;
+ qctrl->chip.dev = dev;
+ qctrl->chip.base = -1;
+ qctrl->chip.ngpio = qctrl->info->npins;
+ qctrl->chip.label = dev_name(dev);
+ qctrl->chip.of_gpio_n_cells = 2;
+ qctrl->chip.can_sleep = true;
+
+ qctrl->desc.pctlops = &qpnp_pinctrl_ops,
+ qctrl->desc.pmxops = &qpnp_pinmux_ops,
+ qctrl->desc.confops = &qpnp_pinconf_ops,
+ qctrl->desc.owner = THIS_MODULE,
+ qctrl->desc.name = dev_name(dev);
+ qctrl->desc.pins = descs;
+ qctrl->desc.npins = npins;
+
+ qctrl->ctrl = pinctrl_register(&qctrl->desc, dev, qctrl);
+ if (!qctrl->ctrl)
+ return -ENODEV;
+
+ ret = gpiochip_add(&qctrl->chip);
+ if (ret) {
+ dev_err(qctrl->dev, "can't add gpio chip\n");
+ goto err_chip;
+ }
+
+ ret = gpiochip_add_pin_range(&qctrl->chip, dev_name(dev),
+ 0, 0, npins);
+ if (ret) {
+ dev_err(dev, "failed to add pin range\n");
+ goto err_range;
+ }
+
+ return 0;
+
+err_chip:
+ pinctrl_unregister(qctrl->ctrl);
+
+err_range:
+ gpiochip_remove(&qctrl->chip);
+
+ return ret;
+
+}
+
+static const struct of_device_id qpnp_pinctrl_of_match[];
+
+static int qpnp_pinctrl_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct qpnp_chipinfo *qchip;
+ struct qpnp_pinctrl *qctrl;
+
+ qctrl = devm_kzalloc(dev, sizeof(*qctrl), GFP_KERNEL);
+ if (!qctrl)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, qctrl);
+
+ qchip = of_match_node(qpnp_pinctrl_of_match, dev->of_node)->data;
+
+ qctrl->info = qchip;
+ qctrl->dev = &pdev->dev;
+ qctrl->map = dev_get_regmap(dev->parent, NULL);
+
+ if (qchip->type == QPNP_GPIO_TYPE) {
+ if (WARN_ON(qchip->npins > ARRAY_SIZE(qpnp_gpio_groups)))
+ return -EINVAL;
+ qctrl->groups = qpnp_gpio_groups;
+ qctrl->functions = qpnp_gpio_functions;
+ } else {
+ if (WARN_ON(qchip->npins > ARRAY_SIZE(qpnp_mpp_groups)))
+ return -EINVAL;
+ qctrl->groups = qpnp_mpp_groups;
+ qctrl->functions = qpnp_mpp_functions;
+ }
+
+ return qpnp_discover(pdev, qctrl);
+}
+
+static int qpnp_pinctrl_remove(struct platform_device *pdev)
+{
+ struct qpnp_pinctrl *qctrl = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&qctrl->chip);
+ pinctrl_unregister(qctrl->ctrl);
+
+ return 0;
+}
+
+static const struct qpnp_chipinfo qpnp_pm8841_mpp_info = {
+ .npins = 4,
+ .base = 0xa000,
+ .type = QPNP_MPP_TYPE,
+};
+
+static const struct qpnp_chipinfo qpnp_pm8941_gpio_info = {
+ .npins = 36,
+ .base = 0xc000,
+ .type = QPNP_GPIO_TYPE,
+};
+
+static const struct qpnp_chipinfo qpnp_pm8941_mpp_info = {
+ .npins = 8,
+ .base = 0xa000,
+ .type = QPNP_MPP_TYPE,
+};
+
+static const struct qpnp_chipinfo qpnp_pma8084_mpp_info = {
+ .npins = 4,
+ .base = 0xa000,
+ .type = QPNP_MPP_TYPE,
+};
+
+static const struct qpnp_chipinfo qpnp_pma8084_gpio_info = {
+ .npins = 22,
+ .base = 0xc000,
+ .type = QPNP_GPIO_TYPE,
+};
+
+static const struct of_device_id qpnp_pinctrl_of_match[] = {
+ { .compatible = "qcom,pm8941-gpio", .data = &qpnp_pm8941_gpio_info },
+ { .compatible = "qcom,pm8941-mpp", .data = &qpnp_pm8941_mpp_info },
+ { .compatible = "qcom,pm8841-mpp", .data = &qpnp_pm8841_mpp_info },
+ { .compatible = "qcom,pma8084-gpio", .data = &qpnp_pma8084_gpio_info },
+ { .compatible = "qcom,pma8084-mpp", .data = &qpnp_pma8084_mpp_info },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qpnp_pinctrl_of_match);
+
+static struct platform_driver qpnp_pinctrl_driver = {
+ .driver = {
+ .name = "spmi-pmic-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = qpnp_pinctrl_of_match,
+ },
+ .probe = qpnp_pinctrl_probe,
+ .remove = qpnp_pinctrl_remove,
+};
+module_platform_driver(qpnp_pinctrl_driver);
+
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC pin control driver");
+MODULE_ALIAS("platform:spmi-pmic-pinctrl");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
new file mode 100644
index 000000000000..2633ea18799a
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
@@ -0,0 +1,876 @@
+/*
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8921-core.h>
+
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+#include "../core.h"
+#include "../pinconf.h"
+#include "../pinctrl-utils.h"
+
+/* direction */
+#define PM8XXX_GPIO_DIR_OUT BIT(0)
+#define PM8XXX_GPIO_DIR_IN BIT(1)
+
+/* output buffer */
+#define PM8XXX_GPIO_PUSH_PULL 0
+#define PM8XXX_GPIO_OPEN_DRAIN 1
+
+/* bias */
+#define PM8XXX_GPIO_BIAS_PU_30 0
+#define PM8XXX_GPIO_BIAS_PU_1P5 1
+#define PM8XXX_GPIO_BIAS_PU_31P5 2
+#define PM8XXX_GPIO_BIAS_PU_1P5_30 3
+#define PM8XXX_GPIO_BIAS_PD 4
+#define PM8XXX_GPIO_BIAS_NP 5
+
+/* GPIO registers */
+#define SSBI_REG_ADDR_GPIO_BASE 0x150
+#define SSBI_REG_ADDR_GPIO(n) (SSBI_REG_ADDR_GPIO_BASE + n)
+
+#define PM8XXX_GPIO_MODE_ENABLE BIT(0)
+#define PM8XXX_GPIO_WRITE BIT(7)
+
+#define PM8XXX_MAX_GPIOS 44
+
+/* Qualcomm specific pin configurations */
+#define PM8XXX_PINCONF_PULL_UP (PIN_CONFIG_END + 1)
+#define PM8XXX_PINCONF_STRENGTH (PIN_CONFIG_END + 2)
+
+struct pm8xxx_pinbindings {
+ const char *property;
+ unsigned param;
+ u32 default_value;
+};
+static struct pm8xxx_pinbindings pm8xxx_pinbindings[] = {
+ /* PMIC_GPIO_PULL_UP_30... */
+ {"qcom,pull-up-strength", PM8XXX_PINCONF_PULL_UP, 0},
+ /* PMIC_GPIO_STRENGTH_NO... */
+ {"qcom,drive-strength", PM8XXX_PINCONF_STRENGTH, 0},
+};
+
+struct pm8xxx_gpio_pin {
+ int irq;
+
+ u8 power_source;
+ u8 direction;
+ u8 output_buffer;
+ u8 output_value;
+ u8 bias;
+ u8 output_strength;
+ u8 disable;
+ u8 function;
+ u8 non_inverted;
+};
+
+struct pm8xxx_gpio_data {
+ int ngpio;
+};
+
+struct pm8xxx_gpio {
+ struct device *dev;
+ struct regmap *regmap;
+ struct pinctrl_dev *pctrl;
+ struct gpio_chip chip;
+
+ const struct pm8xxx_gpio_data *data;
+
+ struct pm8xxx_gpio_pin pins[PM8XXX_MAX_GPIOS];
+};
+
+static inline struct pm8xxx_gpio *to_pm8xxx_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct pm8xxx_gpio, chip);
+};
+
+static const char * const pm8xxx_gpio_groups[PM8XXX_MAX_GPIOS] = {
+ "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8",
+ "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15",
+ "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
+ "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29",
+ "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
+ "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43",
+ "gpio44",
+};
+
+static const char * const pm8xxx_gpio_functions[] = {
+ PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED,
+ PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2,
+ PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2,
+ PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4,
+};
+
+static int pm8xxx_gpio_read(struct pm8xxx_gpio *pctrl, int pin, int bank)
+{
+ int reg = SSBI_REG_ADDR_GPIO(pin);
+ unsigned int val = bank << 4;
+ int ret;
+
+ ret = regmap_write(pctrl->regmap, reg, val);
+ if (ret) {
+ dev_err(pctrl->dev,
+ "failed to select bank %d of pin %d\n", bank, pin);
+ return ret;
+ }
+
+ ret = regmap_read(pctrl->regmap, reg, &val);
+ if (ret) {
+ dev_err(pctrl->dev,
+ "failed to read register %d of pin %d\n", bank, pin);
+ return ret;
+ }
+
+ return val;
+}
+
+static int pm8xxx_gpio_write(struct pm8xxx_gpio *pctrl,
+ int pin, int bank, u8 val)
+{
+ int ret;
+
+ val |= PM8XXX_GPIO_WRITE;
+ val |= bank << 4;
+
+ ret = regmap_write(pctrl->regmap, SSBI_REG_ADDR_GPIO(pin), val);
+ if (ret)
+ dev_err(pctrl->dev, "failed to write register\n");
+
+ return ret;
+}
+
+static int pm8xxx_gpio_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ return pctrl->data->ngpio;
+}
+
+static const char *pm8xxx_gpio_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned group)
+{
+ return pm8xxx_gpio_groups[group];
+}
+
+static int pm8xxx_parse_dt_config(struct device *dev, struct device_node *np,
+ unsigned long **configs, unsigned int *nconfigs)
+{
+ struct pm8xxx_pinbindings *par;
+ unsigned long cfg[ARRAY_SIZE(pm8xxx_pinbindings)];
+ unsigned int ncfg = 0;
+ int ret, idx;
+ u32 val;
+
+ if (!np)
+ return -EINVAL;
+
+ for (idx = 0; idx < ARRAY_SIZE(pm8xxx_pinbindings); idx++) {
+ par = &pm8xxx_pinbindings[idx];
+ ret = of_property_read_u32(np, par->property, &val);
+
+ /* property not found */
+ if (ret == -EINVAL)
+ continue;
+
+ /* use default value, when no value is specified */
+ if (ret)
+ val = par->default_value;
+
+ dev_dbg(dev, "found %s with value %u\n", par->property, val);
+ cfg[ncfg] = pinconf_to_config_packed(par->param, val);
+ ncfg++;
+ }
+
+ ret = 0;
+
+ /* no configs found at qchip->npads */
+ if (ncfg == 0) {
+ *configs = NULL;
+ *nconfigs = 0;
+ goto out;
+ }
+
+ /*
+ * Now limit the number of configs to the real number of
+ * found properties.
+ */
+ *configs = kcalloc(ncfg, sizeof(unsigned long), GFP_KERNEL);
+ if (!*configs) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(*configs, cfg, ncfg * sizeof(unsigned long));
+ *nconfigs = ncfg;
+
+out:
+ return ret;
+}
+
+static int pm8xxx_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map,
+ unsigned *reserv, unsigned *nmaps,
+ enum pinctrl_map_type type)
+{
+ unsigned long *configs = NULL;
+ unsigned num_configs = 0;
+ struct property *prop;
+ const char *group;
+ int ret;
+
+ ret = pm8xxx_parse_dt_config(pctldev->dev, np, &configs, &num_configs);
+ if (ret < 0)
+ return ret;
+
+ if (!num_configs)
+ return 0;
+
+ ret = of_property_count_strings(np, "pins");
+ if (ret < 0)
+ goto exit;
+
+ ret = pinctrl_utils_reserve_map(pctldev, map, reserv,
+ nmaps, ret);
+ if (ret < 0)
+ goto exit;
+
+ of_property_for_each_string(np, "pins", prop, group) {
+ ret = pinctrl_utils_add_map_configs(pctldev, map,
+ reserv, nmaps, group, configs,
+ num_configs, type);
+ if (ret < 0)
+ break;
+ }
+exit:
+ kfree(configs);
+ return ret;
+}
+
+static int pm8xxx_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map,
+ unsigned *nmaps)
+{
+ struct device_node *np;
+ enum pinctrl_map_type type;
+ unsigned reserv;
+ int ret;
+
+ ret = 0;
+ *map = NULL;
+ *nmaps = 0;
+ reserv = 0;
+ type = PIN_MAP_TYPE_CONFIGS_GROUP;
+
+ for_each_child_of_node(np_config, np) {
+
+ ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map,
+ &reserv, nmaps, type);
+ if (ret)
+ break;
+
+ ret = pm8xxx_dt_subnode_to_map(pctldev, np, map, &reserv,
+ nmaps, type);
+ if (ret)
+ break;
+ }
+
+ if (ret < 0)
+ pinctrl_utils_dt_free_map(pctldev, *map, *nmaps);
+
+ return ret;
+}
+
+static const struct pinctrl_ops pm8xxx_gpio_pinctrl_ops = {
+ .get_groups_count = pm8xxx_gpio_get_groups_count,
+ .get_group_name = pm8xxx_gpio_get_group_name,
+ .dt_node_to_map = pm8xxx_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(pm8xxx_gpio_functions);
+}
+
+static const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned function)
+{
+ return pm8xxx_gpio_functions[function];
+}
+
+static int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev,
+ unsigned function,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = pm8xxx_gpio_groups;
+ *num_groups = pctrl->data->ngpio;
+ return 0;
+}
+
+static int pm8xxx_pinmux_enable(struct pinctrl_dev *pctldev,
+ unsigned function,
+ unsigned group)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[group];
+ u8 val;
+
+ pin->function = function;
+ val = pin->function << 1;
+
+ pm8xxx_gpio_write(pctrl, group, 4, val);
+
+ return 0;
+}
+
+static const struct pinmux_ops pm8xxx_pinmux_ops = {
+ .get_functions_count = pm8xxx_get_functions_count,
+ .get_function_name = pm8xxx_get_function_name,
+ .get_function_groups = pm8xxx_get_function_groups,
+ .enable = pm8xxx_pinmux_enable,
+};
+
+static int pm8xxx_gpio_config_get(struct pinctrl_dev *pctldev,
+ unsigned int offset,
+ unsigned long *config)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
+ unsigned param = pinconf_to_config_param(*config);
+ unsigned arg;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ arg = pin->bias == PM8XXX_GPIO_BIAS_NP;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ arg = pin->bias == PM8XXX_GPIO_BIAS_PD;
+ break;
+ case PM8XXX_PINCONF_PULL_UP:
+ if (pin->bias >= PM8XXX_GPIO_BIAS_PU_30 &&
+ pin->bias <= PM8XXX_GPIO_BIAS_PU_1P5_30)
+ arg = PMIC_GPIO_PULL_UP_30 + pin->bias;
+ else
+ arg = 0;
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ arg = pin->disable;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ arg = pin->direction == PM8XXX_GPIO_DIR_IN;
+ break;
+ case PIN_CONFIG_OUTPUT:
+ arg = pin->output_value;
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ arg = pin->power_source;
+ break;
+ case PM8XXX_PINCONF_STRENGTH:
+ arg = pin->output_strength;
+ break;
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ arg = pin->output_buffer == PM8XXX_GPIO_PUSH_PULL;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ arg = pin->output_buffer == PM8XXX_GPIO_OPEN_DRAIN;
+ break;
+ default:
+ dev_err(pctrl->dev,
+ "unsupported config parameter: %x\n",
+ param);
+ return -EINVAL;
+ }
+
+ *config = pinconf_to_config_packed(param, arg);
+
+ return 0;
+}
+
+static int pm8xxx_gpio_config_set(struct pinctrl_dev *pctldev,
+ unsigned int offset,
+ unsigned long *configs,
+ unsigned num_configs)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
+ unsigned param;
+ unsigned arg;
+ unsigned i;
+ u8 banks = 0;
+ u8 val;
+
+ for (i = 0; i < num_configs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ pin->bias = PM8XXX_GPIO_BIAS_NP;
+ banks |= BIT(2);
+ pin->disable = 0;
+ banks |= BIT(3);
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ pin->bias = PM8XXX_GPIO_BIAS_PD;
+ banks |= BIT(2);
+ pin->disable = 0;
+ banks |= BIT(3);
+ break;
+ case PM8XXX_PINCONF_PULL_UP:
+ if (arg < PMIC_GPIO_PULL_UP_30 ||
+ arg > PMIC_GPIO_PULL_UP_1P5_30) {
+ dev_err(pctrl->dev, "invalid pull-up level\n");
+ return -EINVAL;
+ }
+ pin->bias = arg - PM8XXX_GPIO_BIAS_PU_30;
+ banks |= BIT(2);
+ pin->disable = 0;
+ banks |= BIT(3);
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ pin->disable = 1;
+ banks |= BIT(3);
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ pin->direction = PM8XXX_GPIO_DIR_IN;
+ banks |= BIT(1);
+ break;
+ case PIN_CONFIG_OUTPUT:
+ pin->direction = PM8XXX_GPIO_DIR_OUT;
+ pin->output_value = !!arg;
+ banks |= BIT(1);
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ pin->power_source = arg;
+ banks |= BIT(0);
+ break;
+ case PM8XXX_PINCONF_STRENGTH:
+ if (arg > PMIC_GPIO_STRENGTH_LOW) {
+ dev_err(pctrl->dev, "invalid drive strength\n");
+ return -EINVAL;
+ }
+ pin->output_strength = arg;
+ banks |= BIT(3);
+ break;
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ pin->output_buffer = PM8XXX_GPIO_PUSH_PULL;
+ banks |= BIT(1);
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ pin->output_buffer = PM8XXX_GPIO_OPEN_DRAIN;
+ banks |= BIT(1);
+ break;
+ default:
+ dev_err(pctrl->dev,
+ "unsupported config parameter: %x\n",
+ param);
+ return -EINVAL;
+ }
+ }
+
+ if (banks & BIT(0))
+ pm8xxx_gpio_write(pctrl, offset, 0, pin->power_source << 1 |
+ PM8XXX_GPIO_MODE_ENABLE);
+
+ if (banks & BIT(1)) {
+ val = pin->direction << 2;
+ val |= pin->output_buffer << 1;
+ val |= pin->output_value;
+ pm8xxx_gpio_write(pctrl, offset, 1, val);
+ }
+
+ if (banks & BIT(2)) {
+ val = pin->bias << 1;
+ pm8xxx_gpio_write(pctrl, offset, 2, val);
+ }
+
+ if (banks & BIT(3)) {
+ val = pin->output_strength << 2;
+ val |= pin->disable;
+ pm8xxx_gpio_write(pctrl, offset, 3, val);
+ }
+
+ if (banks & BIT(4)) {
+ val = pin->function << 1;
+ pm8xxx_gpio_write(pctrl, offset, 4, val);
+ }
+
+ if (banks & BIT(5)) {
+ val = 0;
+ if (pin->non_inverted)
+ val |= BIT(3);
+ pm8xxx_gpio_write(pctrl, offset, 5, val);
+ }
+
+ return 0;
+}
+
+static const struct pinconf_ops pm8xxx_gpio_pinconf_ops = {
+ .pin_config_group_get = pm8xxx_gpio_config_get,
+ .pin_config_group_set = pm8xxx_gpio_config_set,
+};
+
+static struct pinctrl_desc pm8xxx_gpio_desc = {
+ .pctlops = &pm8xxx_gpio_pinctrl_ops,
+ .pmxops = &pm8xxx_pinmux_ops,
+ .confops = &pm8xxx_gpio_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+static int pm8xxx_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+ u8 val;
+
+ pin->direction = PM8XXX_GPIO_DIR_IN;
+ val = pin->direction << 2;
+
+ pm8xxx_gpio_write(pctrl, offset, 1, val);
+
+ return 0;
+}
+
+static int pm8xxx_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset,
+ int value)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+ u8 val;
+
+ pin->direction = PM8XXX_GPIO_DIR_OUT;
+ pin->output_value = !!value;
+
+ val = pin->direction << 2;
+ val |= pin->output_buffer << 1;
+ val |= pin->output_value;
+
+ pm8xxx_gpio_write(pctrl, offset, 1, val);
+
+ return 0;
+}
+
+static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+
+ if (pin->direction == PM8XXX_GPIO_DIR_OUT)
+ return pin->output_value;
+
+ return pm8xxx_read_irq_status(pin->irq);
+}
+
+static void pm8xxx_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(gc);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+ u8 val;
+
+ pin->output_value = !!value;
+
+ val = pin->direction << 2;
+ val |= pin->output_buffer << 1;
+ val |= pin->output_value;
+
+ pm8xxx_gpio_write(pctrl, offset, 1, val);
+}
+
+static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+
+ return pin->irq;
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+static void pm8xxx_gpio_dbg_show_one(struct seq_file *s,
+ struct pinctrl_dev *pctldev,
+ struct gpio_chip *chip,
+ unsigned offset,
+ unsigned gpio)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
+
+ static const char * const directions[] = {
+ "off", "out", "in", "both"
+ };
+ static const char * const biases[] = {
+ "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
+ "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull"
+ };
+ static const char * const buffer_types[] = {
+ "push-pull", "open-drain"
+ };
+ static const char * const strengths[] = {
+ "no", "high", "medium", "low"
+ };
+
+ seq_printf(s, " gpio%-2d:", offset + 1);
+ if (pin->disable) {
+ seq_puts(s, " ---");
+ } else {
+ seq_printf(s, " %-4s", directions[pin->direction]);
+ seq_printf(s, " %-7s", pm8xxx_gpio_functions[pin->function]);
+ seq_printf(s, " VIN%d", pin->power_source);
+ seq_printf(s, " %-27s", biases[pin->bias]);
+ seq_printf(s, " %-10s", buffer_types[pin->output_buffer]);
+ seq_printf(s, " %-4s", pin->output_value ? "high" : "low");
+ seq_printf(s, " %-7s", strengths[pin->output_strength]);
+ if (!pin->non_inverted)
+ seq_puts(s, " inverted");
+ }
+}
+
+static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ unsigned gpio = chip->base;
+ unsigned i;
+
+ for (i = 0; i < chip->ngpio; i++, gpio++) {
+ pm8xxx_gpio_dbg_show_one(s, NULL, chip, i, gpio);
+ seq_puts(s, "\n");
+ }
+}
+
+#else
+#define msm_gpio_dbg_show NULL
+#endif
+
+static struct gpio_chip pm8xxx_gpio_template = {
+ .direction_input = pm8xxx_gpio_direction_input,
+ .direction_output = pm8xxx_gpio_direction_output,
+ .get = pm8xxx_gpio_get,
+ .set = pm8xxx_gpio_set,
+ .to_irq = pm8xxx_gpio_to_irq,
+ .dbg_show = pm8xxx_gpio_dbg_show,
+ .owner = THIS_MODULE,
+};
+
+static int pm8xxx_gpio_populate(struct pm8xxx_gpio *pctrl)
+{
+ struct pm8xxx_gpio_pin *pin;
+ int val;
+ int i;
+
+ for (i = 0; i < pctrl->data->ngpio; i++) {
+ pin = &pctrl->pins[i];
+
+ val = pm8xxx_gpio_read(pctrl, i, 0);
+ if (val < 0)
+ return val;
+
+ pin->power_source = (val >> 1) & 0x7;
+
+ val = pm8xxx_gpio_read(pctrl, i, 1);
+ if (val < 0)
+ return val;
+
+ pin->direction = (val >> 2) & 0x3;
+ pin->output_buffer = !!(val & BIT(1));
+ pin->output_value = val & BIT(0);
+
+ val = pm8xxx_gpio_read(pctrl, i, 2);
+ if (val < 0)
+ return val;
+
+ pin->bias = (val >> 1) & 0x7;
+
+ val = pm8xxx_gpio_read(pctrl, i, 3);
+ if (val < 0)
+ return val;
+
+ pin->output_strength = (val >> 2) & 0x3;
+ pin->disable = val & BIT(0);
+
+ val = pm8xxx_gpio_read(pctrl, i, 4);
+ if (val < 0)
+ return val;
+
+ pin->function = (val >> 1) & 0x7;
+
+ val = pm8xxx_gpio_read(pctrl, i, 5);
+ if (val < 0)
+ return val;
+
+ pin->non_inverted = !!(val & BIT(3));
+ }
+
+ return 0;
+}
+
+static const struct pm8xxx_gpio_data pm8018_gpio_data = {
+ .ngpio = 6,
+};
+
+static const struct pm8xxx_gpio_data pm8038_gpio_data = {
+ .ngpio = 12,
+};
+
+static const struct pm8xxx_gpio_data pm8058_gpio_data = {
+ .ngpio = 40,
+};
+static const struct pm8xxx_gpio_data pm8917_gpio_data = {
+ .ngpio = 38,
+};
+
+static const struct pm8xxx_gpio_data pm8921_gpio_data = {
+ .ngpio = 44,
+};
+
+static const struct of_device_id pm8xxx_gpio_of_match[] = {
+ { .compatible = "qcom,pm8018-gpio", .data = &pm8018_gpio_data },
+ { .compatible = "qcom,pm8038-gpio", .data = &pm8038_gpio_data },
+ { .compatible = "qcom,pm8058-gpio", .data = &pm8058_gpio_data },
+ { .compatible = "qcom,pm8917-gpio", .data = &pm8917_gpio_data },
+ { .compatible = "qcom,pm8921-gpio", .data = &pm8921_gpio_data },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
+
+static int pm8xxx_gpio_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct pm8xxx_gpio *pctrl;
+ int ret;
+ int i;
+
+ match = of_match_node(pm8xxx_gpio_of_match, pdev->dev.of_node);
+ if (!match)
+ return -ENXIO;
+
+ pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
+ if (!pctrl)
+ return -ENOMEM;
+
+ pctrl->dev = &pdev->dev;
+ pctrl->data = match->data;
+
+ BUG_ON(pctrl->data->ngpio > PM8XXX_MAX_GPIOS);
+
+ pctrl->chip = pm8xxx_gpio_template;
+ pctrl->chip.base = -1;
+ pctrl->chip.dev = &pdev->dev;
+ pctrl->chip.of_node = pdev->dev.of_node;
+ pctrl->chip.label = dev_name(pctrl->dev);
+ pctrl->chip.ngpio = pctrl->data->ngpio;
+
+ pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pctrl->regmap) {
+ dev_err(&pdev->dev, "parent regmap unavailable\n");
+ return -ENXIO;
+ }
+
+ for (i = 0; i < pctrl->data->ngpio; i++) {
+ ret = platform_get_irq(pdev, i);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "missing interrupts for pin %d\n", i);
+ return ret;
+ }
+
+ pctrl->pins[i].irq = ret;
+ }
+
+ ret = pm8xxx_gpio_populate(pctrl);
+ if (ret)
+ return ret;
+
+ pm8xxx_gpio_desc.name = dev_name(&pdev->dev);
+ pctrl->pctrl = pinctrl_register(&pm8xxx_gpio_desc, &pdev->dev, pctrl);
+ if (!pctrl->pctrl) {
+ dev_err(&pdev->dev, "couldn't register pm8xxx gpio driver\n");
+ return -ENODEV;
+ }
+
+ ret = gpiochip_add(&pctrl->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "failed register gpiochip\n");
+ goto unregister_pinctrl;
+ }
+
+ ret = gpiochip_add_pin_range(&pctrl->chip,
+ dev_name(pctrl->dev),
+ 1, 0, pctrl->data->ngpio);
+ if (ret) {
+ dev_err(pctrl->dev, "failed to add pin range\n");
+ goto unregister_gpiochip;
+ }
+
+ platform_set_drvdata(pdev, pctrl);
+
+ dev_dbg(&pdev->dev, "Qualcomm pm8xxx gpio driver probed\n");
+
+ return 0;
+
+unregister_pinctrl:
+ pinctrl_unregister(pctrl->pctrl);
+
+unregister_gpiochip:
+ if (gpiochip_remove(&pctrl->chip))
+ dev_err(&pdev->dev, "unable to unregister gpiochip\n");
+
+ return ret;
+}
+
+static int pm8xxx_gpio_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&pctrl->chip);
+
+ pinctrl_unregister(pctrl->pctrl);
+
+ return 0;
+}
+
+static struct platform_driver pm8xxx_gpio_driver = {
+ .driver = {
+ .name = "ssbi-pmic-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = pm8xxx_gpio_of_match,
+ },
+ .probe = pm8xxx_gpio_probe,
+ .remove = pm8xxx_gpio_remove,
+};
+
+static int pm8xxx_gpio_init(void)
+{
+ return platform_driver_register(&pm8xxx_gpio_driver);
+}
+subsys_initcall(pm8xxx_gpio_init);
+
+MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
+MODULE_DESCRIPTION("Qualcomm SSBI PMIC GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 750a20275664..3f59cb58f1c4 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -193,6 +193,10 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(type),
POWER_SUPPLY_ATTR(scope),
POWER_SUPPLY_ATTR(charge_term_current),
+ /* Local extensions */
+ POWER_SUPPLY_ATTR(usb_hc),
+ POWER_SUPPLY_ATTR(usb_otg),
+ POWER_SUPPLY_ATTR(charge_enabled),
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer),
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 2dc8289e5dba..26e63779c3ea 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -449,6 +449,18 @@ config REGULATOR_PFUZE100
Say y here to support the regulators found on the Freescale
PFUZE100/PFUZE200 PMIC.
+config REGULATOR_QCOM_RPM
+ tristate "Qualcomm RPM regulator driver"
+ depends on MFD_QCOM_RPM
+ help
+ If you say yes to this option, support will be included for the
+ regulators exposed by the Resource Power Manager found in Qualcomm
+ 8660, 8960 and 8064 based devices.
+
+ Say M here if you want to include support for the regulators on the
+ Qualcomm RPM as a module. The module will be named
+ "qcom_rpm-regulator".
+
config REGULATOR_RC5T583
tristate "RICOH RC5T583 Power regulators"
depends on MFD_RC5T583
@@ -658,5 +670,12 @@ config REGULATOR_WM8994
This driver provides support for the voltage regulators on the
WM8994 CODEC.
+config REGULATOR_HI6421
+ tristate "HiSilicon Hi6421 PMIC regulators"
+ depends on MFD_HI6421_PMIC
+ help
+ This driver provides support for the voltage regulators on the
+ HiSilicon Hi6421 PMU / Codec IC.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index aa4a6aa7b558..2eb15280c63e 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
+obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
@@ -88,6 +89,6 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
-
+obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c
new file mode 100644
index 000000000000..222ecd1d2a40
--- /dev/null
+++ b/drivers/regulator/hi6421-regulator.c
@@ -0,0 +1,559 @@
+/*
+ * Device driver for regulators in Hi6421 IC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/hi6421-pmic.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+
+struct hi6421_regulator_register_info {
+ u32 ctrl_reg;
+ u32 enable_mask;
+ u32 eco_mode_mask;
+ u32 vset_reg;
+ u32 vset_mask;
+};
+
+struct hi6421_regulator {
+ const char *name;
+ struct hi6421_regulator_register_info register_info;
+ struct timeval last_off_time;
+ u32 off_on_delay;
+ u32 eco_uA;
+ struct regulator_desc rdesc;
+ int (*dt_parse)(struct hi6421_regulator *, struct platform_device *);
+};
+
+static inline struct hi6421_pmic *rdev_to_pmic(struct regulator_dev *dev)
+{
+ /* regulator_dev parent to->
+ * hi6421 regulator platform device_dev parent to->
+ * hi6421 pmic platform device_dev
+ */
+ return dev_get_drvdata(rdev_get_dev(dev)->parent->parent);
+}
+
+/* helper function to ensure when it returns it is at least 'delay_us'
+ * microseconds after 'since'.
+ */
+static void ensured_time_after(struct timeval since, u32 delay_us)
+{
+ struct timeval now;
+ u64 elapsed_ns64, delay_ns64;
+ u32 actual_us32;
+
+ delay_ns64 = delay_us * NSEC_PER_USEC;
+ do_gettimeofday(&now);
+ elapsed_ns64 = timeval_to_ns(&now) - timeval_to_ns(&since);
+ if (delay_ns64 > elapsed_ns64) {
+ actual_us32 = ((u32)(delay_ns64 - elapsed_ns64) /
+ NSEC_PER_USEC);
+ if (actual_us32 >= 1000) {
+ mdelay(actual_us32 / 1000);
+ udelay(actual_us32 % 1000);
+ } else if (actual_us32 > 0) {
+ udelay(actual_us32);
+ }
+ }
+ return;
+}
+
+static int hi6421_regulator_is_enabled(struct regulator_dev *dev)
+{
+ u32 reg_val;
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+
+ reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg);
+
+ return ((reg_val & sreg->register_info.enable_mask) != 0);
+}
+
+static int hi6421_regulator_enable(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+
+ /* keep a distance of off_on_delay from last time disabled */
+ ensured_time_after(sreg->last_off_time, sreg->off_on_delay);
+
+ /* cannot enable more than one regulator at one time */
+ mutex_lock(&pmic->enable_mutex);
+ ensured_time_after(pmic->last_enabled, HI6421_REGS_ENA_PROTECT_TIME);
+
+ /* set enable register */
+ hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+ sreg->register_info.enable_mask,
+ sreg->register_info.enable_mask);
+
+ do_gettimeofday(&pmic->last_enabled);
+ mutex_unlock(&pmic->enable_mutex);
+
+ return 0;
+}
+
+static int hi6421_regulator_disable(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+
+ /* set enable register to 0 */
+ hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+ sreg->register_info.enable_mask, 0);
+
+ do_gettimeofday(&sreg->last_off_time);
+
+ return 0;
+}
+
+static int hi6421_regulator_get_voltage(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 reg_val, selector;
+
+ /* get voltage selector */
+ reg_val = hi6421_pmic_read(pmic, sreg->register_info.vset_reg);
+ selector = (reg_val & sreg->register_info.vset_mask) >>
+ (ffs(sreg->register_info.vset_mask) - 1);
+
+ return sreg->rdesc.ops->list_voltage(dev, selector);
+}
+
+static int hi6421_regulator_ldo_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 vsel;
+ int ret = 0;
+
+ for (vsel = 0; vsel < sreg->rdesc.n_voltages; vsel++) {
+ int uV = sreg->rdesc.volt_table[vsel];
+ /* Break at the first in-range value */
+ if (min_uV <= uV && uV <= max_uV)
+ break;
+ }
+
+ /* unlikely to happen. sanity test done by regulator core */
+ if (unlikely(vsel == sreg->rdesc.n_voltages))
+ return -EINVAL;
+
+ *selector = vsel;
+ /* set voltage selector */
+ hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg,
+ sreg->register_info.vset_mask,
+ vsel << (ffs(sreg->register_info.vset_mask) - 1));
+
+ return ret;
+}
+
+static int hi6421_regulator_buck012_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 vsel;
+ int ret = 0;
+
+ vsel = DIV_ROUND_UP((max_uV - sreg->rdesc.min_uV),
+ sreg->rdesc.uV_step);
+
+ *selector = vsel;
+ /* set voltage selector */
+ hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg,
+ sreg->register_info.vset_mask,
+ vsel << (ffs(sreg->register_info.vset_mask) - 1));
+
+ return ret;
+}
+
+static unsigned int hi6421_regulator_get_mode(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 reg_val;
+
+ reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg);
+ if (reg_val & sreg->register_info.eco_mode_mask)
+ return REGULATOR_MODE_IDLE;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int hi6421_regulator_set_mode(struct regulator_dev *dev,
+ unsigned int mode)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 eco_mode;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ eco_mode = HI6421_ECO_MODE_DISABLE;
+ break;
+ case REGULATOR_MODE_IDLE:
+ eco_mode = HI6421_ECO_MODE_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set mode */
+ hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+ sreg->register_info.eco_mode_mask,
+ eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1));
+
+ return 0;
+}
+
+
+unsigned int hi6421_regulator_get_optimum_mode(struct regulator_dev *dev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+
+ if ((load_uA == 0) || (load_uA > sreg->eco_uA))
+ return REGULATOR_MODE_NORMAL;
+ else
+ return REGULATOR_MODE_IDLE;
+}
+
+static int hi6421_dt_parse_common(struct hi6421_regulator *sreg,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc = &sreg->rdesc;
+ unsigned int register_info[3];
+ int ret = 0;
+
+ /* parse .register_info.ctrl_reg */
+ ret = of_property_read_u32_array(np, "hisilicon,hi6421-ctrl",
+ register_info, 3);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-ctrl property set\n");
+ goto dt_parse_common_end;
+ }
+ sreg->register_info.ctrl_reg = register_info[0];
+ sreg->register_info.enable_mask = register_info[1];
+ sreg->register_info.eco_mode_mask = register_info[2];
+
+ /* parse .register_info.vset_reg */
+ ret = of_property_read_u32_array(np, "hisilicon,hi6421-vset",
+ register_info, 2);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-vset property set\n");
+ goto dt_parse_common_end;
+ }
+ sreg->register_info.vset_reg = register_info[0];
+ sreg->register_info.vset_mask = register_info[1];
+
+ /* parse .off-on-delay */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-off-on-delay-us",
+ &sreg->off_on_delay);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-off-on-delay-us property set\n");
+ goto dt_parse_common_end;
+ }
+
+ /* parse .enable_time */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-enable-time-us",
+ &rdesc->enable_time);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-enable-time-us property set\n");
+ goto dt_parse_common_end;
+ }
+
+ /* parse .eco_uA */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-eco-microamp",
+ &sreg->eco_uA);
+ if (ret) {
+ sreg->eco_uA = 0;
+ ret = 0;
+ }
+
+dt_parse_common_end:
+ return ret;
+}
+
+static int hi6421_dt_parse_ldo(struct hi6421_regulator *sreg,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc = &sreg->rdesc;
+ unsigned int *v_table;
+ int ret = 0;
+
+ /* parse .n_voltages, and .volt_table */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-n-voltages",
+ &rdesc->n_voltages);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-n-voltages property set\n");
+ goto dt_parse_ldo_end;
+ }
+
+ /* alloc space for .volt_table */
+ v_table = devm_kzalloc(dev, sizeof(unsigned int) * rdesc->n_voltages,
+ GFP_KERNEL);
+ if (unlikely(!v_table)) {
+ ret = -ENOMEM;
+ dev_err(dev, "no memory for .volt_table\n");
+ goto dt_parse_ldo_end;
+ }
+
+ ret = of_property_read_u32_array(np, "hisilicon,hi6421-vset-table",
+ v_table, rdesc->n_voltages);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-vset-table property set\n");
+ goto dt_parse_ldo_end;
+ }
+ rdesc->volt_table = v_table;
+
+ /* parse hi6421 regulator's dt common part */
+ ret = hi6421_dt_parse_common(sreg, pdev);
+ if (ret) {
+ dev_err(dev, "failure in hi6421_dt_parse_common\n");
+ goto dt_parse_ldo_end;
+ }
+
+dt_parse_ldo_end:
+ return ret;
+}
+
+static int hi6421_dt_parse_buck012(struct hi6421_regulator *sreg,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc = &sreg->rdesc;
+ int ret = 0;
+
+ /* parse .n_voltages, and .uV_step */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-n-voltages",
+ &rdesc->n_voltages);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-n-voltages property set\n");
+ goto dt_parse_buck012_end;
+ }
+ ret = of_property_read_u32(np, "hisilicon,hi6421-uv-step",
+ &rdesc->uV_step);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-uv-step property set\n");
+ goto dt_parse_buck012_end;
+ }
+
+ /* parse hi6421 regulator's dt common part */
+ ret = hi6421_dt_parse_common(sreg, pdev);
+ if (ret) {
+ dev_err(dev, "failure in hi6421_dt_parse_common\n");
+ goto dt_parse_buck012_end;
+ }
+
+dt_parse_buck012_end:
+ return ret;
+}
+
+static struct regulator_ops hi6421_ldo_rops = {
+ .is_enabled = hi6421_regulator_is_enabled,
+ .enable = hi6421_regulator_enable,
+ .disable = hi6421_regulator_disable,
+ .list_voltage = regulator_list_voltage_table,
+ .get_voltage = hi6421_regulator_get_voltage,
+ .set_voltage = hi6421_regulator_ldo_set_voltage,
+ .get_mode = hi6421_regulator_get_mode,
+ .set_mode = hi6421_regulator_set_mode,
+ .get_optimum_mode = hi6421_regulator_get_optimum_mode,
+};
+
+static struct regulator_ops hi6421_buck012_rops = {
+ .is_enabled = hi6421_regulator_is_enabled,
+ .enable = hi6421_regulator_enable,
+ .disable = hi6421_regulator_disable,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage = hi6421_regulator_get_voltage,
+ .set_voltage = hi6421_regulator_buck012_set_voltage,
+ .get_mode = hi6421_regulator_get_mode,
+ .set_mode = hi6421_regulator_set_mode,
+ .get_optimum_mode = hi6421_regulator_get_optimum_mode,
+};
+
+static struct regulator_ops hi6421_buck345_rops = {
+ .is_enabled = hi6421_regulator_is_enabled,
+ .enable = hi6421_regulator_enable,
+ .disable = hi6421_regulator_disable,
+ .list_voltage = regulator_list_voltage_table,
+ .get_voltage = hi6421_regulator_get_voltage,
+ .set_voltage = hi6421_regulator_ldo_set_voltage,
+ .get_mode = hi6421_regulator_get_mode,
+ .set_mode = hi6421_regulator_set_mode,
+ .get_optimum_mode = hi6421_regulator_get_optimum_mode,
+};
+
+static const struct hi6421_regulator hi6421_regulator_ldo = {
+ .rdesc = {
+ .ops = &hi6421_ldo_rops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .dt_parse = hi6421_dt_parse_ldo,
+};
+
+static const struct hi6421_regulator hi6421_regulator_buck012 = {
+ .rdesc = {
+ .ops = &hi6421_buck012_rops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .dt_parse = hi6421_dt_parse_buck012,
+};
+
+static const struct hi6421_regulator hi6421_regulator_buck345 = {
+ .rdesc = {
+ .ops = &hi6421_buck345_rops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .dt_parse = hi6421_dt_parse_ldo,
+};
+
+static struct of_device_id of_hi6421_regulator_match_tbl[] = {
+ {
+ .compatible = "hisilicon,hi6421-ldo",
+ .data = &hi6421_regulator_ldo,
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck012",
+ .data = &hi6421_regulator_buck012,
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck345",
+ .data = &hi6421_regulator_buck345,
+ },
+ { /* end */ }
+};
+
+static int hi6421_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc;
+ struct regulator_dev *rdev;
+ struct hi6421_regulator *sreg = NULL;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_config config = { };
+ const struct of_device_id *match;
+ const struct hi6421_regulator *template = NULL;
+ int ret = 0;
+
+ /* to check which type of regulator this is */
+ match = of_match_device(of_hi6421_regulator_match_tbl, &pdev->dev);
+ if (match)
+ template = match->data;
+ else
+ return -EINVAL;
+
+ initdata = of_get_regulator_init_data(dev, np);
+
+ /* hi6421 regulator supports two modes */
+ c = &initdata->constraints;
+ c->valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+ c->valid_ops_mask |= (REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS);
+ c->input_uV = c->min_uV;
+
+ sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
+ if (sreg == NULL)
+ return -ENOMEM;
+ memcpy(sreg, template, sizeof(*sreg));
+
+ sreg->name = initdata->constraints.name;
+ rdesc = &sreg->rdesc;
+ rdesc->name = sreg->name;
+ rdesc->min_uV = initdata->constraints.min_uV;
+ if (of_get_property(np, "ldo-supply", NULL)) {
+ rdesc->supply_name = "ldo";
+ }
+
+ /* to parse device tree data for regulator specific */
+ ret = sreg->dt_parse(sreg, pdev);
+ if (ret) {
+ dev_err(dev, "device tree parameter parse error!\n");
+ goto hi6421_probe_end;
+ }
+
+ config.dev = &pdev->dev;
+ config.init_data = initdata;
+ config.driver_data = sreg;
+ config.of_node = pdev->dev.of_node;
+
+ /* register regulator */
+ rdev = regulator_register(rdesc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register %s\n",
+ rdesc->name);
+ ret = PTR_ERR(rdev);
+ goto hi6421_probe_end;
+ }
+
+ platform_set_drvdata(pdev, rdev);
+
+hi6421_probe_end:
+ return ret;
+}
+
+static int hi6421_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+}
+
+static struct platform_driver hi6421_regulator_driver = {
+ .driver = {
+ .name = "hi6421_regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = of_hi6421_regulator_match_tbl,
+ },
+ .probe = hi6421_regulator_probe,
+ .remove = hi6421_regulator_remove,
+};
+module_platform_driver(hi6421_regulator_driver);
+
+MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
+MODULE_DESCRIPTION("Hi6421 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c
new file mode 100644
index 000000000000..0b24aae838f8
--- /dev/null
+++ b/drivers/regulator/qcom_rpm-regulator.c
@@ -0,0 +1,787 @@
+/*
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/qcom_rpm.h>
+
+#define MAX_REQUEST_LEN 2
+
+struct request_member {
+ int word;
+ unsigned int mask;
+ int shift;
+};
+
+struct rpm_reg_parts {
+ struct request_member mV; /* used if voltage is in mV */
+ struct request_member uV; /* used if voltage is in uV */
+ struct request_member ip; /* peak current in mA */
+ struct request_member pd; /* pull down enable */
+ struct request_member ia; /* average current in mA */
+ struct request_member fm; /* force mode */
+ struct request_member pm; /* power mode */
+ struct request_member pc; /* pin control */
+ struct request_member pf; /* pin function */
+ struct request_member enable_state; /* NCP and switch */
+ struct request_member comp_mode; /* NCP */
+ struct request_member freq; /* frequency: NCP and SMPS */
+ struct request_member freq_clk_src; /* clock source: SMPS */
+ struct request_member hpm; /* switch: control OCP and SS */
+ int request_len;
+};
+
+#define FORCE_MODE_IS_2_BITS(reg) \
+ ((vreg->parts->fm.mask >> vreg->parts->fm.shift) == 3)
+#define FORCE_MODE_IS_3_BITS(reg) \
+ ((vreg->parts->fm.mask >> vreg->parts->fm.shift) == 7)
+
+struct qcom_rpm_reg {
+ struct qcom_rpm *rpm;
+
+ struct mutex lock;
+ struct device *dev;
+ struct regulator_desc desc;
+ const struct rpm_reg_parts *parts;
+
+ int resource;
+ u32 val[MAX_REQUEST_LEN];
+
+ int uV;
+ int is_enabled;
+};
+
+static const struct rpm_reg_parts rpm8660_ldo_parts = {
+ .request_len = 2,
+ .mV = { 0, 0x00000FFF, 0 },
+ .ip = { 0, 0x00FFF000, 12 },
+ .fm = { 0, 0x03000000, 24 },
+ .pc = { 0, 0x3C000000, 26 },
+ .pf = { 0, 0xC0000000, 30 },
+ .pd = { 1, 0x00000001, 0 },
+ .ia = { 1, 0x00001FFE, 1 },
+};
+
+static const struct rpm_reg_parts rpm8660_smps_parts = {
+ .request_len = 2,
+ .mV = { 0, 0x00000FFF, 0 },
+ .ip = { 0, 0x00FFF000, 12 },
+ .fm = { 0, 0x03000000, 24 },
+ .pc = { 0, 0x3C000000, 26 },
+ .pf = { 0, 0xC0000000, 30 },
+ .pd = { 1, 0x00000001, 0 },
+ .ia = { 1, 0x00001FFE, 1 },
+ .freq = { 1, 0x001FE000, 13 },
+ .freq_clk_src = { 1, 0x00600000, 21 },
+};
+
+static const struct rpm_reg_parts rpm8660_switch_parts = {
+ .request_len = 1,
+ .enable_state = { 0, 0x00000001, 0 },
+ .pd = { 0, 0x00000002, 1 },
+ .pc = { 0, 0x0000003C, 2 },
+ .pf = { 0, 0x000000C0, 6 },
+ .hpm = { 0, 0x00000300, 8 },
+};
+
+static const struct rpm_reg_parts rpm8660_ncp_parts = {
+ .request_len = 1,
+ .mV = { 0, 0x00000FFF, 0 },
+ .enable_state = { 0, 0x00001000, 12 },
+ .comp_mode = { 0, 0x00002000, 13 },
+ .freq = { 0, 0x003FC000, 14 },
+};
+
+static const struct rpm_reg_parts rpm8960_ldo_parts = {
+ .request_len = 2,
+ .uV = { 0, 0x007FFFFF, 0 },
+ .pd = { 0, 0x00800000, 23 },
+ .pc = { 0, 0x0F000000, 24 },
+ .pf = { 0, 0xF0000000, 28 },
+ .ip = { 1, 0x000003FF, 0 },
+ .ia = { 1, 0x000FFC00, 10 },
+ .fm = { 1, 0x00700000, 20 },
+};
+
+static const struct rpm_reg_parts rpm8960_smps_parts = {
+ .request_len = 2,
+ .uV = { 0, 0x007FFFFF, 0 },
+ .pd = { 0, 0x00800000, 23 },
+ .pc = { 0, 0x0F000000, 24 },
+ .pf = { 0, 0xF0000000, 28 },
+ .ip = { 1, 0x000003FF, 0 },
+ .ia = { 1, 0x000FFC00, 10 },
+ .fm = { 1, 0x00700000, 20 },
+ .pm = { 1, 0x00800000, 23 },
+ .freq = { 1, 0x1F000000, 24 },
+ .freq_clk_src = { 1, 0x60000000, 29 },
+};
+
+static const struct rpm_reg_parts rpm8960_switch_parts = {
+ .request_len = 1,
+ .enable_state = { 0, 0x00000001, 0 },
+ .pd = { 0, 0x00000002, 1 },
+ .pc = { 0, 0x0000003C, 2 },
+ .pf = { 0, 0x000003C0, 6 },
+ .hpm = { 0, 0x00000C00, 10 },
+};
+
+static const struct rpm_reg_parts rpm8960_ncp_parts = {
+ .request_len = 1,
+ .uV = { 0, 0x007FFFFF, 0 },
+ .enable_state = { 0, 0x00800000, 23 },
+ .comp_mode = { 0, 0x01000000, 24 },
+ .freq = { 0, 0x3E000000, 25 },
+};
+
+/*
+ * Physically available PMIC regulator voltage ranges
+ */
+static const struct regulator_linear_range pldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 750000, 0, 59, 12500),
+ REGULATOR_LINEAR_RANGE(1500000, 60, 123, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 124, 160, 50000),
+};
+
+static const struct regulator_linear_range nldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500),
+};
+
+static const struct regulator_linear_range nldo1200_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 375000, 0, 59, 6250),
+ REGULATOR_LINEAR_RANGE( 750000, 60, 123, 12500),
+};
+
+static const struct regulator_linear_range smps_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 375000, 0, 29, 12500),
+ REGULATOR_LINEAR_RANGE( 750000, 30, 89, 12500),
+ REGULATOR_LINEAR_RANGE(1500000, 90, 153, 25000),
+};
+
+static const struct regulator_linear_range ftsmps_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 350000, 0, 6, 50000),
+ REGULATOR_LINEAR_RANGE( 700000, 7, 63, 12500),
+ REGULATOR_LINEAR_RANGE(1500000, 64, 100, 50000),
+};
+
+static const struct regulator_linear_range ncp_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1500000, 0, 31, 50000),
+};
+
+static int rpm_reg_write(struct qcom_rpm_reg *vreg,
+ const struct request_member *req,
+ const int value)
+{
+ if (WARN_ON((value << req->shift) & ~req->mask))
+ return -EINVAL;
+
+ vreg->val[req->word] &= ~req->mask;
+ vreg->val[req->word] |= value << req->shift;
+
+ return qcom_rpm_write(vreg->rpm,
+ vreg->resource,
+ vreg->val,
+ vreg->parts->request_len);
+}
+
+static int rpm_reg_set_mV_sel(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->mV;
+ int ret = 0;
+ int uV;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ uV = regulator_list_voltage_linear_range(rdev, selector);
+ if (uV < 0)
+ return uV;
+
+ mutex_lock(&vreg->lock);
+ vreg->uV = uV;
+ if (vreg->is_enabled)
+ ret = rpm_reg_write(vreg, req, vreg->uV / 1000);
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_set_uV_sel(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->uV;
+ int ret = 0;
+ int uV;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ uV = regulator_list_voltage_linear_range(rdev, selector);
+ if (uV < 0)
+ return uV;
+
+ mutex_lock(&vreg->lock);
+ vreg->uV = uV;
+ if (vreg->is_enabled)
+ ret = rpm_reg_write(vreg, req, vreg->uV);
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_get_voltage(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->uV;
+}
+
+static int rpm_reg_mV_enable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->mV;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, vreg->uV / 1000);
+ if (!ret)
+ vreg->is_enabled = 1;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_uV_enable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->uV;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, vreg->uV);
+ if (!ret)
+ vreg->is_enabled = 1;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_switch_enable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->enable_state;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, 1);
+ if (!ret)
+ vreg->is_enabled = 1;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_mV_disable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->mV;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, 0);
+ if (!ret)
+ vreg->is_enabled = 0;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_uV_disable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->uV;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, 0);
+ if (!ret)
+ vreg->is_enabled = 0;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_switch_disable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->enable_state;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, 0);
+ if (!ret)
+ vreg->is_enabled = 0;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_is_enabled(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->is_enabled;
+}
+
+static struct regulator_ops uV_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+
+ .set_voltage_sel = rpm_reg_set_uV_sel,
+ .get_voltage = rpm_reg_get_voltage,
+
+ .enable = rpm_reg_uV_enable,
+ .disable = rpm_reg_uV_disable,
+ .is_enabled = rpm_reg_is_enabled,
+};
+
+static struct regulator_ops mV_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+
+ .set_voltage_sel = rpm_reg_set_mV_sel,
+ .get_voltage = rpm_reg_get_voltage,
+
+ .enable = rpm_reg_mV_enable,
+ .disable = rpm_reg_mV_disable,
+ .is_enabled = rpm_reg_is_enabled,
+};
+
+static struct regulator_ops switch_ops = {
+ .enable = rpm_reg_switch_enable,
+ .disable = rpm_reg_switch_disable,
+ .is_enabled = rpm_reg_is_enabled,
+};
+
+/*
+ * PM8058 regulators
+ */
+static const struct qcom_rpm_reg pm8058_pldo = {
+ .desc.linear_ranges = pldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+ .desc.n_voltages = 161,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ldo_parts,
+};
+
+static const struct qcom_rpm_reg pm8058_nldo = {
+ .desc.linear_ranges = nldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+ .desc.n_voltages = 64,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ldo_parts,
+};
+
+static const struct qcom_rpm_reg pm8058_smps = {
+ .desc.linear_ranges = smps_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
+ .desc.n_voltages = 154,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_smps_parts,
+};
+
+static const struct qcom_rpm_reg pm8058_ncp = {
+ .desc.linear_ranges = ncp_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(ncp_ranges),
+ .desc.n_voltages = 32,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ncp_parts,
+};
+
+static const struct qcom_rpm_reg pm8058_switch = {
+ .desc.ops = &switch_ops,
+ .parts = &rpm8660_switch_parts,
+};
+
+/*
+ * PM8901 regulators
+ */
+static const struct qcom_rpm_reg pm8901_pldo = {
+ .desc.linear_ranges = pldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+ .desc.n_voltages = 161,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ldo_parts,
+};
+
+static const struct qcom_rpm_reg pm8901_nldo = {
+ .desc.linear_ranges = nldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+ .desc.n_voltages = 64,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ldo_parts,
+};
+
+static const struct qcom_rpm_reg pm8901_ftsmps = {
+ .desc.linear_ranges = ftsmps_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(ftsmps_ranges),
+ .desc.n_voltages = 101,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_smps_parts,
+};
+
+static const struct qcom_rpm_reg pm8901_switch = {
+ .desc.ops = &switch_ops,
+ .parts = &rpm8660_switch_parts,
+};
+
+/*
+ * PM8921 regulators
+ */
+static const struct qcom_rpm_reg pm8921_pldo = {
+ .desc.linear_ranges = pldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+ .desc.n_voltages = 161,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ldo_parts,
+};
+
+static const struct qcom_rpm_reg pm8921_nldo = {
+ .desc.linear_ranges = nldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+ .desc.n_voltages = 64,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ldo_parts,
+};
+
+static const struct qcom_rpm_reg pm8921_nldo1200 = {
+ .desc.linear_ranges = nldo1200_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(nldo1200_ranges),
+ .desc.n_voltages = 124,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ldo_parts,
+};
+
+static const struct qcom_rpm_reg pm8921_smps = {
+ .desc.linear_ranges = smps_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
+ .desc.n_voltages = 154,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_smps_parts,
+};
+
+static const struct qcom_rpm_reg pm8921_ftsmps = {
+ .desc.linear_ranges = ftsmps_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(ftsmps_ranges),
+ .desc.n_voltages = 101,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_smps_parts,
+};
+
+static const struct qcom_rpm_reg pm8921_ncp = {
+ .desc.linear_ranges = ncp_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(ncp_ranges),
+ .desc.n_voltages = 32,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ncp_parts,
+};
+
+static const struct qcom_rpm_reg pm8921_switch = {
+ .desc.ops = &switch_ops,
+ .parts = &rpm8960_switch_parts,
+};
+
+static const struct of_device_id rpm_of_match[] = {
+ { .compatible = "qcom,rpm-pm8058-pldo", .data = &pm8058_pldo },
+ { .compatible = "qcom,rpm-pm8058-nldo", .data = &pm8058_nldo },
+ { .compatible = "qcom,rpm-pm8058-smps", .data = &pm8058_smps },
+ { .compatible = "qcom,rpm-pm8058-ncp", .data = &pm8058_ncp },
+ { .compatible = "qcom,rpm-pm8058-switch", .data = &pm8058_switch },
+
+ { .compatible = "qcom,rpm-pm8901-pldo", .data = &pm8901_pldo },
+ { .compatible = "qcom,rpm-pm8901-nldo", .data = &pm8901_nldo },
+ { .compatible = "qcom,rpm-pm8901-ftsmps", .data = &pm8901_ftsmps },
+ { .compatible = "qcom,rpm-pm8901-switch", .data = &pm8901_switch },
+
+ { .compatible = "qcom,rpm-pm8921-pldo", .data = &pm8921_pldo },
+ { .compatible = "qcom,rpm-pm8921-nldo", .data = &pm8921_nldo },
+ { .compatible = "qcom,rpm-pm8921-nldo1200", .data = &pm8921_nldo1200 },
+ { .compatible = "qcom,rpm-pm8921-smps", .data = &pm8921_smps },
+ { .compatible = "qcom,rpm-pm8921-ftsmps", .data = &pm8921_ftsmps },
+ { .compatible = "qcom,rpm-pm8921-ncp", .data = &pm8921_ncp },
+ { .compatible = "qcom,rpm-pm8921-switch", .data = &pm8921_switch },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rpm_of_match);
+
+static int rpm_reg_set(struct qcom_rpm_reg *vreg,
+ const struct request_member *req,
+ const int value)
+{
+ if (req->mask == 0 || (value << req->shift) & ~req->mask)
+ return -EINVAL;
+
+ vreg->val[req->word] &= ~req->mask;
+ vreg->val[req->word] |= value << req->shift;
+
+ return 0;
+}
+
+static int rpm_reg_of_parse_freq(struct device *dev, struct qcom_rpm_reg *vreg)
+{
+ static const int freq_table[] = {
+ 19200000, 9600000, 6400000, 4800000, 3840000, 3200000, 2740000,
+ 2400000, 2130000, 1920000, 1750000, 1600000, 1480000, 1370000,
+ 1280000, 1200000,
+
+ };
+ const char *key;
+ u32 freq;
+ int ret;
+ int i;
+
+ key = "qcom,switch-mode-frequency";
+ ret = of_property_read_u32(dev->of_node, key, &freq);
+ if (ret) {
+ dev_err(dev, "regulator requires %s property\n", key);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ if (freq == freq_table[i]) {
+ rpm_reg_set(vreg, &vreg->parts->freq, i + 1);
+ return 0;
+ }
+ }
+
+ dev_err(dev, "invalid frequency %d\n", freq);
+ return -EINVAL;
+}
+
+static int rpm_reg_of_select_one(struct device *dev,
+ const char * const keys[],
+ const int count, int def)
+{
+ int found = -1;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!of_property_read_bool(dev->of_node, keys[i]))
+ continue;
+
+ if (found >= 0) {
+ dev_err(dev, "%s and %s are mutually exclusive\n",
+ keys[i], keys[found]);
+ return -EINVAL;
+ }
+ found = i;
+ }
+
+ if (found == -1)
+ return def;
+
+ return found;
+}
+
+static int rpm_reg_probe(struct platform_device *pdev)
+{
+ struct regulator_init_data *initdata;
+ const struct qcom_rpm_reg *template;
+ const struct of_device_id *match;
+ struct regulator_config config = { 0 };
+ struct regulator_dev *rdev;
+ struct qcom_rpm_reg *vreg;
+ const char *key;
+ u32 val;
+ int ret;
+
+ match = of_match_device(rpm_of_match, &pdev->dev);
+ template = match->data;
+
+ initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
+ if (!initdata)
+ return -EINVAL;
+
+ vreg = devm_kmalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg) {
+ dev_err(&pdev->dev, "failed to allocate vreg\n");
+ return -ENOMEM;
+ }
+ memcpy(vreg, template, sizeof(*vreg));
+ mutex_init(&vreg->lock);
+ vreg->dev = &pdev->dev;
+ vreg->desc.id = -1;
+ vreg->desc.owner = THIS_MODULE;
+ vreg->desc.type = REGULATOR_VOLTAGE;
+ vreg->desc.name = pdev->dev.of_node->name;
+
+ vreg->rpm = dev_get_qcom_rpm(pdev->dev.parent);
+ if (!vreg->rpm) {
+ dev_err(&pdev->dev, "unable to retrieve handle to rpm\n");
+ return -ENODEV;
+ }
+
+ key = "reg";
+ ret = of_property_read_u32(pdev->dev.of_node, key, &val);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read %s\n", key);
+ return ret;
+ }
+ vreg->resource = val;
+
+ if ((vreg->parts->uV.mask || vreg->parts->mV.mask) &&
+ (!initdata->constraints.min_uV || !initdata->constraints.max_uV)) {
+ dev_err(&pdev->dev, "no voltage specified for regulator\n");
+ return -EINVAL;
+ }
+
+ key = "bias-pull-down";
+ if (of_property_read_bool(pdev->dev.of_node, key)) {
+ ret = rpm_reg_set(vreg, &vreg->parts->pd, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "%s is invalid", key);
+ return ret;
+ }
+ }
+
+ if (vreg->parts->freq.mask) {
+ ret = rpm_reg_of_parse_freq(&pdev->dev, vreg);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (vreg->parts->pm.mask) {
+ static const char * const keys[] = {
+ "qcom,power-mode-hysteretic",
+ "qcom,power-mode-pwm",
+ };
+
+ ret = rpm_reg_of_select_one(&pdev->dev,
+ keys, ARRAY_SIZE(keys), 1);
+ if (ret < 0)
+ return ret;
+
+ ret = rpm_reg_set(vreg, &vreg->parts->pm, ret);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set power mode\n");
+ return ret;
+ }
+ }
+
+ if (FORCE_MODE_IS_2_BITS(vreg)) {
+ static const char * const keys[] = {
+ "qcom,force-mode-none",
+ "qcom,force-mode-lpm",
+ "qcom,force-mode-hpm"
+ };
+
+ ret = rpm_reg_of_select_one(&pdev->dev,
+ keys, ARRAY_SIZE(keys), 0);
+ if (ret < 0)
+ return ret;
+
+ ret = rpm_reg_set(vreg, &vreg->parts->fm, ret);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set force mode\n");
+ return ret;
+ }
+ } else if (FORCE_MODE_IS_3_BITS(vreg)) {
+ static const char * const keys[] = {
+ "qcom,force-mode-none",
+ "qcom,force-mode-lpm",
+ "qcom,force-mode-auto",
+ "qcom,force-mode-hpm",
+ "qcom,force-mode-bypass",
+ };
+
+ ret = rpm_reg_of_select_one(&pdev->dev,
+ keys, ARRAY_SIZE(keys), 0);
+ if (ret < 0)
+ return ret;
+
+ ret = rpm_reg_set(vreg, &vreg->parts->fm, ret);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set force mode\n");
+ return ret;
+ }
+ }
+
+ config.dev = &pdev->dev;
+ config.init_data = initdata;
+ config.driver_data = vreg;
+ config.of_node = pdev->dev.of_node;
+ rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "can't register regulator\n");
+ return PTR_ERR(rdev);
+ }
+
+ return 0;
+}
+
+static struct platform_driver rpm_reg_driver = {
+ .probe = rpm_reg_probe,
+ .driver = {
+ .name = "qcom_rpm_reg",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rpm_of_match),
+ },
+};
+
+static int __init rpm_reg_init(void)
+{
+ return platform_driver_register(&rpm_reg_driver);
+}
+subsys_initcall(rpm_reg_init);
+
+static void __exit rpm_reg_exit(void)
+{
+ platform_driver_unregister(&rpm_reg_driver);
+}
+module_exit(rpm_reg_exit)
+
+MODULE_DESCRIPTION("Qualcomm RPM regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a168e96142b9..519ee1db7dff 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -229,6 +229,17 @@ config RTC_DRV_LP8788
help
Say Y to enable support for the LP8788 RTC/ALARM driver.
+config RTC_DRV_HI6421
+ tristate "Hisilicon Hi6421 RTC"
+ depends on MFD_HI6421_PMIC
+ help
+ If you say yes here you get support for Hisilicon Hi6421 PMIC
+ RTC functions. If an interrupt is associated with the device,
+ the alarm functionality is supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-hi6421.
+
config RTC_DRV_MAX6900
tristate "Maxim MAX6900"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 56f061c7c815..d57cd5c465fb 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
+obj-$(CONFIG_RTC_DRV_HI6421) += rtc-hi6421.o
obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
diff --git a/drivers/rtc/rtc-hi6421.c b/drivers/rtc/rtc-hi6421.c
new file mode 100644
index 000000000000..50ec3911861b
--- /dev/null
+++ b/drivers/rtc/rtc-hi6421.c
@@ -0,0 +1,259 @@
+/*
+ * Hisilicon Hi6421 RTC driver
+ *
+ * Copyright (C) 2013 Hisilicon Ltd.
+ * Copyright (C) 2013 Linaro Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/rtc.h>
+#include <linux/mfd/hi6421-pmic.h>
+
+#define REG_IRQ1 0x01
+#define REG_IRQM1 0x04
+#define REG_RTCDR0 0x58
+#define REG_RTCDR1 0x59
+#define REG_RTCDR2 0x5a
+#define REG_RTCDR3 0x5b
+#define REG_RTCMR0 0x5c
+#define REG_RTCMR1 0x5d
+#define REG_RTCMR2 0x5e
+#define REG_RTCMR3 0x5f
+#define REG_RTCLR0 0x60
+#define REG_RTCLR1 0x61
+#define REG_RTCLR2 0x62
+#define REG_RTCLR3 0x63
+#define REG_RTCCTRL 0x64
+#define REG_SOFT_RST 0x86
+
+#define ALARM_ON (1 << HI6421_IRQ_ALARM)
+
+struct hi6421_rtc_info {
+ struct rtc_device *rtc;
+ struct hi6421_pmic *pmic;
+ int irq;
+};
+
+static irqreturn_t hi6421_rtc_handler(int irq, void *data)
+{
+ struct hi6421_rtc_info *info = (struct hi6421_rtc_info *)data;
+
+ /* clear alarm status */
+ hi6421_pmic_rmw(info->pmic, REG_IRQ1, ALARM_ON, ALARM_ON);
+ rtc_update_irq(info->rtc, 1, RTC_AF);
+ return IRQ_HANDLED;
+}
+
+/* read 4 8-bit registers & covert it into a 32-bit data */
+static unsigned int hi6421_read_bulk(struct hi6421_pmic *pmic,
+ unsigned int addr)
+{
+ unsigned int data, sum = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ data = hi6421_pmic_read(pmic, addr + i);
+ sum |= (data & 0xff) << (i * 8);
+ }
+ return sum;
+}
+
+/* write a 32-bit data into 4 8-bit registers */
+static void hi6421_write_bulk(struct hi6421_pmic *pmic, unsigned int addr,
+ unsigned int data)
+{
+ unsigned int value;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ value = (data >> (i * 8)) & 0xff;
+ hi6421_pmic_write(pmic, addr + i, value);
+ }
+}
+
+static int hi6421_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct hi6421_rtc_info *info = dev_get_drvdata(dev);
+ unsigned long ticks;
+
+ ticks = hi6421_read_bulk(info->pmic, REG_RTCDR0);
+ rtc_time_to_tm(ticks, tm);
+ return 0;
+}
+
+static int hi6421_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct hi6421_rtc_info *info = dev_get_drvdata(dev);
+ unsigned long ticks;
+
+ if ((tm->tm_year < 70) || (tm->tm_year > 138)) {
+ dev_dbg(dev, "Set time %d out of range. "
+ "Please set time between 1970 to 2038.\n",
+ 1900 + tm->tm_year);
+ return -EINVAL;
+ }
+ rtc_tm_to_time(tm, &ticks);
+ hi6421_write_bulk(info->pmic, REG_RTCLR0, ticks);
+ return 0;
+}
+
+static int hi6421_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct hi6421_rtc_info *info = dev_get_drvdata(dev);
+ unsigned long ticks, data;
+
+ ticks = hi6421_read_bulk(info->pmic, REG_RTCMR0);
+ rtc_time_to_tm(ticks, &alrm->time);
+
+ data = hi6421_pmic_read(info->pmic, REG_IRQ1);
+ alrm->pending = (data & ALARM_ON) ? 1 : 0;
+
+ data = hi6421_pmic_read(info->pmic, REG_IRQM1);
+ alrm->enabled = (data & ALARM_ON) ? 0 : 1;
+ return 0;
+}
+
+/*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+ */
+static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
+ struct rtc_time *alrm)
+{
+ unsigned long next_time;
+ unsigned long now_time;
+
+ next->tm_year = now->tm_year;
+ next->tm_mon = now->tm_mon;
+ next->tm_mday = now->tm_mday;
+ next->tm_hour = alrm->tm_hour;
+ next->tm_min = alrm->tm_min;
+ next->tm_sec = alrm->tm_sec;
+
+ rtc_tm_to_time(now, &now_time);
+ rtc_tm_to_time(next, &next_time);
+
+ if (next_time < now_time) {
+ /* Advance one day */
+ next_time += 60 * 60 * 24;
+ rtc_time_to_tm(next_time, next);
+ }
+}
+
+static int hi6421_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct hi6421_rtc_info *info = dev_get_drvdata(dev);
+ struct rtc_time now_tm, alarm_tm;
+ unsigned long ticks;
+
+ /* load 32-bit read-only counter */
+ ticks = hi6421_read_bulk(info->pmic, REG_RTCDR0);
+ rtc_time_to_tm(ticks, &now_tm);
+ dev_dbg(dev, "%s, now time : %lu\n", __func__, ticks);
+ rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time);
+
+ /* get new ticks for alarm in 24 hours */
+ rtc_tm_to_time(&alarm_tm, &ticks);
+ dev_dbg(dev, "%s, alarm time: %lu\n", __func__, ticks);
+ if (alrm->enabled)
+ hi6421_write_bulk(info->pmic, REG_RTCMR0, ticks);
+ return 0;
+}
+
+static int hi6421_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct hi6421_rtc_info *info = dev_get_drvdata(dev);
+ unsigned int data = 0;
+
+ if (!enabled)
+ data = ALARM_ON;
+ hi6421_pmic_rmw(info->pmic, REG_IRQM1, ALARM_ON, data);
+ return 0;
+}
+
+static const struct rtc_class_ops hi6421_rtc_ops = {
+ .read_time = hi6421_rtc_read_time,
+ .set_time = hi6421_rtc_set_time,
+ .read_alarm = hi6421_rtc_read_alarm,
+ .set_alarm = hi6421_rtc_set_alarm,
+ .alarm_irq_enable = hi6421_rtc_alarm_irq_enable,
+};
+
+static int hi6421_rtc_probe(struct platform_device *pdev)
+{
+ struct hi6421_rtc_info *info;
+ int ret;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0)
+ return -ENOENT;
+
+ info->pmic = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, info);
+
+ /* enable RTC device */
+ hi6421_pmic_write(info->pmic, REG_RTCCTRL, 1);
+
+ info->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &hi6421_rtc_ops, THIS_MODULE);
+ if (IS_ERR(info->rtc))
+ return PTR_ERR(info->rtc);
+
+ ret = devm_request_irq(&pdev->dev, info->irq, hi6421_rtc_handler,
+ IRQF_DISABLED, "alarm", info);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int hi6421_rtc_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct of_device_id hi6421_rtc_of_match[] = {
+ { .compatible = "hisilicon,hi6421-rtc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hi6421_rtc_of_match);
+
+static struct platform_driver hi6421_rtc_driver = {
+ .driver = {
+ .name = "hi6421-rtc",
+ .of_match_table = of_match_ptr(hi6421_rtc_of_match),
+ },
+ .probe = hi6421_rtc_probe,
+ .remove = hi6421_rtc_remove,
+};
+module_platform_driver(hi6421_rtc_driver);
+
+MODULE_LICENSE("GPLv2");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@linaro.org");
+MODULE_ALIAS("platform:hi6421-rtc");
diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c
index 447458e696a9..7e1f120f2b32 100644
--- a/drivers/soc/qcom/qcom_gsbi.c
+++ b/drivers/soc/qcom/qcom_gsbi.c
@@ -22,44 +22,63 @@
#define GSBI_CTRL_REG 0x0000
#define GSBI_PROTOCOL_SHIFT 4
+struct gsbi_info {
+ struct clk *hclk;
+ u32 mode;
+ u32 crci;
+};
+
static int gsbi_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct resource *res;
void __iomem *base;
- struct clk *hclk;
- u32 mode, crci = 0;
+ struct gsbi_info *gsbi;
+
+ gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
+
+ if (!gsbi)
+ return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
- if (of_property_read_u32(node, "qcom,mode", &mode)) {
+ if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
dev_err(&pdev->dev, "missing mode configuration\n");
return -EINVAL;
}
/* not required, so default to 0 if not present */
- of_property_read_u32(node, "qcom,crci", &crci);
+ of_property_read_u32(node, "qcom,crci", &gsbi->crci);
- dev_info(&pdev->dev, "GSBI port protocol: %d crci: %d\n", mode, crci);
+ dev_info(&pdev->dev, "GSBI port protocol: %d crci: %d\n",
+ gsbi->mode, gsbi->crci);
+ gsbi->hclk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(gsbi->hclk))
+ return PTR_ERR(gsbi->hclk);
- hclk = devm_clk_get(&pdev->dev, "iface");
- if (IS_ERR(hclk))
- return PTR_ERR(hclk);
+ clk_prepare_enable(gsbi->hclk);
- clk_prepare_enable(hclk);
-
- writel_relaxed((mode << GSBI_PROTOCOL_SHIFT) | crci,
+ writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
base + GSBI_CTRL_REG);
/* make sure the gsbi control write is not reordered */
wmb();
- clk_disable_unprepare(hclk);
+ platform_set_drvdata(pdev, gsbi);
+
+ return of_platform_populate(node, NULL, NULL, &pdev->dev);
+}
+
+static int gsbi_remove(struct platform_device *pdev)
+{
+ struct gsbi_info *gsbi = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(gsbi->hclk);
- return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ return 0;
}
static const struct of_device_id gsbi_dt_match[] = {
@@ -76,6 +95,7 @@ static struct platform_driver gsbi_driver = {
.of_match_table = gsbi_dt_match,
},
.probe = gsbi_probe,
+ .remove = gsbi_remove,
};
module_platform_driver(gsbi_driver);
diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c
index 40c3d43c9292..f40b34cdf2fc 100644
--- a/drivers/spi/spi-au1550.c
+++ b/drivers/spi/spi-au1550.c
@@ -945,7 +945,7 @@ static int au1550_spi_remove(struct platform_device *pdev)
spi_bitbang_stop(&hw->bitbang);
free_irq(hw->irq, hw);
iounmap((void __iomem *)hw->regs);
- release_mem_region(r->start, sizeof(psc_spi_t));
+ release_mem_region(hw->ioarea->start, sizeof(psc_spi_t));
if (hw->usedma) {
au1550_spi_dma_rxtmp_free(hw);
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 276a3884fb3c..48f1d26e6ad9 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -417,16 +417,16 @@ static int davinci_spi_setup(struct spi_device *spi)
flags, dev_name(&spi->dev));
internal_cs = false;
}
- }
- if (retval) {
- dev_err(&spi->dev, "GPIO %d setup failed (%d)\n",
- spi->cs_gpio, retval);
- return retval;
- }
+ if (retval) {
+ dev_err(&spi->dev, "GPIO %d setup failed (%d)\n",
+ spi->cs_gpio, retval);
+ return retval;
+ }
- if (internal_cs)
- set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select);
+ if (internal_cs)
+ set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select);
+ }
if (spi->mode & SPI_READY)
set_io_bits(dspi->base + SPIPC0, SPIPC0_SPIENA_MASK);
diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c
index 3f3dc1226edf..e14960470d8d 100644
--- a/drivers/spi/spi-dw-pci.c
+++ b/drivers/spi/spi-dw-pci.c
@@ -62,6 +62,8 @@ static int spi_pci_probe(struct pci_dev *pdev,
if (ret)
return ret;
+ dws->regs = pcim_iomap_table(pdev)[pci_bar];
+
dws->bus_num = 0;
dws->num_cs = 4;
dws->irq = pdev->irq;
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 29f33143b795..670f0627f3bf 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -271,7 +271,7 @@ static void giveback(struct dw_spi *dws)
transfer_list);
if (!last_transfer->cs_change)
- spi_chip_sel(dws, dws->cur_msg->spi, 0);
+ spi_chip_sel(dws, msg->spi, 0);
spi_finalize_current_message(dws->master);
}
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 68441fa448de..352eed7463ac 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -329,7 +329,8 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi,
disable_fifo:
if (t->rx_buf != NULL)
chconf &= ~OMAP2_MCSPI_CHCONF_FFER;
- else
+
+ if (t->tx_buf != NULL)
chconf &= ~OMAP2_MCSPI_CHCONF_FFET;
mcspi_write_chconf0(spi, chconf);
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index fe792106bdc5..46f45ca2c694 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1074,6 +1074,7 @@ static struct acpi_device_id pxa2xx_spi_acpi_match[] = {
{ "INT3430", 0 },
{ "INT3431", 0 },
{ "80860F0E", 0 },
+ { "8086228E", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index c0743604b906..cd0e08b0c9f6 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -499,7 +499,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
}
/* div doesn't support odd number */
- div = rs->max_freq / rs->speed;
+ div = max_t(u32, rs->max_freq / rs->speed, 1);
div = (div + 1) & 0xfffe;
spi_enable_chip(rs, 0);
@@ -678,7 +678,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);
rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);
rs->dma_tx.direction = DMA_MEM_TO_DEV;
- rs->dma_tx.direction = DMA_DEV_TO_MEM;
+ rs->dma_rx.direction = DMA_DEV_TO_MEM;
master->can_dma = rockchip_spi_can_dma;
master->dma_tx = rs->dma_tx.ch;
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index c850dfdfa9e3..ad87a98f8f68 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -472,25 +472,52 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,
dma_cookie_t cookie;
int ret;
- if (tx) {
- desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx,
- tx->sgl, tx->nents, DMA_TO_DEVICE,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc_tx)
- goto no_dma;
-
- irq_mask |= SPCR_SPTIE;
- }
+ /* First prepare and submit the DMA request(s), as this may fail */
if (rx) {
desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx,
rx->sgl, rx->nents, DMA_FROM_DEVICE,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc_rx)
- goto no_dma;
+ if (!desc_rx) {
+ ret = -EAGAIN;
+ goto no_dma_rx;
+ }
+
+ desc_rx->callback = rspi_dma_complete;
+ desc_rx->callback_param = rspi;
+ cookie = dmaengine_submit(desc_rx);
+ if (dma_submit_error(cookie)) {
+ ret = cookie;
+ goto no_dma_rx;
+ }
irq_mask |= SPCR_SPRIE;
}
+ if (tx) {
+ desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx,
+ tx->sgl, tx->nents, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_tx) {
+ ret = -EAGAIN;
+ goto no_dma_tx;
+ }
+
+ if (rx) {
+ /* No callback */
+ desc_tx->callback = NULL;
+ } else {
+ desc_tx->callback = rspi_dma_complete;
+ desc_tx->callback_param = rspi;
+ }
+ cookie = dmaengine_submit(desc_tx);
+ if (dma_submit_error(cookie)) {
+ ret = cookie;
+ goto no_dma_tx;
+ }
+
+ irq_mask |= SPCR_SPTIE;
+ }
+
/*
* DMAC needs SPxIE, but if SPxIE is set, the IRQ routine will be
* called. So, this driver disables the IRQ while DMA transfer.
@@ -503,34 +530,24 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,
rspi_enable_irq(rspi, irq_mask);
rspi->dma_callbacked = 0;
- if (rx) {
- desc_rx->callback = rspi_dma_complete;
- desc_rx->callback_param = rspi;
- cookie = dmaengine_submit(desc_rx);
- if (dma_submit_error(cookie))
- return cookie;
+ /* Now start DMA */
+ if (rx)
dma_async_issue_pending(rspi->master->dma_rx);
- }
- if (tx) {
- if (rx) {
- /* No callback */
- desc_tx->callback = NULL;
- } else {
- desc_tx->callback = rspi_dma_complete;
- desc_tx->callback_param = rspi;
- }
- cookie = dmaengine_submit(desc_tx);
- if (dma_submit_error(cookie))
- return cookie;
+ if (tx)
dma_async_issue_pending(rspi->master->dma_tx);
- }
ret = wait_event_interruptible_timeout(rspi->wait,
rspi->dma_callbacked, HZ);
if (ret > 0 && rspi->dma_callbacked)
ret = 0;
- else if (!ret)
+ else if (!ret) {
+ dev_err(&rspi->master->dev, "DMA timeout\n");
ret = -ETIMEDOUT;
+ if (tx)
+ dmaengine_terminate_all(rspi->master->dma_tx);
+ if (rx)
+ dmaengine_terminate_all(rspi->master->dma_rx);
+ }
rspi_disable_irq(rspi, irq_mask);
@@ -541,11 +558,16 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,
return ret;
-no_dma:
- pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
- dev_driver_string(&rspi->master->dev),
- dev_name(&rspi->master->dev));
- return -EAGAIN;
+no_dma_tx:
+ if (rx)
+ dmaengine_terminate_all(rspi->master->dma_rx);
+no_dma_rx:
+ if (ret == -EAGAIN) {
+ pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
+ dev_driver_string(&rspi->master->dev),
+ dev_name(&rspi->master->dev));
+ }
+ return ret;
}
static void rspi_receive_init(const struct rspi_data *rspi)
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 2a4354dcd661..543075b80f16 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -636,48 +636,38 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
dma_cookie_t cookie;
int ret;
- if (tx) {
- ier_bits |= IER_TDREQE | IER_TDMAE;
- dma_sync_single_for_device(p->master->dma_tx->device->dev,
- p->tx_dma_addr, len, DMA_TO_DEVICE);
- desc_tx = dmaengine_prep_slave_single(p->master->dma_tx,
- p->tx_dma_addr, len, DMA_TO_DEVICE,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc_tx)
- return -EAGAIN;
- }
-
+ /* First prepare and submit the DMA request(s), as this may fail */
if (rx) {
ier_bits |= IER_RDREQE | IER_RDMAE;
desc_rx = dmaengine_prep_slave_single(p->master->dma_rx,
p->rx_dma_addr, len, DMA_FROM_DEVICE,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc_rx)
- return -EAGAIN;
- }
-
- /* 1 stage FIFO watermarks for DMA */
- sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1);
-
- /* setup msiof transfer mode registers (32-bit words) */
- sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4);
-
- sh_msiof_write(p, IER, ier_bits);
-
- reinit_completion(&p->done);
+ if (!desc_rx) {
+ ret = -EAGAIN;
+ goto no_dma_rx;
+ }
- if (rx) {
desc_rx->callback = sh_msiof_dma_complete;
desc_rx->callback_param = p;
cookie = dmaengine_submit(desc_rx);
if (dma_submit_error(cookie)) {
ret = cookie;
- goto stop_ier;
+ goto no_dma_rx;
}
- dma_async_issue_pending(p->master->dma_rx);
}
if (tx) {
+ ier_bits |= IER_TDREQE | IER_TDMAE;
+ dma_sync_single_for_device(p->master->dma_tx->device->dev,
+ p->tx_dma_addr, len, DMA_TO_DEVICE);
+ desc_tx = dmaengine_prep_slave_single(p->master->dma_tx,
+ p->tx_dma_addr, len, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_tx) {
+ ret = -EAGAIN;
+ goto no_dma_tx;
+ }
+
if (rx) {
/* No callback */
desc_tx->callback = NULL;
@@ -688,15 +678,30 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
cookie = dmaengine_submit(desc_tx);
if (dma_submit_error(cookie)) {
ret = cookie;
- goto stop_rx;
+ goto no_dma_tx;
}
- dma_async_issue_pending(p->master->dma_tx);
}
+ /* 1 stage FIFO watermarks for DMA */
+ sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1);
+
+ /* setup msiof transfer mode registers (32-bit words) */
+ sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4);
+
+ sh_msiof_write(p, IER, ier_bits);
+
+ reinit_completion(&p->done);
+
+ /* Now start DMA */
+ if (rx)
+ dma_async_issue_pending(p->master->dma_rx);
+ if (tx)
+ dma_async_issue_pending(p->master->dma_tx);
+
ret = sh_msiof_spi_start(p, rx);
if (ret) {
dev_err(&p->pdev->dev, "failed to start hardware\n");
- goto stop_tx;
+ goto stop_dma;
}
/* wait for tx fifo to be emptied / rx fifo to be filled */
@@ -726,14 +731,14 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
stop_reset:
sh_msiof_reset_str(p);
sh_msiof_spi_stop(p, rx);
-stop_tx:
+stop_dma:
if (tx)
dmaengine_terminate_all(p->master->dma_tx);
-stop_rx:
+no_dma_tx:
if (rx)
dmaengine_terminate_all(p->master->dma_rx);
-stop_ier:
sh_msiof_write(p, IER, 0);
+no_dma_rx:
return ret;
}
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index e0531baf2782..ca935df80c88 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -848,6 +848,7 @@ out:
/**
* spi_finalize_current_transfer - report completion of a transfer
+ * @master: the master reporting completion
*
* Called by SPI drivers using the core transfer_one_message()
* implementation to notify it that the current interrupt driven
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index 7a0e28852965..e7fc907cdce6 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -75,6 +75,15 @@ config ANDROID_LOW_MEMORY_KILLER
---help---
Registers processes to be killed when memory is low
+config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
+ bool "Android Low Memory Killer: detect oom_adj values"
+ depends on ANDROID_LOW_MEMORY_KILLER
+ default y
+ ---help---
+ Detect oom_adj values written to
+ /sys/module/lowmemorykiller/parameters/adj and convert them
+ to oom_score_adj values.
+
config ANDROID_INTF_ALARM_DEV
tristate "Android alarm driver"
depends on RTC_CLASS
diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO
deleted file mode 100644
index b15fb0d6b152..000000000000
--- a/drivers/staging/android/TODO
+++ /dev/null
@@ -1,10 +0,0 @@
-TODO:
- - checkpatch.pl cleanups
- - sparse fixes
- - rename files to be not so "generic"
- - make sure things build as modules properly
- - add proper arch dependencies as needed
- - audit userspace interfaces to make sure they are sane
-
-Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
-Brian Swetland <swetland@google.com>
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 713a97226787..5a26e7c5ab5e 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -396,22 +396,14 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
}
get_file(asma->file);
- /*
- * XXX - Reworked to use shmem_zero_setup() instead of
- * shmem_set_file while we're in staging. -jstultz
- */
- if (vma->vm_flags & VM_SHARED) {
- ret = shmem_zero_setup(vma);
- if (ret) {
- fput(asma->file);
- goto out;
- }
+ if (vma->vm_flags & VM_SHARED)
+ shmem_set_file(vma, asma->file);
+ else {
+ if (vma->vm_file)
+ fput(vma->vm_file);
+ vma->vm_file = asma->file;
}
- if (vma->vm_file)
- fput(vma->vm_file);
- vma->vm_file = asma->file;
-
out:
mutex_unlock(&ashmem_mutex);
return ret;
diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c
index 4f34dc0095b5..0a8cca6fe5be 100644
--- a/drivers/staging/android/binder.c
+++ b/drivers/staging/android/binder.c
@@ -37,6 +37,7 @@
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/pid_namespace.h>
+#include <linux/security.h>
#include "binder.h"
#include "binder_trace.h"
@@ -1315,6 +1316,7 @@ static void binder_transaction(struct binder_proc *proc,
struct binder_transaction *t;
struct binder_work *tcomplete;
binder_size_t *offp, *off_end;
+ binder_size_t off_min;
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
@@ -1395,6 +1397,10 @@ static void binder_transaction(struct binder_proc *proc,
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
+ if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) {
+ return_error = BR_FAILED_REPLY;
+ goto err_invalid_target_handle;
+ }
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
@@ -1511,18 +1517,24 @@ static void binder_transaction(struct binder_proc *proc,
goto err_bad_offset;
}
off_end = (void *)offp + tr->offsets_size;
+ off_min = 0;
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
if (*offp > t->buffer->data_size - sizeof(*fp) ||
+ *offp < off_min ||
t->buffer->data_size < sizeof(*fp) ||
!IS_ALIGNED(*offp, sizeof(u32))) {
- binder_user_error("%d:%d got transaction with invalid offset, %lld\n",
- proc->pid, thread->pid, (u64)*offp);
+ binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n",
+ proc->pid, thread->pid, (u64)*offp,
+ (u64)off_min,
+ (u64)(t->buffer->data_size -
+ sizeof(*fp)));
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
+ off_min = *offp + sizeof(struct flat_binder_object);
switch (fp->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
@@ -1546,6 +1558,10 @@ static void binder_transaction(struct binder_proc *proc,
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
+ return_error = BR_FAILED_REPLY;
+ goto err_binder_get_ref_for_node_failed;
+ }
ref = binder_get_ref_for_node(target_proc, node);
if (ref == NULL) {
return_error = BR_FAILED_REPLY;
@@ -1576,6 +1592,10 @@ static void binder_transaction(struct binder_proc *proc,
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_failed;
}
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
+ return_error = BR_FAILED_REPLY;
+ goto err_binder_get_ref_failed;
+ }
if (ref->node->proc == target_proc) {
if (fp->type == BINDER_TYPE_HANDLE)
fp->type = BINDER_TYPE_BINDER;
@@ -1633,6 +1653,11 @@ static void binder_transaction(struct binder_proc *proc,
return_error = BR_FAILED_REPLY;
goto err_fget_failed;
}
+ if (security_binder_transfer_file(proc->tsk, target_proc->tsk, file) < 0) {
+ fput(file);
+ return_error = BR_FAILED_REPLY;
+ goto err_get_unused_fd_failed;
+ }
target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
if (target_fd < 0) {
fput(file);
@@ -2666,6 +2691,9 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
ret = -EBUSY;
goto out;
}
+ ret = security_binder_set_context_mgr(proc->tsk);
+ if (ret < 0)
+ goto out;
if (uid_valid(binder_context_mgr_uid)) {
if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h
index c8f01757abfa..cb4e01c55d40 100644
--- a/drivers/staging/android/ion/ion_priv.h
+++ b/drivers/staging/android/ion/ion_priv.h
@@ -26,6 +26,7 @@
#include <linux/sched.h>
#include <linux/shrinker.h>
#include <linux/types.h>
+#include <linux/device.h>
#include "ion.h"
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index b545d3d1da3e..331a9d8fc148 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -39,7 +39,6 @@
#include <linux/sched.h>
#include <linux/swap.h>
#include <linux/rcupdate.h>
-#include <linux/profile.h>
#include <linux/notifier.h>
static uint32_t lowmem_debug_level = 1;
@@ -83,6 +82,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
int tasksize;
int i;
short min_score_adj = OOM_SCORE_ADJ_MAX + 1;
+ int minfree = 0;
int selected_tasksize = 0;
short selected_oom_score_adj;
int array_size = ARRAY_SIZE(lowmem_adj);
@@ -96,8 +96,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
if (lowmem_minfree_size < array_size)
array_size = lowmem_minfree_size;
for (i = 0; i < array_size; i++) {
- if (other_free < lowmem_minfree[i] &&
- other_file < lowmem_minfree[i]) {
+ minfree = lowmem_minfree[i];
+ if (other_free < minfree && other_file < minfree) {
min_score_adj = lowmem_adj[i];
break;
}
@@ -152,13 +152,22 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
selected = p;
selected_tasksize = tasksize;
selected_oom_score_adj = oom_score_adj;
- lowmem_print(2, "select %d (%s), adj %hd, size %d, to kill\n",
- p->pid, p->comm, oom_score_adj, tasksize);
+ lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n",
+ p->comm, p->pid, oom_score_adj, tasksize);
}
if (selected) {
- lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n",
- selected->pid, selected->comm,
- selected_oom_score_adj, selected_tasksize);
+ lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \
+ " to free %ldkB on behalf of '%s' (%d) because\n" \
+ " cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \
+ " Free memory is %ldkB above reserved\n",
+ selected->comm, selected->pid,
+ selected_oom_score_adj,
+ selected_tasksize * (long)(PAGE_SIZE / 1024),
+ current->comm, current->pid,
+ other_file * (long)(PAGE_SIZE / 1024),
+ minfree * (long)(PAGE_SIZE / 1024),
+ min_score_adj,
+ other_free * (long)(PAGE_SIZE / 1024));
lowmem_deathpending_timeout = jiffies + HZ;
set_tsk_thread_flag(selected, TIF_MEMDIE);
send_sig(SIGKILL, selected, 0);
@@ -188,9 +197,94 @@ static void __exit lowmem_exit(void)
unregister_shrinker(&lowmem_shrinker);
}
+#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
+static short lowmem_oom_adj_to_oom_score_adj(short oom_adj)
+{
+ if (oom_adj == OOM_ADJUST_MAX)
+ return OOM_SCORE_ADJ_MAX;
+ else
+ return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
+}
+
+static void lowmem_autodetect_oom_adj_values(void)
+{
+ int i;
+ short oom_adj;
+ short oom_score_adj;
+ int array_size = ARRAY_SIZE(lowmem_adj);
+
+ if (lowmem_adj_size < array_size)
+ array_size = lowmem_adj_size;
+
+ if (array_size <= 0)
+ return;
+
+ oom_adj = lowmem_adj[array_size - 1];
+ if (oom_adj > OOM_ADJUST_MAX)
+ return;
+
+ oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);
+ if (oom_score_adj <= OOM_ADJUST_MAX)
+ return;
+
+ lowmem_print(1, "lowmem_shrink: convert oom_adj to oom_score_adj:\n");
+ for (i = 0; i < array_size; i++) {
+ oom_adj = lowmem_adj[i];
+ oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);
+ lowmem_adj[i] = oom_score_adj;
+ lowmem_print(1, "oom_adj %d => oom_score_adj %d\n",
+ oom_adj, oom_score_adj);
+ }
+}
+
+static int lowmem_adj_array_set(const char *val, const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_array_ops.set(val, kp);
+
+ /* HACK: Autodetect oom_adj values in lowmem_adj array */
+ lowmem_autodetect_oom_adj_values();
+
+ return ret;
+}
+
+static int lowmem_adj_array_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_array_ops.get(buffer, kp);
+}
+
+static void lowmem_adj_array_free(void *arg)
+{
+ param_array_ops.free(arg);
+}
+
+static struct kernel_param_ops lowmem_adj_array_ops = {
+ .set = lowmem_adj_array_set,
+ .get = lowmem_adj_array_get,
+ .free = lowmem_adj_array_free,
+};
+
+static const struct kparam_array __param_arr_adj = {
+ .max = ARRAY_SIZE(lowmem_adj),
+ .num = &lowmem_adj_size,
+ .ops = &param_ops_short,
+ .elemsize = sizeof(lowmem_adj[0]),
+ .elem = lowmem_adj,
+};
+#endif
+
module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
+#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
+__module_param_call(MODULE_PARAM_PREFIX, adj,
+ &lowmem_adj_array_ops,
+ .arr = &__param_arr_adj,
+ S_IRUGO | S_IWUSR, -1);
+__MODULE_PARM_TYPE(adj, "array of short");
+#else
module_param_array_named(adj, lowmem_adj, short, &lowmem_adj_size,
S_IRUGO | S_IWUSR);
+#endif
module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size,
S_IRUGO | S_IWUSR);
module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
diff --git a/drivers/switch/Kconfig b/drivers/switch/Kconfig
new file mode 100644
index 000000000000..19404b6f7778
--- /dev/null
+++ b/drivers/switch/Kconfig
@@ -0,0 +1,15 @@
+menuconfig SWITCH
+ tristate "Switch class support"
+ help
+ Say Y here to enable switch class support. This allows
+ monitoring switches by userspace via sysfs and uevent.
+
+if SWITCH
+
+config SWITCH_GPIO
+ tristate "GPIO Swith support"
+ depends on GPIOLIB
+ help
+ Say Y here to enable GPIO based switch support.
+
+endif # SWITCH
diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile
new file mode 100644
index 000000000000..f7606ed4a719
--- /dev/null
+++ b/drivers/switch/Makefile
@@ -0,0 +1,4 @@
+# Switch Class Driver
+obj-$(CONFIG_SWITCH) += switch_class.o
+obj-$(CONFIG_SWITCH_GPIO) += switch_gpio.o
+
diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c
new file mode 100644
index 000000000000..e373b625806e
--- /dev/null
+++ b/drivers/switch/switch_class.c
@@ -0,0 +1,174 @@
+/*
+ * drivers/switch/switch_class.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/switch.h>
+
+struct class *switch_class;
+static atomic_t device_count;
+
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct switch_dev *sdev = (struct switch_dev *)
+ dev_get_drvdata(dev);
+
+ if (sdev->print_state) {
+ int ret = sdev->print_state(sdev, buf);
+ if (ret >= 0)
+ return ret;
+ }
+ return sprintf(buf, "%d\n", sdev->state);
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct switch_dev *sdev = (struct switch_dev *)
+ dev_get_drvdata(dev);
+
+ if (sdev->print_name) {
+ int ret = sdev->print_name(sdev, buf);
+ if (ret >= 0)
+ return ret;
+ }
+ return sprintf(buf, "%s\n", sdev->name);
+}
+
+static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
+static DEVICE_ATTR(name, S_IRUGO, name_show, NULL);
+
+void switch_set_state(struct switch_dev *sdev, int state)
+{
+ char name_buf[120];
+ char state_buf[120];
+ char *prop_buf;
+ char *envp[3];
+ int env_offset = 0;
+ int length;
+
+ if (sdev->state != state) {
+ sdev->state = state;
+
+ prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+ if (prop_buf) {
+ length = name_show(sdev->dev, NULL, prop_buf);
+ if (length > 0) {
+ if (prop_buf[length - 1] == '\n')
+ prop_buf[length - 1] = 0;
+ snprintf(name_buf, sizeof(name_buf),
+ "SWITCH_NAME=%s", prop_buf);
+ envp[env_offset++] = name_buf;
+ }
+ length = state_show(sdev->dev, NULL, prop_buf);
+ if (length > 0) {
+ if (prop_buf[length - 1] == '\n')
+ prop_buf[length - 1] = 0;
+ snprintf(state_buf, sizeof(state_buf),
+ "SWITCH_STATE=%s", prop_buf);
+ envp[env_offset++] = state_buf;
+ }
+ envp[env_offset] = NULL;
+ kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
+ free_page((unsigned long)prop_buf);
+ } else {
+ printk(KERN_ERR "out of memory in switch_set_state\n");
+ kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(switch_set_state);
+
+static int create_switch_class(void)
+{
+ if (!switch_class) {
+ switch_class = class_create(THIS_MODULE, "switch");
+ if (IS_ERR(switch_class))
+ return PTR_ERR(switch_class);
+ atomic_set(&device_count, 0);
+ }
+
+ return 0;
+}
+
+int switch_dev_register(struct switch_dev *sdev)
+{
+ int ret;
+
+ if (!switch_class) {
+ ret = create_switch_class();
+ if (ret < 0)
+ return ret;
+ }
+
+ sdev->index = atomic_inc_return(&device_count);
+ sdev->dev = device_create(switch_class, NULL,
+ MKDEV(0, sdev->index), NULL, sdev->name);
+ if (IS_ERR(sdev->dev))
+ return PTR_ERR(sdev->dev);
+
+ ret = device_create_file(sdev->dev, &dev_attr_state);
+ if (ret < 0)
+ goto err_create_file_1;
+ ret = device_create_file(sdev->dev, &dev_attr_name);
+ if (ret < 0)
+ goto err_create_file_2;
+
+ dev_set_drvdata(sdev->dev, sdev);
+ sdev->state = 0;
+ return 0;
+
+err_create_file_2:
+ device_remove_file(sdev->dev, &dev_attr_state);
+err_create_file_1:
+ device_destroy(switch_class, MKDEV(0, sdev->index));
+ printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(switch_dev_register);
+
+void switch_dev_unregister(struct switch_dev *sdev)
+{
+ device_remove_file(sdev->dev, &dev_attr_name);
+ device_remove_file(sdev->dev, &dev_attr_state);
+ device_destroy(switch_class, MKDEV(0, sdev->index));
+ dev_set_drvdata(sdev->dev, NULL);
+}
+EXPORT_SYMBOL_GPL(switch_dev_unregister);
+
+static int __init switch_class_init(void)
+{
+ return create_switch_class();
+}
+
+static void __exit switch_class_exit(void)
+{
+ class_destroy(switch_class);
+}
+
+module_init(switch_class_init);
+module_exit(switch_class_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("Switch class driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/switch/switch_gpio.c b/drivers/switch/switch_gpio.c
new file mode 100644
index 000000000000..621d62d20c99
--- /dev/null
+++ b/drivers/switch/switch_gpio.c
@@ -0,0 +1,172 @@
+/*
+ * drivers/switch/switch_gpio.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/switch.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+
+struct gpio_switch_data {
+ struct switch_dev sdev;
+ unsigned gpio;
+ const char *name_on;
+ const char *name_off;
+ const char *state_on;
+ const char *state_off;
+ int irq;
+ struct work_struct work;
+};
+
+static void gpio_switch_work(struct work_struct *work)
+{
+ int state;
+ struct gpio_switch_data *data =
+ container_of(work, struct gpio_switch_data, work);
+
+ state = gpio_get_value(data->gpio);
+ switch_set_state(&data->sdev, state);
+}
+
+static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_switch_data *switch_data =
+ (struct gpio_switch_data *)dev_id;
+
+ schedule_work(&switch_data->work);
+ return IRQ_HANDLED;
+}
+
+static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf)
+{
+ struct gpio_switch_data *switch_data =
+ container_of(sdev, struct gpio_switch_data, sdev);
+ const char *state;
+ if (switch_get_state(sdev))
+ state = switch_data->state_on;
+ else
+ state = switch_data->state_off;
+
+ if (state)
+ return sprintf(buf, "%s\n", state);
+ return -1;
+}
+
+static int gpio_switch_probe(struct platform_device *pdev)
+{
+ struct gpio_switch_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_switch_data *switch_data;
+ int ret = 0;
+
+ if (!pdata)
+ return -EBUSY;
+
+ switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL);
+ if (!switch_data)
+ return -ENOMEM;
+
+ switch_data->sdev.name = pdata->name;
+ switch_data->gpio = pdata->gpio;
+ switch_data->name_on = pdata->name_on;
+ switch_data->name_off = pdata->name_off;
+ switch_data->state_on = pdata->state_on;
+ switch_data->state_off = pdata->state_off;
+ switch_data->sdev.print_state = switch_gpio_print_state;
+
+ ret = switch_dev_register(&switch_data->sdev);
+ if (ret < 0)
+ goto err_switch_dev_register;
+
+ ret = gpio_request(switch_data->gpio, pdev->name);
+ if (ret < 0)
+ goto err_request_gpio;
+
+ ret = gpio_direction_input(switch_data->gpio);
+ if (ret < 0)
+ goto err_set_gpio_input;
+
+ INIT_WORK(&switch_data->work, gpio_switch_work);
+
+ switch_data->irq = gpio_to_irq(switch_data->gpio);
+ if (switch_data->irq < 0) {
+ ret = switch_data->irq;
+ goto err_detect_irq_num_failed;
+ }
+
+ ret = request_irq(switch_data->irq, gpio_irq_handler,
+ IRQF_TRIGGER_LOW, pdev->name, switch_data);
+ if (ret < 0)
+ goto err_request_irq;
+
+ /* Perform initial detection */
+ gpio_switch_work(&switch_data->work);
+
+ return 0;
+
+err_request_irq:
+err_detect_irq_num_failed:
+err_set_gpio_input:
+ gpio_free(switch_data->gpio);
+err_request_gpio:
+ switch_dev_unregister(&switch_data->sdev);
+err_switch_dev_register:
+ kfree(switch_data);
+
+ return ret;
+}
+
+static int gpio_switch_remove(struct platform_device *pdev)
+{
+ struct gpio_switch_data *switch_data = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&switch_data->work);
+ gpio_free(switch_data->gpio);
+ switch_dev_unregister(&switch_data->sdev);
+ kfree(switch_data);
+
+ return 0;
+}
+
+static struct platform_driver gpio_switch_driver = {
+ .probe = gpio_switch_probe,
+ .remove = gpio_switch_remove,
+ .driver = {
+ .name = "switch-gpio",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gpio_switch_init(void)
+{
+ return platform_driver_register(&gpio_switch_driver);
+}
+
+static void __exit gpio_switch_exit(void)
+{
+ platform_driver_unregister(&gpio_switch_driver);
+}
+
+module_init(gpio_switch_init);
+module_exit(gpio_switch_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("GPIO Switch driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c
index acbff14da3a4..e28f3dbdc9a6 100644
--- a/drivers/thermal/samsung/exynos_tmu.c
+++ b/drivers/thermal/samsung/exynos_tmu.c
@@ -165,7 +165,7 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
}
if (TMU_SUPPORTS(pdata, TRIM_RELOAD))
- __raw_writel(1, data->base + reg->triminfo_ctrl);
+ writel_relaxed(1, data->base + reg->triminfo_ctrl);
if (pdata->cal_mode == HW_MODE)
goto skip_calib_data;
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 4db7987ec225..05a9c72a9810 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -214,6 +214,43 @@ out:
serial8250_do_set_termios(p, termios, old);
}
+/*
+ * If our clock was configured through the common clock framework we
+ * might be able to support a wider range of baud rates by changing
+ * its frequency. For the "traditional" baud rates (115200 or
+ * less), request the conventional 1.8432 MHz clock. The clock
+ * frequency needs to be at least 16 times the baud rate, so
+ * use 16 times the requested rate for those higher than 115200.
+ */
+static void
+dw8250_set_termios_qcom(struct uart_port *p, struct ktermios *new,
+ struct ktermios *old)
+{
+ struct dw8250_data *data = p->private_data;
+
+ if (!IS_ERR(data->clk)) {
+ speed_t baud;
+ unsigned int rate;
+
+ baud = tty_termios_baud_rate(new);
+ BUG_ON(baud > (speed_t)UINT_MAX);
+ rate = 16 * max(115200U, (unsigned int)baud);
+
+ /*
+ * Request a different clock rate if necessary, and
+ * record it if successful.
+ */
+ if (rate != p->uartclk) {
+ BUG_ON(!data->clk);
+ if (!clk_set_rate(data->clk, (unsigned long)rate))
+ p->uartclk = rate;
+ }
+ }
+
+ /* The standard code does most of the work. */
+ serial8250_do_set_termios(p, new, old);
+}
+
static bool dw8250_dma_filter(struct dma_chan *chan, void *param)
{
struct dw8250_data *data = param;
@@ -254,6 +291,21 @@ static void dw8250_setup_port(struct uart_8250_port *up)
up->capabilities |= UART_CAP_AFE;
}
+/*
+ * Returns true if the UART clock's rate can be adjusted in order to
+ * achieve higher baud rates.
+ */
+static bool dw8250_adjustable_clk(struct uart_port *p)
+{
+ struct device_node *np = p->dev->of_node;
+
+ if (of_device_is_compatible(np, "brcm,bcm11351-dw-apb-uart"))
+ return true;
+ if (of_device_is_compatible(np, "brcm,bcm21664-dw-apb-uart"))
+ return true;
+ return false;
+}
+
static int dw8250_probe_of(struct uart_port *p,
struct dw8250_data *data)
{
@@ -262,6 +314,9 @@ static int dw8250_probe_of(struct uart_port *p,
u32 val;
bool has_ucv = true;
+ if (dw8250_adjustable_clk(p))
+ p->set_termios = dw8250_set_termios_qcom;
+
if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) {
#ifdef __BIG_ENDIAN
/*
@@ -530,6 +585,8 @@ static const struct dev_pm_ops dw8250_pm_ops = {
static const struct of_device_id dw8250_of_match[] = {
{ .compatible = "snps,dw-apb-uart" },
{ .compatible = "cavium,octeon-3860-uart" },
+ { .compatible = "brcm,bcm11351-dw-apb-uart" },
+ { .compatible = "brcm,bcm21664-dw-apb-uart" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, dw8250_of_match);
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index c78f43a481ce..c0bc932d3ec8 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -78,6 +78,41 @@ static void dbg(const char *fmt, ...)
#define S3C24XX_SERIAL_MAJOR 204
#define S3C24XX_SERIAL_MINOR 64
+#ifndef CONFIG_CPU_BIG_ENDIAN /* little endian */
+static inline void __hw_set_bit(int nr, volatile unsigned long *addr)
+{
+ __set_bit(nr, addr);
+}
+
+static inline void __hw_clear_bit(int nr, volatile unsigned long *addr)
+{
+ __clear_bit(nr, addr);
+}
+#else
+static inline void __hw_set_bit(int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+ unsigned long val = le32_to_cpu(*p);
+
+ val |= mask;
+
+ *p = cpu_to_le32(val);
+}
+
+static inline void __hw_clear_bit(int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+ unsigned long val = le32_to_cpu(*p);
+
+ val &= ~mask;
+
+ *p = cpu_to_le32(val);
+}
+#endif
+
/* macros to change one thing to another */
#define tx_enabled(port) ((port)->unused[0])
@@ -157,7 +192,7 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port)
if (tx_enabled(port)) {
if (s3c24xx_serial_has_interrupt_mask(port))
- __set_bit(S3C64XX_UINTM_TXD,
+ __hw_set_bit(S3C64XX_UINTM_TXD,
portaddrl(port, S3C64XX_UINTM));
else
disable_irq_nosync(ourport->tx_irq);
@@ -176,7 +211,7 @@ static void s3c24xx_serial_start_tx(struct uart_port *port)
s3c24xx_serial_rx_disable(port);
if (s3c24xx_serial_has_interrupt_mask(port))
- __clear_bit(S3C64XX_UINTM_TXD,
+ __hw_clear_bit(S3C64XX_UINTM_TXD,
portaddrl(port, S3C64XX_UINTM));
else
enable_irq(ourport->tx_irq);
@@ -191,7 +226,7 @@ static void s3c24xx_serial_stop_rx(struct uart_port *port)
if (rx_enabled(port)) {
dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
if (s3c24xx_serial_has_interrupt_mask(port))
- __set_bit(S3C64XX_UINTM_RXD,
+ __hw_set_bit(S3C64XX_UINTM_RXD,
portaddrl(port, S3C64XX_UINTM));
else
disable_irq_nosync(ourport->rx_irq);
@@ -548,7 +583,7 @@ static int s3c64xx_serial_startup(struct uart_port *port)
ourport->tx_claimed = 1;
/* Enable Rx Interrupt */
- __clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
+ __hw_clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
dbg("s3c64xx_serial_startup ok\n");
return ret;
}
diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h
index eb071dd19b2d..e86249605e76 100644
--- a/drivers/tty/serial/samsung.h
+++ b/drivers/tty/serial/samsung.h
@@ -75,9 +75,9 @@ struct s3c24xx_uart_port {
((unsigned long *)(unsigned long)((port)->membase + (reg)))
#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
-#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
+#define rd_regl(port, reg) (readl_relaxed(portaddr(port, reg)))
#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg))
-#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg))
+#define wr_regl(port, reg, val) writel_relaxed(val, portaddr(port, reg))
#endif
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 29a7be47389a..50d692054f10 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -90,6 +90,9 @@ static void __uart_start(struct tty_struct *tty)
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
+ if (port->ops->wake_peer)
+ port->ops->wake_peer(port);
+
if (!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port);
}
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 6935a822ce2b..0de9162b2f56 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -845,7 +845,7 @@ done:
}
EXPORT_SYMBOL_GPL(usb_add_config);
-static void remove_config(struct usb_composite_dev *cdev,
+static void unbind_config(struct usb_composite_dev *cdev,
struct usb_configuration *config)
{
while (!list_empty(&config->functions)) {
@@ -860,7 +860,6 @@ static void remove_config(struct usb_composite_dev *cdev,
/* may free memory for "f" */
}
}
- list_del(&config->list);
if (config->unbind) {
DBG(cdev, "unbind config '%s'/%p\n", config->label, config);
config->unbind(config);
@@ -887,9 +886,11 @@ void usb_remove_config(struct usb_composite_dev *cdev,
if (cdev->config == config)
reset_config(cdev);
+ list_del(&config->list);
+
spin_unlock_irqrestore(&cdev->lock, flags);
- remove_config(cdev, config);
+ unbind_config(cdev, config);
}
/*-------------------------------------------------------------------------*/
@@ -1818,7 +1819,8 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
struct usb_configuration *c;
c = list_first_entry(&cdev->configs,
struct usb_configuration, list);
- remove_config(cdev, c);
+ list_del(&c->list);
+ unbind_config(cdev, c);
}
if (cdev->driver->unbind && unbind_driver)
cdev->driver->unbind(cdev);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index dc30adf15a01..d91d1cac581c 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -78,6 +78,7 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
return container_of(f, struct ffs_function, function);
}
+static void ffs_func_free(struct ffs_function *func);
static inline enum ffs_setup_state
ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
@@ -92,6 +93,8 @@ static int __must_check ffs_func_eps_enable(struct ffs_function *func);
static int ffs_func_bind(struct usb_configuration *,
struct usb_function *);
+static void old_ffs_func_unbind(struct usb_configuration *,
+ struct usb_function *);
static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
static void ffs_func_disable(struct usb_function *);
static int ffs_func_setup(struct usb_function *,
@@ -1561,6 +1564,73 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
}
+static int functionfs_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct ffs_data *ffs)
+{
+ struct ffs_function *func;
+ int ret;
+
+ ENTER();
+
+ func = kzalloc(sizeof *func, GFP_KERNEL);
+ if (unlikely(!func))
+ return -ENOMEM;
+
+ func->function.name = "Function FS Gadget";
+ func->function.strings = ffs->stringtabs;
+
+ func->function.bind = ffs_func_bind;
+ func->function.unbind = old_ffs_func_unbind;
+ func->function.set_alt = ffs_func_set_alt;
+ func->function.disable = ffs_func_disable;
+ func->function.setup = ffs_func_setup;
+ func->function.suspend = ffs_func_suspend;
+ func->function.resume = ffs_func_resume;
+
+ func->conf = c;
+ func->gadget = cdev->gadget;
+ func->ffs = ffs;
+ ffs_data_get(ffs);
+
+ ret = usb_add_function(c, &func->function);
+ if (unlikely(ret))
+ ffs_func_free(func);
+
+ return ret;
+}
+
+static void ffs_func_free(struct ffs_function *func)
+{
+ struct ffs_ep *ep = func->eps;
+ unsigned count = func->ffs->eps_count;
+ unsigned long flags;
+
+ ENTER();
+
+ /* cleanup after autoconfig */
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ if (ep->ep && ep->req)
+ usb_ep_free_request(ep->ep, ep->req);
+ ep->req = NULL;
+ ++ep;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+ ffs_data_put(func->ffs);
+
+ kfree(func->eps);
+ /*
+ * eps and interfaces_nums are allocated in the same chunk so
+ * only one free is required. Descriptors are also allocated
+ * in the same chunk.
+ */
+
+ kfree(func);
+}
+
+
static void ffs_func_eps_disable(struct ffs_function *func)
{
struct ffs_ep *ep = func->eps;
@@ -2780,6 +2850,26 @@ static int ffs_func_bind(struct usb_configuration *c,
/* Other USB function hooks *************************************************/
+
+static void old_ffs_func_unbind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+
+ ENTER();
+
+ if (ffs->func == func) {
+ ffs_func_eps_disable(func);
+ ffs->func = NULL;
+ }
+
+ ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+
+ ffs_func_free(func);
+}
+
+
static int ffs_func_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
{
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index ddb09dc6d1f2..4625ab01f54d 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -840,6 +840,62 @@ fail:
return status;
}
+static void
+rndis_old_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+
+ rndis_deregister(rndis->config);
+
+ usb_free_all_descriptors(f);
+
+ kfree(rndis->notify_req->buf);
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+
+ kfree(rndis);
+}
+
+int
+rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
+ u32 vendorID, const char *manufacturer, struct eth_dev *dev)
+{
+ struct f_rndis *rndis;
+ int status;
+
+ /* allocate and initialize one new instance */
+ status = -ENOMEM;
+ rndis = kzalloc(sizeof *rndis, GFP_KERNEL);
+ if (!rndis)
+ goto fail;
+
+ memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
+ rndis->vendorID = vendorID;
+ rndis->manufacturer = manufacturer;
+
+ rndis->port.ioport = dev;
+ /* RNDIS activates when the host changes this filter */
+ rndis->port.cdc_filter = 0;
+
+ /* RNDIS has special (and complex) framing */
+ rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
+ rndis->port.wrap = rndis_add_header;
+ rndis->port.unwrap = rndis_rm_hdr;
+
+ rndis->port.func.name = "rndis";
+ /* descriptors are per-instance copies */
+ rndis->port.func.bind = rndis_bind;
+ rndis->port.func.unbind = rndis_old_unbind;
+ rndis->port.func.set_alt = rndis_set_alt;
+ rndis->port.func.setup = rndis_setup;
+ rndis->port.func.disable = rndis_disable;
+
+ status = usb_add_function(c, &rndis->port.func);
+ if (status)
+ kfree(rndis);
+fail:
+ return status;
+}
+
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
{
struct f_rndis_opts *opts;
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index ad0aca812002..b131751933cb 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -1126,6 +1126,7 @@ int gserial_alloc_line(unsigned char *line_num)
tty_dev = tty_port_register_device(&ports[port_num].port->port,
gs_tty_driver, port_num, NULL);
+
if (IS_ERR(tty_dev)) {
struct gs_port *port;
pr_err("%s: failed to register tty for port %d, err %ld\n",
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index aa376f006333..b609ba780cf1 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -311,6 +311,26 @@ config USB_G_PRINTER
For more information, see Documentation/usb/gadget_printer.txt
which includes sample code for accessing the device file.
+config USB_G_ANDROID
+ boolean "Android Composite Gadget"
+ select USB_F_ACM
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_F_MASS_STORAGE
+ select SND_PCM
+ help
+ The Android Composite Gadget supports multiple USB
+ functions: adb, acm, mass storage, mtp, accessory
+ and rndis.
+ Each function can be configured and enabled/disabled
+ dynamically from userspace through a sysfs interface.
+
+config USB_ANDROID_RNDIS_DWORD_ALIGNED
+ boolean "Use double word aligned"
+ depends on USB_G_ANDROID
+ help
+ Provides dword aligned for DMA controller.
+
if TTY
config USB_CDC_COMPOSITE
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
index edba2d1ee0f3..9672bc14672e 100644
--- a/drivers/usb/gadget/legacy/Makefile
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -22,6 +22,7 @@ g_nokia-y := nokia.o
g_webcam-y := webcam.o
g_ncm-y := ncm.o
g_acm_ms-y := acm_ms.o
+g_android-y := android.o
g_tcm_usb_gadget-y := tcm_usb_gadget.o
obj-$(CONFIG_USB_ZERO) += g_zero.o
@@ -41,4 +42,5 @@ obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
+obj-$(CONFIG_USB_G_ANDROID) += g_android.o
obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
diff --git a/drivers/usb/gadget/legacy/android.c b/drivers/usb/gadget/legacy/android.c
new file mode 100644
index 000000000000..132b69f713d7
--- /dev/null
+++ b/drivers/usb/gadget/legacy/android.c
@@ -0,0 +1,1561 @@
+/*
+ * Gadget Driver for Android
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ * Benoit Goby <benoit@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+
+#include "gadget_chips.h"
+
+#include "../function/f_fs.c"
+#include "f_audio_source.c"
+#include "f_mtp.c"
+#include "f_accessory.c"
+#define USB_ETH_RNDIS y
+#include "../function/f_rndis.c"
+#include "../function/rndis.c"
+#include "../function/u_ether.c"
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+MODULE_AUTHOR("Mike Lockwood");
+MODULE_DESCRIPTION("Android Composite USB Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+static const char longname[] = "Gadget Android";
+
+/* Default vendor and product IDs, overridden by userspace */
+#define VENDOR_ID 0x18D1
+#define PRODUCT_ID 0x0001
+
+struct android_usb_function {
+ char *name;
+ void *config;
+
+ struct device *dev;
+ char *dev_name;
+ struct device_attribute **attributes;
+
+ /* for android_dev.enabled_functions */
+ struct list_head enabled_list;
+
+ /* Optional: initialization during gadget bind */
+ int (*init)(struct android_usb_function *, struct usb_composite_dev *);
+ /* Optional: cleanup during gadget unbind */
+ void (*cleanup)(struct android_usb_function *);
+ /* Optional: called when the function is added the list of
+ * enabled functions */
+ void (*enable)(struct android_usb_function *);
+ /* Optional: called when it is removed */
+ void (*disable)(struct android_usb_function *);
+
+ int (*bind_config)(struct android_usb_function *,
+ struct usb_configuration *);
+
+ /* Optional: called when the configuration is removed */
+ void (*unbind_config)(struct android_usb_function *,
+ struct usb_configuration *);
+ /* Optional: handle ctrl requests before the device is configured */
+ int (*ctrlrequest)(struct android_usb_function *,
+ struct usb_composite_dev *,
+ const struct usb_ctrlrequest *);
+};
+
+struct android_dev {
+ struct android_usb_function **functions;
+ struct list_head enabled_functions;
+ struct usb_composite_dev *cdev;
+ struct device *dev;
+
+ bool enabled;
+ int disable_depth;
+ struct mutex mutex;
+ bool connected;
+ bool sw_connected;
+ struct work_struct work;
+ char ffs_aliases[256];
+};
+
+static struct class *android_class;
+static struct android_dev *_android_dev;
+static int android_bind_config(struct usb_configuration *c);
+static void android_unbind_config(struct usb_configuration *c);
+
+/* string IDs are assigned dynamically */
+#define STRING_MANUFACTURER_IDX 0
+#define STRING_PRODUCT_IDX 1
+#define STRING_SERIAL_IDX 2
+
+static char manufacturer_string[256];
+static char product_string[256];
+static char serial_string[256];
+
+/* String Table */
+static struct usb_string strings_dev[] = {
+ [STRING_MANUFACTURER_IDX].s = manufacturer_string,
+ [STRING_PRODUCT_IDX].s = product_string,
+ [STRING_SERIAL_IDX].s = serial_string,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof(device_desc),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .idVendor = __constant_cpu_to_le16(VENDOR_ID),
+ .idProduct = __constant_cpu_to_le16(PRODUCT_ID),
+ .bcdDevice = __constant_cpu_to_le16(0xffff),
+ .bNumConfigurations = 1,
+};
+
+static struct usb_configuration android_config_driver = {
+ .label = "android",
+ .unbind = android_unbind_config,
+ .bConfigurationValue = 1,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .MaxPower = 500, /* 500ma */
+};
+
+static void android_work(struct work_struct *data)
+{
+ struct android_dev *dev = container_of(data, struct android_dev, work);
+ struct usb_composite_dev *cdev = dev->cdev;
+ char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL };
+ char *connected[2] = { "USB_STATE=CONNECTED", NULL };
+ char *configured[2] = { "USB_STATE=CONFIGURED", NULL };
+ char **uevent_envp = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (cdev->config)
+ uevent_envp = configured;
+ else if (dev->connected != dev->sw_connected)
+ uevent_envp = dev->connected ? connected : disconnected;
+ dev->sw_connected = dev->connected;
+ spin_unlock_irqrestore(&cdev->lock, flags);
+
+ if (uevent_envp) {
+ kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp);
+ pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]);
+ } else {
+ pr_info("%s: did not send uevent (%d %d %p)\n", __func__,
+ dev->connected, dev->sw_connected, cdev->config);
+ }
+}
+
+static void android_enable(struct android_dev *dev)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+
+ if (WARN_ON(!dev->disable_depth))
+ return;
+
+ if (--dev->disable_depth == 0) {
+ usb_add_config(cdev, &android_config_driver,
+ android_bind_config);
+ usb_gadget_connect(cdev->gadget);
+ }
+}
+
+static void android_disable(struct android_dev *dev)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+
+ if (dev->disable_depth++ == 0) {
+ usb_gadget_disconnect(cdev->gadget);
+ /* Cancel pending control requests */
+ usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
+ usb_remove_config(cdev, &android_config_driver);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/* Supported functions initialization */
+
+struct functionfs_config {
+ bool opened;
+ bool enabled;
+ struct ffs_data *data;
+};
+
+static int ffs_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ f->config = kzalloc(sizeof(struct functionfs_config), GFP_KERNEL);
+ if (!f->config)
+ return -ENOMEM;
+
+ return functionfs_init();
+}
+
+static void ffs_function_cleanup(struct android_usb_function *f)
+{
+ functionfs_cleanup();
+ kfree(f->config);
+}
+
+static void ffs_function_enable(struct android_usb_function *f)
+{
+ struct android_dev *dev = _android_dev;
+ struct functionfs_config *config = f->config;
+
+ config->enabled = true;
+
+ /* Disable the gadget until the function is ready */
+ if (!config->opened)
+ android_disable(dev);
+}
+
+static void ffs_function_disable(struct android_usb_function *f)
+{
+ struct android_dev *dev = _android_dev;
+ struct functionfs_config *config = f->config;
+
+ config->enabled = false;
+
+ /* Balance the disable that was called in closed_callback */
+ if (!config->opened)
+ android_enable(dev);
+}
+
+static int ffs_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct functionfs_config *config = f->config;
+ return functionfs_bind_config(c->cdev, c, config->data);
+}
+
+static ssize_t
+ffs_aliases_show(struct device *pdev, struct device_attribute *attr, char *buf)
+{
+ struct android_dev *dev = _android_dev;
+ int ret;
+
+ mutex_lock(&dev->mutex);
+ ret = sprintf(buf, "%s\n", dev->ffs_aliases);
+ mutex_unlock(&dev->mutex);
+
+ return ret;
+}
+
+static ssize_t
+ffs_aliases_store(struct device *pdev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct android_dev *dev = _android_dev;
+ char buff[256];
+
+ mutex_lock(&dev->mutex);
+
+ if (dev->enabled) {
+ mutex_unlock(&dev->mutex);
+ return -EBUSY;
+ }
+
+ strlcpy(buff, buf, sizeof(buff));
+ strlcpy(dev->ffs_aliases, strim(buff), sizeof(dev->ffs_aliases));
+
+ mutex_unlock(&dev->mutex);
+
+ return size;
+}
+
+static DEVICE_ATTR(aliases, S_IRUGO | S_IWUSR, ffs_aliases_show,
+ ffs_aliases_store);
+static struct device_attribute *ffs_function_attributes[] = {
+ &dev_attr_aliases,
+ NULL
+};
+
+static struct android_usb_function ffs_function = {
+ .name = "ffs",
+ .init = ffs_function_init,
+ .enable = ffs_function_enable,
+ .disable = ffs_function_disable,
+ .cleanup = ffs_function_cleanup,
+ .bind_config = ffs_function_bind_config,
+ .attributes = ffs_function_attributes,
+};
+
+static int functionfs_ready_callback(struct ffs_data *ffs)
+{
+ struct android_dev *dev = _android_dev;
+ struct functionfs_config *config = ffs_function.config;
+ int ret = 0;
+
+ mutex_lock(&dev->mutex);
+
+ ret = functionfs_bind(ffs, dev->cdev);
+ if (ret)
+ goto err;
+
+ config->data = ffs;
+ config->opened = true;
+
+ if (config->enabled)
+ android_enable(dev);
+
+err:
+ mutex_unlock(&dev->mutex);
+ return ret;
+}
+
+static void functionfs_closed_callback(struct ffs_data *ffs)
+{
+ struct android_dev *dev = _android_dev;
+ struct functionfs_config *config = ffs_function.config;
+
+ mutex_lock(&dev->mutex);
+
+ if (config->enabled)
+ android_disable(dev);
+
+ config->opened = false;
+ config->data = NULL;
+
+ functionfs_unbind(ffs);
+
+ mutex_unlock(&dev->mutex);
+}
+
+static void *functionfs_acquire_dev_callback(const char *dev_name)
+{
+ return 0;
+}
+
+static void functionfs_release_dev_callback(struct ffs_data *ffs_data)
+{
+}
+
+#define MAX_ACM_INSTANCES 4
+struct acm_function_config {
+ int instances;
+ int instances_on;
+ struct usb_function *f_acm[MAX_ACM_INSTANCES];
+ struct usb_function_instance *f_acm_inst[MAX_ACM_INSTANCES];
+};
+
+static int
+acm_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ int i;
+ int ret;
+ struct acm_function_config *config;
+
+ config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+ f->config = config;
+
+ for (i = 0; i < MAX_ACM_INSTANCES; i++) {
+ config->f_acm_inst[i] = usb_get_function_instance("acm");
+ if (IS_ERR(config->f_acm_inst[i])) {
+ ret = PTR_ERR(config->f_acm_inst[i]);
+ goto err_usb_get_function_instance;
+ }
+ config->f_acm[i] = usb_get_function(config->f_acm_inst[i]);
+ if (IS_ERR(config->f_acm[i])) {
+ ret = PTR_ERR(config->f_acm[i]);
+ goto err_usb_get_function;
+ }
+ }
+ return 0;
+err_usb_get_function_instance:
+ while (i-- > 0) {
+ usb_put_function(config->f_acm[i]);
+err_usb_get_function:
+ usb_put_function_instance(config->f_acm_inst[i]);
+ }
+ return ret;
+}
+
+static void acm_function_cleanup(struct android_usb_function *f)
+{
+ int i;
+ struct acm_function_config *config = f->config;
+
+ for (i = 0; i < MAX_ACM_INSTANCES; i++) {
+ usb_put_function(config->f_acm[i]);
+ usb_put_function_instance(config->f_acm_inst[i]);
+ }
+ kfree(f->config);
+ f->config = NULL;
+}
+
+static int
+acm_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ int i;
+ int ret = 0;
+ struct acm_function_config *config = f->config;
+
+ config->instances_on = config->instances;
+ for (i = 0; i < config->instances_on; i++) {
+ ret = usb_add_function(c, config->f_acm[i]);
+ if (ret) {
+ pr_err("Could not bind acm%u config\n", i);
+ goto err_usb_add_function;
+ }
+ }
+
+ return 0;
+
+err_usb_add_function:
+ while (i-- > 0)
+ usb_remove_function(c, config->f_acm[i]);
+ return ret;
+}
+
+static void acm_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ int i;
+ struct acm_function_config *config = f->config;
+
+ for (i = 0; i < config->instances_on; i++)
+ usb_remove_function(c, config->f_acm[i]);
+}
+
+static ssize_t acm_instances_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct acm_function_config *config = f->config;
+ return sprintf(buf, "%d\n", config->instances);
+}
+
+static ssize_t acm_instances_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct acm_function_config *config = f->config;
+ int value;
+
+ sscanf(buf, "%d", &value);
+ if (value > MAX_ACM_INSTANCES)
+ value = MAX_ACM_INSTANCES;
+ config->instances = value;
+ return size;
+}
+
+static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show,
+ acm_instances_store);
+static struct device_attribute *acm_function_attributes[] = {
+ &dev_attr_instances,
+ NULL
+};
+
+static struct android_usb_function acm_function = {
+ .name = "acm",
+ .init = acm_function_init,
+ .cleanup = acm_function_cleanup,
+ .bind_config = acm_function_bind_config,
+ .unbind_config = acm_function_unbind_config,
+ .attributes = acm_function_attributes,
+};
+
+
+static int
+mtp_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ return mtp_setup();
+}
+
+static void mtp_function_cleanup(struct android_usb_function *f)
+{
+ mtp_cleanup();
+}
+
+static int
+mtp_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ return mtp_bind_config(c, false);
+}
+
+static int
+ptp_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ /* nothing to do - initialization is handled by mtp_function_init */
+ return 0;
+}
+
+static void ptp_function_cleanup(struct android_usb_function *f)
+{
+ /* nothing to do - cleanup is handled by mtp_function_cleanup */
+}
+
+static int
+ptp_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ return mtp_bind_config(c, true);
+}
+
+static int mtp_function_ctrlrequest(struct android_usb_function *f,
+ struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *c)
+{
+ return mtp_ctrlrequest(cdev, c);
+}
+
+static struct android_usb_function mtp_function = {
+ .name = "mtp",
+ .init = mtp_function_init,
+ .cleanup = mtp_function_cleanup,
+ .bind_config = mtp_function_bind_config,
+ .ctrlrequest = mtp_function_ctrlrequest,
+};
+
+/* PTP function is same as MTP with slightly different interface descriptor */
+static struct android_usb_function ptp_function = {
+ .name = "ptp",
+ .init = ptp_function_init,
+ .cleanup = ptp_function_cleanup,
+ .bind_config = ptp_function_bind_config,
+};
+
+
+struct rndis_function_config {
+ u8 ethaddr[ETH_ALEN];
+ u32 vendorID;
+ char manufacturer[256];
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ bool wceis;
+ struct eth_dev *dev;
+};
+
+static int
+rndis_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL);
+ if (!f->config)
+ return -ENOMEM;
+ return 0;
+}
+
+static void rndis_function_cleanup(struct android_usb_function *f)
+{
+ kfree(f->config);
+ f->config = NULL;
+}
+
+static int
+rndis_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ int ret;
+ struct eth_dev *dev;
+ struct rndis_function_config *rndis = f->config;
+
+ if (!rndis) {
+ pr_err("%s: rndis_pdata\n", __func__);
+ return -1;
+ }
+
+ pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__,
+ rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2],
+ rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]);
+
+ dev = gether_setup_name(c->cdev->gadget,dev_addr, host_addr, rndis->ethaddr, qmult, "rndis");
+ if (IS_ERR(dev)) {
+ ret = PTR_ERR(dev);
+ pr_err("%s: gether_setup failed\n", __func__);
+ return ret;
+ }
+ rndis->dev = dev;
+
+ if (rndis->wceis) {
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ rndis_iad_descriptor.bFunctionClass =
+ USB_CLASS_WIRELESS_CONTROLLER;
+ rndis_iad_descriptor.bFunctionSubClass = 0x01;
+ rndis_iad_descriptor.bFunctionProtocol = 0x03;
+ rndis_control_intf.bInterfaceClass =
+ USB_CLASS_WIRELESS_CONTROLLER;
+ rndis_control_intf.bInterfaceSubClass = 0x01;
+ rndis_control_intf.bInterfaceProtocol = 0x03;
+ }
+
+ return rndis_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID,
+ rndis->manufacturer, rndis->dev);
+}
+
+static void rndis_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct rndis_function_config *rndis = f->config;
+ gether_cleanup(rndis->dev);
+}
+
+static ssize_t rndis_manufacturer_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *config = f->config;
+ return sprintf(buf, "%s\n", config->manufacturer);
+}
+
+static ssize_t rndis_manufacturer_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *config = f->config;
+
+ if (size >= sizeof(config->manufacturer))
+ return -EINVAL;
+ if (sscanf(buf, "%s", config->manufacturer) == 1)
+ return size;
+ return -1;
+}
+
+static DEVICE_ATTR(manufacturer, S_IRUGO | S_IWUSR, rndis_manufacturer_show,
+ rndis_manufacturer_store);
+
+static ssize_t rndis_wceis_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *config = f->config;
+ return sprintf(buf, "%d\n", config->wceis);
+}
+
+static ssize_t rndis_wceis_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *config = f->config;
+ int value;
+
+ if (sscanf(buf, "%d", &value) == 1) {
+ config->wceis = value;
+ return size;
+ }
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(wceis, S_IRUGO | S_IWUSR, rndis_wceis_show,
+ rndis_wceis_store);
+
+static ssize_t rndis_ethaddr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *rndis = f->config;
+ return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2],
+ rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]);
+}
+
+static ssize_t rndis_ethaddr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *rndis = f->config;
+
+ if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ (int *)&rndis->ethaddr[0], (int *)&rndis->ethaddr[1],
+ (int *)&rndis->ethaddr[2], (int *)&rndis->ethaddr[3],
+ (int *)&rndis->ethaddr[4], (int *)&rndis->ethaddr[5]) == 6)
+ return size;
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(ethaddr, S_IRUGO | S_IWUSR, rndis_ethaddr_show,
+ rndis_ethaddr_store);
+
+static ssize_t rndis_vendorID_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *config = f->config;
+ return sprintf(buf, "%04x\n", config->vendorID);
+}
+
+static ssize_t rndis_vendorID_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *config = f->config;
+ int value;
+
+ if (sscanf(buf, "%04x", &value) == 1) {
+ config->vendorID = value;
+ return size;
+ }
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(vendorID, S_IRUGO | S_IWUSR, rndis_vendorID_show,
+ rndis_vendorID_store);
+
+static struct device_attribute *rndis_function_attributes[] = {
+ &dev_attr_manufacturer,
+ &dev_attr_wceis,
+ &dev_attr_ethaddr,
+ &dev_attr_vendorID,
+ NULL
+};
+
+static struct android_usb_function rndis_function = {
+ .name = "rndis",
+ .init = rndis_function_init,
+ .cleanup = rndis_function_cleanup,
+ .bind_config = rndis_function_bind_config,
+ .unbind_config = rndis_function_unbind_config,
+ .attributes = rndis_function_attributes,
+};
+
+
+#define MAX_MS_INSTANCES 1
+struct mass_storage_function_config {
+ int instances;
+ int instances_on;
+ struct usb_function *f_ms[MAX_MS_INSTANCES];
+ struct usb_function_instance *f_ms_inst[MAX_MS_INSTANCES];
+};
+
+static int mass_storage_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ struct mass_storage_function_config *config;
+ int i;
+ int ret;
+
+ config = kzalloc(sizeof(struct mass_storage_function_config),
+ GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+ f->config = config;
+
+ for (i = 0; i < MAX_MS_INSTANCES; i++) {
+ config->f_ms_inst[i] = usb_get_function_instance("mass_storage");
+ if (IS_ERR(config->f_ms_inst[i])) {
+ ret = PTR_ERR(config->f_ms_inst[i]);
+ goto err_usb_get_function_instance;
+ }
+ config->f_ms[i] = usb_get_function(config->f_ms_inst[i]);
+ if (IS_ERR(config->f_ms[i])) {
+ ret = PTR_ERR(config->f_ms[i]);
+ goto err_usb_get_function;
+ }
+ }
+ return 0;
+err_usb_get_function_instance:
+ while (i-- > 0) {
+ usb_put_function(config->f_ms[i]);
+err_usb_get_function:
+ usb_put_function_instance(config->f_ms_inst[i]);
+ }
+ return ret;
+}
+
+static void mass_storage_function_cleanup(struct android_usb_function *f)
+{
+ struct mass_storage_function_config *config = f->config;
+ int i;
+
+ for (i = 0; i < MAX_MS_INSTANCES; i++) {
+ usb_put_function(config->f_ms[i]);
+ usb_put_function_instance(config->f_ms_inst[i]);
+ }
+ kfree(f->config);
+ f->config = NULL;
+}
+
+static int mass_storage_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct mass_storage_function_config *config = f->config;
+ int ret = 0;
+ int i;
+
+ config->instances_on = config->instances;
+ for (i = 0; i < config->instances_on; i++) {
+ ret = usb_add_function(c, config->f_ms[i]);
+ if (ret) {
+ pr_err("Could not bind ms%u config\n", i);
+ goto err_usb_add_function;
+ }
+ }
+
+ return 0;
+
+err_usb_add_function:
+ while (i-- > 0)
+ usb_remove_function(c, config->f_ms[i]);
+ return ret;
+}
+
+static void mass_storage_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ int i;
+ struct mass_storage_function_config *config = f->config;
+
+ for (i = 0; i < config->instances_on; i++)
+ usb_remove_function(c, config->f_ms[i]);
+}
+
+static ssize_t mass_storage_inquiry_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct mass_storage_function_config *config = f->config;
+ return sprintf(buf, "%d\n", config->instances);
+}
+
+static ssize_t mass_storage_inquiry_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct mass_storage_function_config *config = f->config;
+ int value;
+
+ sscanf(buf, "%d", &value);
+ if (value > MAX_MS_INSTANCES)
+ value = MAX_MS_INSTANCES;
+ config->instances = value;
+ return size;
+}
+
+static DEVICE_ATTR(inquiry_string, S_IRUGO | S_IWUSR,
+ mass_storage_inquiry_show,
+ mass_storage_inquiry_store);
+
+static struct device_attribute *mass_storage_function_attributes[] = {
+ &dev_attr_inquiry_string,
+ NULL
+};
+
+static struct android_usb_function mass_storage_function = {
+ .name = "mass_storage",
+ .init = mass_storage_function_init,
+ .cleanup = mass_storage_function_cleanup,
+ .bind_config = mass_storage_function_bind_config,
+ .attributes = mass_storage_function_attributes,
+};
+
+
+static int accessory_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ return acc_setup();
+}
+
+static void accessory_function_cleanup(struct android_usb_function *f)
+{
+ acc_cleanup();
+}
+
+static int accessory_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ return acc_bind_config(c);
+}
+
+static int accessory_function_ctrlrequest(struct android_usb_function *f,
+ struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *c)
+{
+ return acc_ctrlrequest(cdev, c);
+}
+
+static struct android_usb_function accessory_function = {
+ .name = "accessory",
+ .init = accessory_function_init,
+ .cleanup = accessory_function_cleanup,
+ .bind_config = accessory_function_bind_config,
+ .ctrlrequest = accessory_function_ctrlrequest,
+};
+
+static int audio_source_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ struct audio_source_config *config;
+
+ config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+ config->card = -1;
+ config->device = -1;
+ f->config = config;
+ return 0;
+}
+
+static void audio_source_function_cleanup(struct android_usb_function *f)
+{
+ kfree(f->config);
+}
+
+static int audio_source_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct audio_source_config *config = f->config;
+
+ return audio_source_bind_config(c, config);
+}
+
+static void audio_source_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ struct audio_source_config *config = f->config;
+
+ config->card = -1;
+ config->device = -1;
+}
+
+static ssize_t audio_source_pcm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct audio_source_config *config = f->config;
+
+ /* print PCM card and device numbers */
+ return sprintf(buf, "%d %d\n", config->card, config->device);
+}
+
+static DEVICE_ATTR(pcm, S_IRUGO, audio_source_pcm_show, NULL);
+
+static struct device_attribute *audio_source_function_attributes[] = {
+ &dev_attr_pcm,
+ NULL
+};
+
+static struct android_usb_function audio_source_function = {
+ .name = "audio_source",
+ .init = audio_source_function_init,
+ .cleanup = audio_source_function_cleanup,
+ .bind_config = audio_source_function_bind_config,
+ .unbind_config = audio_source_function_unbind_config,
+ .attributes = audio_source_function_attributes,
+};
+
+static struct android_usb_function *supported_functions[] = {
+ &ffs_function,
+ &acm_function,
+ &mtp_function,
+ &ptp_function,
+ &rndis_function,
+ &mass_storage_function,
+ &accessory_function,
+ &audio_source_function,
+ NULL
+};
+
+
+static int android_init_functions(struct android_usb_function **functions,
+ struct usb_composite_dev *cdev)
+{
+ struct android_dev *dev = _android_dev;
+ struct android_usb_function *f;
+ struct device_attribute **attrs;
+ struct device_attribute *attr;
+ int err;
+ int index = 0;
+
+ for (; (f = *functions++); index++) {
+ f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name);
+ f->dev = device_create(android_class, dev->dev,
+ MKDEV(0, index), f, f->dev_name);
+ if (IS_ERR(f->dev)) {
+ pr_err("%s: Failed to create dev %s", __func__,
+ f->dev_name);
+ err = PTR_ERR(f->dev);
+ goto err_create;
+ }
+
+ if (f->init) {
+ err = f->init(f, cdev);
+ if (err) {
+ pr_err("%s: Failed to init %s", __func__,
+ f->name);
+ goto err_out;
+ }
+ }
+
+ attrs = f->attributes;
+ if (attrs) {
+ while ((attr = *attrs++) && !err)
+ err = device_create_file(f->dev, attr);
+ }
+ if (err) {
+ pr_err("%s: Failed to create function %s attributes",
+ __func__, f->name);
+ goto err_out;
+ }
+ }
+ return 0;
+
+err_out:
+ device_destroy(android_class, f->dev->devt);
+err_create:
+ kfree(f->dev_name);
+ return err;
+}
+
+static void android_cleanup_functions(struct android_usb_function **functions)
+{
+ struct android_usb_function *f;
+
+ while (*functions) {
+ f = *functions++;
+
+ if (f->dev) {
+ device_destroy(android_class, f->dev->devt);
+ kfree(f->dev_name);
+ }
+
+ if (f->cleanup)
+ f->cleanup(f);
+ }
+}
+
+static int
+android_bind_enabled_functions(struct android_dev *dev,
+ struct usb_configuration *c)
+{
+ struct android_usb_function *f;
+ int ret;
+
+ list_for_each_entry(f, &dev->enabled_functions, enabled_list) {
+ ret = f->bind_config(f, c);
+ if (ret) {
+ pr_err("%s: %s failed", __func__, f->name);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static void
+android_unbind_enabled_functions(struct android_dev *dev,
+ struct usb_configuration *c)
+{
+ struct android_usb_function *f;
+
+ list_for_each_entry(f, &dev->enabled_functions, enabled_list) {
+ if (f->unbind_config)
+ f->unbind_config(f, c);
+ }
+}
+
+static int android_enable_function(struct android_dev *dev, char *name)
+{
+ struct android_usb_function **functions = dev->functions;
+ struct android_usb_function *f;
+ while ((f = *functions++)) {
+ if (!strcmp(name, f->name)) {
+ list_add_tail(&f->enabled_list,
+ &dev->enabled_functions);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+/* /sys/class/android_usb/android%d/ interface */
+
+static ssize_t
+functions_show(struct device *pdev, struct device_attribute *attr, char *buf)
+{
+ struct android_dev *dev = dev_get_drvdata(pdev);
+ struct android_usb_function *f;
+ char *buff = buf;
+
+ mutex_lock(&dev->mutex);
+
+ list_for_each_entry(f, &dev->enabled_functions, enabled_list)
+ buff += sprintf(buff, "%s,", f->name);
+
+ mutex_unlock(&dev->mutex);
+
+ if (buff != buf)
+ *(buff-1) = '\n';
+ return buff - buf;
+}
+
+static ssize_t
+functions_store(struct device *pdev, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ struct android_dev *dev = dev_get_drvdata(pdev);
+ char *name;
+ char buf[256], *b;
+ char aliases[256], *a;
+ int err;
+ int is_ffs;
+ int ffs_enabled = 0;
+
+ mutex_lock(&dev->mutex);
+
+ if (dev->enabled) {
+ mutex_unlock(&dev->mutex);
+ return -EBUSY;
+ }
+
+ INIT_LIST_HEAD(&dev->enabled_functions);
+
+ strlcpy(buf, buff, sizeof(buf));
+ b = strim(buf);
+
+ while (b) {
+ name = strsep(&b, ",");
+ if (!name)
+ continue;
+
+ is_ffs = 0;
+ strlcpy(aliases, dev->ffs_aliases, sizeof(aliases));
+ a = aliases;
+
+ while (a) {
+ char *alias = strsep(&a, ",");
+ if (alias && !strcmp(name, alias)) {
+ is_ffs = 1;
+ break;
+ }
+ }
+
+ if (is_ffs) {
+ if (ffs_enabled)
+ continue;
+ err = android_enable_function(dev, "ffs");
+ if (err)
+ pr_err("android_usb: Cannot enable ffs (%d)",
+ err);
+ else
+ ffs_enabled = 1;
+ continue;
+ }
+
+ err = android_enable_function(dev, name);
+ if (err)
+ pr_err("android_usb: Cannot enable '%s' (%d)",
+ name, err);
+ }
+
+ mutex_unlock(&dev->mutex);
+
+ return size;
+}
+
+static ssize_t enable_show(struct device *pdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct android_dev *dev = dev_get_drvdata(pdev);
+ return sprintf(buf, "%d\n", dev->enabled);
+}
+
+static ssize_t enable_store(struct device *pdev, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ struct android_dev *dev = dev_get_drvdata(pdev);
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct android_usb_function *f;
+ int enabled = 0;
+
+
+ if (!cdev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+
+ sscanf(buff, "%d", &enabled);
+ if (enabled && !dev->enabled) {
+ cdev->next_string_id = 0;
+ /*
+ * Update values in composite driver's copy of
+ * device descriptor.
+ */
+ cdev->desc.idVendor = device_desc.idVendor;
+ cdev->desc.idProduct = device_desc.idProduct;
+ cdev->desc.bcdDevice = device_desc.bcdDevice;
+ cdev->desc.bDeviceClass = device_desc.bDeviceClass;
+ cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass;
+ cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol;
+ list_for_each_entry(f, &dev->enabled_functions, enabled_list) {
+ if (f->enable)
+ f->enable(f);
+ }
+ android_enable(dev);
+ dev->enabled = true;
+ } else if (!enabled && dev->enabled) {
+ android_disable(dev);
+ list_for_each_entry(f, &dev->enabled_functions, enabled_list) {
+ if (f->disable)
+ f->disable(f);
+ }
+ dev->enabled = false;
+ } else {
+ pr_err("android_usb: already %s\n",
+ dev->enabled ? "enabled" : "disabled");
+ }
+
+ mutex_unlock(&dev->mutex);
+ return size;
+}
+
+static ssize_t state_show(struct device *pdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct android_dev *dev = dev_get_drvdata(pdev);
+ struct usb_composite_dev *cdev = dev->cdev;
+ char *state = "DISCONNECTED";
+ unsigned long flags;
+
+ if (!cdev)
+ goto out;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (cdev->config)
+ state = "CONFIGURED";
+ else if (dev->connected)
+ state = "CONNECTED";
+ spin_unlock_irqrestore(&cdev->lock, flags);
+out:
+ return sprintf(buf, "%s\n", state);
+}
+
+#define DESCRIPTOR_ATTR(field, format_string) \
+static ssize_t \
+field ## _show(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return sprintf(buf, format_string, device_desc.field); \
+} \
+static ssize_t \
+field ## _store(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t size) \
+{ \
+ int value; \
+ if (sscanf(buf, format_string, &value) == 1) { \
+ device_desc.field = value; \
+ return size; \
+ } \
+ return -1; \
+} \
+static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store);
+
+#define DESCRIPTOR_STRING_ATTR(field, buffer) \
+static ssize_t \
+field ## _show(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return sprintf(buf, "%s", buffer); \
+} \
+static ssize_t \
+field ## _store(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t size) \
+{ \
+ if (size >= sizeof(buffer)) \
+ return -EINVAL; \
+ return strlcpy(buffer, buf, sizeof(buffer)); \
+} \
+static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store);
+
+
+DESCRIPTOR_ATTR(idVendor, "%04x\n")
+DESCRIPTOR_ATTR(idProduct, "%04x\n")
+DESCRIPTOR_ATTR(bcdDevice, "%04x\n")
+DESCRIPTOR_ATTR(bDeviceClass, "%d\n")
+DESCRIPTOR_ATTR(bDeviceSubClass, "%d\n")
+DESCRIPTOR_ATTR(bDeviceProtocol, "%d\n")
+DESCRIPTOR_STRING_ATTR(iManufacturer, manufacturer_string)
+DESCRIPTOR_STRING_ATTR(iProduct, product_string)
+DESCRIPTOR_STRING_ATTR(iSerial, serial_string)
+
+static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show,
+ functions_store);
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
+static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
+
+static struct device_attribute *android_usb_attributes[] = {
+ &dev_attr_idVendor,
+ &dev_attr_idProduct,
+ &dev_attr_bcdDevice,
+ &dev_attr_bDeviceClass,
+ &dev_attr_bDeviceSubClass,
+ &dev_attr_bDeviceProtocol,
+ &dev_attr_iManufacturer,
+ &dev_attr_iProduct,
+ &dev_attr_iSerial,
+ &dev_attr_functions,
+ &dev_attr_enable,
+ &dev_attr_state,
+ NULL
+};
+
+/*-------------------------------------------------------------------------*/
+/* Composite driver */
+
+static int android_bind_config(struct usb_configuration *c)
+{
+ struct android_dev *dev = _android_dev;
+ int ret = 0;
+
+ ret = android_bind_enabled_functions(dev, c);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void android_unbind_config(struct usb_configuration *c)
+{
+ struct android_dev *dev = _android_dev;
+
+ android_unbind_enabled_functions(dev, c);
+}
+
+static int android_bind(struct usb_composite_dev *cdev)
+{
+ struct android_dev *dev = _android_dev;
+ struct usb_gadget *gadget = cdev->gadget;
+ int id, ret;
+
+ /*
+ * Start disconnected. Userspace will connect the gadget once
+ * it is done configuring the functions.
+ */
+ usb_gadget_disconnect(gadget);
+
+ ret = android_init_functions(dev->functions, cdev);
+ if (ret)
+ return ret;
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_MANUFACTURER_IDX].id = id;
+ device_desc.iManufacturer = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_PRODUCT_IDX].id = id;
+ device_desc.iProduct = id;
+
+ /* Default strings - should be updated by userspace */
+ strncpy(manufacturer_string, "Android", sizeof(manufacturer_string)-1);
+ strncpy(product_string, "Android", sizeof(product_string) - 1);
+ strncpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1);
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_SERIAL_IDX].id = id;
+ device_desc.iSerialNumber = id;
+
+ usb_gadget_set_selfpowered(gadget);
+ dev->cdev = cdev;
+
+ return 0;
+}
+
+static int android_usb_unbind(struct usb_composite_dev *cdev)
+{
+ struct android_dev *dev = _android_dev;
+
+ cancel_work_sync(&dev->work);
+ android_cleanup_functions(dev->functions);
+ return 0;
+}
+
+/* HACK: android needs to override setup for accessory to work */
+static int (*composite_setup_func)(struct usb_gadget *gadget, const struct usb_ctrlrequest *c);
+
+static int
+android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c)
+{
+ struct android_dev *dev = _android_dev;
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_request *req = cdev->req;
+ struct android_usb_function *f;
+ int value = -EOPNOTSUPP;
+ unsigned long flags;
+
+ req->zero = 0;
+ req->length = 0;
+ gadget->ep0->driver_data = cdev;
+
+ list_for_each_entry(f, &dev->enabled_functions, enabled_list) {
+ if (f->ctrlrequest) {
+ value = f->ctrlrequest(f, cdev, c);
+ if (value >= 0)
+ break;
+ }
+ }
+
+ /* Special case the accessory function.
+ * It needs to handle control requests before it is enabled.
+ */
+ if (value < 0)
+ value = acc_ctrlrequest(cdev, c);
+
+ if (value < 0)
+ value = composite_setup_func(gadget, c);
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (!dev->connected) {
+ dev->connected = 1;
+ schedule_work(&dev->work);
+ } else if (c->bRequest == USB_REQ_SET_CONFIGURATION &&
+ cdev->config) {
+ schedule_work(&dev->work);
+ }
+ spin_unlock_irqrestore(&cdev->lock, flags);
+
+ return value;
+}
+
+static void android_disconnect(struct usb_composite_dev *cdev)
+{
+ struct android_dev *dev = _android_dev;
+
+ /* accessory HID support can be active while the
+ accessory function is not actually enabled,
+ so we need to inform it when we are disconnected.
+ */
+ acc_disconnect();
+
+ dev->connected = 0;
+ schedule_work(&dev->work);
+}
+
+static struct usb_composite_driver android_usb_driver = {
+ .name = "android_usb",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .bind = android_bind,
+ .unbind = android_usb_unbind,
+ .disconnect = android_disconnect,
+ .max_speed = USB_SPEED_HIGH,
+};
+
+static int android_create_device(struct android_dev *dev)
+{
+ struct device_attribute **attrs = android_usb_attributes;
+ struct device_attribute *attr;
+ int err;
+
+ dev->dev = device_create(android_class, NULL,
+ MKDEV(0, 0), NULL, "android0");
+ if (IS_ERR(dev->dev))
+ return PTR_ERR(dev->dev);
+
+ dev_set_drvdata(dev->dev, dev);
+
+ while ((attr = *attrs++)) {
+ err = device_create_file(dev->dev, attr);
+ if (err) {
+ device_destroy(android_class, dev->dev->devt);
+ return err;
+ }
+ }
+ return 0;
+}
+
+
+static int __init init(void)
+{
+ struct android_dev *dev;
+ int err;
+
+ android_class = class_create(THIS_MODULE, "android_usb");
+ if (IS_ERR(android_class))
+ return PTR_ERR(android_class);
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ err = -ENOMEM;
+ goto err_dev;
+ }
+
+ dev->disable_depth = 1;
+ dev->functions = supported_functions;
+ INIT_LIST_HEAD(&dev->enabled_functions);
+ INIT_WORK(&dev->work, android_work);
+ mutex_init(&dev->mutex);
+
+ err = android_create_device(dev);
+ if (err) {
+ pr_err("%s: failed to create android device %d", __func__, err);
+ goto err_create;
+ }
+
+ _android_dev = dev;
+
+ err = usb_composite_probe(&android_usb_driver);
+ if (err) {
+ pr_err("%s: failed to probe driver %d", __func__, err);
+ goto err_probe;
+ }
+
+ /* HACK: exchange composite's setup with ours */
+ composite_setup_func = android_usb_driver.gadget_driver.setup;
+ android_usb_driver.gadget_driver.setup = android_setup;
+
+ return 0;
+
+err_probe:
+ device_destroy(android_class, dev->dev->devt);
+err_create:
+ kfree(dev);
+err_dev:
+ class_destroy(android_class);
+ return err;
+}
+late_initcall(init);
+
+static void __exit cleanup(void)
+{
+ usb_composite_unregister(&android_usb_driver);
+ class_destroy(android_class);
+ kfree(_android_dev);
+ _android_dev = NULL;
+}
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/legacy/f_accessory.c b/drivers/usb/gadget/legacy/f_accessory.c
new file mode 100644
index 000000000000..53e50b5e8612
--- /dev/null
+++ b/drivers/usb/gadget/legacy/f_accessory.c
@@ -0,0 +1,1204 @@
+/*
+ * Gadget Function Driver for Android USB accessories
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+
+#include <linux/hid.h>
+#include <linux/hiddev.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/f_accessory.h>
+
+#define BULK_BUFFER_SIZE 16384
+#define ACC_STRING_SIZE 256
+
+#define PROTOCOL_VERSION 2
+
+/* String IDs */
+#define INTERFACE_STRING_INDEX 0
+
+/* number of tx and rx requests to allocate */
+#define TX_REQ_MAX 4
+#define RX_REQ_MAX 2
+
+struct acc_hid_dev {
+ struct list_head list;
+ struct hid_device *hid;
+ struct acc_dev *dev;
+ /* accessory defined ID */
+ int id;
+ /* HID report descriptor */
+ u8 *report_desc;
+ /* length of HID report descriptor */
+ int report_desc_len;
+ /* number of bytes of report_desc we have received so far */
+ int report_desc_offset;
+};
+
+struct acc_dev {
+ struct usb_function function;
+ struct usb_composite_dev *cdev;
+ spinlock_t lock;
+
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
+
+ /* set to 1 when we connect */
+ int online:1;
+ /* Set to 1 when we disconnect.
+ * Not cleared until our file is closed.
+ */
+ int disconnected:1;
+
+ /* strings sent by the host */
+ char manufacturer[ACC_STRING_SIZE];
+ char model[ACC_STRING_SIZE];
+ char description[ACC_STRING_SIZE];
+ char version[ACC_STRING_SIZE];
+ char uri[ACC_STRING_SIZE];
+ char serial[ACC_STRING_SIZE];
+
+ /* for acc_complete_set_string */
+ int string_index;
+
+ /* set to 1 if we have a pending start request */
+ int start_requested;
+
+ int audio_mode;
+
+ /* synchronize access to our device file */
+ atomic_t open_excl;
+
+ struct list_head tx_idle;
+
+ wait_queue_head_t read_wq;
+ wait_queue_head_t write_wq;
+ struct usb_request *rx_req[RX_REQ_MAX];
+ int rx_done;
+
+ /* delayed work for handling ACCESSORY_START */
+ struct delayed_work start_work;
+
+ /* worker for registering and unregistering hid devices */
+ struct work_struct hid_work;
+
+ /* list of active HID devices */
+ struct list_head hid_list;
+
+ /* list of new HID devices to register */
+ struct list_head new_hid_list;
+
+ /* list of dead HID devices to unregister */
+ struct list_head dead_hid_list;
+};
+
+static struct usb_interface_descriptor acc_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC,
+ .bInterfaceProtocol = 0,
+};
+
+static struct usb_endpoint_descriptor acc_highspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor acc_highspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor acc_fullspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor acc_fullspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_acc_descs[] = {
+ (struct usb_descriptor_header *) &acc_interface_desc,
+ (struct usb_descriptor_header *) &acc_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &acc_fullspeed_out_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_acc_descs[] = {
+ (struct usb_descriptor_header *) &acc_interface_desc,
+ (struct usb_descriptor_header *) &acc_highspeed_in_desc,
+ (struct usb_descriptor_header *) &acc_highspeed_out_desc,
+ NULL,
+};
+
+static struct usb_string acc_string_defs[] = {
+ [INTERFACE_STRING_INDEX].s = "Android Accessory Interface",
+ { }, /* end of list */
+};
+
+static struct usb_gadget_strings acc_string_table = {
+ .language = 0x0409, /* en-US */
+ .strings = acc_string_defs,
+};
+
+static struct usb_gadget_strings *acc_strings[] = {
+ &acc_string_table,
+ NULL,
+};
+
+/* temporary variable used between acc_open() and acc_gadget_bind() */
+static struct acc_dev *_acc_dev;
+
+static inline struct acc_dev *func_to_dev(struct usb_function *f)
+{
+ return container_of(f, struct acc_dev, function);
+}
+
+static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ /* now allocate buffers for the requests */
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static void acc_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+/* add a request to the tail of a list */
+static void req_put(struct acc_dev *dev, struct list_head *head,
+ struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_add_tail(&req->list, head);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* remove a request from the head of a list */
+static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (list_empty(head)) {
+ req = 0;
+ } else {
+ req = list_first_entry(head, struct usb_request, list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return req;
+}
+
+static void acc_set_disconnected(struct acc_dev *dev)
+{
+ dev->online = 0;
+ dev->disconnected = 1;
+}
+
+static void acc_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+ struct acc_dev *dev = _acc_dev;
+
+ if (req->status == -ESHUTDOWN) {
+ pr_debug("acc_complete_in set disconnected");
+ acc_set_disconnected(dev);
+ }
+
+ req_put(dev, &dev->tx_idle, req);
+
+ wake_up(&dev->write_wq);
+}
+
+static void acc_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+ struct acc_dev *dev = _acc_dev;
+
+ dev->rx_done = 1;
+ if (req->status == -ESHUTDOWN) {
+ pr_debug("acc_complete_out set disconnected");
+ acc_set_disconnected(dev);
+ }
+
+ wake_up(&dev->read_wq);
+}
+
+static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req)
+{
+ struct acc_dev *dev = ep->driver_data;
+ char *string_dest = NULL;
+ int length = req->actual;
+
+ if (req->status != 0) {
+ pr_err("acc_complete_set_string, err %d\n", req->status);
+ return;
+ }
+
+ switch (dev->string_index) {
+ case ACCESSORY_STRING_MANUFACTURER:
+ string_dest = dev->manufacturer;
+ break;
+ case ACCESSORY_STRING_MODEL:
+ string_dest = dev->model;
+ break;
+ case ACCESSORY_STRING_DESCRIPTION:
+ string_dest = dev->description;
+ break;
+ case ACCESSORY_STRING_VERSION:
+ string_dest = dev->version;
+ break;
+ case ACCESSORY_STRING_URI:
+ string_dest = dev->uri;
+ break;
+ case ACCESSORY_STRING_SERIAL:
+ string_dest = dev->serial;
+ break;
+ }
+ if (string_dest) {
+ unsigned long flags;
+
+ if (length >= ACC_STRING_SIZE)
+ length = ACC_STRING_SIZE - 1;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ memcpy(string_dest, req->buf, length);
+ /* ensure zero termination */
+ string_dest[length] = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ } else {
+ pr_err("unknown accessory string index %d\n",
+ dev->string_index);
+ }
+}
+
+static void acc_complete_set_hid_report_desc(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct acc_hid_dev *hid = req->context;
+ struct acc_dev *dev = hid->dev;
+ int length = req->actual;
+
+ if (req->status != 0) {
+ pr_err("acc_complete_set_hid_report_desc, err %d\n",
+ req->status);
+ return;
+ }
+
+ memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length);
+ hid->report_desc_offset += length;
+ if (hid->report_desc_offset == hid->report_desc_len) {
+ /* After we have received the entire report descriptor
+ * we schedule work to initialize the HID device
+ */
+ schedule_work(&dev->hid_work);
+ }
+}
+
+static void acc_complete_send_hid_event(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct acc_hid_dev *hid = req->context;
+ int length = req->actual;
+
+ if (req->status != 0) {
+ pr_err("acc_complete_send_hid_event, err %d\n", req->status);
+ return;
+ }
+
+ hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1);
+}
+
+static int acc_hid_parse(struct hid_device *hid)
+{
+ struct acc_hid_dev *hdev = hid->driver_data;
+
+ hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len);
+ return 0;
+}
+
+static int acc_hid_start(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void acc_hid_stop(struct hid_device *hid)
+{
+}
+
+static int acc_hid_open(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void acc_hid_close(struct hid_device *hid)
+{
+}
+
+static struct hid_ll_driver acc_hid_ll_driver = {
+ .parse = acc_hid_parse,
+ .start = acc_hid_start,
+ .stop = acc_hid_stop,
+ .open = acc_hid_open,
+ .close = acc_hid_close,
+};
+
+static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev,
+ int id, int desc_len)
+{
+ struct acc_hid_dev *hdev;
+
+ hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC);
+ if (!hdev)
+ return NULL;
+ hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC);
+ if (!hdev->report_desc) {
+ kfree(hdev);
+ return NULL;
+ }
+ hdev->dev = dev;
+ hdev->id = id;
+ hdev->report_desc_len = desc_len;
+
+ return hdev;
+}
+
+static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id)
+{
+ struct acc_hid_dev *hid;
+
+ list_for_each_entry(hid, list, list) {
+ if (hid->id == id)
+ return hid;
+ }
+ return NULL;
+}
+
+static int acc_register_hid(struct acc_dev *dev, int id, int desc_length)
+{
+ struct acc_hid_dev *hid;
+ unsigned long flags;
+
+ /* report descriptor length must be > 0 */
+ if (desc_length <= 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ /* replace HID if one already exists with this ID */
+ hid = acc_hid_get(&dev->hid_list, id);
+ if (!hid)
+ hid = acc_hid_get(&dev->new_hid_list, id);
+ if (hid)
+ list_move(&hid->list, &dev->dead_hid_list);
+
+ hid = acc_hid_new(dev, id, desc_length);
+ if (!hid) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENOMEM;
+ }
+
+ list_add(&hid->list, &dev->new_hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* schedule work to register the HID device */
+ schedule_work(&dev->hid_work);
+ return 0;
+}
+
+static int acc_unregister_hid(struct acc_dev *dev, int id)
+{
+ struct acc_hid_dev *hid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->hid_list, id);
+ if (!hid)
+ hid = acc_hid_get(&dev->new_hid_list, id);
+ if (!hid) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EINVAL;
+ }
+
+ list_move(&hid->list, &dev->dead_hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ schedule_work(&dev->hid_work);
+ return 0;
+}
+
+static int create_bulk_endpoints(struct acc_dev *dev,
+ struct usb_endpoint_descriptor *in_desc,
+ struct usb_endpoint_descriptor *out_desc)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ int i;
+
+ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev);
+
+ ep = usb_ep_autoconfig(cdev->gadget, in_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_in = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_out = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_out = ep;
+
+ /* now allocate requests for our endpoints */
+ for (i = 0; i < TX_REQ_MAX; i++) {
+ req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = acc_complete_in;
+ req_put(dev, &dev->tx_idle, req);
+ }
+ for (i = 0; i < RX_REQ_MAX; i++) {
+ req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = acc_complete_out;
+ dev->rx_req[i] = req;
+ }
+
+ return 0;
+
+fail:
+ pr_err("acc_bind() could not allocate requests\n");
+ while ((req = req_get(dev, &dev->tx_idle)))
+ acc_request_free(req, dev->ep_in);
+ for (i = 0; i < RX_REQ_MAX; i++)
+ acc_request_free(dev->rx_req[i], dev->ep_out);
+ return -1;
+}
+
+static ssize_t acc_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct acc_dev *dev = fp->private_data;
+ struct usb_request *req;
+ ssize_t r = count;
+ unsigned xfer;
+ int ret = 0;
+
+ pr_debug("acc_read(%zu)\n", count);
+
+ if (dev->disconnected) {
+ pr_debug("acc_read disconnected");
+ return -ENODEV;
+ }
+
+ if (count > BULK_BUFFER_SIZE)
+ count = BULK_BUFFER_SIZE;
+
+ /* we will block until we're online */
+ pr_debug("acc_read: waiting for online\n");
+ ret = wait_event_interruptible(dev->read_wq, dev->online);
+ if (ret < 0) {
+ r = ret;
+ goto done;
+ }
+
+ if (dev->rx_done) {
+ // last req cancelled. try to get it.
+ req = dev->rx_req[0];
+ goto copy_data;
+ }
+
+requeue_req:
+ /* queue a request */
+ req = dev->rx_req[0];
+ req->length = count;
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
+ if (ret < 0) {
+ r = -EIO;
+ goto done;
+ } else {
+ pr_debug("rx %p queue\n", req);
+ }
+
+ /* wait for a request to complete */
+ ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+ if (ret < 0) {
+ r = ret;
+ ret = usb_ep_dequeue(dev->ep_out, req);
+ if (ret != 0) {
+ // cancel failed. There can be a data already received.
+ // it will be retrieved in the next read.
+ pr_debug("acc_read: cancelling failed %d", ret);
+ }
+ goto done;
+ }
+
+copy_data:
+ dev->rx_done = 0;
+ if (dev->online) {
+ /* If we got a 0-len packet, throw it back and try again. */
+ if (req->actual == 0)
+ goto requeue_req;
+
+ pr_debug("rx %p %u\n", req, req->actual);
+ xfer = (req->actual < count) ? req->actual : count;
+ r = xfer;
+ if (copy_to_user(buf, req->buf, xfer))
+ r = -EFAULT;
+ } else
+ r = -EIO;
+
+done:
+ pr_debug("acc_read returning %zd\n", r);
+ return r;
+}
+
+static ssize_t acc_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct acc_dev *dev = fp->private_data;
+ struct usb_request *req = 0;
+ ssize_t r = count;
+ unsigned xfer;
+ int ret;
+
+ pr_debug("acc_write(%zu)\n", count);
+
+ if (!dev->online || dev->disconnected) {
+ pr_debug("acc_write disconnected or not online");
+ return -ENODEV;
+ }
+
+ while (count > 0) {
+ if (!dev->online) {
+ pr_debug("acc_write dev->error\n");
+ r = -EIO;
+ break;
+ }
+
+ /* get an idle tx request to use */
+ req = 0;
+ ret = wait_event_interruptible(dev->write_wq,
+ ((req = req_get(dev, &dev->tx_idle)) || !dev->online));
+ if (!req) {
+ r = ret;
+ break;
+ }
+
+ if (count > BULK_BUFFER_SIZE)
+ xfer = BULK_BUFFER_SIZE;
+ else
+ xfer = count;
+ if (copy_from_user(req->buf, buf, xfer)) {
+ r = -EFAULT;
+ break;
+ }
+
+ req->length = xfer;
+ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
+ if (ret < 0) {
+ pr_debug("acc_write: xfer error %d\n", ret);
+ r = -EIO;
+ break;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ /* zero this so we don't try to free it on error exit */
+ req = 0;
+ }
+
+ if (req)
+ req_put(dev, &dev->tx_idle, req);
+
+ pr_debug("acc_write returning %zd\n", r);
+ return r;
+}
+
+static long acc_ioctl(struct file *fp, unsigned code, unsigned long value)
+{
+ struct acc_dev *dev = fp->private_data;
+ char *src = NULL;
+ int ret;
+
+ switch (code) {
+ case ACCESSORY_GET_STRING_MANUFACTURER:
+ src = dev->manufacturer;
+ break;
+ case ACCESSORY_GET_STRING_MODEL:
+ src = dev->model;
+ break;
+ case ACCESSORY_GET_STRING_DESCRIPTION:
+ src = dev->description;
+ break;
+ case ACCESSORY_GET_STRING_VERSION:
+ src = dev->version;
+ break;
+ case ACCESSORY_GET_STRING_URI:
+ src = dev->uri;
+ break;
+ case ACCESSORY_GET_STRING_SERIAL:
+ src = dev->serial;
+ break;
+ case ACCESSORY_IS_START_REQUESTED:
+ return dev->start_requested;
+ case ACCESSORY_GET_AUDIO_MODE:
+ return dev->audio_mode;
+ }
+ if (!src)
+ return -EINVAL;
+
+ ret = strlen(src) + 1;
+ if (copy_to_user((void __user *)value, src, ret))
+ ret = -EFAULT;
+ return ret;
+}
+
+static int acc_open(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "acc_open\n");
+ if (atomic_xchg(&_acc_dev->open_excl, 1))
+ return -EBUSY;
+
+ _acc_dev->disconnected = 0;
+ fp->private_data = _acc_dev;
+ return 0;
+}
+
+static int acc_release(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "acc_release\n");
+
+ WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0));
+ _acc_dev->disconnected = 0;
+ return 0;
+}
+
+/* file operations for /dev/usb_accessory */
+static const struct file_operations acc_fops = {
+ .owner = THIS_MODULE,
+ .read = acc_read,
+ .write = acc_write,
+ .unlocked_ioctl = acc_ioctl,
+ .open = acc_open,
+ .release = acc_release,
+};
+
+static int acc_hid_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+}
+
+static struct miscdevice acc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "usb_accessory",
+ .fops = &acc_fops,
+};
+
+static const struct hid_device_id acc_hid_table[] = {
+ { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
+ { }
+};
+
+static struct hid_driver acc_hid_driver = {
+ .name = "USB accessory",
+ .id_table = acc_hid_table,
+ .probe = acc_hid_probe,
+};
+
+static int acc_ctrlrequest(struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct acc_dev *dev = _acc_dev;
+ int value = -EOPNOTSUPP;
+ struct acc_hid_dev *hid;
+ int offset;
+ u8 b_requestType = ctrl->bRequestType;
+ u8 b_request = ctrl->bRequest;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ unsigned long flags;
+
+/*
+ printk(KERN_INFO "acc_ctrlrequest "
+ "%02x.%02x v%04x i%04x l%u\n",
+ b_requestType, b_request,
+ w_value, w_index, w_length);
+*/
+
+ if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) {
+ if (b_request == ACCESSORY_START) {
+ dev->start_requested = 1;
+ schedule_delayed_work(
+ &dev->start_work, msecs_to_jiffies(10));
+ value = 0;
+ } else if (b_request == ACCESSORY_SEND_STRING) {
+ dev->string_index = w_index;
+ cdev->gadget->ep0->driver_data = dev;
+ cdev->req->complete = acc_complete_set_string;
+ value = w_length;
+ } else if (b_request == ACCESSORY_SET_AUDIO_MODE &&
+ w_index == 0 && w_length == 0) {
+ dev->audio_mode = w_value;
+ value = 0;
+ } else if (b_request == ACCESSORY_REGISTER_HID) {
+ value = acc_register_hid(dev, w_value, w_index);
+ } else if (b_request == ACCESSORY_UNREGISTER_HID) {
+ value = acc_unregister_hid(dev, w_value);
+ } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) {
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->new_hid_list, w_value);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!hid) {
+ value = -EINVAL;
+ goto err;
+ }
+ offset = w_index;
+ if (offset != hid->report_desc_offset
+ || offset + w_length > hid->report_desc_len) {
+ value = -EINVAL;
+ goto err;
+ }
+ cdev->req->context = hid;
+ cdev->req->complete = acc_complete_set_hid_report_desc;
+ value = w_length;
+ } else if (b_request == ACCESSORY_SEND_HID_EVENT) {
+ spin_lock_irqsave(&dev->lock, flags);
+ hid = acc_hid_get(&dev->hid_list, w_value);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!hid) {
+ value = -EINVAL;
+ goto err;
+ }
+ cdev->req->context = hid;
+ cdev->req->complete = acc_complete_send_hid_event;
+ value = w_length;
+ }
+ } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) {
+ if (b_request == ACCESSORY_GET_PROTOCOL) {
+ *((u16 *)cdev->req->buf) = PROTOCOL_VERSION;
+ value = sizeof(u16);
+
+ /* clear any string left over from a previous session */
+ memset(dev->manufacturer, 0, sizeof(dev->manufacturer));
+ memset(dev->model, 0, sizeof(dev->model));
+ memset(dev->description, 0, sizeof(dev->description));
+ memset(dev->version, 0, sizeof(dev->version));
+ memset(dev->uri, 0, sizeof(dev->uri));
+ memset(dev->serial, 0, sizeof(dev->serial));
+ dev->start_requested = 0;
+ dev->audio_mode = 0;
+ }
+ }
+
+ if (value >= 0) {
+ cdev->req->zero = 0;
+ cdev->req->length = value;
+ value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(cdev, "%s setup response queue error\n",
+ __func__);
+ }
+
+err:
+ if (value == -EOPNOTSUPP)
+ VDBG(cdev,
+ "unknown class-specific control req "
+ "%02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ return value;
+}
+
+static int
+acc_function_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct acc_dev *dev = func_to_dev(f);
+ int id;
+ int ret;
+
+ DBG(cdev, "acc_function_bind dev: %p\n", dev);
+
+ ret = hid_register_driver(&acc_hid_driver);
+ if (ret)
+ return ret;
+
+ dev->start_requested = 0;
+
+ /* allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ acc_interface_desc.bInterfaceNumber = id;
+
+ /* allocate endpoints */
+ ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc,
+ &acc_fullspeed_out_desc);
+ if (ret)
+ return ret;
+
+ /* support high speed hardware */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ acc_highspeed_in_desc.bEndpointAddress =
+ acc_fullspeed_in_desc.bEndpointAddress;
+ acc_highspeed_out_desc.bEndpointAddress =
+ acc_fullspeed_out_desc.bEndpointAddress;
+ }
+
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ f->name, dev->ep_in->name, dev->ep_out->name);
+ return 0;
+}
+
+static void
+kill_all_hid_devices(struct acc_dev *dev)
+{
+ struct acc_hid_dev *hid;
+ struct list_head *entry, *temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_for_each_safe(entry, temp, &dev->hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ list_add(&hid->list, &dev->dead_hid_list);
+ }
+ list_for_each_safe(entry, temp, &dev->new_hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ list_add(&hid->list, &dev->dead_hid_list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ schedule_work(&dev->hid_work);
+}
+
+static void
+acc_hid_unbind(struct acc_dev *dev)
+{
+ hid_unregister_driver(&acc_hid_driver);
+ kill_all_hid_devices(dev);
+}
+
+static void
+acc_function_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct acc_dev *dev = func_to_dev(f);
+ struct usb_request *req;
+ int i;
+
+ while ((req = req_get(dev, &dev->tx_idle)))
+ acc_request_free(req, dev->ep_in);
+ for (i = 0; i < RX_REQ_MAX; i++)
+ acc_request_free(dev->rx_req[i], dev->ep_out);
+
+ acc_hid_unbind(dev);
+}
+
+static void acc_start_work(struct work_struct *data)
+{
+ char *envp[2] = { "ACCESSORY=START", NULL };
+ kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
+}
+
+static int acc_hid_init(struct acc_hid_dev *hdev)
+{
+ struct hid_device *hid;
+ int ret;
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid))
+ return PTR_ERR(hid);
+
+ hid->ll_driver = &acc_hid_ll_driver;
+ hid->dev.parent = acc_device.this_device;
+
+ hid->bus = BUS_USB;
+ hid->vendor = HID_ANY_ID;
+ hid->product = HID_ANY_ID;
+ hid->driver_data = hdev;
+ ret = hid_add_device(hid);
+ if (ret) {
+ pr_err("can't add hid device: %d\n", ret);
+ hid_destroy_device(hid);
+ return ret;
+ }
+
+ hdev->hid = hid;
+ return 0;
+}
+
+static void acc_hid_delete(struct acc_hid_dev *hid)
+{
+ kfree(hid->report_desc);
+ kfree(hid);
+}
+
+static void acc_hid_work(struct work_struct *data)
+{
+ struct acc_dev *dev = _acc_dev;
+ struct list_head *entry, *temp;
+ struct acc_hid_dev *hid;
+ struct list_head new_list, dead_list;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&new_list);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* copy hids that are ready for initialization to new_list */
+ list_for_each_safe(entry, temp, &dev->new_hid_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ if (hid->report_desc_offset == hid->report_desc_len)
+ list_move(&hid->list, &new_list);
+ }
+
+ if (list_empty(&dev->dead_hid_list)) {
+ INIT_LIST_HEAD(&dead_list);
+ } else {
+ /* move all of dev->dead_hid_list to dead_list */
+ dead_list.prev = dev->dead_hid_list.prev;
+ dead_list.next = dev->dead_hid_list.next;
+ dead_list.next->prev = &dead_list;
+ dead_list.prev->next = &dead_list;
+ INIT_LIST_HEAD(&dev->dead_hid_list);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* register new HID devices */
+ list_for_each_safe(entry, temp, &new_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ if (acc_hid_init(hid)) {
+ pr_err("can't add HID device %p\n", hid);
+ acc_hid_delete(hid);
+ } else {
+ spin_lock_irqsave(&dev->lock, flags);
+ list_move(&hid->list, &dev->hid_list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+ }
+
+ /* remove dead HID devices */
+ list_for_each_safe(entry, temp, &dead_list) {
+ hid = list_entry(entry, struct acc_hid_dev, list);
+ list_del(&hid->list);
+ if (hid->hid)
+ hid_destroy_device(hid->hid);
+ acc_hid_delete(hid);
+ }
+}
+
+static int acc_function_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct acc_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt);
+
+ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
+ if (ret)
+ return ret;
+
+ ret = usb_ep_enable(dev->ep_in);
+ if (ret)
+ return ret;
+
+ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
+ if (ret)
+ return ret;
+
+ ret = usb_ep_enable(dev->ep_out);
+ if (ret) {
+ usb_ep_disable(dev->ep_in);
+ return ret;
+ }
+
+ dev->online = 1;
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+ return 0;
+}
+
+static void acc_function_disable(struct usb_function *f)
+{
+ struct acc_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = dev->cdev;
+
+ DBG(cdev, "acc_function_disable\n");
+ acc_set_disconnected(dev);
+ usb_ep_disable(dev->ep_in);
+ usb_ep_disable(dev->ep_out);
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+
+ VDBG(cdev, "%s disabled\n", dev->function.name);
+}
+
+static int acc_bind_config(struct usb_configuration *c)
+{
+ struct acc_dev *dev = _acc_dev;
+ int ret;
+
+ printk(KERN_INFO "acc_bind_config\n");
+
+ /* allocate a string ID for our interface */
+ if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) {
+ ret = usb_string_id(c->cdev);
+ if (ret < 0)
+ return ret;
+ acc_string_defs[INTERFACE_STRING_INDEX].id = ret;
+ acc_interface_desc.iInterface = ret;
+ }
+
+ dev->cdev = c->cdev;
+ dev->function.name = "accessory";
+ dev->function.strings = acc_strings,
+ dev->function.fs_descriptors = fs_acc_descs;
+ dev->function.hs_descriptors = hs_acc_descs;
+ dev->function.bind = acc_function_bind;
+ dev->function.unbind = acc_function_unbind;
+ dev->function.set_alt = acc_function_set_alt;
+ dev->function.disable = acc_function_disable;
+
+ return usb_add_function(c, &dev->function);
+}
+
+static int acc_setup(void)
+{
+ struct acc_dev *dev;
+ int ret;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->lock);
+ init_waitqueue_head(&dev->read_wq);
+ init_waitqueue_head(&dev->write_wq);
+ atomic_set(&dev->open_excl, 0);
+ INIT_LIST_HEAD(&dev->tx_idle);
+ INIT_LIST_HEAD(&dev->hid_list);
+ INIT_LIST_HEAD(&dev->new_hid_list);
+ INIT_LIST_HEAD(&dev->dead_hid_list);
+ INIT_DELAYED_WORK(&dev->start_work, acc_start_work);
+ INIT_WORK(&dev->hid_work, acc_hid_work);
+
+ /* _acc_dev must be set before calling usb_gadget_register_driver */
+ _acc_dev = dev;
+
+ ret = misc_register(&acc_device);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ kfree(dev);
+ pr_err("USB accessory gadget driver failed to initialize\n");
+ return ret;
+}
+
+static void acc_disconnect(void)
+{
+ /* unregister all HID devices if USB is disconnected */
+ kill_all_hid_devices(_acc_dev);
+}
+
+static void acc_cleanup(void)
+{
+ misc_deregister(&acc_device);
+ kfree(_acc_dev);
+ _acc_dev = NULL;
+}
diff --git a/drivers/usb/gadget/legacy/f_audio_source.c b/drivers/usb/gadget/legacy/f_audio_source.c
new file mode 100644
index 000000000000..644f6be05281
--- /dev/null
+++ b/drivers/usb/gadget/legacy/f_audio_source.c
@@ -0,0 +1,828 @@
+/*
+ * Gadget Function Driver for USB audio source device
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/device.h>
+#include <linux/usb/audio.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+
+#define SAMPLE_RATE 44100
+#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000)
+
+#define IN_EP_MAX_PACKET_SIZE 384
+
+/* Number of requests to allocate */
+#define IN_EP_REQ_COUNT 4
+
+#define AUDIO_AC_INTERFACE 0
+#define AUDIO_AS_INTERFACE 1
+#define AUDIO_NUM_INTERFACES 2
+
+/* B.3.1 Standard AC Interface Descriptor */
+static struct usb_interface_descriptor ac_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+};
+
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
+
+#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES)
+/* 1 input terminal, 1 output terminal and 1 feature unit */
+#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \
+ + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \
+ + UAC_DT_FEATURE_UNIT_SIZE(0))
+/* B.3.2 Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_2 ac_header_desc = {
+ .bLength = UAC_DT_AC_HEADER_LENGTH,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_HEADER,
+ .bcdADC = __constant_cpu_to_le16(0x0100),
+ .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
+ .bInCollection = AUDIO_NUM_INTERFACES,
+ .baInterfaceNr = {
+ [0] = AUDIO_AC_INTERFACE,
+ [1] = AUDIO_AS_INTERFACE,
+ }
+};
+
+#define INPUT_TERMINAL_ID 1
+static struct uac_input_terminal_descriptor input_terminal_desc = {
+ .bLength = UAC_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = INPUT_TERMINAL_ID,
+ .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE,
+ .bAssocTerminal = 0,
+ .wChannelConfig = 0x3,
+};
+
+DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
+
+#define FEATURE_UNIT_ID 2
+static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
+ .bLength = UAC_DT_FEATURE_UNIT_SIZE(0),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_FEATURE_UNIT,
+ .bUnitID = FEATURE_UNIT_ID,
+ .bSourceID = INPUT_TERMINAL_ID,
+ .bControlSize = 2,
+};
+
+#define OUTPUT_TERMINAL_ID 3
+static struct uac1_output_terminal_descriptor output_terminal_desc = {
+ .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = OUTPUT_TERMINAL_ID,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = FEATURE_UNIT_ID,
+ .bSourceID = FEATURE_UNIT_ID,
+};
+
+/* B.4.1 Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_interface_alt_0_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_interface_alt_1_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2 Class-Specific AS Interface Descriptor */
+static struct uac1_as_header_descriptor as_header_desc = {
+ .bLength = UAC_DT_AS_HEADER_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_AS_GENERAL,
+ .bTerminalLink = INPUT_TERMINAL_ID,
+ .bDelay = 1,
+ .wFormatTag = UAC_FORMAT_TYPE_I_PCM,
+};
+
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
+
+static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
+ .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_FORMAT_TYPE,
+ .bFormatType = UAC_FORMAT_TYPE_I,
+ .bSubframeSize = 2,
+ .bBitResolution = 16,
+ .bSamFreqType = 1,
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor hs_as_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_SYNC_SYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+ .bInterval = 4, /* poll 1 per millisecond */
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor fs_as_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_SYNC_SYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+ .bInterval = 1, /* poll 1 per millisecond */
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
+ .bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = UAC_EP_GENERAL,
+ .bmAttributes = 1,
+ .bLockDelayUnits = 1,
+ .wLockDelay = __constant_cpu_to_le16(1),
+};
+
+static struct usb_descriptor_header *hs_audio_desc[] = {
+ (struct usb_descriptor_header *)&ac_interface_desc,
+ (struct usb_descriptor_header *)&ac_header_desc,
+
+ (struct usb_descriptor_header *)&input_terminal_desc,
+ (struct usb_descriptor_header *)&output_terminal_desc,
+ (struct usb_descriptor_header *)&feature_unit_desc,
+
+ (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_header_desc,
+
+ (struct usb_descriptor_header *)&as_type_i_desc,
+
+ (struct usb_descriptor_header *)&hs_as_in_ep_desc,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *fs_audio_desc[] = {
+ (struct usb_descriptor_header *)&ac_interface_desc,
+ (struct usb_descriptor_header *)&ac_header_desc,
+
+ (struct usb_descriptor_header *)&input_terminal_desc,
+ (struct usb_descriptor_header *)&output_terminal_desc,
+ (struct usb_descriptor_header *)&feature_unit_desc,
+
+ (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_header_desc,
+
+ (struct usb_descriptor_header *)&as_type_i_desc,
+
+ (struct usb_descriptor_header *)&fs_as_in_ep_desc,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
+static struct snd_pcm_hardware audio_hw_info = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = SAMPLE_RATE,
+ .rate_max = SAMPLE_RATE,
+
+ .buffer_bytes_max = 1024 * 1024,
+ .period_bytes_min = 64,
+ .period_bytes_max = 512 * 1024,
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+/*-------------------------------------------------------------------------*/
+
+struct audio_source_config {
+ int card;
+ int device;
+};
+
+struct audio_dev {
+ struct usb_function func;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+
+ struct list_head idle_reqs;
+ struct usb_ep *in_ep;
+
+ spinlock_t lock;
+
+ /* beginning, end and current position in our buffer */
+ void *buffer_start;
+ void *buffer_end;
+ void *buffer_pos;
+
+ /* byte size of a "period" */
+ unsigned int period;
+ /* bytes sent since last call to snd_pcm_period_elapsed */
+ unsigned int period_offset;
+ /* time we started playing */
+ ktime_t start_time;
+ /* number of frames sent since start_time */
+ s64 frames_sent;
+};
+
+static inline struct audio_dev *func_to_audio(struct usb_function *f)
+{
+ return container_of(f, struct audio_dev, func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+ req->length = buffer_size;
+ return req;
+}
+
+static void audio_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static void audio_req_put(struct audio_dev *audio, struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ list_add_tail(&req->list, &audio->idle_reqs);
+ spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static struct usb_request *audio_req_get(struct audio_dev *audio)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ if (list_empty(&audio->idle_reqs)) {
+ req = 0;
+ } else {
+ req = list_first_entry(&audio->idle_reqs, struct usb_request,
+ list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&audio->lock, flags);
+ return req;
+}
+
+/* send the appropriate number of packets to match our bitrate */
+static void audio_send(struct audio_dev *audio)
+{
+ struct snd_pcm_runtime *runtime;
+ struct usb_request *req;
+ int length, length1, length2, ret;
+ s64 msecs;
+ s64 frames;
+ ktime_t now;
+
+ /* audio->substream will be null if we have been closed */
+ if (!audio->substream)
+ return;
+ /* audio->buffer_pos will be null if we have been stopped */
+ if (!audio->buffer_pos)
+ return;
+
+ runtime = audio->substream->runtime;
+
+ /* compute number of frames to send */
+ now = ktime_get();
+ msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
+ do_div(msecs, 1000000);
+ frames = msecs * SAMPLE_RATE;
+ do_div(frames, 1000);
+
+ /* Readjust our frames_sent if we fall too far behind.
+ * If we get too far behind it is better to drop some frames than
+ * to keep sending data too fast in an attempt to catch up.
+ */
+ if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC)
+ audio->frames_sent = frames - FRAMES_PER_MSEC;
+
+ frames -= audio->frames_sent;
+
+ /* We need to send something to keep the pipeline going */
+ if (frames <= 0)
+ frames = FRAMES_PER_MSEC;
+
+ while (frames > 0) {
+ req = audio_req_get(audio);
+ if (!req)
+ break;
+
+ length = frames_to_bytes(runtime, frames);
+ if (length > IN_EP_MAX_PACKET_SIZE)
+ length = IN_EP_MAX_PACKET_SIZE;
+
+ if (audio->buffer_pos + length > audio->buffer_end)
+ length1 = audio->buffer_end - audio->buffer_pos;
+ else
+ length1 = length;
+ memcpy(req->buf, audio->buffer_pos, length1);
+ if (length1 < length) {
+ /* Wrap around and copy remaining length
+ * at beginning of buffer.
+ */
+ length2 = length - length1;
+ memcpy(req->buf + length1, audio->buffer_start,
+ length2);
+ audio->buffer_pos = audio->buffer_start + length2;
+ } else {
+ audio->buffer_pos += length1;
+ if (audio->buffer_pos >= audio->buffer_end)
+ audio->buffer_pos = audio->buffer_start;
+ }
+
+ req->length = length;
+ ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC);
+ if (ret < 0) {
+ pr_err("usb_ep_queue failed ret: %d\n", ret);
+ audio_req_put(audio, req);
+ break;
+ }
+
+ frames -= bytes_to_frames(runtime, length);
+ audio->frames_sent += bytes_to_frames(runtime, length);
+ }
+}
+
+static void audio_control_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ /* nothing to do here */
+}
+
+static void audio_data_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct audio_dev *audio = req->context;
+
+ pr_debug("audio_data_complete req->status %d req->actual %d\n",
+ req->status, req->actual);
+
+ audio_req_put(audio, req);
+
+ if (!audio->buffer_start || req->status)
+ return;
+
+ audio->period_offset += req->actual;
+ if (audio->period_offset >= audio->period) {
+ snd_pcm_period_elapsed(audio->substream);
+ audio->period_offset = 0;
+ }
+ audio_send(audio);
+}
+
+static int audio_set_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int value = -EOPNOTSUPP;
+ u16 ep = le16_to_cpu(ctrl->wIndex);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+
+ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ switch (ctrl->bRequest) {
+ case UAC_SET_CUR:
+ case UAC_SET_MIN:
+ case UAC_SET_MAX:
+ case UAC_SET_RES:
+ value = len;
+ break;
+ default:
+ break;
+ }
+
+ return value;
+}
+
+static int audio_get_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int value = -EOPNOTSUPP;
+ u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 *buf = cdev->req->buf;
+
+ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) {
+ switch (ctrl->bRequest) {
+ case UAC_GET_CUR:
+ case UAC_GET_MIN:
+ case UAC_GET_MAX:
+ case UAC_GET_RES:
+ /* return our sample rate */
+ buf[0] = (u8)SAMPLE_RATE;
+ buf[1] = (u8)(SAMPLE_RATE >> 8);
+ buf[2] = (u8)(SAMPLE_RATE >> 16);
+ value = 3;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return value;
+}
+
+static int
+audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything; interface
+ * activation uses set_alt().
+ */
+ switch (ctrl->bRequestType) {
+ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_set_endpoint_req(f, ctrl);
+ break;
+
+ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_get_endpoint_req(f, ctrl);
+ break;
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ pr_debug("audio req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ req->complete = audio_control_complete;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ pr_err("audio response on err %d\n", value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct audio_dev *audio = func_to_audio(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt);
+
+ ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep);
+ if (ret)
+ return ret;
+
+ usb_ep_enable(audio->in_ep);
+ return 0;
+}
+
+static void audio_disable(struct usb_function *f)
+{
+ struct audio_dev *audio = func_to_audio(f);
+
+ pr_debug("audio_disable\n");
+ usb_ep_disable(audio->in_ep);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void audio_build_desc(struct audio_dev *audio)
+{
+ u8 *sam_freq;
+ int rate;
+
+ /* Set channel numbers */
+ input_terminal_desc.bNrChannels = 2;
+ as_type_i_desc.bNrChannels = 2;
+
+ /* Set sample rates */
+ rate = SAMPLE_RATE;
+ sam_freq = as_type_i_desc.tSamFreq[0];
+ memcpy(sam_freq, &rate, 3);
+}
+
+/* audio function driver setup/binding */
+static int
+audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct audio_dev *audio = func_to_audio(f);
+ int status;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ int i;
+
+ audio_build_desc(audio);
+
+ /* allocate instance-specific interface IDs, and patch descriptors */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ ac_interface_desc.bInterfaceNumber = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ as_interface_alt_0_desc.bInterfaceNumber = status;
+ as_interface_alt_1_desc.bInterfaceNumber = status;
+
+ status = -ENODEV;
+
+ /* allocate our endpoint */
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc);
+ if (!ep)
+ goto fail;
+ audio->in_ep = ep;
+ ep->driver_data = audio; /* claim */
+
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ hs_as_in_ep_desc.bEndpointAddress =
+ fs_as_in_ep_desc.bEndpointAddress;
+
+ f->fs_descriptors = fs_audio_desc;
+ f->hs_descriptors = hs_audio_desc;
+
+ for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) {
+ req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE);
+ if (req) {
+ req->context = audio;
+ req->complete = audio_data_complete;
+ audio_req_put(audio, req);
+ } else
+ status = -ENOMEM;
+ }
+
+fail:
+ return status;
+}
+
+static void
+audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct audio_dev *audio = func_to_audio(f);
+ struct usb_request *req;
+
+ while ((req = audio_req_get(audio)))
+ audio_request_free(req, audio->in_ep);
+
+ snd_card_free_when_closed(audio->card);
+ audio->card = NULL;
+ audio->pcm = NULL;
+ audio->substream = NULL;
+ audio->in_ep = NULL;
+}
+
+static void audio_pcm_playback_start(struct audio_dev *audio)
+{
+ audio->start_time = ktime_get();
+ audio->frames_sent = 0;
+ audio_send(audio);
+}
+
+static void audio_pcm_playback_stop(struct audio_dev *audio)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ audio->buffer_start = 0;
+ audio->buffer_end = 0;
+ audio->buffer_pos = 0;
+ spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static int audio_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = substream->private_data;
+
+ runtime->private_data = audio;
+ runtime->hw = audio_hw_info;
+ snd_pcm_limit_hw_rates(runtime);
+ runtime->hw.channels_max = 2;
+
+ audio->substream = substream;
+ return 0;
+}
+
+static int audio_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct audio_dev *audio = substream->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->lock, flags);
+ audio->substream = NULL;
+ spin_unlock_irqrestore(&audio->lock, flags);
+
+ return 0;
+}
+
+static int audio_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+
+ if (rate != SAMPLE_RATE)
+ return -EINVAL;
+ if (channels != 2)
+ return -EINVAL;
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+}
+
+static int audio_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = runtime->private_data;
+
+ audio->period = snd_pcm_lib_period_bytes(substream);
+ audio->period_offset = 0;
+ audio->buffer_start = runtime->dma_area;
+ audio->buffer_end = audio->buffer_start
+ + snd_pcm_lib_buffer_bytes(substream);
+ audio->buffer_pos = audio->buffer_start;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_dev *audio = runtime->private_data;
+ ssize_t bytes = audio->buffer_pos - audio->buffer_start;
+
+ /* return offset of next frame to fill in our buffer */
+ return bytes_to_frames(runtime, bytes);
+}
+
+static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct audio_dev *audio = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ audio_pcm_playback_start(audio);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ audio_pcm_playback_stop(audio);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct audio_dev _audio_dev = {
+ .func = {
+ .name = "audio_source",
+ .bind = audio_bind,
+ .unbind = audio_unbind,
+ .set_alt = audio_set_alt,
+ .setup = audio_setup,
+ .disable = audio_disable,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock),
+ .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs),
+};
+
+static struct snd_pcm_ops audio_playback_ops = {
+ .open = audio_pcm_open,
+ .close = audio_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = audio_pcm_hw_params,
+ .hw_free = audio_pcm_hw_free,
+ .prepare = audio_pcm_prepare,
+ .trigger = audio_pcm_playback_trigger,
+ .pointer = audio_pcm_pointer,
+};
+
+int audio_source_bind_config(struct usb_configuration *c,
+ struct audio_source_config *config)
+{
+ struct audio_dev *audio;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ int err;
+
+ config->card = -1;
+ config->device = -1;
+
+ audio = &_audio_dev;
+
+ err = snd_card_new(NULL, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (err)
+ return err;
+
+ snd_card_set_dev(card, &c->cdev->gadget->dev);
+
+ err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm);
+ if (err)
+ goto pcm_fail;
+ pcm->private_data = audio;
+ pcm->info_flags = 0;
+ audio->pcm = pcm;
+
+ strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name));
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ NULL, 0, 64 * 1024);
+
+ strlcpy(card->driver, "audio_source", sizeof(card->driver));
+ strlcpy(card->shortname, card->driver, sizeof(card->shortname));
+ strlcpy(card->longname, "USB accessory audio source",
+ sizeof(card->longname));
+
+ err = snd_card_register(card);
+ if (err)
+ goto register_fail;
+
+ err = usb_add_function(c, &audio->func);
+ if (err)
+ goto add_fail;
+
+ config->card = pcm->card->number;
+ config->device = pcm->device;
+ audio->card = card;
+ return 0;
+
+add_fail:
+register_fail:
+pcm_fail:
+ snd_card_free(audio->card);
+ return err;
+}
diff --git a/drivers/usb/gadget/legacy/f_mtp.c b/drivers/usb/gadget/legacy/f_mtp.c
new file mode 100644
index 000000000000..620aeaaf2d72
--- /dev/null
+++ b/drivers/usb/gadget/legacy/f_mtp.c
@@ -0,0 +1,1287 @@
+/*
+ * Gadget Function Driver for MTP
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+
+#include <linux/usb.h>
+#include <linux/usb_usual.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/f_mtp.h>
+
+#define MTP_BULK_BUFFER_SIZE 16384
+#define INTR_BUFFER_SIZE 28
+
+/* String IDs */
+#define INTERFACE_STRING_INDEX 0
+
+/* values for mtp_dev.state */
+#define STATE_OFFLINE 0 /* initial state, disconnected */
+#define STATE_READY 1 /* ready for userspace calls */
+#define STATE_BUSY 2 /* processing userspace calls */
+#define STATE_CANCELED 3 /* transaction canceled by host */
+#define STATE_ERROR 4 /* error from completion routine */
+
+/* number of tx and rx requests to allocate */
+#define TX_REQ_MAX 4
+#define RX_REQ_MAX 2
+#define INTR_REQ_MAX 5
+
+/* ID for Microsoft MTP OS String */
+#define MTP_OS_STRING_ID 0xEE
+
+/* MTP class reqeusts */
+#define MTP_REQ_CANCEL 0x64
+#define MTP_REQ_GET_EXT_EVENT_DATA 0x65
+#define MTP_REQ_RESET 0x66
+#define MTP_REQ_GET_DEVICE_STATUS 0x67
+
+/* constants for device status */
+#define MTP_RESPONSE_OK 0x2001
+#define MTP_RESPONSE_DEVICE_BUSY 0x2019
+
+static const char mtp_shortname[] = "mtp_usb";
+
+struct mtp_dev {
+ struct usb_function function;
+ struct usb_composite_dev *cdev;
+ spinlock_t lock;
+
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
+ struct usb_ep *ep_intr;
+
+ int state;
+
+ /* synchronize access to our device file */
+ atomic_t open_excl;
+ /* to enforce only one ioctl at a time */
+ atomic_t ioctl_excl;
+
+ struct list_head tx_idle;
+ struct list_head intr_idle;
+
+ wait_queue_head_t read_wq;
+ wait_queue_head_t write_wq;
+ wait_queue_head_t intr_wq;
+ struct usb_request *rx_req[RX_REQ_MAX];
+ int rx_done;
+
+ /* for processing MTP_SEND_FILE, MTP_RECEIVE_FILE and
+ * MTP_SEND_FILE_WITH_HEADER ioctls on a work queue
+ */
+ struct workqueue_struct *wq;
+ struct work_struct send_file_work;
+ struct work_struct receive_file_work;
+ struct file *xfer_file;
+ loff_t xfer_file_offset;
+ int64_t xfer_file_length;
+ unsigned xfer_send_header;
+ uint16_t xfer_command;
+ uint32_t xfer_transaction_id;
+ int xfer_result;
+};
+
+static struct usb_interface_descriptor mtp_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC,
+ .bInterfaceProtocol = 0,
+};
+
+static struct usb_interface_descriptor ptp_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = USB_CLASS_STILL_IMAGE,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 1,
+};
+
+static struct usb_endpoint_descriptor mtp_highspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor mtp_highspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor mtp_fullspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor mtp_intr_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE),
+ .bInterval = 6,
+};
+
+static struct usb_descriptor_header *fs_mtp_descs[] = {
+ (struct usb_descriptor_header *) &mtp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_mtp_descs[] = {
+ (struct usb_descriptor_header *) &mtp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *fs_ptp_descs[] = {
+ (struct usb_descriptor_header *) &ptp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_ptp_descs[] = {
+ (struct usb_descriptor_header *) &ptp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_string mtp_string_defs[] = {
+ /* Naming interface "MTP" so libmtp will recognize us */
+ [INTERFACE_STRING_INDEX].s = "MTP",
+ { }, /* end of list */
+};
+
+static struct usb_gadget_strings mtp_string_table = {
+ .language = 0x0409, /* en-US */
+ .strings = mtp_string_defs,
+};
+
+static struct usb_gadget_strings *mtp_strings[] = {
+ &mtp_string_table,
+ NULL,
+};
+
+/* Microsoft MTP OS String */
+static u8 mtp_os_string[] = {
+ 18, /* sizeof(mtp_os_string) */
+ USB_DT_STRING,
+ /* Signature field: "MSFT100" */
+ 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0,
+ /* vendor code */
+ 1,
+ /* padding */
+ 0
+};
+
+/* Microsoft Extended Configuration Descriptor Header Section */
+struct mtp_ext_config_desc_header {
+ __le32 dwLength;
+ __u16 bcdVersion;
+ __le16 wIndex;
+ __u8 bCount;
+ __u8 reserved[7];
+};
+
+/* Microsoft Extended Configuration Descriptor Function Section */
+struct mtp_ext_config_desc_function {
+ __u8 bFirstInterfaceNumber;
+ __u8 bInterfaceCount;
+ __u8 compatibleID[8];
+ __u8 subCompatibleID[8];
+ __u8 reserved[6];
+};
+
+/* MTP Extended Configuration Descriptor */
+struct {
+ struct mtp_ext_config_desc_header header;
+ struct mtp_ext_config_desc_function function;
+} mtp_ext_config_desc = {
+ .header = {
+ .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)),
+ .bcdVersion = __constant_cpu_to_le16(0x0100),
+ .wIndex = __constant_cpu_to_le16(4),
+ .bCount = __constant_cpu_to_le16(1),
+ },
+ .function = {
+ .bFirstInterfaceNumber = 0,
+ .bInterfaceCount = 1,
+ .compatibleID = { 'M', 'T', 'P' },
+ },
+};
+
+struct mtp_device_status {
+ __le16 wLength;
+ __le16 wCode;
+};
+
+struct mtp_data_header {
+ /* length of packet, including this header */
+ __le32 length;
+ /* container type (2 for data packet) */
+ __le16 type;
+ /* MTP command code */
+ __le16 command;
+ /* MTP transaction ID */
+ __le32 transaction_id;
+};
+
+/* temporary variable used between mtp_open() and mtp_gadget_bind() */
+static struct mtp_dev *_mtp_dev;
+
+static inline struct mtp_dev *func_to_mtp(struct usb_function *f)
+{
+ return container_of(f, struct mtp_dev, function);
+}
+
+static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ /* now allocate buffers for the requests */
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static void mtp_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static inline int mtp_lock(atomic_t *excl)
+{
+ if (atomic_inc_return(excl) == 1) {
+ return 0;
+ } else {
+ atomic_dec(excl);
+ return -1;
+ }
+}
+
+static inline void mtp_unlock(atomic_t *excl)
+{
+ atomic_dec(excl);
+}
+
+/* add a request to the tail of a list */
+static void mtp_req_put(struct mtp_dev *dev, struct list_head *head,
+ struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_add_tail(&req->list, head);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* remove a request from the head of a list */
+static struct usb_request
+*mtp_req_get(struct mtp_dev *dev, struct list_head *head)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (list_empty(head)) {
+ req = 0;
+ } else {
+ req = list_first_entry(head, struct usb_request, list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return req;
+}
+
+static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ if (req->status != 0)
+ dev->state = STATE_ERROR;
+
+ mtp_req_put(dev, &dev->tx_idle, req);
+
+ wake_up(&dev->write_wq);
+}
+
+static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ dev->rx_done = 1;
+ if (req->status != 0)
+ dev->state = STATE_ERROR;
+
+ wake_up(&dev->read_wq);
+}
+
+static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ if (req->status != 0)
+ dev->state = STATE_ERROR;
+
+ mtp_req_put(dev, &dev->intr_idle, req);
+
+ wake_up(&dev->intr_wq);
+}
+
+static int mtp_create_bulk_endpoints(struct mtp_dev *dev,
+ struct usb_endpoint_descriptor *in_desc,
+ struct usb_endpoint_descriptor *out_desc,
+ struct usb_endpoint_descriptor *intr_desc)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ int i;
+
+ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev);
+
+ ep = usb_ep_autoconfig(cdev->gadget, in_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_in = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_out = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, intr_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for mtp ep_intr got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_intr = ep;
+
+ /* now allocate requests for our endpoints */
+ for (i = 0; i < TX_REQ_MAX; i++) {
+ req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = mtp_complete_in;
+ mtp_req_put(dev, &dev->tx_idle, req);
+ }
+ for (i = 0; i < RX_REQ_MAX; i++) {
+ req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = mtp_complete_out;
+ dev->rx_req[i] = req;
+ }
+ for (i = 0; i < INTR_REQ_MAX; i++) {
+ req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = mtp_complete_intr;
+ mtp_req_put(dev, &dev->intr_idle, req);
+ }
+
+ return 0;
+
+fail:
+ printk(KERN_ERR "mtp_bind() could not allocate requests\n");
+ return -1;
+}
+
+static ssize_t mtp_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mtp_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ ssize_t r = count;
+ unsigned xfer;
+ int ret = 0;
+
+ DBG(cdev, "mtp_read(%zu)\n", count);
+
+ if (count > MTP_BULK_BUFFER_SIZE)
+ return -EINVAL;
+
+ /* we will block until we're online */
+ DBG(cdev, "mtp_read: waiting for online state\n");
+ ret = wait_event_interruptible(dev->read_wq,
+ dev->state != STATE_OFFLINE);
+ if (ret < 0) {
+ r = ret;
+ goto done;
+ }
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED) {
+ /* report cancelation to userspace */
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ return -ECANCELED;
+ }
+ dev->state = STATE_BUSY;
+ spin_unlock_irq(&dev->lock);
+
+requeue_req:
+ /* queue a request */
+ req = dev->rx_req[0];
+ req->length = count;
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
+ if (ret < 0) {
+ r = -EIO;
+ goto done;
+ } else {
+ DBG(cdev, "rx %p queue\n", req);
+ }
+
+ /* wait for a request to complete */
+ ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+ if (ret < 0) {
+ r = ret;
+ usb_ep_dequeue(dev->ep_out, req);
+ goto done;
+ }
+ if (dev->state == STATE_BUSY) {
+ /* If we got a 0-len packet, throw it back and try again. */
+ if (req->actual == 0)
+ goto requeue_req;
+
+ DBG(cdev, "rx %p %d\n", req, req->actual);
+ xfer = (req->actual < count) ? req->actual : count;
+ r = xfer;
+ if (copy_to_user(buf, req->buf, xfer))
+ r = -EFAULT;
+ } else
+ r = -EIO;
+
+done:
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED)
+ r = -ECANCELED;
+ else if (dev->state != STATE_OFFLINE)
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+
+ DBG(cdev, "mtp_read returning %zd\n", r);
+ return r;
+}
+
+static ssize_t mtp_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mtp_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req = 0;
+ ssize_t r = count;
+ unsigned xfer;
+ int sendZLP = 0;
+ int ret;
+
+ DBG(cdev, "mtp_write(%zu)\n", count);
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED) {
+ /* report cancelation to userspace */
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ return -ECANCELED;
+ }
+ if (dev->state == STATE_OFFLINE) {
+ spin_unlock_irq(&dev->lock);
+ return -ENODEV;
+ }
+ dev->state = STATE_BUSY;
+ spin_unlock_irq(&dev->lock);
+
+ /* we need to send a zero length packet to signal the end of transfer
+ * if the transfer size is aligned to a packet boundary.
+ */
+ if ((count & (dev->ep_in->maxpacket - 1)) == 0)
+ sendZLP = 1;
+
+ while (count > 0 || sendZLP) {
+ /* so we exit after sending ZLP */
+ if (count == 0)
+ sendZLP = 0;
+
+ if (dev->state != STATE_BUSY) {
+ DBG(cdev, "mtp_write dev->error\n");
+ r = -EIO;
+ break;
+ }
+
+ /* get an idle tx request to use */
+ req = 0;
+ ret = wait_event_interruptible(dev->write_wq,
+ ((req = mtp_req_get(dev, &dev->tx_idle))
+ || dev->state != STATE_BUSY));
+ if (!req) {
+ r = ret;
+ break;
+ }
+
+ if (count > MTP_BULK_BUFFER_SIZE)
+ xfer = MTP_BULK_BUFFER_SIZE;
+ else
+ xfer = count;
+ if (xfer && copy_from_user(req->buf, buf, xfer)) {
+ r = -EFAULT;
+ break;
+ }
+
+ req->length = xfer;
+ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
+ if (ret < 0) {
+ DBG(cdev, "mtp_write: xfer error %d\n", ret);
+ r = -EIO;
+ break;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ /* zero this so we don't try to free it on error exit */
+ req = 0;
+ }
+
+ if (req)
+ mtp_req_put(dev, &dev->tx_idle, req);
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED)
+ r = -ECANCELED;
+ else if (dev->state != STATE_OFFLINE)
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+
+ DBG(cdev, "mtp_write returning %zd\n", r);
+ return r;
+}
+
+/* read from a local file and write to USB */
+static void send_file_work(struct work_struct *data)
+{
+ struct mtp_dev *dev = container_of(data, struct mtp_dev,
+ send_file_work);
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req = 0;
+ struct mtp_data_header *header;
+ struct file *filp;
+ loff_t offset;
+ int64_t count;
+ int xfer, ret, hdr_size;
+ int r = 0;
+ int sendZLP = 0;
+
+ /* read our parameters */
+ smp_rmb();
+ filp = dev->xfer_file;
+ offset = dev->xfer_file_offset;
+ count = dev->xfer_file_length;
+
+ DBG(cdev, "send_file_work(%lld %lld)\n", offset, count);
+
+ if (dev->xfer_send_header) {
+ hdr_size = sizeof(struct mtp_data_header);
+ count += hdr_size;
+ } else {
+ hdr_size = 0;
+ }
+
+ /* we need to send a zero length packet to signal the end of transfer
+ * if the transfer size is aligned to a packet boundary.
+ */
+ if ((count & (dev->ep_in->maxpacket - 1)) == 0)
+ sendZLP = 1;
+
+ while (count > 0 || sendZLP) {
+ /* so we exit after sending ZLP */
+ if (count == 0)
+ sendZLP = 0;
+
+ /* get an idle tx request to use */
+ req = 0;
+ ret = wait_event_interruptible(dev->write_wq,
+ (req = mtp_req_get(dev, &dev->tx_idle))
+ || dev->state != STATE_BUSY);
+ if (dev->state == STATE_CANCELED) {
+ r = -ECANCELED;
+ break;
+ }
+ if (!req) {
+ r = ret;
+ break;
+ }
+
+ if (count > MTP_BULK_BUFFER_SIZE)
+ xfer = MTP_BULK_BUFFER_SIZE;
+ else
+ xfer = count;
+
+ if (hdr_size) {
+ /* prepend MTP data header */
+ header = (struct mtp_data_header *)req->buf;
+ header->length = __cpu_to_le32(count);
+ header->type = __cpu_to_le16(2); /* data packet */
+ header->command = __cpu_to_le16(dev->xfer_command);
+ header->transaction_id =
+ __cpu_to_le32(dev->xfer_transaction_id);
+ }
+
+ ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size,
+ &offset);
+ if (ret < 0) {
+ r = ret;
+ break;
+ }
+ xfer = ret + hdr_size;
+ hdr_size = 0;
+
+ req->length = xfer;
+ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
+ if (ret < 0) {
+ DBG(cdev, "send_file_work: xfer error %d\n", ret);
+ dev->state = STATE_ERROR;
+ r = -EIO;
+ break;
+ }
+
+ count -= xfer;
+
+ /* zero this so we don't try to free it on error exit */
+ req = 0;
+ }
+
+ if (req)
+ mtp_req_put(dev, &dev->tx_idle, req);
+
+ DBG(cdev, "send_file_work returning %d\n", r);
+ /* write the result */
+ dev->xfer_result = r;
+ smp_wmb();
+}
+
+/* read from USB and write to a local file */
+static void receive_file_work(struct work_struct *data)
+{
+ struct mtp_dev *dev = container_of(data, struct mtp_dev,
+ receive_file_work);
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *read_req = NULL, *write_req = NULL;
+ struct file *filp;
+ loff_t offset;
+ int64_t count;
+ int ret, cur_buf = 0;
+ int r = 0;
+
+ /* read our parameters */
+ smp_rmb();
+ filp = dev->xfer_file;
+ offset = dev->xfer_file_offset;
+ count = dev->xfer_file_length;
+
+ DBG(cdev, "receive_file_work(%lld)\n", count);
+
+ while (count > 0 || write_req) {
+ if (count > 0) {
+ /* queue a request */
+ read_req = dev->rx_req[cur_buf];
+ cur_buf = (cur_buf + 1) % RX_REQ_MAX;
+
+ read_req->length = (count > MTP_BULK_BUFFER_SIZE
+ ? MTP_BULK_BUFFER_SIZE : count);
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
+ if (ret < 0) {
+ r = -EIO;
+ dev->state = STATE_ERROR;
+ break;
+ }
+ }
+
+ if (write_req) {
+ DBG(cdev, "rx %p %d\n", write_req, write_req->actual);
+ ret = vfs_write(filp, write_req->buf, write_req->actual,
+ &offset);
+ DBG(cdev, "vfs_write %d\n", ret);
+ if (ret != write_req->actual) {
+ r = -EIO;
+ dev->state = STATE_ERROR;
+ break;
+ }
+ write_req = NULL;
+ }
+
+ if (read_req) {
+ /* wait for our last read to complete */
+ ret = wait_event_interruptible(dev->read_wq,
+ dev->rx_done || dev->state != STATE_BUSY);
+ if (dev->state == STATE_CANCELED) {
+ r = -ECANCELED;
+ if (!dev->rx_done)
+ usb_ep_dequeue(dev->ep_out, read_req);
+ break;
+ }
+ /* if xfer_file_length is 0xFFFFFFFF, then we read until
+ * we get a zero length packet
+ */
+ if (count != 0xFFFFFFFF)
+ count -= read_req->actual;
+ if (read_req->actual < read_req->length) {
+ /*
+ * short packet is used to signal EOF for
+ * sizes > 4 gig
+ */
+ DBG(cdev, "got short packet\n");
+ count = 0;
+ }
+
+ write_req = read_req;
+ read_req = NULL;
+ }
+ }
+
+ DBG(cdev, "receive_file_work returning %d\n", r);
+ /* write the result */
+ dev->xfer_result = r;
+ smp_wmb();
+}
+
+static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event)
+{
+ struct usb_request *req = NULL;
+ int ret;
+ int length = event->length;
+
+ DBG(dev->cdev, "mtp_send_event(%zu)\n", event->length);
+
+ if (length < 0 || length > INTR_BUFFER_SIZE)
+ return -EINVAL;
+ if (dev->state == STATE_OFFLINE)
+ return -ENODEV;
+
+ ret = wait_event_interruptible_timeout(dev->intr_wq,
+ (req = mtp_req_get(dev, &dev->intr_idle)),
+ msecs_to_jiffies(1000));
+ if (!req)
+ return -ETIME;
+
+ if (copy_from_user(req->buf, (void __user *)event->data, length)) {
+ mtp_req_put(dev, &dev->intr_idle, req);
+ return -EFAULT;
+ }
+ req->length = length;
+ ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL);
+ if (ret)
+ mtp_req_put(dev, &dev->intr_idle, req);
+
+ return ret;
+}
+
+static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
+{
+ struct mtp_dev *dev = fp->private_data;
+ struct file *filp = NULL;
+ int ret = -EINVAL;
+
+ if (mtp_lock(&dev->ioctl_excl))
+ return -EBUSY;
+
+ switch (code) {
+ case MTP_SEND_FILE:
+ case MTP_RECEIVE_FILE:
+ case MTP_SEND_FILE_WITH_HEADER:
+ {
+ struct mtp_file_range mfr;
+ struct work_struct *work;
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED) {
+ /* report cancelation to userspace */
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ ret = -ECANCELED;
+ goto out;
+ }
+ if (dev->state == STATE_OFFLINE) {
+ spin_unlock_irq(&dev->lock);
+ ret = -ENODEV;
+ goto out;
+ }
+ dev->state = STATE_BUSY;
+ spin_unlock_irq(&dev->lock);
+
+ if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ /* hold a reference to the file while we are working with it */
+ filp = fget(mfr.fd);
+ if (!filp) {
+ ret = -EBADF;
+ goto fail;
+ }
+
+ /* write the parameters */
+ dev->xfer_file = filp;
+ dev->xfer_file_offset = mfr.offset;
+ dev->xfer_file_length = mfr.length;
+ smp_wmb();
+
+ if (code == MTP_SEND_FILE_WITH_HEADER) {
+ work = &dev->send_file_work;
+ dev->xfer_send_header = 1;
+ dev->xfer_command = mfr.command;
+ dev->xfer_transaction_id = mfr.transaction_id;
+ } else if (code == MTP_SEND_FILE) {
+ work = &dev->send_file_work;
+ dev->xfer_send_header = 0;
+ } else {
+ work = &dev->receive_file_work;
+ }
+
+ /* We do the file transfer on a work queue so it will run
+ * in kernel context, which is necessary for vfs_read and
+ * vfs_write to use our buffers in the kernel address space.
+ */
+ queue_work(dev->wq, work);
+ /* wait for operation to complete */
+ flush_workqueue(dev->wq);
+ fput(filp);
+
+ /* read the result */
+ smp_rmb();
+ ret = dev->xfer_result;
+ break;
+ }
+ case MTP_SEND_EVENT:
+ {
+ struct mtp_event event;
+ /* return here so we don't change dev->state below,
+ * which would interfere with bulk transfer state.
+ */
+ if (copy_from_user(&event, (void __user *)value, sizeof(event)))
+ ret = -EFAULT;
+ else
+ ret = mtp_send_event(dev, &event);
+ goto out;
+ }
+ }
+
+fail:
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED)
+ ret = -ECANCELED;
+ else if (dev->state != STATE_OFFLINE)
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+out:
+ mtp_unlock(&dev->ioctl_excl);
+ DBG(dev->cdev, "ioctl returning %d\n", ret);
+ return ret;
+}
+
+static int mtp_open(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "mtp_open\n");
+ if (mtp_lock(&_mtp_dev->open_excl))
+ return -EBUSY;
+
+ /* clear any error condition */
+ if (_mtp_dev->state != STATE_OFFLINE)
+ _mtp_dev->state = STATE_READY;
+
+ fp->private_data = _mtp_dev;
+ return 0;
+}
+
+static int mtp_release(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "mtp_release\n");
+
+ mtp_unlock(&_mtp_dev->open_excl);
+ return 0;
+}
+
+/* file operations for /dev/mtp_usb */
+static const struct file_operations mtp_fops = {
+ .owner = THIS_MODULE,
+ .read = mtp_read,
+ .write = mtp_write,
+ .unlocked_ioctl = mtp_ioctl,
+ .open = mtp_open,
+ .release = mtp_release,
+};
+
+static struct miscdevice mtp_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = mtp_shortname,
+ .fops = &mtp_fops,
+};
+
+static int mtp_ctrlrequest(struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct mtp_dev *dev = _mtp_dev;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ unsigned long flags;
+
+ VDBG(cdev, "mtp_ctrlrequest "
+ "%02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /* Handle MTP OS string */
+ if (ctrl->bRequestType ==
+ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)
+ && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR
+ && (w_value >> 8) == USB_DT_STRING
+ && (w_value & 0xFF) == MTP_OS_STRING_ID) {
+ value = (w_length < sizeof(mtp_os_string)
+ ? w_length : sizeof(mtp_os_string));
+ memcpy(cdev->req->buf, mtp_os_string, value);
+ } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
+ /* Handle MTP OS descriptor */
+ DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n",
+ ctrl->bRequest, w_index, w_value, w_length);
+
+ if (ctrl->bRequest == 1
+ && (ctrl->bRequestType & USB_DIR_IN)
+ && (w_index == 4 || w_index == 5)) {
+ value = (w_length < sizeof(mtp_ext_config_desc) ?
+ w_length : sizeof(mtp_ext_config_desc));
+ memcpy(cdev->req->buf, &mtp_ext_config_desc, value);
+ }
+ } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+ DBG(cdev, "class request: %d index: %d value: %d length: %d\n",
+ ctrl->bRequest, w_index, w_value, w_length);
+
+ if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0
+ && w_value == 0) {
+ DBG(cdev, "MTP_REQ_CANCEL\n");
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state == STATE_BUSY) {
+ dev->state = STATE_CANCELED;
+ wake_up(&dev->read_wq);
+ wake_up(&dev->write_wq);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* We need to queue a request to read the remaining
+ * bytes, but we don't actually need to look at
+ * the contents.
+ */
+ value = w_length;
+ } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS
+ && w_index == 0 && w_value == 0) {
+ struct mtp_device_status *status = cdev->req->buf;
+ status->wLength =
+ __constant_cpu_to_le16(sizeof(*status));
+
+ DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ /* device status is "busy" until we report
+ * the cancelation to userspace
+ */
+ if (dev->state == STATE_CANCELED)
+ status->wCode =
+ __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY);
+ else
+ status->wCode =
+ __cpu_to_le16(MTP_RESPONSE_OK);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ value = sizeof(*status);
+ }
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ int rc;
+ cdev->req->zero = value < w_length;
+ cdev->req->length = value;
+ rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+ if (rc < 0)
+ ERROR(cdev, "%s: response queue error\n", __func__);
+ }
+ return value;
+}
+
+static int
+mtp_function_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct mtp_dev *dev = func_to_mtp(f);
+ int id;
+ int ret;
+
+ dev->cdev = cdev;
+ DBG(cdev, "mtp_function_bind dev: %p\n", dev);
+
+ /* allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ mtp_interface_desc.bInterfaceNumber = id;
+
+ /* allocate endpoints */
+ ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc,
+ &mtp_fullspeed_out_desc, &mtp_intr_desc);
+ if (ret)
+ return ret;
+
+ /* support high speed hardware */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ mtp_highspeed_in_desc.bEndpointAddress =
+ mtp_fullspeed_in_desc.bEndpointAddress;
+ mtp_highspeed_out_desc.bEndpointAddress =
+ mtp_fullspeed_out_desc.bEndpointAddress;
+ }
+
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ f->name, dev->ep_in->name, dev->ep_out->name);
+ return 0;
+}
+
+static void
+mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct mtp_dev *dev = func_to_mtp(f);
+ struct usb_request *req;
+ int i;
+
+ while ((req = mtp_req_get(dev, &dev->tx_idle)))
+ mtp_request_free(req, dev->ep_in);
+ for (i = 0; i < RX_REQ_MAX; i++)
+ mtp_request_free(dev->rx_req[i], dev->ep_out);
+ while ((req = mtp_req_get(dev, &dev->intr_idle)))
+ mtp_request_free(req, dev->ep_intr);
+ dev->state = STATE_OFFLINE;
+}
+
+static int mtp_function_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct mtp_dev *dev = func_to_mtp(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt);
+
+ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
+ if (ret)
+ return ret;
+
+ ret = usb_ep_enable(dev->ep_in);
+ if (ret)
+ return ret;
+
+ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
+ if (ret)
+ return ret;
+
+ ret = usb_ep_enable(dev->ep_out);
+ if (ret) {
+ usb_ep_disable(dev->ep_in);
+ return ret;
+ }
+
+ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_intr);
+ if (ret)
+ return ret;
+
+ ret = usb_ep_enable(dev->ep_intr);
+ if (ret) {
+ usb_ep_disable(dev->ep_out);
+ usb_ep_disable(dev->ep_in);
+ return ret;
+ }
+ dev->state = STATE_READY;
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+ return 0;
+}
+
+static void mtp_function_disable(struct usb_function *f)
+{
+ struct mtp_dev *dev = func_to_mtp(f);
+ struct usb_composite_dev *cdev = dev->cdev;
+
+ DBG(cdev, "mtp_function_disable\n");
+ dev->state = STATE_OFFLINE;
+ usb_ep_disable(dev->ep_in);
+ usb_ep_disable(dev->ep_out);
+ usb_ep_disable(dev->ep_intr);
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+
+ VDBG(cdev, "%s disabled\n", dev->function.name);
+}
+
+static int mtp_bind_config(struct usb_configuration *c, bool ptp_config)
+{
+ struct mtp_dev *dev = _mtp_dev;
+ int ret = 0;
+
+ printk(KERN_INFO "mtp_bind_config\n");
+
+ /* allocate a string ID for our interface */
+ if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) {
+ ret = usb_string_id(c->cdev);
+ if (ret < 0)
+ return ret;
+ mtp_string_defs[INTERFACE_STRING_INDEX].id = ret;
+ mtp_interface_desc.iInterface = ret;
+ }
+
+ dev->cdev = c->cdev;
+ dev->function.name = "mtp";
+ dev->function.strings = mtp_strings;
+ if (ptp_config) {
+ dev->function.fs_descriptors = fs_ptp_descs;
+ dev->function.hs_descriptors = hs_ptp_descs;
+ } else {
+ dev->function.fs_descriptors = fs_mtp_descs;
+ dev->function.hs_descriptors = hs_mtp_descs;
+ }
+ dev->function.bind = mtp_function_bind;
+ dev->function.unbind = mtp_function_unbind;
+ dev->function.set_alt = mtp_function_set_alt;
+ dev->function.disable = mtp_function_disable;
+
+ return usb_add_function(c, &dev->function);
+}
+
+static int mtp_setup(void)
+{
+ struct mtp_dev *dev;
+ int ret;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->lock);
+ init_waitqueue_head(&dev->read_wq);
+ init_waitqueue_head(&dev->write_wq);
+ init_waitqueue_head(&dev->intr_wq);
+ atomic_set(&dev->open_excl, 0);
+ atomic_set(&dev->ioctl_excl, 0);
+ INIT_LIST_HEAD(&dev->tx_idle);
+ INIT_LIST_HEAD(&dev->intr_idle);
+
+ dev->wq = create_singlethread_workqueue("f_mtp");
+ if (!dev->wq) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+ INIT_WORK(&dev->send_file_work, send_file_work);
+ INIT_WORK(&dev->receive_file_work, receive_file_work);
+
+ _mtp_dev = dev;
+
+ ret = misc_register(&mtp_device);
+ if (ret)
+ goto err2;
+
+ return 0;
+
+err2:
+ destroy_workqueue(dev->wq);
+err1:
+ _mtp_dev = NULL;
+ kfree(dev);
+ printk(KERN_ERR "mtp gadget driver failed to initialize\n");
+ return ret;
+}
+
+static void mtp_cleanup(void)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ if (!dev)
+ return;
+
+ misc_deregister(&mtp_device);
+ destroy_workqueue(dev->wq);
+ _mtp_dev = NULL;
+ kfree(dev);
+}
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index b0d98172bc07..2439514b703f 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -351,7 +351,15 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
driver->unbind(udc->gadget);
goto err1;
}
- usb_gadget_connect(udc->gadget);
+ /*
+ * HACK: The Android gadget driver disconnects the gadget
+ * on bind and expects the gadget to stay disconnected until
+ * it calls usb_gadget_connect when userspace is ready. Remove
+ * the call to usb_gadget_connect bellow to avoid enabling the
+ * pullup before userspace is ready.
+ *
+ * usb_gadget_connect(udc->gadget);
+ */
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
return 0;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 82800a775501..634dfb6d6c22 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -174,6 +174,13 @@ config USB_EHCI_HCD_SPEAR
Enables support for the on-chip EHCI controller on
ST SPEAr chips.
+config USB_EHCI_HCD_SYNOPSYS
+ tristate "Support for Synopsys Host-AHB USB 2.0 controller"
+ depends on USB_EHCI_HCD
+ ---help---
+ Enable support for onchip USB controllers based on DesignWare USB 2.0
+ Host-AHB Controller IP from Synopsys.
+
config USB_EHCI_HCD_AT91
tristate "Support for Atmel on-chip EHCI USB controller"
depends on USB_EHCI_HCD && ARCH_AT91
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 144c038ef70f..e458c4b3c3ff 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o
obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o
obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o
obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o
+obj-$(CONFIG_USB_EHCI_HCD_SYNOPSYS) += ehci-h20ahb.o
obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o
obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o
obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o
diff --git a/drivers/usb/host/ehci-h20ahb.c b/drivers/usb/host/ehci-h20ahb.c
new file mode 100644
index 000000000000..7724bab1828b
--- /dev/null
+++ b/drivers/usb/host/ehci-h20ahb.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2007-2013 Texas Instruments, Inc.
+ * Author: Vikram Pandita <vikram.pandita@ti.com>
+ * Author: Anand Gadiyar <gadiyar@ti.com>
+ * Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * Based on ehci-omap.c - driver for USBHOST on OMAP3/4 processors
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb/ulpi.h>
+#include <linux/pm_runtime.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/of.h>
+#include <linux/dma-mapping.h>
+
+#include "ehci.h"
+
+#define H20AHB_HS_USB_PORTS 1
+
+/* EHCI Synopsys-specific Register Set */
+#define EHCI_INSNREG04 (0xA0)
+#define EHCI_INSNREG04_DISABLE_UNSUSPEND (1 << 5)
+#define EHCI_INSNREG05_ULPI (0xA4)
+#define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31
+#define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24
+#define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22
+#define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16
+#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
+#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
+
+#define DRIVER_DESC "H20AHB-EHCI Host Controller driver"
+
+static const char hcd_name[] = "ehci-h20ahb";
+
+/*-------------------------------------------------------------------------*/
+
+struct h20ahb_hcd {
+ struct usb_phy *phy[H20AHB_HS_USB_PORTS]; /* one PHY for each port */
+ int nports;
+};
+
+static inline void ehci_write(void __iomem *base, u32 reg, u32 val)
+{
+ writel_relaxed(val, base + reg);
+}
+
+static inline u32 ehci_read(void __iomem *base, u32 reg)
+{
+ return readl_relaxed(base + reg);
+}
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+static struct hc_driver __read_mostly ehci_h20ahb_hc_driver;
+
+static const struct ehci_driver_overrides ehci_h20ahb_overrides __initdata = {
+ .extra_priv_size = sizeof(struct h20ahb_hcd),
+};
+
+static int ehci_h20ahb_phy_read(struct usb_phy *x, u32 reg)
+{
+ u32 val = (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) |
+ (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) |
+ (3 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) |
+ (reg << EHCI_INSNREG05_ULPI_REGADD_SHIFT);
+ ehci_write(x->io_priv, 0, val);
+ while ((val = ehci_read(x->io_priv, 0)) &
+ (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT));
+ return val & 0xff;
+}
+
+static int ehci_h20ahb_phy_write(struct usb_phy *x, u32 val, u32 reg)
+{
+ u32 v = (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) |
+ (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) |
+ (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) |
+ (reg << EHCI_INSNREG05_ULPI_REGADD_SHIFT) |
+ (val & 0xff);
+ ehci_write(x->io_priv, 0, v);
+ while ((v = ehci_read(x->io_priv, 0)) &
+ (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT));
+ return 0;
+}
+
+static struct usb_phy_io_ops ehci_h20ahb_phy_io_ops = {
+ .read = ehci_h20ahb_phy_read,
+ .write = ehci_h20ahb_phy_write,
+};
+
+
+/**
+ * ehci_hcd_h20ahb_probe - initialize Synopsis-based HCDs
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int ehci_hcd_h20ahb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct usb_hcd *hcd;
+ void __iomem *regs;
+ int ret;
+ int irq;
+ int i;
+ struct h20ahb_hcd *h20ahb;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /* if (!dev->parent) {
+ dev_err(dev, "Missing parent device\n");
+ return -ENODEV;
+ }*/
+
+ /* For DT boot, get platform data from parent. i.e. usbhshost */
+ /*if (dev->of_node) {
+ pdata = dev_get_platdata(dev->parent);
+ dev->platform_data = pdata;
+ }
+
+ if (!pdata) {
+ dev_err(dev, "Missing platform data\n");
+ return -ENODEV;
+ }*/
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "EHCI irq failed\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ ret = -ENODEV;
+ hcd = usb_create_hcd(&ehci_h20ahb_hc_driver, dev,
+ dev_name(dev));
+ if (!hcd) {
+ dev_err(dev, "Failed to create HCD\n");
+ return -ENOMEM;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = regs;
+ hcd_to_ehci(hcd)->caps = regs;
+
+ h20ahb = (struct h20ahb_hcd *)hcd_to_ehci(hcd)->priv;
+ h20ahb->nports = 1;
+
+ platform_set_drvdata(pdev, hcd);
+
+ /* get the PHY devices if needed */
+ for (i = 0 ; i < h20ahb->nports ; i++) {
+ struct usb_phy *phy;
+
+ /* get the PHY device */
+#if 0
+ if (dev->of_node)
+ phy = devm_usb_get_phy_by_phandle(dev, "phys", i);
+ else
+ phy = devm_usb_get_phy_dev(dev, i);
+#endif
+ phy = otg_ulpi_create(&ehci_h20ahb_phy_io_ops, 0);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ dev_err(dev, "Can't get PHY device for port %d: %d\n",
+ i, ret);
+ goto err_phy;
+ }
+ phy->dev = dev;
+ usb_add_phy_dev(phy);
+
+ h20ahb->phy[i] = phy;
+ phy->io_priv = hcd->regs + EHCI_INSNREG05_ULPI;
+
+#if 0
+ usb_phy_init(h20ahb->phy[i]);
+ /* bring PHY out of suspend */
+ usb_phy_set_suspend(h20ahb->phy[i], 0);
+#endif
+ }
+
+ /* make the first port's phy the one used by hcd as well */
+ hcd->phy = h20ahb->phy[0];
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ /*
+ * An undocumented "feature" in the H20AHB EHCI controller,
+ * causes suspended ports to be taken out of suspend when
+ * the USBCMD.Run/Stop bit is cleared (for example when
+ * we do ehci_bus_suspend).
+ * This breaks suspend-resume if the root-hub is allowed
+ * to suspend. Writing 1 to this undocumented register bit
+ * disables this feature and restores normal behavior.
+ */
+ ehci_write(regs, EHCI_INSNREG04,
+ EHCI_INSNREG04_DISABLE_UNSUSPEND);
+
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret) {
+ dev_err(dev, "failed to add hcd with err %d\n", ret);
+ goto err_pm_runtime;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ /*
+ * Bring PHYs out of reset for non PHY modes.
+ * Even though HSIC mode is a PHY-less mode, the reset
+ * line exists between the chips and can be modelled
+ * as a PHY device for reset control.
+ */
+ for (i = 0; i < h20ahb->nports; i++) {
+ usb_phy_init(h20ahb->phy[i]);
+ /* bring PHY out of suspend */
+ usb_phy_set_suspend(h20ahb->phy[i], 0);
+ }
+
+ return 0;
+
+err_pm_runtime:
+ pm_runtime_put_sync(dev);
+
+err_phy:
+ for (i = 0; i < h20ahb->nports; i++) {
+ if (h20ahb->phy[i])
+ usb_phy_shutdown(h20ahb->phy[i]);
+ }
+
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+
+/**
+ * ehci_hcd_h20ahb_remove - shutdown processing for EHCI HCDs
+ * @pdev: USB Host Controller being removed
+ *
+ * Reverses the effect of usb_ehci_hcd_h20ahb_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ */
+static int ehci_hcd_h20ahb_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct h20ahb_hcd *h20ahb = (struct h20ahb_hcd *)hcd_to_ehci(hcd)->priv;
+ int i;
+
+ usb_remove_hcd(hcd);
+
+ for (i = 0; i < h20ahb->nports; i++) {
+ if (h20ahb->phy[i])
+ usb_phy_shutdown(h20ahb->phy[i]);
+ }
+
+ usb_put_hcd(hcd);
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+static const struct of_device_id h20ahb_ehci_dt_ids[] = {
+ { .compatible = "snps,ehci-h20ahb" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, h20ahb_ehci_dt_ids);
+
+static struct platform_driver ehci_hcd_h20ahb_driver = {
+ .probe = ehci_hcd_h20ahb_probe,
+ .remove = ehci_hcd_h20ahb_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ /*.suspend = ehci_hcd_h20ahb_suspend, */
+ /*.resume = ehci_hcd_h20ahb_resume, */
+ .driver = {
+ .name = hcd_name,
+ .of_match_table = h20ahb_ehci_dt_ids,
+ }
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init ehci_h20ahb_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_h20ahb_hc_driver, &ehci_h20ahb_overrides);
+ return platform_driver_register(&ehci_hcd_h20ahb_driver);
+}
+module_init(ehci_h20ahb_init);
+
+static void __exit ehci_h20ahb_cleanup(void)
+{
+ platform_driver_unregister(&ehci_hcd_h20ahb_driver);
+}
+module_exit(ehci_h20ahb_cleanup);
+
+MODULE_ALIAS("platform:ehci-h20ahb");
+MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>");
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 81cda09b47e3..d1a48e49c0e8 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -590,11 +590,16 @@ static int ehci_run (struct usb_hcd *hcd)
*/
hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
if (HCC_64BIT_ADDR(hcc_params)) {
- ehci_writel(ehci, 0, &ehci->regs->segment);
-#if 0
-// this is deeply broken on almost all architectures
+#if CONFIG_ARM64
+ ehci_writel(ehci, ehci->periodic_dma >> 32, &ehci->regs->segment);
+ /*
+ * this is deeply broken on almost all architectures
+ * but arm64 can use it so enable it
+ */
if (!dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)))
ehci_info(ehci, "enabled 64bit DMA\n");
+#else
+ ehci_writel(ehci, 0, &ehci->regs->segment);
#endif
}
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index a24720beb39d..f5870aa626ef 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -73,12 +73,12 @@ struct omap_hcd {
static inline void ehci_write(void __iomem *base, u32 reg, u32 val)
{
- __raw_writel(val, base + reg);
+ writel_relaxed(val, base + reg);
}
static inline u32 ehci_read(void __iomem *base, u32 reg)
{
- return __raw_readl(base + reg);
+ return readl_relaxed(base + reg);
}
/* configure so an HC device and id are always provided */
diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h
index eebeed78edd6..76f4d2afe528 100644
--- a/drivers/usb/musb/musb_io.h
+++ b/drivers/usb/musb/musb_io.h
@@ -42,17 +42,17 @@
/* NOTE: these offsets are all in bytes */
static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
- { return __raw_readw(addr + offset); }
+ { return readw_relaxed(addr + offset); }
static inline u32 musb_readl(const void __iomem *addr, unsigned offset)
- { return __raw_readl(addr + offset); }
+ { return readl_relaxed(addr + offset); }
static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data)
- { __raw_writew(data, addr + offset); }
+ { writew_relaxed(data, addr + offset); }
static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
- { __raw_writel(data, addr + offset); }
+ { writel_relaxed(data, addr + offset); }
#if defined(CONFIG_USB_MUSB_TUSB6010) || defined (CONFIG_USB_MUSB_TUSB6010_MODULE)
@@ -65,7 +65,7 @@ static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
u16 tmp;
u8 val;
- tmp = __raw_readw(addr + (offset & ~1));
+ tmp = readw_relaxed(addr + (offset & ~1));
if (offset & 1)
val = (tmp >> 8);
else
@@ -78,22 +78,22 @@ static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
{
u16 tmp;
- tmp = __raw_readw(addr + (offset & ~1));
+ tmp = readw_relaxed(addr + (offset & ~1));
if (offset & 1)
tmp = (data << 8) | (tmp & 0xff);
else
tmp = (tmp & 0xff00) | data;
- __raw_writew(tmp, addr + (offset & ~1));
+ writew_relaxed(tmp, addr + (offset & ~1));
}
#else
static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
- { return __raw_readb(addr + offset); }
+ { return readb_relaxed(addr + offset); }
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
- { __raw_writeb(data, addr + offset); }
+ { writeb_relaxed(data, addr + offset); }
#endif /* CONFIG_USB_MUSB_TUSB6010 */
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index e253fa05be68..8f12b5a8a889 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -6,6 +6,14 @@ menu "USB Physical Layer drivers"
config USB_PHY
def_bool n
+config USB_OTG_WAKELOCK
+ bool "Hold a wakelock when USB connected"
+ depends on WAKELOCK
+ select USB_OTG_UTILS
+ help
+ Select this to automatically hold a wakelock when USB is
+ connected, preventing suspend.
+
#
# USB Transceiver Drivers
#
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 24a91332d4ad..31b4528f6d9c 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_USB_PHY) += phy.o
obj-$(CONFIG_OF) += of.o
+obj-$(CONFIG_USB_OTG_WAKELOCK) += otg-wakelock.o
# transceiver drivers, keep the list sorted
diff --git a/drivers/usb/phy/otg-wakelock.c b/drivers/usb/phy/otg-wakelock.c
new file mode 100644
index 000000000000..479376bfa484
--- /dev/null
+++ b/drivers/usb/phy/otg-wakelock.c
@@ -0,0 +1,173 @@
+/*
+ * otg-wakelock.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/wakelock.h>
+#include <linux/spinlock.h>
+#include <linux/usb/otg.h>
+
+#define TEMPORARY_HOLD_TIME 2000
+
+static bool enabled = true;
+static struct usb_phy *otgwl_xceiv;
+static struct notifier_block otgwl_nb;
+
+/*
+ * otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the
+ * held field is updated to match.
+ */
+
+static DEFINE_SPINLOCK(otgwl_spinlock);
+
+/*
+ * Only one lock, but since these 3 fields are associated with each other...
+ */
+
+struct otgwl_lock {
+ char name[40];
+ struct wake_lock wakelock;
+ bool held;
+};
+
+/*
+ * VBUS present lock. Also used as a timed lock on charger
+ * connect/disconnect and USB host disconnect, to allow the system
+ * to react to the change in power.
+ */
+
+static struct otgwl_lock vbus_lock;
+
+static void otgwl_hold(struct otgwl_lock *lock)
+{
+ if (!lock->held) {
+ wake_lock(&lock->wakelock);
+ lock->held = true;
+ }
+}
+
+static void otgwl_temporary_hold(struct otgwl_lock *lock)
+{
+ wake_lock_timeout(&lock->wakelock,
+ msecs_to_jiffies(TEMPORARY_HOLD_TIME));
+ lock->held = false;
+}
+
+static void otgwl_drop(struct otgwl_lock *lock)
+{
+ if (lock->held) {
+ wake_unlock(&lock->wakelock);
+ lock->held = false;
+ }
+}
+
+static void otgwl_handle_event(unsigned long event)
+{
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&otgwl_spinlock, irqflags);
+
+ if (!enabled) {
+ otgwl_drop(&vbus_lock);
+ spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
+ return;
+ }
+
+ switch (event) {
+ case USB_EVENT_VBUS:
+ case USB_EVENT_ENUMERATED:
+ otgwl_hold(&vbus_lock);
+ break;
+
+ case USB_EVENT_NONE:
+ case USB_EVENT_ID:
+ case USB_EVENT_CHARGER:
+ otgwl_temporary_hold(&vbus_lock);
+ break;
+
+ default:
+ break;
+ }
+
+ spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
+}
+
+static int otgwl_otg_notifications(struct notifier_block *nb,
+ unsigned long event, void *unused)
+{
+ otgwl_handle_event(event);
+ return NOTIFY_OK;
+}
+
+static int set_enabled(const char *val, const struct kernel_param *kp)
+{
+ int rv = param_set_bool(val, kp);
+
+ if (rv)
+ return rv;
+
+ if (otgwl_xceiv)
+ otgwl_handle_event(otgwl_xceiv->last_event);
+
+ return 0;
+}
+
+static struct kernel_param_ops enabled_param_ops = {
+ .set = set_enabled,
+ .get = param_get_bool,
+};
+
+module_param_cb(enabled, &enabled_param_ops, &enabled, 0644);
+MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present");
+
+static int __init otg_wakelock_init(void)
+{
+ int ret;
+ struct usb_phy *phy;
+
+ phy = usb_get_phy(USB_PHY_TYPE_USB2);
+
+ if (IS_ERR(phy)) {
+ pr_err("%s: No USB transceiver found\n", __func__);
+ return PTR_ERR(phy);
+ }
+ otgwl_xceiv = phy;
+
+ snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s",
+ dev_name(otgwl_xceiv->dev));
+ wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND,
+ vbus_lock.name);
+
+ otgwl_nb.notifier_call = otgwl_otg_notifications;
+ ret = usb_register_notifier(otgwl_xceiv, &otgwl_nb);
+
+ if (ret) {
+ pr_err("%s: usb_register_notifier on transceiver %s"
+ " failed\n", __func__,
+ dev_name(otgwl_xceiv->dev));
+ otgwl_xceiv = NULL;
+ wake_lock_destroy(&vbus_lock.wakelock);
+ return ret;
+ }
+
+ otgwl_handle_event(otgwl_xceiv->last_event);
+ return ret;
+}
+
+late_initcall(otg_wakelock_init);
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index afc09087ec36..ad7c25abe1fb 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -281,7 +281,7 @@ static int msm_otg_phy_clk_reset(struct msm_otg *motg)
{
int ret = 0;
- if (motg->pdata->phy_clk_reset && motg->phy_reset_clk)
+ if (motg->pdata->phy_clk_reset)
ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk);
else if (motg->phy_rst)
ret = reset_control_reset(motg->phy_rst);
@@ -1554,11 +1554,14 @@ static int msm_otg_probe(struct platform_device *pdev)
phy = &motg->phy;
phy->dev = &pdev->dev;
- motg->phy_reset_clk = devm_clk_get(&pdev->dev,
+ if (motg->pdata->phy_clk_reset) {
+ motg->phy_reset_clk = devm_clk_get(&pdev->dev,
np ? "phy" : "usb_phy_clk");
- if (IS_ERR(motg->phy_reset_clk)) {
- dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
- motg->phy_reset_clk = NULL;
+
+ if (IS_ERR(motg->phy_reset_clk)) {
+ dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
+ return PTR_ERR(motg->phy_reset_clk);
+ }
}
motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 8bf495ffb020..52451f9c86ac 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -31,6 +31,7 @@ source "drivers/video/fbdev/Kconfig"
endmenu
source "drivers/video/backlight/Kconfig"
+source "drivers/video/adf/Kconfig"
config VGASTATE
tristate
@@ -42,6 +43,11 @@ config VIDEOMODE_HELPERS
config HDMI
bool
+config VEXPRESS_DVI_CONTROL
+ bool "Versatile Express DVI control"
+ depends on FB && VEXPRESS_CONFIG
+ default y
+
if VT
source "drivers/video/console/Kconfig"
endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 9ad3c17d6456..b880b31bdaa6 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_VGASTATE) += vgastate.o
obj-$(CONFIG_HDMI) += hdmi.o
+obj-$(CONFIG_ADF) += adf/
obj-$(CONFIG_VT) += console/
obj-$(CONFIG_LOGO) += logo/
obj-y += backlight/
@@ -11,3 +12,6 @@ obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_VIDEOMODE_HELPERS) += of_display_timing.o of_videomode.o
endif
+
+# platform specific output drivers
+obj-$(CONFIG_VEXPRESS_DVI_CONTROL) += vexpress-dvi.o
diff --git a/drivers/video/adf/Kconfig b/drivers/video/adf/Kconfig
new file mode 100644
index 000000000000..33858b73d8bb
--- /dev/null
+++ b/drivers/video/adf/Kconfig
@@ -0,0 +1,14 @@
+menuconfig ADF
+ depends on SYNC
+ depends on DMA_SHARED_BUFFER
+ tristate "Atomic Display Framework"
+
+menuconfig ADF_FBDEV
+ depends on ADF
+ depends on FB
+ tristate "Helper for implementing the fbdev API in ADF drivers"
+
+menuconfig ADF_MEMBLOCK
+ depends on ADF
+ depends on HAVE_MEMBLOCK
+ tristate "Helper for using memblocks as buffers in ADF drivers"
diff --git a/drivers/video/adf/Makefile b/drivers/video/adf/Makefile
new file mode 100644
index 000000000000..78d0915122f4
--- /dev/null
+++ b/drivers/video/adf/Makefile
@@ -0,0 +1,15 @@
+ccflags-y := -Idrivers/staging/android
+
+CFLAGS_adf.o := -I$(src)
+
+obj-$(CONFIG_ADF) += adf.o \
+ adf_client.o \
+ adf_fops.o \
+ adf_format.o \
+ adf_sysfs.o
+
+obj-$(CONFIG_COMPAT) += adf_fops32.o
+
+obj-$(CONFIG_ADF_FBDEV) += adf_fbdev.o
+
+obj-$(CONFIG_ADF_MEMBLOCK) += adf_memblock.o
diff --git a/drivers/video/adf/adf.c b/drivers/video/adf/adf.c
new file mode 100644
index 000000000000..2d20024fd251
--- /dev/null
+++ b/drivers/video/adf/adf.c
@@ -0,0 +1,1167 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ * adf_modeinfo_{set_name,set_vrefresh} modified from
+ * drivers/gpu/drm/drm_modes.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/device.h>
+#include <linux/idr.h>
+#include <linux/highmem.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/adf_format.h>
+
+#include "sw_sync.h"
+#include "sync.h"
+
+#include "adf.h"
+#include "adf_fops.h"
+#include "adf_sysfs.h"
+
+#define CREATE_TRACE_POINTS
+#include "adf_trace.h"
+
+#define ADF_SHORT_FENCE_TIMEOUT (1 * MSEC_PER_SEC)
+#define ADF_LONG_FENCE_TIMEOUT (10 * MSEC_PER_SEC)
+
+static DEFINE_IDR(adf_devices);
+
+static void adf_fence_wait(struct adf_device *dev, struct sync_fence *fence)
+{
+ /* sync_fence_wait() dumps debug information on timeout. Experience
+ has shown that if the pipeline gets stuck, a short timeout followed
+ by a longer one provides useful information for debugging. */
+ int err = sync_fence_wait(fence, ADF_SHORT_FENCE_TIMEOUT);
+ if (err >= 0)
+ return;
+
+ if (err == -ETIME)
+ err = sync_fence_wait(fence, ADF_LONG_FENCE_TIMEOUT);
+
+ if (err < 0)
+ dev_warn(&dev->base.dev, "error waiting on fence: %d\n", err);
+}
+
+void adf_buffer_cleanup(struct adf_buffer *buf)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(buf->dma_bufs); i++)
+ if (buf->dma_bufs[i])
+ dma_buf_put(buf->dma_bufs[i]);
+
+ if (buf->acquire_fence)
+ sync_fence_put(buf->acquire_fence);
+}
+
+void adf_buffer_mapping_cleanup(struct adf_buffer_mapping *mapping,
+ struct adf_buffer *buf)
+{
+ /* calling adf_buffer_mapping_cleanup() is safe even if mapping is
+ uninitialized or partially-initialized, as long as it was
+ zeroed on allocation */
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(mapping->sg_tables); i++) {
+ if (mapping->sg_tables[i])
+ dma_buf_unmap_attachment(mapping->attachments[i],
+ mapping->sg_tables[i], DMA_TO_DEVICE);
+ if (mapping->attachments[i])
+ dma_buf_detach(buf->dma_bufs[i],
+ mapping->attachments[i]);
+ }
+}
+
+void adf_post_cleanup(struct adf_device *dev, struct adf_pending_post *post)
+{
+ size_t i;
+
+ if (post->state)
+ dev->ops->state_free(dev, post->state);
+
+ for (i = 0; i < post->config.n_bufs; i++) {
+ adf_buffer_mapping_cleanup(&post->config.mappings[i],
+ &post->config.bufs[i]);
+ adf_buffer_cleanup(&post->config.bufs[i]);
+ }
+
+ kfree(post->config.custom_data);
+ kfree(post->config.mappings);
+ kfree(post->config.bufs);
+ kfree(post);
+}
+
+static void adf_sw_advance_timeline(struct adf_device *dev)
+{
+#ifdef CONFIG_SW_SYNC
+ sw_sync_timeline_inc(dev->timeline, 1);
+#else
+ BUG();
+#endif
+}
+
+static void adf_post_work_func(struct kthread_work *work)
+{
+ struct adf_device *dev =
+ container_of(work, struct adf_device, post_work);
+ struct adf_pending_post *post, *next;
+ struct list_head saved_list;
+
+ mutex_lock(&dev->post_lock);
+ memcpy(&saved_list, &dev->post_list, sizeof(saved_list));
+ list_replace_init(&dev->post_list, &saved_list);
+ mutex_unlock(&dev->post_lock);
+
+ list_for_each_entry_safe(post, next, &saved_list, head) {
+ int i;
+
+ for (i = 0; i < post->config.n_bufs; i++) {
+ struct sync_fence *fence =
+ post->config.bufs[i].acquire_fence;
+ if (fence)
+ adf_fence_wait(dev, fence);
+ }
+
+ dev->ops->post(dev, &post->config, post->state);
+
+ if (dev->ops->advance_timeline)
+ dev->ops->advance_timeline(dev, &post->config,
+ post->state);
+ else
+ adf_sw_advance_timeline(dev);
+
+ list_del(&post->head);
+ if (dev->onscreen)
+ adf_post_cleanup(dev, dev->onscreen);
+ dev->onscreen = post;
+ }
+}
+
+void adf_attachment_free(struct adf_attachment_list *attachment)
+{
+ list_del(&attachment->head);
+ kfree(attachment);
+}
+
+struct adf_event_refcount *adf_obj_find_event_refcount(struct adf_obj *obj,
+ enum adf_event_type type)
+{
+ struct rb_root *root = &obj->event_refcount;
+ struct rb_node **new = &(root->rb_node);
+ struct rb_node *parent = NULL;
+ struct adf_event_refcount *refcount;
+
+ while (*new) {
+ refcount = container_of(*new, struct adf_event_refcount, node);
+ parent = *new;
+
+ if (refcount->type > type)
+ new = &(*new)->rb_left;
+ else if (refcount->type < type)
+ new = &(*new)->rb_right;
+ else
+ return refcount;
+ }
+
+ refcount = kzalloc(sizeof(*refcount), GFP_KERNEL);
+ if (!refcount)
+ return NULL;
+ refcount->type = type;
+
+ rb_link_node(&refcount->node, parent, new);
+ rb_insert_color(&refcount->node, root);
+ return refcount;
+}
+
+/**
+ * adf_event_get - increase the refcount for an event
+ *
+ * @obj: the object that produces the event
+ * @type: the event type
+ *
+ * ADF will call the object's set_event() op if needed. ops are allowed
+ * to sleep, so adf_event_get() must NOT be called from an atomic context.
+ *
+ * Returns 0 if successful, or -%EINVAL if the object does not support the
+ * requested event type.
+ */
+int adf_event_get(struct adf_obj *obj, enum adf_event_type type)
+{
+ struct adf_event_refcount *refcount;
+ int old_refcount;
+ int ret;
+
+ ret = adf_obj_check_supports_event(obj, type);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&obj->event_lock);
+
+ refcount = adf_obj_find_event_refcount(obj, type);
+ if (!refcount) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ old_refcount = refcount->refcount++;
+
+ if (old_refcount == 0) {
+ obj->ops->set_event(obj, type, true);
+ trace_adf_event_enable(obj, type);
+ }
+
+done:
+ mutex_unlock(&obj->event_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_event_get);
+
+/**
+ * adf_event_put - decrease the refcount for an event
+ *
+ * @obj: the object that produces the event
+ * @type: the event type
+ *
+ * ADF will call the object's set_event() op if needed. ops are allowed
+ * to sleep, so adf_event_put() must NOT be called from an atomic context.
+ *
+ * Returns 0 if successful, -%EINVAL if the object does not support the
+ * requested event type, or -%EALREADY if the refcount is already 0.
+ */
+int adf_event_put(struct adf_obj *obj, enum adf_event_type type)
+{
+ struct adf_event_refcount *refcount;
+ int old_refcount;
+ int ret;
+
+ ret = adf_obj_check_supports_event(obj, type);
+ if (ret < 0)
+ return ret;
+
+
+ mutex_lock(&obj->event_lock);
+
+ refcount = adf_obj_find_event_refcount(obj, type);
+ if (!refcount) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ old_refcount = refcount->refcount--;
+
+ if (WARN_ON(old_refcount == 0)) {
+ refcount->refcount++;
+ ret = -EALREADY;
+ } else if (old_refcount == 1) {
+ obj->ops->set_event(obj, type, false);
+ trace_adf_event_disable(obj, type);
+ }
+
+done:
+ mutex_unlock(&obj->event_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_event_put);
+
+/**
+ * adf_vsync_wait - wait for a vsync event on a display interface
+ *
+ * @intf: the display interface
+ * @timeout: timeout in jiffies (0 = wait indefinitely)
+ *
+ * adf_vsync_wait() may sleep, so it must NOT be called from an atomic context.
+ *
+ * This function returns -%ERESTARTSYS if it is interrupted by a signal.
+ * If @timeout == 0 then this function returns 0 on vsync. If @timeout > 0 then
+ * this function returns the number of remaining jiffies or -%ETIMEDOUT on
+ * timeout.
+ */
+int adf_vsync_wait(struct adf_interface *intf, long timeout)
+{
+ ktime_t timestamp;
+ int ret;
+ unsigned long flags;
+
+ read_lock_irqsave(&intf->vsync_lock, flags);
+ timestamp = intf->vsync_timestamp;
+ read_unlock_irqrestore(&intf->vsync_lock, flags);
+
+ adf_vsync_get(intf);
+ if (timeout) {
+ ret = wait_event_interruptible_timeout(intf->vsync_wait,
+ !ktime_equal(timestamp,
+ intf->vsync_timestamp),
+ msecs_to_jiffies(timeout));
+ if (ret == 0 && ktime_equal(timestamp, intf->vsync_timestamp))
+ ret = -ETIMEDOUT;
+ } else {
+ ret = wait_event_interruptible(intf->vsync_wait,
+ !ktime_equal(timestamp,
+ intf->vsync_timestamp));
+ }
+ adf_vsync_put(intf);
+
+ return ret;
+}
+EXPORT_SYMBOL(adf_vsync_wait);
+
+static void adf_event_queue(struct adf_obj *obj, struct adf_event *event)
+{
+ struct adf_file *file;
+ unsigned long flags;
+
+ trace_adf_event(obj, event->type);
+
+ spin_lock_irqsave(&obj->file_lock, flags);
+
+ list_for_each_entry(file, &obj->file_list, head)
+ if (test_bit(event->type, file->event_subscriptions))
+ adf_file_queue_event(file, event);
+
+ spin_unlock_irqrestore(&obj->file_lock, flags);
+}
+
+/**
+ * adf_event_notify - notify userspace of a driver-private event
+ *
+ * @obj: the ADF object that produced the event
+ * @event: the event
+ *
+ * adf_event_notify() may be called safely from an atomic context. It will
+ * copy @event if needed, so @event may point to a variable on the stack.
+ *
+ * Drivers must NOT call adf_event_notify() for vsync and hotplug events.
+ * ADF provides adf_vsync_notify() and
+ * adf_hotplug_notify_{connected,disconnected}() for these events.
+ */
+int adf_event_notify(struct adf_obj *obj, struct adf_event *event)
+{
+ if (WARN_ON(event->type == ADF_EVENT_VSYNC ||
+ event->type == ADF_EVENT_HOTPLUG))
+ return -EINVAL;
+
+ adf_event_queue(obj, event);
+ return 0;
+}
+EXPORT_SYMBOL(adf_event_notify);
+
+/**
+ * adf_vsync_notify - notify ADF of a display interface's vsync event
+ *
+ * @intf: the display interface
+ * @timestamp: the time the vsync occurred
+ *
+ * adf_vsync_notify() may be called safely from an atomic context.
+ */
+void adf_vsync_notify(struct adf_interface *intf, ktime_t timestamp)
+{
+ unsigned long flags;
+ struct adf_vsync_event event;
+
+ write_lock_irqsave(&intf->vsync_lock, flags);
+ intf->vsync_timestamp = timestamp;
+ write_unlock_irqrestore(&intf->vsync_lock, flags);
+
+ wake_up_interruptible_all(&intf->vsync_wait);
+
+ event.base.type = ADF_EVENT_VSYNC;
+ event.base.length = sizeof(event);
+ event.timestamp = ktime_to_ns(timestamp);
+ adf_event_queue(&intf->base, &event.base);
+}
+EXPORT_SYMBOL(adf_vsync_notify);
+
+void adf_hotplug_notify(struct adf_interface *intf, bool connected,
+ struct drm_mode_modeinfo *modelist, size_t n_modes)
+{
+ unsigned long flags;
+ struct adf_hotplug_event event;
+ struct drm_mode_modeinfo *old_modelist;
+
+ write_lock_irqsave(&intf->hotplug_modelist_lock, flags);
+ old_modelist = intf->modelist;
+ intf->hotplug_detect = connected;
+ intf->modelist = modelist;
+ intf->n_modes = n_modes;
+ write_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
+
+ kfree(old_modelist);
+
+ event.base.length = sizeof(event);
+ event.base.type = ADF_EVENT_HOTPLUG;
+ event.connected = connected;
+ adf_event_queue(&intf->base, &event.base);
+}
+
+/**
+ * adf_hotplug_notify_connected - notify ADF of a display interface being
+ * connected to a display
+ *
+ * @intf: the display interface
+ * @modelist: hardware modes supported by display
+ * @n_modes: length of modelist
+ *
+ * @modelist is copied as needed, so it may point to a variable on the stack.
+ *
+ * adf_hotplug_notify_connected() may NOT be called safely from an atomic
+ * context.
+ *
+ * Returns 0 on success or error code (<0) on error.
+ */
+int adf_hotplug_notify_connected(struct adf_interface *intf,
+ struct drm_mode_modeinfo *modelist, size_t n_modes)
+{
+ struct drm_mode_modeinfo *modelist_copy;
+
+ if (n_modes > ADF_MAX_MODES)
+ return -ENOMEM;
+
+ modelist_copy = kzalloc(sizeof(modelist_copy[0]) * n_modes,
+ GFP_KERNEL);
+ if (!modelist_copy)
+ return -ENOMEM;
+ memcpy(modelist_copy, modelist, sizeof(modelist_copy[0]) * n_modes);
+
+ adf_hotplug_notify(intf, true, modelist_copy, n_modes);
+ return 0;
+}
+EXPORT_SYMBOL(adf_hotplug_notify_connected);
+
+/**
+ * adf_hotplug_notify_disconnected - notify ADF of a display interface being
+ * disconnected from a display
+ *
+ * @intf: the display interface
+ *
+ * adf_hotplug_notify_disconnected() may be called safely from an atomic
+ * context.
+ */
+void adf_hotplug_notify_disconnected(struct adf_interface *intf)
+{
+ adf_hotplug_notify(intf, false, NULL, 0);
+}
+EXPORT_SYMBOL(adf_hotplug_notify_disconnected);
+
+static int adf_obj_init(struct adf_obj *obj, enum adf_obj_type type,
+ struct idr *idr, struct adf_device *parent,
+ const struct adf_obj_ops *ops, const char *fmt, va_list args)
+{
+ int ret;
+
+ if (ops && ops->supports_event && !ops->set_event) {
+ pr_err("%s: %s implements supports_event but not set_event\n",
+ __func__, adf_obj_type_str(type));
+ return -EINVAL;
+ }
+
+ ret = idr_alloc(idr, obj, 0, 0, GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("%s: allocating object id failed: %d\n", __func__, ret);
+ return ret;
+ }
+ obj->id = ret;
+
+ vscnprintf(obj->name, sizeof(obj->name), fmt, args);
+
+ obj->type = type;
+ obj->ops = ops;
+ obj->parent = parent;
+ mutex_init(&obj->event_lock);
+ obj->event_refcount = RB_ROOT;
+ spin_lock_init(&obj->file_lock);
+ INIT_LIST_HEAD(&obj->file_list);
+ return 0;
+}
+
+static void adf_obj_destroy(struct adf_obj *obj, struct idr *idr)
+{
+ struct rb_node *node = rb_first(&obj->event_refcount);
+
+ while (node) {
+ struct adf_event_refcount *refcount =
+ container_of(node, struct adf_event_refcount,
+ node);
+ kfree(refcount);
+ node = rb_first(&obj->event_refcount);
+ }
+
+ mutex_destroy(&obj->event_lock);
+ idr_remove(idr, obj->id);
+}
+
+/**
+ * adf_device_init - initialize ADF-internal data for a display device
+ * and create sysfs entries
+ *
+ * @dev: the display device
+ * @parent: the device's parent device
+ * @ops: the device's associated ops
+ * @fmt: formatting string for the display device's name
+ *
+ * @fmt specifies the device's sysfs filename and the name returned to
+ * userspace through the %ADF_GET_DEVICE_DATA ioctl.
+ *
+ * Returns 0 on success or error code (<0) on failure.
+ */
+int adf_device_init(struct adf_device *dev, struct device *parent,
+ const struct adf_device_ops *ops, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+
+ if (!ops->validate || !ops->post) {
+ pr_err("%s: device must implement validate and post\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!ops->complete_fence && !ops->advance_timeline) {
+ if (!IS_ENABLED(CONFIG_SW_SYNC)) {
+ pr_err("%s: device requires sw_sync but it is not enabled in the kernel\n",
+ __func__);
+ return -EINVAL;
+ }
+ } else if (!(ops->complete_fence && ops->advance_timeline)) {
+ pr_err("%s: device must implement both complete_fence and advance_timeline, or implement neither\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ va_start(args, fmt);
+ ret = adf_obj_init(&dev->base, ADF_OBJ_DEVICE, &adf_devices, dev,
+ &ops->base, fmt, args);
+ va_end(args);
+ if (ret < 0)
+ return ret;
+
+ dev->dev = parent;
+ dev->ops = ops;
+ idr_init(&dev->overlay_engines);
+ idr_init(&dev->interfaces);
+ mutex_init(&dev->client_lock);
+ INIT_LIST_HEAD(&dev->post_list);
+ mutex_init(&dev->post_lock);
+ init_kthread_worker(&dev->post_worker);
+ INIT_LIST_HEAD(&dev->attached);
+ INIT_LIST_HEAD(&dev->attach_allowed);
+
+ dev->post_thread = kthread_run(kthread_worker_fn,
+ &dev->post_worker, dev->base.name);
+ if (IS_ERR(dev->post_thread)) {
+ ret = PTR_ERR(dev->post_thread);
+ dev->post_thread = NULL;
+
+ pr_err("%s: failed to run config posting thread: %d\n",
+ __func__, ret);
+ goto err;
+ }
+ init_kthread_work(&dev->post_work, adf_post_work_func);
+
+ ret = adf_device_sysfs_init(dev);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ adf_device_destroy(dev);
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_init);
+
+/**
+ * adf_device_destroy - clean up ADF-internal data for a display device
+ *
+ * @dev: the display device
+ */
+void adf_device_destroy(struct adf_device *dev)
+{
+ struct adf_attachment_list *entry, *next;
+
+ idr_destroy(&dev->interfaces);
+ idr_destroy(&dev->overlay_engines);
+
+ if (dev->post_thread) {
+ flush_kthread_worker(&dev->post_worker);
+ kthread_stop(dev->post_thread);
+ }
+
+ if (dev->onscreen)
+ adf_post_cleanup(dev, dev->onscreen);
+ adf_device_sysfs_destroy(dev);
+ list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) {
+ adf_attachment_free(entry);
+ }
+ list_for_each_entry_safe(entry, next, &dev->attached, head) {
+ adf_attachment_free(entry);
+ }
+ mutex_destroy(&dev->post_lock);
+ mutex_destroy(&dev->client_lock);
+ adf_obj_destroy(&dev->base, &adf_devices);
+}
+EXPORT_SYMBOL(adf_device_destroy);
+
+/**
+ * adf_interface_init - initialize ADF-internal data for a display interface
+ * and create sysfs entries
+ *
+ * @intf: the display interface
+ * @dev: the interface's "parent" display device
+ * @type: interface type (see enum @adf_interface_type)
+ * @idx: which interface of type @type;
+ * e.g. interface DSI.1 -> @type=%ADF_INTF_TYPE_DSI, @idx=1
+ * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values)
+ * @ops: the interface's associated ops
+ * @fmt: formatting string for the display interface's name
+ *
+ * @dev must have previously been initialized with adf_device_init().
+ *
+ * @fmt affects the name returned to userspace through the
+ * %ADF_GET_INTERFACE_DATA ioctl. It does not affect the sysfs filename,
+ * which is derived from @dev's name.
+ *
+ * Returns 0 on success or error code (<0) on failure.
+ */
+int adf_interface_init(struct adf_interface *intf, struct adf_device *dev,
+ enum adf_interface_type type, u32 idx, u32 flags,
+ const struct adf_interface_ops *ops, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+ const u32 allowed_flags = ADF_INTF_FLAG_PRIMARY |
+ ADF_INTF_FLAG_EXTERNAL;
+
+ if (dev->n_interfaces == ADF_MAX_INTERFACES) {
+ pr_err("%s: parent device %s has too many interfaces\n",
+ __func__, dev->base.name);
+ return -ENOMEM;
+ }
+
+ if (type >= ADF_INTF_MEMORY && type <= ADF_INTF_TYPE_DEVICE_CUSTOM) {
+ pr_err("%s: invalid interface type %u\n", __func__, type);
+ return -EINVAL;
+ }
+
+ if (flags & ~allowed_flags) {
+ pr_err("%s: invalid interface flags 0x%X\n", __func__,
+ flags & ~allowed_flags);
+ return -EINVAL;
+ }
+
+ memset(intf, 0, sizeof(*intf));
+
+ va_start(args, fmt);
+ ret = adf_obj_init(&intf->base, ADF_OBJ_INTERFACE, &dev->interfaces,
+ dev, ops ? &ops->base : NULL, fmt, args);
+ va_end(args);
+ if (ret < 0)
+ return ret;
+
+ intf->type = type;
+ intf->idx = idx;
+ intf->flags = flags;
+ intf->ops = ops;
+ intf->dpms_state = DRM_MODE_DPMS_OFF;
+ init_waitqueue_head(&intf->vsync_wait);
+ rwlock_init(&intf->vsync_lock);
+ rwlock_init(&intf->hotplug_modelist_lock);
+
+ ret = adf_interface_sysfs_init(intf);
+ if (ret < 0)
+ goto err;
+ dev->n_interfaces++;
+
+ return 0;
+
+err:
+ adf_obj_destroy(&intf->base, &dev->interfaces);
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_init);
+
+/**
+ * adf_interface_destroy - clean up ADF-internal data for a display interface
+ *
+ * @intf: the display interface
+ */
+void adf_interface_destroy(struct adf_interface *intf)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ struct adf_attachment_list *entry, *next;
+
+ mutex_lock(&dev->client_lock);
+ list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) {
+ if (entry->attachment.interface == intf) {
+ adf_attachment_free(entry);
+ dev->n_attach_allowed--;
+ }
+ }
+ list_for_each_entry_safe(entry, next, &dev->attached, head) {
+ if (entry->attachment.interface == intf) {
+ adf_device_detach_op(dev,
+ entry->attachment.overlay_engine, intf);
+ adf_attachment_free(entry);
+ dev->n_attached--;
+ }
+ }
+ kfree(intf->modelist);
+ adf_interface_sysfs_destroy(intf);
+ adf_obj_destroy(&intf->base, &dev->interfaces);
+ dev->n_interfaces--;
+ mutex_unlock(&dev->client_lock);
+}
+EXPORT_SYMBOL(adf_interface_destroy);
+
+static bool adf_overlay_engine_has_custom_formats(
+ const struct adf_overlay_engine_ops *ops)
+{
+ size_t i;
+ for (i = 0; i < ops->n_supported_formats; i++)
+ if (!adf_format_is_standard(ops->supported_formats[i]))
+ return true;
+ return false;
+}
+
+/**
+ * adf_overlay_engine_init - initialize ADF-internal data for an
+ * overlay engine and create sysfs entries
+ *
+ * @eng: the overlay engine
+ * @dev: the overlay engine's "parent" display device
+ * @ops: the overlay engine's associated ops
+ * @fmt: formatting string for the overlay engine's name
+ *
+ * @dev must have previously been initialized with adf_device_init().
+ *
+ * @fmt affects the name returned to userspace through the
+ * %ADF_GET_OVERLAY_ENGINE_DATA ioctl. It does not affect the sysfs filename,
+ * which is derived from @dev's name.
+ *
+ * Returns 0 on success or error code (<0) on failure.
+ */
+int adf_overlay_engine_init(struct adf_overlay_engine *eng,
+ struct adf_device *dev,
+ const struct adf_overlay_engine_ops *ops, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+
+ if (!ops->supported_formats) {
+ pr_err("%s: overlay engine must support at least one format\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (ops->n_supported_formats > ADF_MAX_SUPPORTED_FORMATS) {
+ pr_err("%s: overlay engine supports too many formats\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (adf_overlay_engine_has_custom_formats(ops) &&
+ !dev->ops->validate_custom_format) {
+ pr_err("%s: overlay engine has custom formats but parent device %s does not implement validate_custom_format\n",
+ __func__, dev->base.name);
+ return -EINVAL;
+ }
+
+ memset(eng, 0, sizeof(*eng));
+
+ va_start(args, fmt);
+ ret = adf_obj_init(&eng->base, ADF_OBJ_OVERLAY_ENGINE,
+ &dev->overlay_engines, dev, &ops->base, fmt, args);
+ va_end(args);
+ if (ret < 0)
+ return ret;
+
+ eng->ops = ops;
+
+ ret = adf_overlay_engine_sysfs_init(eng);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ adf_obj_destroy(&eng->base, &dev->overlay_engines);
+ return ret;
+}
+EXPORT_SYMBOL(adf_overlay_engine_init);
+
+/**
+ * adf_interface_destroy - clean up ADF-internal data for an overlay engine
+ *
+ * @eng: the overlay engine
+ */
+void adf_overlay_engine_destroy(struct adf_overlay_engine *eng)
+{
+ struct adf_device *dev = adf_overlay_engine_parent(eng);
+ struct adf_attachment_list *entry, *next;
+
+ mutex_lock(&dev->client_lock);
+ list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) {
+ if (entry->attachment.overlay_engine == eng) {
+ adf_attachment_free(entry);
+ dev->n_attach_allowed--;
+ }
+ }
+ list_for_each_entry_safe(entry, next, &dev->attached, head) {
+ if (entry->attachment.overlay_engine == eng) {
+ adf_device_detach_op(dev, eng,
+ entry->attachment.interface);
+ adf_attachment_free(entry);
+ dev->n_attached--;
+ }
+ }
+ adf_overlay_engine_sysfs_destroy(eng);
+ adf_obj_destroy(&eng->base, &dev->overlay_engines);
+ mutex_unlock(&dev->client_lock);
+}
+EXPORT_SYMBOL(adf_overlay_engine_destroy);
+
+struct adf_attachment_list *adf_attachment_find(struct list_head *list,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ struct adf_attachment_list *entry;
+ list_for_each_entry(entry, list, head) {
+ if (entry->attachment.interface == intf &&
+ entry->attachment.overlay_engine == eng)
+ return entry;
+ }
+ return NULL;
+}
+
+int adf_attachment_validate(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ struct adf_device *intf_dev = adf_interface_parent(intf);
+ struct adf_device *eng_dev = adf_overlay_engine_parent(eng);
+
+ if (intf_dev != dev) {
+ dev_err(&dev->base.dev, "can't attach interface %s belonging to device %s\n",
+ intf->base.name, intf_dev->base.name);
+ return -EINVAL;
+ }
+
+ if (eng_dev != dev) {
+ dev_err(&dev->base.dev, "can't attach overlay engine %s belonging to device %s\n",
+ eng->base.name, eng_dev->base.name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * adf_attachment_allow - add a new entry to the list of allowed
+ * attachments
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ *
+ * adf_attachment_allow() indicates that the underlying display hardware allows
+ * @intf to scan out @eng's output. It is intended to be called at
+ * driver initialization for each supported overlay engine + interface pair.
+ *
+ * Returns 0 on success, -%EALREADY if the entry already exists, or -errno on
+ * any other failure.
+ */
+int adf_attachment_allow(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ int ret;
+ struct adf_attachment_list *entry = NULL;
+
+ ret = adf_attachment_validate(dev, eng, intf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&dev->client_lock);
+
+ if (dev->n_attach_allowed == ADF_MAX_ATTACHMENTS) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (adf_attachment_find(&dev->attach_allowed, eng, intf)) {
+ ret = -EALREADY;
+ goto done;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ entry->attachment.interface = intf;
+ entry->attachment.overlay_engine = eng;
+ list_add_tail(&entry->head, &dev->attach_allowed);
+ dev->n_attach_allowed++;
+
+done:
+ mutex_unlock(&dev->client_lock);
+ if (ret < 0)
+ kfree(entry);
+
+ return ret;
+}
+EXPORT_SYMBOL(adf_attachment_allow);
+
+/**
+ * adf_obj_type_str - string representation of an adf_obj_type
+ *
+ * @type: the object type
+ */
+const char *adf_obj_type_str(enum adf_obj_type type)
+{
+ switch (type) {
+ case ADF_OBJ_OVERLAY_ENGINE:
+ return "overlay engine";
+
+ case ADF_OBJ_INTERFACE:
+ return "interface";
+
+ case ADF_OBJ_DEVICE:
+ return "device";
+
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(adf_obj_type_str);
+
+/**
+ * adf_interface_type_str - string representation of an adf_interface's type
+ *
+ * @intf: the interface
+ */
+const char *adf_interface_type_str(struct adf_interface *intf)
+{
+ switch (intf->type) {
+ case ADF_INTF_DSI:
+ return "DSI";
+
+ case ADF_INTF_eDP:
+ return "eDP";
+
+ case ADF_INTF_DPI:
+ return "DPI";
+
+ case ADF_INTF_VGA:
+ return "VGA";
+
+ case ADF_INTF_DVI:
+ return "DVI";
+
+ case ADF_INTF_HDMI:
+ return "HDMI";
+
+ case ADF_INTF_MEMORY:
+ return "memory";
+
+ default:
+ if (intf->type >= ADF_INTF_TYPE_DEVICE_CUSTOM) {
+ if (intf->ops && intf->ops->type_str)
+ return intf->ops->type_str(intf);
+ return "custom";
+ }
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(adf_interface_type_str);
+
+/**
+ * adf_event_type_str - string representation of an adf_event_type
+ *
+ * @obj: ADF object that produced the event
+ * @type: event type
+ */
+const char *adf_event_type_str(struct adf_obj *obj, enum adf_event_type type)
+{
+ switch (type) {
+ case ADF_EVENT_VSYNC:
+ return "vsync";
+
+ case ADF_EVENT_HOTPLUG:
+ return "hotplug";
+
+ default:
+ if (type >= ADF_EVENT_DEVICE_CUSTOM) {
+ if (obj->ops && obj->ops->event_type_str)
+ return obj->ops->event_type_str(obj, type);
+ return "custom";
+ }
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(adf_event_type_str);
+
+/**
+ * adf_format_str - string representation of an ADF/DRM fourcc format
+ *
+ * @format: format fourcc
+ * @buf: target buffer for the format's string representation
+ */
+void adf_format_str(u32 format, char buf[ADF_FORMAT_STR_SIZE])
+{
+ buf[0] = format & 0xFF;
+ buf[1] = (format >> 8) & 0xFF;
+ buf[2] = (format >> 16) & 0xFF;
+ buf[3] = (format >> 24) & 0xFF;
+ buf[4] = '\0';
+}
+EXPORT_SYMBOL(adf_format_str);
+
+/**
+ * adf_format_validate_yuv - validate the number and size of planes in buffers
+ * with a custom YUV format.
+ *
+ * @dev: ADF device performing the validation
+ * @buf: buffer to validate
+ * @num_planes: expected number of planes
+ * @hsub: expected horizontal chroma subsampling factor, in pixels
+ * @vsub: expected vertical chroma subsampling factor, in pixels
+ * @cpp: expected bytes per pixel for each plane (length @num_planes)
+ *
+ * adf_format_validate_yuv() is intended to be called as a helper from @dev's
+ * validate_custom_format() op.
+ *
+ * Returns 0 if @buf has the expected number of planes and each plane
+ * has sufficient size, or -EINVAL otherwise.
+ */
+int adf_format_validate_yuv(struct adf_device *dev, struct adf_buffer *buf,
+ u8 num_planes, u8 hsub, u8 vsub, u8 cpp[])
+{
+ u8 i;
+
+ if (num_planes != buf->n_planes) {
+ char format_str[ADF_FORMAT_STR_SIZE];
+ adf_format_str(buf->format, format_str);
+ dev_err(&dev->base.dev, "%u planes expected for format %s but %u planes provided\n",
+ num_planes, format_str, buf->n_planes);
+ return -EINVAL;
+ }
+
+ if (buf->w == 0 || buf->w % hsub) {
+ dev_err(&dev->base.dev, "bad buffer width %u\n", buf->w);
+ return -EINVAL;
+ }
+
+ if (buf->h == 0 || buf->h % vsub) {
+ dev_err(&dev->base.dev, "bad buffer height %u\n", buf->h);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_planes; i++) {
+ u32 width = buf->w / (i != 0 ? hsub : 1);
+ u32 height = buf->h / (i != 0 ? vsub : 1);
+ u8 cpp = adf_format_plane_cpp(buf->format, i);
+
+ if (buf->pitch[i] < (u64) width * cpp) {
+ dev_err(&dev->base.dev, "plane %u pitch is shorter than buffer width (pitch = %u, width = %u, bpp = %u)\n",
+ i, buf->pitch[i], width, cpp * 8);
+ return -EINVAL;
+ }
+
+ if ((u64) height * buf->pitch[i] + buf->offset[i] >
+ buf->dma_bufs[i]->size) {
+ dev_err(&dev->base.dev, "plane %u buffer too small (height = %u, pitch = %u, offset = %u, size = %zu)\n",
+ i, height, buf->pitch[i],
+ buf->offset[i], buf->dma_bufs[i]->size);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(adf_format_validate_yuv);
+
+/**
+ * adf_modeinfo_set_name - sets the name of a mode from its display resolution
+ *
+ * @mode: mode
+ *
+ * adf_modeinfo_set_name() fills in @mode->name in the format
+ * "[hdisplay]x[vdisplay](i)". It is intended to help drivers create
+ * ADF/DRM-style modelists from other mode formats.
+ */
+void adf_modeinfo_set_name(struct drm_mode_modeinfo *mode)
+{
+ bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
+
+ snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d%s",
+ mode->hdisplay, mode->vdisplay,
+ interlaced ? "i" : "");
+}
+EXPORT_SYMBOL(adf_modeinfo_set_name);
+
+/**
+ * adf_modeinfo_set_vrefresh - sets the vrefresh of a mode from its other
+ * timing data
+ *
+ * @mode: mode
+ *
+ * adf_modeinfo_set_vrefresh() calculates @mode->vrefresh from
+ * @mode->{h,v}display and @mode->flags. It is intended to help drivers
+ * create ADF/DRM-style modelists from other mode formats.
+ */
+void adf_modeinfo_set_vrefresh(struct drm_mode_modeinfo *mode)
+{
+ int refresh = 0;
+ unsigned int calc_val;
+
+ if (mode->vrefresh > 0)
+ return;
+
+ if (mode->htotal <= 0 || mode->vtotal <= 0)
+ return;
+
+ /* work out vrefresh the value will be x1000 */
+ calc_val = (mode->clock * 1000);
+ calc_val /= mode->htotal;
+ refresh = (calc_val + mode->vtotal / 2) / mode->vtotal;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ refresh *= 2;
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ refresh /= 2;
+ if (mode->vscan > 1)
+ refresh /= mode->vscan;
+
+ mode->vrefresh = refresh;
+}
+EXPORT_SYMBOL(adf_modeinfo_set_vrefresh);
+
+static int __init adf_init(void)
+{
+ int err;
+
+ err = adf_sysfs_init();
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void __exit adf_exit(void)
+{
+ adf_sysfs_destroy();
+}
+
+module_init(adf_init);
+module_exit(adf_exit);
diff --git a/drivers/video/adf/adf.h b/drivers/video/adf/adf.h
new file mode 100644
index 000000000000..3bcf1fabc23c
--- /dev/null
+++ b/drivers/video/adf/adf.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __VIDEO_ADF_ADF_H
+#define __VIDEO_ADF_ADF_H
+
+#include <linux/idr.h>
+#include <linux/list.h>
+#include <video/adf.h>
+#include "sync.h"
+
+struct adf_event_refcount {
+ struct rb_node node;
+ enum adf_event_type type;
+ int refcount;
+};
+
+void adf_buffer_cleanup(struct adf_buffer *buf);
+void adf_buffer_mapping_cleanup(struct adf_buffer_mapping *mapping,
+ struct adf_buffer *buf);
+void adf_post_cleanup(struct adf_device *dev, struct adf_pending_post *post);
+
+struct adf_attachment_list *adf_attachment_find(struct list_head *list,
+ struct adf_overlay_engine *eng, struct adf_interface *intf);
+int adf_attachment_validate(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf);
+void adf_attachment_free(struct adf_attachment_list *attachment);
+
+struct adf_event_refcount *adf_obj_find_event_refcount(struct adf_obj *obj,
+ enum adf_event_type type);
+
+static inline int adf_obj_check_supports_event(struct adf_obj *obj,
+ enum adf_event_type type)
+{
+ if (!obj->ops || !obj->ops->supports_event)
+ return -EOPNOTSUPP;
+ if (!obj->ops->supports_event(obj, type))
+ return -EINVAL;
+ return 0;
+}
+
+static inline int adf_device_attach_op(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ if (!dev->ops->attach)
+ return 0;
+
+ return dev->ops->attach(dev, eng, intf);
+}
+
+static inline int adf_device_detach_op(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ if (!dev->ops->detach)
+ return 0;
+
+ return dev->ops->detach(dev, eng, intf);
+}
+
+#endif /* __VIDEO_ADF_ADF_H */
diff --git a/drivers/video/adf/adf_client.c b/drivers/video/adf/adf_client.c
new file mode 100644
index 000000000000..8061d8e6b9fb
--- /dev/null
+++ b/drivers/video/adf/adf_client.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include "sw_sync.h"
+
+#include <video/adf.h>
+#include <video/adf_client.h>
+#include <video/adf_format.h>
+
+#include "adf.h"
+
+static inline bool vsync_active(u8 state)
+{
+ return state == DRM_MODE_DPMS_ON || state == DRM_MODE_DPMS_STANDBY;
+}
+
+/**
+ * adf_interface_blank - set interface's DPMS state
+ *
+ * @intf: the interface
+ * @state: one of %DRM_MODE_DPMS_*
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_interface_blank(struct adf_interface *intf, u8 state)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ u8 prev_state;
+ bool disable_vsync;
+ bool enable_vsync;
+ int ret = 0;
+ struct adf_event_refcount *vsync_refcount;
+
+ if (!intf->ops || !intf->ops->blank)
+ return -EOPNOTSUPP;
+
+ if (state > DRM_MODE_DPMS_OFF)
+ return -EINVAL;
+
+ mutex_lock(&dev->client_lock);
+ if (state != DRM_MODE_DPMS_ON)
+ flush_kthread_worker(&dev->post_worker);
+ mutex_lock(&intf->base.event_lock);
+
+ vsync_refcount = adf_obj_find_event_refcount(&intf->base,
+ ADF_EVENT_VSYNC);
+ if (!vsync_refcount) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ prev_state = intf->dpms_state;
+ if (prev_state == state) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ disable_vsync = vsync_active(prev_state) &&
+ !vsync_active(state) &&
+ vsync_refcount->refcount;
+ enable_vsync = !vsync_active(prev_state) &&
+ vsync_active(state) &&
+ vsync_refcount->refcount;
+
+ if (disable_vsync)
+ intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC,
+ false);
+
+ ret = intf->ops->blank(intf, state);
+ if (ret < 0) {
+ if (disable_vsync)
+ intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC,
+ true);
+ goto done;
+ }
+
+ if (enable_vsync)
+ intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC,
+ true);
+
+ intf->dpms_state = state;
+done:
+ mutex_unlock(&intf->base.event_lock);
+ mutex_unlock(&dev->client_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_blank);
+
+/**
+ * adf_interface_blank - get interface's current DPMS state
+ *
+ * @intf: the interface
+ *
+ * Returns one of %DRM_MODE_DPMS_*.
+ */
+u8 adf_interface_dpms_state(struct adf_interface *intf)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ u8 dpms_state;
+
+ mutex_lock(&dev->client_lock);
+ dpms_state = intf->dpms_state;
+ mutex_unlock(&dev->client_lock);
+
+ return dpms_state;
+}
+EXPORT_SYMBOL(adf_interface_dpms_state);
+
+/**
+ * adf_interface_current_mode - get interface's current display mode
+ *
+ * @intf: the interface
+ * @mode: returns the current mode
+ */
+void adf_interface_current_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+
+ mutex_lock(&dev->client_lock);
+ memcpy(mode, &intf->current_mode, sizeof(*mode));
+ mutex_unlock(&dev->client_lock);
+}
+EXPORT_SYMBOL(adf_interface_current_mode);
+
+/**
+ * adf_interface_modelist - get interface's modelist
+ *
+ * @intf: the interface
+ * @modelist: storage for the modelist (optional)
+ * @n_modes: length of @modelist
+ *
+ * If @modelist is not NULL, adf_interface_modelist() will copy up to @n_modes
+ * modelist entries into @modelist.
+ *
+ * Returns the length of the modelist.
+ */
+size_t adf_interface_modelist(struct adf_interface *intf,
+ struct drm_mode_modeinfo *modelist, size_t n_modes)
+{
+ unsigned long flags;
+ size_t retval;
+
+ read_lock_irqsave(&intf->hotplug_modelist_lock, flags);
+ if (modelist)
+ memcpy(modelist, intf->modelist, sizeof(modelist[0]) *
+ min(n_modes, intf->n_modes));
+ retval = intf->n_modes;
+ read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
+
+ return retval;
+}
+EXPORT_SYMBOL(adf_interface_modelist);
+
+/**
+ * adf_interface_set_mode - set interface's display mode
+ *
+ * @intf: the interface
+ * @mode: the new mode
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_interface_set_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ int ret = 0;
+
+ if (!intf->ops || !intf->ops->modeset)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&dev->client_lock);
+ flush_kthread_worker(&dev->post_worker);
+
+ ret = intf->ops->modeset(intf, mode);
+ if (ret < 0)
+ goto done;
+
+ memcpy(&intf->current_mode, mode, sizeof(*mode));
+done:
+ mutex_unlock(&dev->client_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_set_mode);
+
+/**
+ * adf_interface_screen_size - get size of screen connected to interface
+ *
+ * @intf: the interface
+ * @width_mm: returns the screen width in mm
+ * @height_mm: returns the screen width in mm
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_interface_get_screen_size(struct adf_interface *intf, u16 *width_mm,
+ u16 *height_mm)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ int ret;
+
+ if (!intf->ops || !intf->ops->screen_size)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&dev->client_lock);
+ ret = intf->ops->screen_size(intf, width_mm, height_mm);
+ mutex_unlock(&dev->client_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_get_screen_size);
+
+/**
+ * adf_overlay_engine_supports_format - returns whether a format is in an
+ * overlay engine's supported list
+ *
+ * @eng: the overlay engine
+ * @format: format fourcc
+ */
+bool adf_overlay_engine_supports_format(struct adf_overlay_engine *eng,
+ u32 format)
+{
+ size_t i;
+ for (i = 0; i < eng->ops->n_supported_formats; i++)
+ if (format == eng->ops->supported_formats[i])
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(adf_overlay_engine_supports_format);
+
+static int adf_buffer_validate(struct adf_buffer *buf)
+{
+ struct adf_overlay_engine *eng = buf->overlay_engine;
+ struct device *dev = &eng->base.dev;
+ struct adf_device *parent = adf_overlay_engine_parent(eng);
+ u8 hsub, vsub, num_planes, cpp[ADF_MAX_PLANES], i;
+
+ if (!adf_overlay_engine_supports_format(eng, buf->format)) {
+ char format_str[ADF_FORMAT_STR_SIZE];
+ adf_format_str(buf->format, format_str);
+ dev_err(dev, "unsupported format %s\n", format_str);
+ return -EINVAL;
+ }
+
+ if (!adf_format_is_standard(buf->format))
+ return parent->ops->validate_custom_format(parent, buf);
+
+ hsub = adf_format_horz_chroma_subsampling(buf->format);
+ vsub = adf_format_vert_chroma_subsampling(buf->format);
+ num_planes = adf_format_num_planes(buf->format);
+ for (i = 0; i < num_planes; i++)
+ cpp[i] = adf_format_plane_cpp(buf->format, i);
+
+ return adf_format_validate_yuv(parent, buf, num_planes, hsub, vsub,
+ cpp);
+}
+
+static int adf_buffer_map(struct adf_device *dev, struct adf_buffer *buf,
+ struct adf_buffer_mapping *mapping)
+{
+ int ret = 0;
+ size_t i;
+
+ for (i = 0; i < buf->n_planes; i++) {
+ struct dma_buf_attachment *attachment;
+ struct sg_table *sg_table;
+
+ attachment = dma_buf_attach(buf->dma_bufs[i], dev->dev);
+ if (IS_ERR(attachment)) {
+ ret = PTR_ERR(attachment);
+ dev_err(&dev->base.dev, "attaching plane %zu failed: %d\n",
+ i, ret);
+ goto done;
+ }
+ mapping->attachments[i] = attachment;
+
+ sg_table = dma_buf_map_attachment(attachment, DMA_TO_DEVICE);
+ if (IS_ERR(sg_table)) {
+ ret = PTR_ERR(sg_table);
+ dev_err(&dev->base.dev, "mapping plane %zu failed: %d",
+ i, ret);
+ goto done;
+ } else if (!sg_table) {
+ ret = -ENOMEM;
+ dev_err(&dev->base.dev, "mapping plane %zu failed\n",
+ i);
+ goto done;
+ }
+ mapping->sg_tables[i] = sg_table;
+ }
+
+done:
+ if (ret < 0)
+ adf_buffer_mapping_cleanup(mapping, buf);
+
+ return ret;
+}
+
+static struct sync_fence *adf_sw_complete_fence(struct adf_device *dev)
+{
+ struct sync_pt *pt;
+ struct sync_fence *complete_fence;
+
+ if (!dev->timeline) {
+ dev->timeline = sw_sync_timeline_create(dev->base.name);
+ if (!dev->timeline)
+ return ERR_PTR(-ENOMEM);
+ dev->timeline_max = 1;
+ }
+
+ dev->timeline_max++;
+ pt = sw_sync_pt_create(dev->timeline, dev->timeline_max);
+ if (!pt)
+ goto err_pt_create;
+ complete_fence = sync_fence_create(dev->base.name, pt);
+ if (!complete_fence)
+ goto err_fence_create;
+
+ return complete_fence;
+
+err_fence_create:
+ sync_pt_free(pt);
+err_pt_create:
+ dev->timeline_max--;
+ return ERR_PTR(-ENOSYS);
+}
+
+/**
+ * adf_device_post - flip to a new set of buffers
+ *
+ * @dev: device targeted by the flip
+ * @intfs: interfaces targeted by the flip
+ * @n_intfs: number of targeted interfaces
+ * @bufs: description of buffers displayed
+ * @n_bufs: number of buffers displayed
+ * @custom_data: driver-private data
+ * @custom_data_size: size of driver-private data
+ *
+ * adf_device_post() will copy @intfs, @bufs, and @custom_data, so they may
+ * point to variables on the stack. adf_device_post() also takes its own
+ * reference on each of the dma-bufs in @bufs. The adf_device_post_nocopy()
+ * variant transfers ownership of these resources to ADF instead.
+ *
+ * On success, returns a sync fence which signals when the buffers are removed
+ * from the screen. On failure, returns ERR_PTR(-errno).
+ */
+struct sync_fence *adf_device_post(struct adf_device *dev,
+ struct adf_interface **intfs, size_t n_intfs,
+ struct adf_buffer *bufs, size_t n_bufs, void *custom_data,
+ size_t custom_data_size)
+{
+ struct adf_interface **intfs_copy = NULL;
+ struct adf_buffer *bufs_copy = NULL;
+ void *custom_data_copy = NULL;
+ struct sync_fence *ret;
+ size_t i;
+
+ intfs_copy = kzalloc(sizeof(intfs_copy[0]) * n_intfs, GFP_KERNEL);
+ if (!intfs_copy)
+ return ERR_PTR(-ENOMEM);
+
+ bufs_copy = kzalloc(sizeof(bufs_copy[0]) * n_bufs, GFP_KERNEL);
+ if (!bufs_copy) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err_alloc;
+ }
+
+ custom_data_copy = kzalloc(custom_data_size, GFP_KERNEL);
+ if (!custom_data_copy) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err_alloc;
+ }
+
+ for (i = 0; i < n_bufs; i++) {
+ size_t j;
+ for (j = 0; j < bufs[i].n_planes; j++)
+ get_dma_buf(bufs[i].dma_bufs[j]);
+ }
+
+ memcpy(intfs_copy, intfs, sizeof(intfs_copy[0]) * n_intfs);
+ memcpy(bufs_copy, bufs, sizeof(bufs_copy[0]) * n_bufs);
+ memcpy(custom_data_copy, custom_data, custom_data_size);
+
+ ret = adf_device_post_nocopy(dev, intfs_copy, n_intfs, bufs_copy,
+ n_bufs, custom_data_copy, custom_data_size);
+ if (IS_ERR(ret))
+ goto err_post;
+
+ return ret;
+
+err_post:
+ for (i = 0; i < n_bufs; i++) {
+ size_t j;
+ for (j = 0; j < bufs[i].n_planes; j++)
+ dma_buf_put(bufs[i].dma_bufs[j]);
+ }
+err_alloc:
+ kfree(custom_data_copy);
+ kfree(bufs_copy);
+ kfree(intfs_copy);
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_post);
+
+/**
+ * adf_device_post_nocopy - flip to a new set of buffers
+ *
+ * adf_device_post_nocopy() has the same behavior as adf_device_post(),
+ * except ADF does not copy @intfs, @bufs, or @custom_data, and it does
+ * not take an extra reference on the dma-bufs in @bufs.
+ *
+ * @intfs, @bufs, and @custom_data must point to buffers allocated by
+ * kmalloc(). On success, ADF takes ownership of these buffers and the dma-bufs
+ * in @bufs, and will kfree()/dma_buf_put() them when they are no longer needed.
+ * On failure, adf_device_post_nocopy() does NOT take ownership of these
+ * buffers or the dma-bufs, and the caller must clean them up.
+ *
+ * adf_device_post_nocopy() is mainly intended for implementing ADF's ioctls.
+ * Clients may find the nocopy variant useful in limited cases, but most should
+ * call adf_device_post() instead.
+ */
+struct sync_fence *adf_device_post_nocopy(struct adf_device *dev,
+ struct adf_interface **intfs, size_t n_intfs,
+ struct adf_buffer *bufs, size_t n_bufs,
+ void *custom_data, size_t custom_data_size)
+{
+ struct adf_pending_post *cfg;
+ struct adf_buffer_mapping *mappings;
+ struct sync_fence *ret;
+ size_t i;
+ int err;
+
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return ERR_PTR(-ENOMEM);
+
+ mappings = kzalloc(sizeof(mappings[0]) * n_bufs, GFP_KERNEL);
+ if (!mappings) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err_alloc;
+ }
+
+ mutex_lock(&dev->client_lock);
+
+ for (i = 0; i < n_bufs; i++) {
+ err = adf_buffer_validate(&bufs[i]);
+ if (err < 0) {
+ ret = ERR_PTR(err);
+ goto err_buf;
+ }
+
+ err = adf_buffer_map(dev, &bufs[i], &mappings[i]);
+ if (err < 0) {
+ ret = ERR_PTR(err);
+ goto err_buf;
+ }
+ }
+
+ INIT_LIST_HEAD(&cfg->head);
+ cfg->config.n_bufs = n_bufs;
+ cfg->config.bufs = bufs;
+ cfg->config.mappings = mappings;
+ cfg->config.custom_data = custom_data;
+ cfg->config.custom_data_size = custom_data_size;
+
+ err = dev->ops->validate(dev, &cfg->config, &cfg->state);
+ if (err < 0) {
+ ret = ERR_PTR(err);
+ goto err_buf;
+ }
+
+ mutex_lock(&dev->post_lock);
+
+ if (dev->ops->complete_fence)
+ ret = dev->ops->complete_fence(dev, &cfg->config,
+ cfg->state);
+ else
+ ret = adf_sw_complete_fence(dev);
+
+ if (IS_ERR(ret))
+ goto err_fence;
+
+ list_add_tail(&cfg->head, &dev->post_list);
+ queue_kthread_work(&dev->post_worker, &dev->post_work);
+ mutex_unlock(&dev->post_lock);
+ mutex_unlock(&dev->client_lock);
+ kfree(intfs);
+ return ret;
+
+err_fence:
+ mutex_unlock(&dev->post_lock);
+
+err_buf:
+ for (i = 0; i < n_bufs; i++)
+ adf_buffer_mapping_cleanup(&mappings[i], &bufs[i]);
+
+ mutex_unlock(&dev->client_lock);
+ kfree(mappings);
+
+err_alloc:
+ kfree(cfg);
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_post_nocopy);
+
+static void adf_attachment_list_to_array(struct adf_device *dev,
+ struct list_head *src, struct adf_attachment *dst, size_t size)
+{
+ struct adf_attachment_list *entry;
+ size_t i = 0;
+
+ if (!dst)
+ return;
+
+ list_for_each_entry(entry, src, head) {
+ if (i == size)
+ return;
+ dst[i] = entry->attachment;
+ i++;
+ }
+}
+
+/**
+ * adf_device_attachments - get device's list of active attachments
+ *
+ * @dev: the device
+ * @attachments: storage for the attachment list (optional)
+ * @n_attachments: length of @attachments
+ *
+ * If @attachments is not NULL, adf_device_attachments() will copy up to
+ * @n_attachments entries into @attachments.
+ *
+ * Returns the length of the active attachment list.
+ */
+size_t adf_device_attachments(struct adf_device *dev,
+ struct adf_attachment *attachments, size_t n_attachments)
+{
+ size_t retval;
+
+ mutex_lock(&dev->client_lock);
+ adf_attachment_list_to_array(dev, &dev->attached, attachments,
+ n_attachments);
+ retval = dev->n_attached;
+ mutex_unlock(&dev->client_lock);
+
+ return retval;
+}
+EXPORT_SYMBOL(adf_device_attachments);
+
+/**
+ * adf_device_attachments_allowed - get device's list of allowed attachments
+ *
+ * @dev: the device
+ * @attachments: storage for the attachment list (optional)
+ * @n_attachments: length of @attachments
+ *
+ * If @attachments is not NULL, adf_device_attachments_allowed() will copy up to
+ * @n_attachments entries into @attachments.
+ *
+ * Returns the length of the allowed attachment list.
+ */
+size_t adf_device_attachments_allowed(struct adf_device *dev,
+ struct adf_attachment *attachments, size_t n_attachments)
+{
+ size_t retval;
+
+ mutex_lock(&dev->client_lock);
+ adf_attachment_list_to_array(dev, &dev->attach_allowed, attachments,
+ n_attachments);
+ retval = dev->n_attach_allowed;
+ mutex_unlock(&dev->client_lock);
+
+ return retval;
+}
+EXPORT_SYMBOL(adf_device_attachments_allowed);
+
+/**
+ * adf_device_attached - return whether an overlay engine and interface are
+ * attached
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ */
+bool adf_device_attached(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf)
+{
+ struct adf_attachment_list *attachment;
+
+ mutex_lock(&dev->client_lock);
+ attachment = adf_attachment_find(&dev->attached, eng, intf);
+ mutex_unlock(&dev->client_lock);
+
+ return attachment != NULL;
+}
+EXPORT_SYMBOL(adf_device_attached);
+
+/**
+ * adf_device_attach_allowed - return whether the ADF device supports attaching
+ * an overlay engine and interface
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ */
+bool adf_device_attach_allowed(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf)
+{
+ struct adf_attachment_list *attachment;
+
+ mutex_lock(&dev->client_lock);
+ attachment = adf_attachment_find(&dev->attach_allowed, eng, intf);
+ mutex_unlock(&dev->client_lock);
+
+ return attachment != NULL;
+}
+EXPORT_SYMBOL(adf_device_attach_allowed);
+/**
+ * adf_device_attach - attach an overlay engine to an interface
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ *
+ * Returns 0 on success, -%EINVAL if attaching @intf and @eng is not allowed,
+ * -%EALREADY if @intf and @eng are already attached, or -errno on any other
+ * failure.
+ */
+int adf_device_attach(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf)
+{
+ int ret;
+ struct adf_attachment_list *attachment = NULL;
+
+ ret = adf_attachment_validate(dev, eng, intf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&dev->client_lock);
+
+ if (dev->n_attached == ADF_MAX_ATTACHMENTS) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (!adf_attachment_find(&dev->attach_allowed, eng, intf)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (adf_attachment_find(&dev->attached, eng, intf)) {
+ ret = -EALREADY;
+ goto done;
+ }
+
+ ret = adf_device_attach_op(dev, eng, intf);
+ if (ret < 0)
+ goto done;
+
+ attachment = kzalloc(sizeof(*attachment), GFP_KERNEL);
+ if (!attachment) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ attachment->attachment.interface = intf;
+ attachment->attachment.overlay_engine = eng;
+ list_add_tail(&attachment->head, &dev->attached);
+ dev->n_attached++;
+
+done:
+ mutex_unlock(&dev->client_lock);
+ if (ret < 0)
+ kfree(attachment);
+
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_attach);
+
+/**
+ * adf_device_detach - detach an overlay engine from an interface
+ *
+ * @dev: the parent device
+ * @eng: the overlay engine
+ * @intf: the interface
+ *
+ * Returns 0 on success, -%EINVAL if @intf and @eng are not attached,
+ * or -errno on any other failure.
+ */
+int adf_device_detach(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf)
+{
+ int ret;
+ struct adf_attachment_list *attachment;
+
+ ret = adf_attachment_validate(dev, eng, intf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&dev->client_lock);
+
+ attachment = adf_attachment_find(&dev->attached, eng, intf);
+ if (!attachment) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = adf_device_detach_op(dev, eng, intf);
+ if (ret < 0)
+ goto done;
+
+ adf_attachment_free(attachment);
+ dev->n_attached--;
+done:
+ mutex_unlock(&dev->client_lock);
+ return ret;
+}
+EXPORT_SYMBOL(adf_device_detach);
+
+/**
+ * adf_interface_simple_buffer_alloc - allocate a simple buffer
+ *
+ * @intf: target interface
+ * @w: width in pixels
+ * @h: height in pixels
+ * @format: format fourcc
+ * @dma_buf: returns the allocated buffer
+ * @offset: returns the byte offset of the allocated buffer's first pixel
+ * @pitch: returns the allocated buffer's pitch
+ *
+ * See &struct adf_simple_buffer_alloc for a description of simple buffers and
+ * their limitations.
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_interface_simple_buffer_alloc(struct adf_interface *intf, u16 w, u16 h,
+ u32 format, struct dma_buf **dma_buf, u32 *offset, u32 *pitch)
+{
+ if (!intf->ops || !intf->ops->alloc_simple_buffer)
+ return -EOPNOTSUPP;
+
+ if (!adf_format_is_rgb(format))
+ return -EINVAL;
+
+ return intf->ops->alloc_simple_buffer(intf, w, h, format, dma_buf,
+ offset, pitch);
+}
+EXPORT_SYMBOL(adf_interface_simple_buffer_alloc);
+
+/**
+ * adf_interface_simple_post - flip to a single buffer
+ *
+ * @intf: interface targeted by the flip
+ * @buf: buffer to display
+ *
+ * adf_interface_simple_post() can be used generically for simple display
+ * configurations, since the client does not need to provide any driver-private
+ * configuration data.
+ *
+ * adf_interface_simple_post() has the same copying semantics as
+ * adf_device_post().
+ *
+ * On success, returns a sync fence which signals when the buffer is removed
+ * from the screen. On failure, returns ERR_PTR(-errno).
+ */
+struct sync_fence *adf_interface_simple_post(struct adf_interface *intf,
+ struct adf_buffer *buf)
+{
+ size_t custom_data_size = 0;
+ void *custom_data = NULL;
+ struct sync_fence *ret;
+
+ if (intf->ops && intf->ops->describe_simple_post) {
+ int err;
+
+ custom_data = kzalloc(ADF_MAX_CUSTOM_DATA_SIZE, GFP_KERNEL);
+ if (!custom_data) {
+ ret = ERR_PTR(-ENOMEM);
+ goto done;
+ }
+
+ err = intf->ops->describe_simple_post(intf, buf, custom_data,
+ &custom_data_size);
+ if (err < 0) {
+ ret = ERR_PTR(err);
+ goto done;
+ }
+ }
+
+ ret = adf_device_post(adf_interface_parent(intf), &intf, 1, buf, 1,
+ custom_data, custom_data_size);
+done:
+ kfree(custom_data);
+ return ret;
+}
+EXPORT_SYMBOL(adf_interface_simple_post);
diff --git a/drivers/video/adf/adf_fbdev.c b/drivers/video/adf/adf_fbdev.c
new file mode 100644
index 000000000000..cac34d14cbc2
--- /dev/null
+++ b/drivers/video/adf/adf_fbdev.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/vmalloc.h>
+
+#include <video/adf.h>
+#include <video/adf_client.h>
+#include <video/adf_fbdev.h>
+#include <video/adf_format.h>
+
+#include "adf.h"
+
+struct adf_fbdev_format {
+ u32 fourcc;
+ u32 bpp;
+ u32 r_length;
+ u32 g_length;
+ u32 b_length;
+ u32 a_length;
+ u32 r_offset;
+ u32 g_offset;
+ u32 b_offset;
+ u32 a_offset;
+};
+
+static const struct adf_fbdev_format format_table[] = {
+ {DRM_FORMAT_RGB332, 8, 3, 3, 2, 0, 5, 2, 0, 0},
+ {DRM_FORMAT_BGR233, 8, 3, 3, 2, 0, 0, 3, 5, 0},
+
+ {DRM_FORMAT_XRGB4444, 16, 4, 4, 4, 0, 8, 4, 0, 0},
+ {DRM_FORMAT_XBGR4444, 16, 4, 4, 4, 0, 0, 4, 8, 0},
+ {DRM_FORMAT_RGBX4444, 16, 4, 4, 4, 0, 12, 8, 4, 0},
+ {DRM_FORMAT_BGRX4444, 16, 4, 4, 4, 0, 0, 4, 8, 0},
+
+ {DRM_FORMAT_ARGB4444, 16, 4, 4, 4, 4, 8, 4, 0, 12},
+ {DRM_FORMAT_ABGR4444, 16, 4, 4, 4, 4, 0, 4, 8, 12},
+ {DRM_FORMAT_RGBA4444, 16, 4, 4, 4, 4, 12, 8, 4, 0},
+ {DRM_FORMAT_BGRA4444, 16, 4, 4, 4, 4, 0, 4, 8, 0},
+
+ {DRM_FORMAT_XRGB1555, 16, 5, 5, 5, 0, 10, 5, 0, 0},
+ {DRM_FORMAT_XBGR1555, 16, 5, 5, 5, 0, 0, 5, 10, 0},
+ {DRM_FORMAT_RGBX5551, 16, 5, 5, 5, 0, 11, 6, 1, 0},
+ {DRM_FORMAT_BGRX5551, 16, 5, 5, 5, 0, 1, 6, 11, 0},
+
+ {DRM_FORMAT_ARGB1555, 16, 5, 5, 5, 1, 10, 5, 0, 15},
+ {DRM_FORMAT_ABGR1555, 16, 5, 5, 5, 1, 0, 5, 10, 15},
+ {DRM_FORMAT_RGBA5551, 16, 5, 5, 5, 1, 11, 6, 1, 0},
+ {DRM_FORMAT_BGRA5551, 16, 5, 5, 5, 1, 1, 6, 11, 0},
+
+ {DRM_FORMAT_RGB565, 16, 5, 6, 5, 0, 11, 5, 0, 0},
+ {DRM_FORMAT_BGR565, 16, 5, 6, 5, 0, 0, 5, 11, 0},
+
+ {DRM_FORMAT_RGB888, 24, 8, 8, 8, 0, 16, 8, 0, 0},
+ {DRM_FORMAT_BGR888, 24, 8, 8, 8, 0, 0, 8, 16, 0},
+
+ {DRM_FORMAT_XRGB8888, 32, 8, 8, 8, 0, 16, 8, 0, 0},
+ {DRM_FORMAT_XBGR8888, 32, 8, 8, 8, 0, 0, 8, 16, 0},
+ {DRM_FORMAT_RGBX8888, 32, 8, 8, 8, 0, 24, 16, 8, 0},
+ {DRM_FORMAT_BGRX8888, 32, 8, 8, 8, 0, 8, 16, 24, 0},
+
+ {DRM_FORMAT_ARGB8888, 32, 8, 8, 8, 8, 16, 8, 0, 24},
+ {DRM_FORMAT_ABGR8888, 32, 8, 8, 8, 8, 0, 8, 16, 24},
+ {DRM_FORMAT_RGBA8888, 32, 8, 8, 8, 8, 24, 16, 8, 0},
+ {DRM_FORMAT_BGRA8888, 32, 8, 8, 8, 8, 8, 16, 24, 0},
+
+ {DRM_FORMAT_XRGB2101010, 32, 10, 10, 10, 0, 20, 10, 0, 0},
+ {DRM_FORMAT_XBGR2101010, 32, 10, 10, 10, 0, 0, 10, 20, 0},
+ {DRM_FORMAT_RGBX1010102, 32, 10, 10, 10, 0, 22, 12, 2, 0},
+ {DRM_FORMAT_BGRX1010102, 32, 10, 10, 10, 0, 2, 12, 22, 0},
+
+ {DRM_FORMAT_ARGB2101010, 32, 10, 10, 10, 2, 20, 10, 0, 30},
+ {DRM_FORMAT_ABGR2101010, 32, 10, 10, 10, 2, 0, 10, 20, 30},
+ {DRM_FORMAT_RGBA1010102, 32, 10, 10, 10, 2, 22, 12, 2, 0},
+ {DRM_FORMAT_BGRA1010102, 32, 10, 10, 10, 2, 2, 12, 22, 0},
+};
+
+static u32 drm_fourcc_from_fb_var(struct fb_var_screeninfo *var)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(format_table); i++) {
+ const struct adf_fbdev_format *f = &format_table[i];
+ if (var->red.length == f->r_length &&
+ var->red.offset == f->r_offset &&
+ var->green.length == f->g_length &&
+ var->green.offset == f->g_offset &&
+ var->blue.length == f->b_length &&
+ var->blue.offset == f->b_offset &&
+ var->transp.length == f->a_length &&
+ (var->transp.length == 0 ||
+ var->transp.offset == f->a_offset))
+ return f->fourcc;
+ }
+
+ return 0;
+}
+
+static const struct adf_fbdev_format *fbdev_format_info(u32 format)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(format_table); i++) {
+ const struct adf_fbdev_format *f = &format_table[i];
+ if (f->fourcc == format)
+ return f;
+ }
+
+ BUG();
+}
+
+void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode,
+ struct fb_videomode *vmode)
+{
+ memset(vmode, 0, sizeof(*vmode));
+
+ vmode->refresh = mode->vrefresh;
+
+ vmode->xres = mode->hdisplay;
+ vmode->yres = mode->vdisplay;
+
+ vmode->pixclock = mode->clock ? KHZ2PICOS(mode->clock) : 0;
+ vmode->left_margin = mode->htotal - mode->hsync_end;
+ vmode->right_margin = mode->hsync_start - mode->hdisplay;
+ vmode->upper_margin = mode->vtotal - mode->vsync_end;
+ vmode->lower_margin = mode->vsync_start - mode->vdisplay;
+ vmode->hsync_len = mode->hsync_end - mode->hsync_start;
+ vmode->vsync_len = mode->vsync_end - mode->vsync_start;
+
+ vmode->sync = 0;
+ if (mode->flags | DRM_MODE_FLAG_PHSYNC)
+ vmode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (mode->flags | DRM_MODE_FLAG_PVSYNC)
+ vmode->sync |= FB_SYNC_VERT_HIGH_ACT;
+ if (mode->flags | DRM_MODE_FLAG_PCSYNC)
+ vmode->sync |= FB_SYNC_COMP_HIGH_ACT;
+ if (mode->flags | DRM_MODE_FLAG_BCAST)
+ vmode->sync |= FB_SYNC_BROADCAST;
+
+ vmode->vmode = 0;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ vmode->vmode |= FB_VMODE_INTERLACED;
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ vmode->vmode |= FB_VMODE_DOUBLE;
+}
+EXPORT_SYMBOL(adf_modeinfo_to_fb_videomode);
+
+void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode,
+ struct drm_mode_modeinfo *mode)
+{
+ memset(mode, 0, sizeof(*mode));
+
+ mode->hdisplay = vmode->xres;
+ mode->hsync_start = mode->hdisplay + vmode->right_margin;
+ mode->hsync_end = mode->hsync_start + vmode->hsync_len;
+ mode->htotal = mode->hsync_end + vmode->left_margin;
+
+ mode->vdisplay = vmode->yres;
+ mode->vsync_start = mode->vdisplay + vmode->lower_margin;
+ mode->vsync_end = mode->vsync_start + vmode->vsync_len;
+ mode->vtotal = mode->vsync_end + vmode->upper_margin;
+
+ mode->clock = vmode->pixclock ? PICOS2KHZ(vmode->pixclock) : 0;
+
+ mode->flags = 0;
+ if (vmode->sync & FB_SYNC_HOR_HIGH_ACT)
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ if (vmode->sync & FB_SYNC_VERT_HIGH_ACT)
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
+ if (vmode->sync & FB_SYNC_COMP_HIGH_ACT)
+ mode->flags |= DRM_MODE_FLAG_PCSYNC;
+ if (vmode->sync & FB_SYNC_BROADCAST)
+ mode->flags |= DRM_MODE_FLAG_BCAST;
+ if (vmode->vmode & FB_VMODE_INTERLACED)
+ mode->flags |= DRM_MODE_FLAG_INTERLACE;
+ if (vmode->vmode & FB_VMODE_DOUBLE)
+ mode->flags |= DRM_MODE_FLAG_DBLSCAN;
+
+ if (vmode->refresh)
+ mode->vrefresh = vmode->refresh;
+ else
+ adf_modeinfo_set_vrefresh(mode);
+
+ if (vmode->name)
+ strlcpy(mode->name, vmode->name, sizeof(mode->name));
+ else
+ adf_modeinfo_set_name(mode);
+}
+EXPORT_SYMBOL(adf_modeinfo_from_fb_videomode);
+
+static int adf_fbdev_post(struct adf_fbdev *fbdev)
+{
+ struct adf_buffer buf;
+ struct sync_fence *complete_fence;
+ int ret = 0;
+
+ memset(&buf, 0, sizeof(buf));
+ buf.overlay_engine = fbdev->eng;
+ buf.w = fbdev->info->var.xres;
+ buf.h = fbdev->info->var.yres;
+ buf.format = fbdev->format;
+ buf.dma_bufs[0] = fbdev->dma_buf;
+ buf.offset[0] = fbdev->offset +
+ fbdev->info->var.yoffset * fbdev->pitch +
+ fbdev->info->var.xoffset *
+ (fbdev->info->var.bits_per_pixel / 8);
+ buf.pitch[0] = fbdev->pitch;
+ buf.n_planes = 1;
+
+ complete_fence = adf_interface_simple_post(fbdev->intf, &buf);
+ if (IS_ERR(complete_fence)) {
+ ret = PTR_ERR(complete_fence);
+ goto done;
+ }
+
+ sync_fence_put(complete_fence);
+done:
+ return ret;
+}
+
+static const u16 vga_palette[][3] = {
+ {0x0000, 0x0000, 0x0000},
+ {0x0000, 0x0000, 0xAAAA},
+ {0x0000, 0xAAAA, 0x0000},
+ {0x0000, 0xAAAA, 0xAAAA},
+ {0xAAAA, 0x0000, 0x0000},
+ {0xAAAA, 0x0000, 0xAAAA},
+ {0xAAAA, 0x5555, 0x0000},
+ {0xAAAA, 0xAAAA, 0xAAAA},
+ {0x5555, 0x5555, 0x5555},
+ {0x5555, 0x5555, 0xFFFF},
+ {0x5555, 0xFFFF, 0x5555},
+ {0x5555, 0xFFFF, 0xFFFF},
+ {0xFFFF, 0x5555, 0x5555},
+ {0xFFFF, 0x5555, 0xFFFF},
+ {0xFFFF, 0xFFFF, 0x5555},
+ {0xFFFF, 0xFFFF, 0xFFFF},
+};
+
+static int adf_fb_alloc(struct adf_fbdev *fbdev)
+{
+ int ret;
+
+ ret = adf_interface_simple_buffer_alloc(fbdev->intf,
+ fbdev->default_xres_virtual,
+ fbdev->default_yres_virtual,
+ fbdev->default_format,
+ &fbdev->dma_buf, &fbdev->offset, &fbdev->pitch);
+ if (ret < 0) {
+ dev_err(fbdev->info->dev, "allocating fb failed: %d\n", ret);
+ return ret;
+ }
+
+ fbdev->vaddr = dma_buf_vmap(fbdev->dma_buf);
+ if (!fbdev->vaddr) {
+ ret = -ENOMEM;
+ dev_err(fbdev->info->dev, "vmapping fb failed\n");
+ goto err_vmap;
+ }
+ fbdev->info->fix.line_length = fbdev->pitch;
+ fbdev->info->var.xres_virtual = fbdev->default_xres_virtual;
+ fbdev->info->var.yres_virtual = fbdev->default_yres_virtual;
+ fbdev->info->fix.smem_len = fbdev->dma_buf->size;
+ fbdev->info->screen_base = fbdev->vaddr;
+
+ return 0;
+
+err_vmap:
+ dma_buf_put(fbdev->dma_buf);
+ return ret;
+}
+
+static void adf_fb_destroy(struct adf_fbdev *fbdev)
+{
+ dma_buf_vunmap(fbdev->dma_buf, fbdev->vaddr);
+ dma_buf_put(fbdev->dma_buf);
+}
+
+static void adf_fbdev_set_format(struct adf_fbdev *fbdev, u32 format)
+{
+ size_t i;
+ const struct adf_fbdev_format *info = fbdev_format_info(format);
+ for (i = 0; i < ARRAY_SIZE(vga_palette); i++) {
+ u16 r = vga_palette[i][0];
+ u16 g = vga_palette[i][1];
+ u16 b = vga_palette[i][2];
+
+ r >>= (16 - info->r_length);
+ g >>= (16 - info->g_length);
+ b >>= (16 - info->b_length);
+
+ fbdev->pseudo_palette[i] =
+ (r << info->r_offset) |
+ (g << info->g_offset) |
+ (b << info->b_offset);
+
+ if (info->a_length) {
+ u16 a = BIT(info->a_length) - 1;
+ fbdev->pseudo_palette[i] |= (a << info->a_offset);
+ }
+ }
+
+ fbdev->info->var.bits_per_pixel = adf_format_bpp(format);
+ fbdev->info->var.red.length = info->r_length;
+ fbdev->info->var.red.offset = info->r_offset;
+ fbdev->info->var.green.length = info->g_length;
+ fbdev->info->var.green.offset = info->g_offset;
+ fbdev->info->var.blue.length = info->b_length;
+ fbdev->info->var.blue.offset = info->b_offset;
+ fbdev->info->var.transp.length = info->a_length;
+ fbdev->info->var.transp.offset = info->a_offset;
+ fbdev->format = format;
+}
+
+static void adf_fbdev_fill_modelist(struct adf_fbdev *fbdev)
+{
+ struct drm_mode_modeinfo *modelist;
+ struct fb_videomode fbmode;
+ size_t n_modes, i;
+ int ret = 0;
+
+ n_modes = adf_interface_modelist(fbdev->intf, NULL, 0);
+ modelist = kzalloc(sizeof(modelist[0]) * n_modes, GFP_KERNEL);
+ if (!modelist) {
+ dev_warn(fbdev->info->dev, "allocating new modelist failed; keeping old modelist\n");
+ return;
+ }
+ adf_interface_modelist(fbdev->intf, modelist, n_modes);
+
+ fb_destroy_modelist(&fbdev->info->modelist);
+
+ for (i = 0; i < n_modes; i++) {
+ adf_modeinfo_to_fb_videomode(&modelist[i], &fbmode);
+ ret = fb_add_videomode(&fbmode, &fbdev->info->modelist);
+ if (ret < 0)
+ dev_warn(fbdev->info->dev, "adding mode %s to modelist failed: %d\n",
+ modelist[i].name, ret);
+ }
+
+ kfree(modelist);
+}
+
+/**
+ * adf_fbdev_open - default implementation of fbdev open op
+ */
+int adf_fbdev_open(struct fb_info *info, int user)
+{
+ struct adf_fbdev *fbdev = info->par;
+ int ret;
+
+ if (!fbdev->open) {
+ struct drm_mode_modeinfo mode;
+ struct fb_videomode fbmode;
+ struct adf_device *dev = adf_interface_parent(fbdev->intf);
+
+ ret = adf_device_attach(dev, fbdev->eng, fbdev->intf);
+ if (ret < 0 && ret != -EALREADY)
+ return ret;
+
+ ret = adf_fb_alloc(fbdev);
+ if (ret < 0)
+ return ret;
+
+ adf_interface_current_mode(fbdev->intf, &mode);
+ adf_modeinfo_to_fb_videomode(&mode, &fbmode);
+ fb_videomode_to_var(&fbdev->info->var, &fbmode);
+
+ adf_fbdev_set_format(fbdev, fbdev->default_format);
+ adf_fbdev_fill_modelist(fbdev);
+ }
+
+ ret = adf_fbdev_post(fbdev);
+ if (ret < 0) {
+ if (!fbdev->open)
+ adf_fb_destroy(fbdev);
+ return ret;
+ }
+
+ fbdev->open = true;
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_open);
+
+/**
+ * adf_fbdev_release - default implementation of fbdev release op
+ */
+int adf_fbdev_release(struct fb_info *info, int user)
+{
+ struct adf_fbdev *fbdev = info->par;
+ adf_fb_destroy(fbdev);
+ fbdev->open = false;
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_release);
+
+/**
+ * adf_fbdev_check_var - default implementation of fbdev check_var op
+ */
+int adf_fbdev_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct adf_fbdev *fbdev = info->par;
+ bool valid_format = true;
+ u32 format = drm_fourcc_from_fb_var(var);
+ u32 pitch = var->xres_virtual * var->bits_per_pixel / 8;
+
+ if (!format) {
+ dev_dbg(info->dev, "%s: unrecognized format\n", __func__);
+ valid_format = false;
+ }
+
+ if (valid_format && var->grayscale) {
+ dev_dbg(info->dev, "%s: grayscale modes not supported\n",
+ __func__);
+ valid_format = false;
+ }
+
+ if (valid_format && var->nonstd) {
+ dev_dbg(info->dev, "%s: nonstandard formats not supported\n",
+ __func__);
+ valid_format = false;
+ }
+
+ if (valid_format && !adf_overlay_engine_supports_format(fbdev->eng,
+ format)) {
+ char format_str[ADF_FORMAT_STR_SIZE];
+ adf_format_str(format, format_str);
+ dev_dbg(info->dev, "%s: format %s not supported by overlay engine %s\n",
+ __func__, format_str, fbdev->eng->base.name);
+ valid_format = false;
+ }
+
+ if (valid_format && pitch > fbdev->pitch) {
+ dev_dbg(info->dev, "%s: fb pitch too small for var (pitch = %u, xres_virtual = %u, bits_per_pixel = %u)\n",
+ __func__, fbdev->pitch, var->xres_virtual,
+ var->bits_per_pixel);
+ valid_format = false;
+ }
+
+ if (valid_format && var->yres_virtual > fbdev->default_yres_virtual) {
+ dev_dbg(info->dev, "%s: fb height too small for var (h = %u, yres_virtual = %u)\n",
+ __func__, fbdev->default_yres_virtual,
+ var->yres_virtual);
+ valid_format = false;
+ }
+
+ if (valid_format) {
+ var->activate = info->var.activate;
+ var->height = info->var.height;
+ var->width = info->var.width;
+ var->accel_flags = info->var.accel_flags;
+ var->rotate = info->var.rotate;
+ var->colorspace = info->var.colorspace;
+ /* userspace can't change these */
+ } else {
+ /* if any part of the format is invalid then fixing it up is
+ impractical, so save just the modesetting bits and
+ overwrite everything else */
+ struct fb_videomode mode;
+ fb_var_to_videomode(&mode, var);
+ memcpy(var, &info->var, sizeof(*var));
+ fb_videomode_to_var(var, &mode);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_check_var);
+
+/**
+ * adf_fbdev_set_par - default implementation of fbdev set_par op
+ */
+int adf_fbdev_set_par(struct fb_info *info)
+{
+ struct adf_fbdev *fbdev = info->par;
+ struct adf_interface *intf = fbdev->intf;
+ struct fb_videomode vmode;
+ struct drm_mode_modeinfo mode;
+ int ret;
+ u32 format = drm_fourcc_from_fb_var(&info->var);
+
+ fb_var_to_videomode(&vmode, &info->var);
+ adf_modeinfo_from_fb_videomode(&vmode, &mode);
+ ret = adf_interface_set_mode(intf, &mode);
+ if (ret < 0)
+ return ret;
+
+ ret = adf_fbdev_post(fbdev);
+ if (ret < 0)
+ return ret;
+
+ if (format != fbdev->format)
+ adf_fbdev_set_format(fbdev, format);
+
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_set_par);
+
+/**
+ * adf_fbdev_blank - default implementation of fbdev blank op
+ */
+int adf_fbdev_blank(int blank, struct fb_info *info)
+{
+ struct adf_fbdev *fbdev = info->par;
+ struct adf_interface *intf = fbdev->intf;
+ u8 dpms_state;
+
+ switch (blank) {
+ case FB_BLANK_UNBLANK:
+ dpms_state = DRM_MODE_DPMS_ON;
+ break;
+ case FB_BLANK_NORMAL:
+ dpms_state = DRM_MODE_DPMS_STANDBY;
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ dpms_state = DRM_MODE_DPMS_SUSPEND;
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ dpms_state = DRM_MODE_DPMS_STANDBY;
+ break;
+ case FB_BLANK_POWERDOWN:
+ dpms_state = DRM_MODE_DPMS_OFF;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return adf_interface_blank(intf, dpms_state);
+}
+EXPORT_SYMBOL(adf_fbdev_blank);
+
+/**
+ * adf_fbdev_pan_display - default implementation of fbdev pan_display op
+ */
+int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct adf_fbdev *fbdev = info->par;
+ return adf_fbdev_post(fbdev);
+}
+EXPORT_SYMBOL(adf_fbdev_pan_display);
+
+/**
+ * adf_fbdev_mmap - default implementation of fbdev mmap op
+ */
+int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct adf_fbdev *fbdev = info->par;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ return dma_buf_mmap(fbdev->dma_buf, vma, 0);
+}
+EXPORT_SYMBOL(adf_fbdev_mmap);
+
+/**
+ * adf_fbdev_init - initialize helper to wrap ADF device in fbdev API
+ *
+ * @fbdev: the fbdev helper
+ * @interface: the ADF interface that will display the framebuffer
+ * @eng: the ADF overlay engine that will scan out the framebuffer
+ * @xres_virtual: the virtual width of the framebuffer
+ * @yres_virtual: the virtual height of the framebuffer
+ * @format: the format of the framebuffer
+ * @fbops: the device's fbdev ops
+ * @fmt: formatting for the framebuffer identification string
+ * @...: variable arguments
+ *
+ * @format must be a standard, non-indexed RGB format, i.e.,
+ * adf_format_is_rgb(@format) && @format != @DRM_FORMAT_C8.
+ *
+ * Returns 0 on success or -errno on failure.
+ */
+int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface,
+ struct adf_overlay_engine *eng,
+ u16 xres_virtual, u16 yres_virtual, u32 format,
+ struct fb_ops *fbops, const char *fmt, ...)
+{
+ struct adf_device *parent = adf_interface_parent(interface);
+ struct device *dev = &parent->base.dev;
+ u16 width_mm, height_mm;
+ va_list args;
+ int ret;
+
+ if (!adf_format_is_rgb(format) ||
+ format == DRM_FORMAT_C8) {
+ dev_err(dev, "fbdev helper does not support format %u\n",
+ format);
+ return -EINVAL;
+ }
+
+ memset(fbdev, 0, sizeof(*fbdev));
+ fbdev->intf = interface;
+ fbdev->eng = eng;
+ fbdev->info = framebuffer_alloc(0, dev);
+ if (!fbdev->info) {
+ dev_err(dev, "allocating framebuffer device failed\n");
+ return -ENOMEM;
+ }
+ fbdev->default_xres_virtual = xres_virtual;
+ fbdev->default_yres_virtual = yres_virtual;
+ fbdev->default_format = format;
+
+ fbdev->info->flags = FBINFO_FLAG_DEFAULT;
+ ret = adf_interface_get_screen_size(interface, &width_mm, &height_mm);
+ if (ret < 0) {
+ width_mm = 0;
+ height_mm = 0;
+ }
+ fbdev->info->var.width = width_mm;
+ fbdev->info->var.height = height_mm;
+ fbdev->info->var.activate = FB_ACTIVATE_VBL;
+ va_start(args, fmt);
+ vsnprintf(fbdev->info->fix.id, sizeof(fbdev->info->fix.id), fmt, args);
+ va_end(args);
+ fbdev->info->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbdev->info->fix.visual = FB_VISUAL_TRUECOLOR;
+ fbdev->info->fix.xpanstep = 1;
+ fbdev->info->fix.ypanstep = 1;
+ INIT_LIST_HEAD(&fbdev->info->modelist);
+ fbdev->info->fbops = fbops;
+ fbdev->info->pseudo_palette = fbdev->pseudo_palette;
+ fbdev->info->par = fbdev;
+
+ ret = register_framebuffer(fbdev->info);
+ if (ret < 0) {
+ dev_err(dev, "registering framebuffer failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(adf_fbdev_init);
+
+/**
+ * adf_fbdev_destroy - destroy helper to wrap ADF device in fbdev API
+ *
+ * @fbdev: the fbdev helper
+ */
+void adf_fbdev_destroy(struct adf_fbdev *fbdev)
+{
+ unregister_framebuffer(fbdev->info);
+ if (WARN_ON(fbdev->open))
+ adf_fb_destroy(fbdev);
+ framebuffer_release(fbdev->info);
+}
+EXPORT_SYMBOL(adf_fbdev_destroy);
diff --git a/drivers/video/adf/adf_fops.c b/drivers/video/adf/adf_fops.c
new file mode 100644
index 000000000000..abec58ea2ed8
--- /dev/null
+++ b/drivers/video/adf/adf_fops.c
@@ -0,0 +1,957 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/bitops.h>
+#include <linux/circ_buf.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <video/adf_client.h>
+#include <video/adf_format.h>
+
+#include "sw_sync.h"
+#include "sync.h"
+
+#include "adf.h"
+#include "adf_fops.h"
+#include "adf_sysfs.h"
+
+#ifdef CONFIG_COMPAT
+#include "adf_fops32.h"
+#endif
+
+static int adf_obj_set_event(struct adf_obj *obj, struct adf_file *file,
+ struct adf_set_event __user *arg)
+{
+ struct adf_set_event data;
+ bool enabled;
+ unsigned long flags;
+ int err;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ err = adf_obj_check_supports_event(obj, data.type);
+ if (err < 0)
+ return err;
+
+ spin_lock_irqsave(&obj->file_lock, flags);
+ if (data.enabled)
+ enabled = test_and_set_bit(data.type,
+ file->event_subscriptions);
+ else
+ enabled = test_and_clear_bit(data.type,
+ file->event_subscriptions);
+ spin_unlock_irqrestore(&obj->file_lock, flags);
+
+ if (data.enabled == enabled)
+ return -EALREADY;
+
+ if (data.enabled)
+ adf_event_get(obj, data.type);
+ else
+ adf_event_put(obj, data.type);
+
+ return 0;
+}
+
+static int adf_obj_copy_custom_data_to_user(struct adf_obj *obj,
+ void __user *dst, size_t *dst_size)
+{
+ void *custom_data;
+ size_t custom_data_size;
+ int ret;
+
+ if (!obj->ops || !obj->ops->custom_data) {
+ dev_dbg(&obj->dev, "%s: no custom_data op\n", __func__);
+ return 0;
+ }
+
+ custom_data = kzalloc(ADF_MAX_CUSTOM_DATA_SIZE, GFP_KERNEL);
+ if (!custom_data)
+ return -ENOMEM;
+
+ ret = obj->ops->custom_data(obj, custom_data, &custom_data_size);
+ if (ret < 0)
+ goto done;
+
+ if (copy_to_user(dst, custom_data, min(*dst_size, custom_data_size))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ *dst_size = custom_data_size;
+
+done:
+ kfree(custom_data);
+ return ret;
+}
+
+static int adf_eng_get_data(struct adf_overlay_engine *eng,
+ struct adf_overlay_engine_data __user *arg)
+{
+ struct adf_device *dev = adf_overlay_engine_parent(eng);
+ struct adf_overlay_engine_data data;
+ size_t n_supported_formats;
+ u32 *supported_formats = NULL;
+ int ret = 0;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ strlcpy(data.name, eng->base.name, sizeof(data.name));
+
+ if (data.n_supported_formats > ADF_MAX_SUPPORTED_FORMATS)
+ return -EINVAL;
+
+ n_supported_formats = data.n_supported_formats;
+ data.n_supported_formats = eng->ops->n_supported_formats;
+
+ if (n_supported_formats) {
+ supported_formats = kzalloc(n_supported_formats *
+ sizeof(supported_formats[0]), GFP_KERNEL);
+ if (!supported_formats)
+ return -ENOMEM;
+ }
+
+ memcpy(supported_formats, eng->ops->supported_formats,
+ sizeof(u32) * min(n_supported_formats,
+ eng->ops->n_supported_formats));
+
+ mutex_lock(&dev->client_lock);
+ ret = adf_obj_copy_custom_data_to_user(&eng->base, arg->custom_data,
+ &data.custom_data_size);
+ mutex_unlock(&dev->client_lock);
+
+ if (ret < 0)
+ goto done;
+
+ if (copy_to_user(arg, &data, sizeof(data))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (supported_formats && copy_to_user(arg->supported_formats,
+ supported_formats,
+ n_supported_formats * sizeof(supported_formats[0])))
+ ret = -EFAULT;
+
+done:
+ kfree(supported_formats);
+ return ret;
+}
+
+static int adf_buffer_import(struct adf_device *dev,
+ struct adf_buffer_config __user *cfg, struct adf_buffer *buf)
+{
+ struct adf_buffer_config user_buf;
+ size_t i;
+ int ret = 0;
+
+ if (copy_from_user(&user_buf, cfg, sizeof(user_buf)))
+ return -EFAULT;
+
+ memset(buf, 0, sizeof(*buf));
+
+ if (user_buf.n_planes > ADF_MAX_PLANES) {
+ dev_err(&dev->base.dev, "invalid plane count %u\n",
+ user_buf.n_planes);
+ return -EINVAL;
+ }
+
+ buf->overlay_engine = idr_find(&dev->overlay_engines,
+ user_buf.overlay_engine);
+ if (!buf->overlay_engine) {
+ dev_err(&dev->base.dev, "invalid overlay engine id %u\n",
+ user_buf.overlay_engine);
+ return -ENOENT;
+ }
+
+ buf->w = user_buf.w;
+ buf->h = user_buf.h;
+ buf->format = user_buf.format;
+ for (i = 0; i < user_buf.n_planes; i++) {
+ buf->dma_bufs[i] = dma_buf_get(user_buf.fd[i]);
+ if (IS_ERR(buf->dma_bufs[i])) {
+ ret = PTR_ERR(buf->dma_bufs[i]);
+ dev_err(&dev->base.dev, "importing dma_buf fd %llu failed: %d\n",
+ user_buf.fd[i], ret);
+ buf->dma_bufs[i] = NULL;
+ goto done;
+ }
+ buf->offset[i] = user_buf.offset[i];
+ buf->pitch[i] = user_buf.pitch[i];
+ }
+ buf->n_planes = user_buf.n_planes;
+
+ if (user_buf.acquire_fence >= 0) {
+ buf->acquire_fence = sync_fence_fdget(user_buf.acquire_fence);
+ if (!buf->acquire_fence) {
+ dev_err(&dev->base.dev, "getting fence fd %lld failed\n",
+ user_buf.acquire_fence);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ if (ret < 0)
+ adf_buffer_cleanup(buf);
+ return ret;
+}
+
+static int adf_device_post_config(struct adf_device *dev,
+ struct adf_post_config __user *arg)
+{
+ struct sync_fence *complete_fence;
+ int complete_fence_fd;
+ struct adf_buffer *bufs = NULL;
+ struct adf_interface **intfs = NULL;
+ size_t n_intfs, n_bufs, i;
+ void *custom_data = NULL;
+ size_t custom_data_size;
+ int ret = 0;
+
+ complete_fence_fd = get_unused_fd();
+ if (complete_fence_fd < 0)
+ return complete_fence_fd;
+
+ if (get_user(n_intfs, &arg->n_interfaces)) {
+ ret = -EFAULT;
+ goto err_get_user;
+ }
+
+ if (n_intfs > ADF_MAX_INTERFACES) {
+ ret = -EINVAL;
+ goto err_get_user;
+ }
+
+ if (get_user(n_bufs, &arg->n_bufs)) {
+ ret = -EFAULT;
+ goto err_get_user;
+ }
+
+ if (n_bufs > ADF_MAX_BUFFERS) {
+ ret = -EINVAL;
+ goto err_get_user;
+ }
+
+ if (get_user(custom_data_size, &arg->custom_data_size)) {
+ ret = -EFAULT;
+ goto err_get_user;
+ }
+
+ if (custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) {
+ ret = -EINVAL;
+ goto err_get_user;
+ }
+
+ if (n_intfs) {
+ intfs = kmalloc(sizeof(intfs[0]) * n_intfs, GFP_KERNEL);
+ if (!intfs) {
+ ret = -ENOMEM;
+ goto err_get_user;
+ }
+ }
+
+ for (i = 0; i < n_intfs; i++) {
+ u32 intf_id;
+ if (get_user(intf_id, &arg->interfaces[i])) {
+ ret = -EFAULT;
+ goto err_get_user;
+ }
+
+ intfs[i] = idr_find(&dev->interfaces, intf_id);
+ if (!intfs[i]) {
+ ret = -EINVAL;
+ goto err_get_user;
+ }
+ }
+
+ if (n_bufs) {
+ bufs = kzalloc(sizeof(bufs[0]) * n_bufs, GFP_KERNEL);
+ if (!bufs) {
+ ret = -ENOMEM;
+ goto err_get_user;
+ }
+ }
+
+ for (i = 0; i < n_bufs; i++) {
+ ret = adf_buffer_import(dev, &arg->bufs[i], &bufs[i]);
+ if (ret < 0) {
+ memset(&bufs[i], 0, sizeof(bufs[i]));
+ goto err_import;
+ }
+ }
+
+ if (custom_data_size) {
+ custom_data = kzalloc(custom_data_size, GFP_KERNEL);
+ if (!custom_data) {
+ ret = -ENOMEM;
+ goto err_import;
+ }
+
+ if (copy_from_user(custom_data, arg->custom_data,
+ custom_data_size)) {
+ ret = -EFAULT;
+ goto err_import;
+ }
+ }
+
+ if (put_user(complete_fence_fd, &arg->complete_fence)) {
+ ret = -EFAULT;
+ goto err_import;
+ }
+
+ complete_fence = adf_device_post_nocopy(dev, intfs, n_intfs, bufs,
+ n_bufs, custom_data, custom_data_size);
+ if (IS_ERR(complete_fence)) {
+ ret = PTR_ERR(complete_fence);
+ goto err_import;
+ }
+
+ sync_fence_install(complete_fence, complete_fence_fd);
+ return 0;
+
+err_import:
+ for (i = 0; i < n_bufs; i++)
+ adf_buffer_cleanup(&bufs[i]);
+
+err_get_user:
+ kfree(custom_data);
+ kfree(bufs);
+ kfree(intfs);
+ put_unused_fd(complete_fence_fd);
+ return ret;
+}
+
+static int adf_intf_simple_post_config(struct adf_interface *intf,
+ struct adf_simple_post_config __user *arg)
+{
+ struct adf_device *dev = intf->base.parent;
+ struct sync_fence *complete_fence;
+ int complete_fence_fd;
+ struct adf_buffer buf;
+ int ret = 0;
+
+ complete_fence_fd = get_unused_fd();
+ if (complete_fence_fd < 0)
+ return complete_fence_fd;
+
+ ret = adf_buffer_import(dev, &arg->buf, &buf);
+ if (ret < 0)
+ goto err_import;
+
+ if (put_user(complete_fence_fd, &arg->complete_fence)) {
+ ret = -EFAULT;
+ goto err_put_user;
+ }
+
+ complete_fence = adf_interface_simple_post(intf, &buf);
+ if (IS_ERR(complete_fence)) {
+ ret = PTR_ERR(complete_fence);
+ goto err_put_user;
+ }
+
+ sync_fence_install(complete_fence, complete_fence_fd);
+ return 0;
+
+err_put_user:
+ adf_buffer_cleanup(&buf);
+err_import:
+ put_unused_fd(complete_fence_fd);
+ return ret;
+}
+
+static int adf_intf_simple_buffer_alloc(struct adf_interface *intf,
+ struct adf_simple_buffer_alloc __user *arg)
+{
+ struct adf_simple_buffer_alloc data;
+ struct dma_buf *dma_buf;
+ int ret = 0;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ data.fd = get_unused_fd_flags(O_CLOEXEC);
+ if (data.fd < 0)
+ return data.fd;
+
+ ret = adf_interface_simple_buffer_alloc(intf, data.w, data.h,
+ data.format, &dma_buf, &data.offset, &data.pitch);
+ if (ret < 0)
+ goto err_alloc;
+
+ if (copy_to_user(arg, &data, sizeof(*arg))) {
+ ret = -EFAULT;
+ goto err_copy;
+ }
+
+ fd_install(data.fd, dma_buf->file);
+ return 0;
+
+err_copy:
+ dma_buf_put(dma_buf);
+
+err_alloc:
+ put_unused_fd(data.fd);
+ return ret;
+}
+
+static int adf_copy_attachment_list_to_user(
+ struct adf_attachment_config __user *to, size_t n_to,
+ struct adf_attachment *from, size_t n_from)
+{
+ struct adf_attachment_config *temp;
+ size_t n = min(n_to, n_from);
+ size_t i;
+ int ret = 0;
+
+ if (!n)
+ return 0;
+
+ temp = kzalloc(n * sizeof(temp[0]), GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++) {
+ temp[i].interface = from[i].interface->base.id;
+ temp[i].overlay_engine = from[i].overlay_engine->base.id;
+ }
+
+ if (copy_to_user(to, temp, n * sizeof(to[0]))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+done:
+ kfree(temp);
+ return ret;
+}
+
+static int adf_device_get_data(struct adf_device *dev,
+ struct adf_device_data __user *arg)
+{
+ struct adf_device_data data;
+ size_t n_attach;
+ struct adf_attachment *attach = NULL;
+ size_t n_allowed_attach;
+ struct adf_attachment *allowed_attach = NULL;
+ int ret = 0;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ if (data.n_attachments > ADF_MAX_ATTACHMENTS ||
+ data.n_allowed_attachments > ADF_MAX_ATTACHMENTS)
+ return -EINVAL;
+
+ strlcpy(data.name, dev->base.name, sizeof(data.name));
+
+ if (data.n_attachments) {
+ attach = kzalloc(data.n_attachments * sizeof(attach[0]),
+ GFP_KERNEL);
+ if (!attach)
+ return -ENOMEM;
+ }
+ n_attach = adf_device_attachments(dev, attach, data.n_attachments);
+
+ if (data.n_allowed_attachments) {
+ allowed_attach = kzalloc(data.n_allowed_attachments *
+ sizeof(allowed_attach[0]), GFP_KERNEL);
+ if (!allowed_attach) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+ n_allowed_attach = adf_device_attachments_allowed(dev, allowed_attach,
+ data.n_allowed_attachments);
+
+ mutex_lock(&dev->client_lock);
+ ret = adf_obj_copy_custom_data_to_user(&dev->base, arg->custom_data,
+ &data.custom_data_size);
+ mutex_unlock(&dev->client_lock);
+
+ if (ret < 0)
+ goto done;
+
+ ret = adf_copy_attachment_list_to_user(arg->attachments,
+ data.n_attachments, attach, n_attach);
+ if (ret < 0)
+ goto done;
+
+ ret = adf_copy_attachment_list_to_user(arg->allowed_attachments,
+ data.n_allowed_attachments, allowed_attach,
+ n_allowed_attach);
+ if (ret < 0)
+ goto done;
+
+ data.n_attachments = n_attach;
+ data.n_allowed_attachments = n_allowed_attach;
+
+ if (copy_to_user(arg, &data, sizeof(data)))
+ ret = -EFAULT;
+
+done:
+ kfree(allowed_attach);
+ kfree(attach);
+ return ret;
+}
+
+static int adf_device_handle_attachment(struct adf_device *dev,
+ struct adf_attachment_config __user *arg, bool attach)
+{
+ struct adf_attachment_config data;
+ struct adf_overlay_engine *eng;
+ struct adf_interface *intf;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ eng = idr_find(&dev->overlay_engines, data.overlay_engine);
+ if (!eng) {
+ dev_err(&dev->base.dev, "invalid overlay engine id %u\n",
+ data.overlay_engine);
+ return -EINVAL;
+ }
+
+ intf = idr_find(&dev->interfaces, data.interface);
+ if (!intf) {
+ dev_err(&dev->base.dev, "invalid interface id %u\n",
+ data.interface);
+ return -EINVAL;
+ }
+
+ if (attach)
+ return adf_device_attach(dev, eng, intf);
+ else
+ return adf_device_detach(dev, eng, intf);
+}
+
+static int adf_intf_set_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo __user *arg)
+{
+ struct drm_mode_modeinfo mode;
+
+ if (copy_from_user(&mode, arg, sizeof(mode)))
+ return -EFAULT;
+
+ return adf_interface_set_mode(intf, &mode);
+}
+
+static int adf_intf_get_data(struct adf_interface *intf,
+ struct adf_interface_data __user *arg)
+{
+ struct adf_device *dev = adf_interface_parent(intf);
+ struct adf_interface_data data;
+ struct drm_mode_modeinfo *modelist;
+ size_t modelist_size;
+ int err;
+ int ret = 0;
+ unsigned long flags;
+
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
+ strlcpy(data.name, intf->base.name, sizeof(data.name));
+
+ data.type = intf->type;
+ data.id = intf->idx;
+ data.flags = intf->flags;
+
+ err = adf_interface_get_screen_size(intf, &data.width_mm,
+ &data.height_mm);
+ if (err < 0) {
+ data.width_mm = 0;
+ data.height_mm = 0;
+ }
+
+ modelist = kmalloc(sizeof(modelist[0]) * ADF_MAX_MODES, GFP_KERNEL);
+ if (!modelist)
+ return -ENOMEM;
+
+ mutex_lock(&dev->client_lock);
+ read_lock_irqsave(&intf->hotplug_modelist_lock, flags);
+ data.hotplug_detect = intf->hotplug_detect;
+ modelist_size = min(data.n_available_modes, intf->n_modes) *
+ sizeof(intf->modelist[0]);
+ memcpy(modelist, intf->modelist, modelist_size);
+ data.n_available_modes = intf->n_modes;
+ read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
+
+ if (copy_to_user(arg->available_modes, modelist, modelist_size)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ data.dpms_state = intf->dpms_state;
+ memcpy(&data.current_mode, &intf->current_mode,
+ sizeof(intf->current_mode));
+
+ ret = adf_obj_copy_custom_data_to_user(&intf->base, arg->custom_data,
+ &data.custom_data_size);
+done:
+ mutex_unlock(&dev->client_lock);
+ kfree(modelist);
+
+ if (ret < 0)
+ return ret;
+
+ if (copy_to_user(arg, &data, sizeof(data)))
+ ret = -EFAULT;
+
+ return ret;
+}
+
+static inline long adf_obj_custom_ioctl(struct adf_obj *obj, unsigned int cmd,
+ unsigned long arg)
+{
+ if (obj->ops && obj->ops->ioctl)
+ return obj->ops->ioctl(obj, cmd, arg);
+ return -ENOTTY;
+}
+
+static long adf_overlay_engine_ioctl(struct adf_overlay_engine *eng,
+ struct adf_file *file, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case ADF_SET_EVENT:
+ return adf_obj_set_event(&eng->base, file,
+ (struct adf_set_event __user *)arg);
+
+ case ADF_GET_OVERLAY_ENGINE_DATA:
+ return adf_eng_get_data(eng,
+ (struct adf_overlay_engine_data __user *)arg);
+
+ case ADF_BLANK:
+ case ADF_POST_CONFIG:
+ case ADF_SET_MODE:
+ case ADF_GET_DEVICE_DATA:
+ case ADF_GET_INTERFACE_DATA:
+ case ADF_SIMPLE_POST_CONFIG:
+ case ADF_SIMPLE_BUFFER_ALLOC:
+ case ADF_ATTACH:
+ case ADF_DETACH:
+ return -EINVAL;
+
+ default:
+ return adf_obj_custom_ioctl(&eng->base, cmd, arg);
+ }
+}
+
+static long adf_interface_ioctl(struct adf_interface *intf,
+ struct adf_file *file, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case ADF_SET_EVENT:
+ return adf_obj_set_event(&intf->base, file,
+ (struct adf_set_event __user *)arg);
+
+ case ADF_BLANK:
+ return adf_interface_blank(intf, arg);
+
+ case ADF_SET_MODE:
+ return adf_intf_set_mode(intf,
+ (struct drm_mode_modeinfo __user *)arg);
+
+ case ADF_GET_INTERFACE_DATA:
+ return adf_intf_get_data(intf,
+ (struct adf_interface_data __user *)arg);
+
+ case ADF_SIMPLE_POST_CONFIG:
+ return adf_intf_simple_post_config(intf,
+ (struct adf_simple_post_config __user *)arg);
+
+ case ADF_SIMPLE_BUFFER_ALLOC:
+ return adf_intf_simple_buffer_alloc(intf,
+ (struct adf_simple_buffer_alloc __user *)arg);
+
+ case ADF_POST_CONFIG:
+ case ADF_GET_DEVICE_DATA:
+ case ADF_GET_OVERLAY_ENGINE_DATA:
+ case ADF_ATTACH:
+ case ADF_DETACH:
+ return -EINVAL;
+
+ default:
+ return adf_obj_custom_ioctl(&intf->base, cmd, arg);
+ }
+}
+
+static long adf_device_ioctl(struct adf_device *dev, struct adf_file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case ADF_SET_EVENT:
+ return adf_obj_set_event(&dev->base, file,
+ (struct adf_set_event __user *)arg);
+
+ case ADF_POST_CONFIG:
+ return adf_device_post_config(dev,
+ (struct adf_post_config __user *)arg);
+
+ case ADF_GET_DEVICE_DATA:
+ return adf_device_get_data(dev,
+ (struct adf_device_data __user *)arg);
+
+ case ADF_ATTACH:
+ return adf_device_handle_attachment(dev,
+ (struct adf_attachment_config __user *)arg,
+ true);
+
+ case ADF_DETACH:
+ return adf_device_handle_attachment(dev,
+ (struct adf_attachment_config __user *)arg,
+ false);
+
+ case ADF_BLANK:
+ case ADF_SET_MODE:
+ case ADF_GET_INTERFACE_DATA:
+ case ADF_GET_OVERLAY_ENGINE_DATA:
+ case ADF_SIMPLE_POST_CONFIG:
+ case ADF_SIMPLE_BUFFER_ALLOC:
+ return -EINVAL;
+
+ default:
+ return adf_obj_custom_ioctl(&dev->base, cmd, arg);
+ }
+}
+
+static int adf_file_open(struct inode *inode, struct file *file)
+{
+ struct adf_obj *obj;
+ struct adf_file *fpriv = NULL;
+ unsigned long flags;
+ int ret = 0;
+
+ obj = adf_obj_sysfs_find(iminor(inode));
+ if (!obj)
+ return -ENODEV;
+
+ dev_dbg(&obj->dev, "opening %s\n", dev_name(&obj->dev));
+
+ if (!try_module_get(obj->parent->ops->owner)) {
+ dev_err(&obj->dev, "getting owner module failed\n");
+ return -ENODEV;
+ }
+
+ fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
+ if (!fpriv) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ INIT_LIST_HEAD(&fpriv->head);
+ fpriv->obj = obj;
+ init_waitqueue_head(&fpriv->event_wait);
+
+ file->private_data = fpriv;
+
+ if (obj->ops && obj->ops->open) {
+ ret = obj->ops->open(obj, inode, file);
+ if (ret < 0)
+ goto done;
+ }
+
+ spin_lock_irqsave(&obj->file_lock, flags);
+ list_add_tail(&fpriv->head, &obj->file_list);
+ spin_unlock_irqrestore(&obj->file_lock, flags);
+
+done:
+ if (ret < 0) {
+ kfree(fpriv);
+ module_put(obj->parent->ops->owner);
+ }
+ return ret;
+}
+
+static int adf_file_release(struct inode *inode, struct file *file)
+{
+ struct adf_file *fpriv = file->private_data;
+ struct adf_obj *obj = fpriv->obj;
+ enum adf_event_type event_type;
+ unsigned long flags;
+
+ if (obj->ops && obj->ops->release)
+ obj->ops->release(obj, inode, file);
+
+ spin_lock_irqsave(&obj->file_lock, flags);
+ list_del(&fpriv->head);
+ spin_unlock_irqrestore(&obj->file_lock, flags);
+
+ for_each_set_bit(event_type, fpriv->event_subscriptions,
+ ADF_EVENT_TYPE_MAX) {
+ adf_event_put(obj, event_type);
+ }
+
+ kfree(fpriv);
+ module_put(obj->parent->ops->owner);
+
+ dev_dbg(&obj->dev, "released %s\n", dev_name(&obj->dev));
+ return 0;
+}
+
+long adf_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct adf_file *fpriv = file->private_data;
+ struct adf_obj *obj = fpriv->obj;
+ long ret = -EINVAL;
+
+ dev_dbg(&obj->dev, "%s ioctl %u\n", dev_name(&obj->dev), _IOC_NR(cmd));
+
+ switch (obj->type) {
+ case ADF_OBJ_OVERLAY_ENGINE:
+ ret = adf_overlay_engine_ioctl(adf_obj_to_overlay_engine(obj),
+ fpriv, cmd, arg);
+ break;
+
+ case ADF_OBJ_INTERFACE:
+ ret = adf_interface_ioctl(adf_obj_to_interface(obj), fpriv, cmd,
+ arg);
+ break;
+
+ case ADF_OBJ_DEVICE:
+ ret = adf_device_ioctl(adf_obj_to_device(obj), fpriv, cmd, arg);
+ break;
+ }
+
+ return ret;
+}
+
+static inline bool adf_file_event_available(struct adf_file *fpriv)
+{
+ int head = fpriv->event_head;
+ int tail = fpriv->event_tail;
+ return CIRC_CNT(head, tail, sizeof(fpriv->event_buf)) != 0;
+}
+
+void adf_file_queue_event(struct adf_file *fpriv, struct adf_event *event)
+{
+ int head = fpriv->event_head;
+ int tail = fpriv->event_tail;
+ size_t space = CIRC_SPACE(head, tail, sizeof(fpriv->event_buf));
+ size_t space_to_end =
+ CIRC_SPACE_TO_END(head, tail, sizeof(fpriv->event_buf));
+
+ if (space < event->length) {
+ dev_dbg(&fpriv->obj->dev,
+ "insufficient buffer space for event %u\n",
+ event->type);
+ return;
+ }
+
+ if (space_to_end >= event->length) {
+ memcpy(fpriv->event_buf + head, event, event->length);
+ } else {
+ memcpy(fpriv->event_buf + head, event, space_to_end);
+ memcpy(fpriv->event_buf, (u8 *)event + space_to_end,
+ event->length - space_to_end);
+ }
+
+ smp_wmb();
+ fpriv->event_head = (fpriv->event_head + event->length) &
+ (sizeof(fpriv->event_buf) - 1);
+ wake_up_interruptible_all(&fpriv->event_wait);
+}
+
+static ssize_t adf_file_copy_to_user(struct adf_file *fpriv,
+ char __user *buffer, size_t buffer_size)
+{
+ int head, tail;
+ u8 *event_buf;
+ size_t cnt, cnt_to_end, copy_size = 0;
+ ssize_t ret = 0;
+ unsigned long flags;
+
+ event_buf = kmalloc(min(buffer_size, sizeof(fpriv->event_buf)),
+ GFP_KERNEL);
+ if (!event_buf)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&fpriv->obj->file_lock, flags);
+
+ if (!adf_file_event_available(fpriv))
+ goto out;
+
+ head = fpriv->event_head;
+ tail = fpriv->event_tail;
+
+ cnt = CIRC_CNT(head, tail, sizeof(fpriv->event_buf));
+ cnt_to_end = CIRC_CNT_TO_END(head, tail, sizeof(fpriv->event_buf));
+ copy_size = min(buffer_size, cnt);
+
+ if (cnt_to_end >= copy_size) {
+ memcpy(event_buf, fpriv->event_buf + tail, copy_size);
+ } else {
+ memcpy(event_buf, fpriv->event_buf + tail, cnt_to_end);
+ memcpy(event_buf + cnt_to_end, fpriv->event_buf,
+ copy_size - cnt_to_end);
+ }
+
+ fpriv->event_tail = (fpriv->event_tail + copy_size) &
+ (sizeof(fpriv->event_buf) - 1);
+
+out:
+ spin_unlock_irqrestore(&fpriv->obj->file_lock, flags);
+ if (copy_size) {
+ if (copy_to_user(buffer, event_buf, copy_size))
+ ret = -EFAULT;
+ else
+ ret = copy_size;
+ }
+ kfree(event_buf);
+ return ret;
+}
+
+ssize_t adf_file_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *offset)
+{
+ struct adf_file *fpriv = filp->private_data;
+ int err;
+
+ err = wait_event_interruptible(fpriv->event_wait,
+ adf_file_event_available(fpriv));
+ if (err < 0)
+ return err;
+
+ return adf_file_copy_to_user(fpriv, buffer, count);
+}
+
+unsigned int adf_file_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ struct adf_file *fpriv = filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &fpriv->event_wait, wait);
+
+ if (adf_file_event_available(fpriv))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+const struct file_operations adf_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = adf_file_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = adf_file_compat_ioctl,
+#endif
+ .open = adf_file_open,
+ .release = adf_file_release,
+ .llseek = default_llseek,
+ .read = adf_file_read,
+ .poll = adf_file_poll,
+};
diff --git a/drivers/video/adf/adf_fops.h b/drivers/video/adf/adf_fops.h
new file mode 100644
index 000000000000..90a3a74796d6
--- /dev/null
+++ b/drivers/video/adf/adf_fops.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __VIDEO_ADF_ADF_FOPS_H
+#define __VIDEO_ADF_ADF_FOPS_H
+
+#include <linux/bitmap.h>
+#include <linux/fs.h>
+
+extern const struct file_operations adf_fops;
+
+struct adf_file {
+ struct list_head head;
+ struct adf_obj *obj;
+
+ DECLARE_BITMAP(event_subscriptions, ADF_EVENT_TYPE_MAX);
+ u8 event_buf[4096];
+ int event_head;
+ int event_tail;
+ wait_queue_head_t event_wait;
+};
+
+void adf_file_queue_event(struct adf_file *file, struct adf_event *event);
+long adf_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+
+#endif /* __VIDEO_ADF_ADF_FOPS_H */
diff --git a/drivers/video/adf/adf_fops32.c b/drivers/video/adf/adf_fops32.c
new file mode 100644
index 000000000000..d299a8161491
--- /dev/null
+++ b/drivers/video/adf/adf_fops32.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/uaccess.h>
+#include <video/adf.h>
+
+#include "adf_fops.h"
+#include "adf_fops32.h"
+
+long adf_compat_post_config(struct file *file,
+ struct adf_post_config32 __user *arg)
+{
+ struct adf_post_config32 cfg32;
+ struct adf_post_config __user *cfg;
+ int ret;
+
+ if (copy_from_user(&cfg32, arg, sizeof(cfg32)))
+ return -EFAULT;
+
+ cfg = compat_alloc_user_space(sizeof(*cfg));
+ if (!access_ok(VERIFY_WRITE, cfg, sizeof(*cfg)))
+ return -EFAULT;
+
+ if (put_user(cfg32.n_interfaces, &cfg->n_interfaces) ||
+ put_user(compat_ptr(cfg32.interfaces),
+ &cfg->interfaces) ||
+ put_user(cfg32.n_bufs, &cfg->n_bufs) ||
+ put_user(compat_ptr(cfg32.bufs), &cfg->bufs) ||
+ put_user(cfg32.custom_data_size,
+ &cfg->custom_data_size) ||
+ put_user(compat_ptr(cfg32.custom_data),
+ &cfg->custom_data))
+ return -EFAULT;
+
+ ret = adf_file_ioctl(file, ADF_POST_CONFIG, (unsigned long)cfg);
+ if (ret < 0)
+ return ret;
+
+ if (copy_in_user(&arg->complete_fence, &cfg->complete_fence,
+ sizeof(cfg->complete_fence)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long adf_compat_get_device_data(struct file *file,
+ struct adf_device_data32 __user *arg)
+{
+ struct adf_device_data32 data32;
+ struct adf_device_data __user *data;
+ int ret;
+
+ if (copy_from_user(&data32, arg, sizeof(data32)))
+ return -EFAULT;
+
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!access_ok(VERIFY_WRITE, data, sizeof(*data)))
+ return -EFAULT;
+
+ if (put_user(data32.n_attachments, &data->n_attachments) ||
+ put_user(compat_ptr(data32.attachments),
+ &data->attachments) ||
+ put_user(data32.n_allowed_attachments,
+ &data->n_allowed_attachments) ||
+ put_user(compat_ptr(data32.allowed_attachments),
+ &data->allowed_attachments) ||
+ put_user(data32.custom_data_size,
+ &data->custom_data_size) ||
+ put_user(compat_ptr(data32.custom_data),
+ &data->custom_data))
+ return -EFAULT;
+
+ ret = adf_file_ioctl(file, ADF_GET_DEVICE_DATA, (unsigned long)data);
+ if (ret < 0)
+ return ret;
+
+ if (copy_in_user(arg->name, data->name, sizeof(arg->name)) ||
+ copy_in_user(&arg->n_attachments, &data->n_attachments,
+ sizeof(arg->n_attachments)) ||
+ copy_in_user(&arg->n_allowed_attachments,
+ &data->n_allowed_attachments,
+ sizeof(arg->n_allowed_attachments)) ||
+ copy_in_user(&arg->custom_data_size,
+ &data->custom_data_size,
+ sizeof(arg->custom_data_size)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long adf_compat_get_interface_data(struct file *file,
+ struct adf_interface_data32 __user *arg)
+{
+ struct adf_interface_data32 data32;
+ struct adf_interface_data __user *data;
+ int ret;
+
+ if (copy_from_user(&data32, arg, sizeof(data32)))
+ return -EFAULT;
+
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!access_ok(VERIFY_WRITE, data, sizeof(*data)))
+ return -EFAULT;
+
+ if (put_user(data32.n_available_modes, &data->n_available_modes) ||
+ put_user(compat_ptr(data32.available_modes),
+ &data->available_modes) ||
+ put_user(data32.custom_data_size,
+ &data->custom_data_size) ||
+ put_user(compat_ptr(data32.custom_data),
+ &data->custom_data))
+ return -EFAULT;
+
+ ret = adf_file_ioctl(file, ADF_GET_INTERFACE_DATA, (unsigned long)data);
+ if (ret < 0)
+ return ret;
+
+ if (copy_in_user(arg->name, data->name, sizeof(arg->name)) ||
+ copy_in_user(&arg->type, &data->type,
+ sizeof(arg->type)) ||
+ copy_in_user(&arg->id, &data->id, sizeof(arg->id)) ||
+ copy_in_user(&arg->flags, &data->flags,
+ sizeof(arg->flags)) ||
+ copy_in_user(&arg->dpms_state, &data->dpms_state,
+ sizeof(arg->dpms_state)) ||
+ copy_in_user(&arg->hotplug_detect,
+ &data->hotplug_detect,
+ sizeof(arg->hotplug_detect)) ||
+ copy_in_user(&arg->width_mm, &data->width_mm,
+ sizeof(arg->width_mm)) ||
+ copy_in_user(&arg->height_mm, &data->height_mm,
+ sizeof(arg->height_mm)) ||
+ copy_in_user(&arg->current_mode, &data->current_mode,
+ sizeof(arg->current_mode)) ||
+ copy_in_user(&arg->n_available_modes,
+ &data->n_available_modes,
+ sizeof(arg->n_available_modes)) ||
+ copy_in_user(&arg->custom_data_size,
+ &data->custom_data_size,
+ sizeof(arg->custom_data_size)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long adf_compat_get_overlay_engine_data(struct file *file,
+ struct adf_overlay_engine_data32 __user *arg)
+{
+ struct adf_overlay_engine_data32 data32;
+ struct adf_overlay_engine_data __user *data;
+ int ret;
+
+ if (copy_from_user(&data32, arg, sizeof(data32)))
+ return -EFAULT;
+
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!access_ok(VERIFY_WRITE, data, sizeof(*data)))
+ return -EFAULT;
+
+ if (put_user(data32.n_supported_formats, &data->n_supported_formats) ||
+ put_user(compat_ptr(data32.supported_formats),
+ &data->supported_formats) ||
+ put_user(data32.custom_data_size,
+ &data->custom_data_size) ||
+ put_user(compat_ptr(data32.custom_data),
+ &data->custom_data))
+ return -EFAULT;
+
+ ret = adf_file_ioctl(file, ADF_GET_OVERLAY_ENGINE_DATA,
+ (unsigned long)data);
+ if (ret < 0)
+ return ret;
+
+ if (copy_in_user(arg->name, data->name, sizeof(arg->name)) ||
+ copy_in_user(&arg->n_supported_formats,
+ &data->n_supported_formats,
+ sizeof(arg->n_supported_formats)) ||
+ copy_in_user(&arg->custom_data_size,
+ &data->custom_data_size,
+ sizeof(arg->custom_data_size)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long adf_file_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case ADF_POST_CONFIG32:
+ return adf_compat_post_config(file, compat_ptr(arg));
+
+ case ADF_GET_DEVICE_DATA32:
+ return adf_compat_get_device_data(file, compat_ptr(arg));
+
+ case ADF_GET_INTERFACE_DATA32:
+ return adf_compat_get_interface_data(file, compat_ptr(arg));
+
+ case ADF_GET_OVERLAY_ENGINE_DATA32:
+ return adf_compat_get_overlay_engine_data(file,
+ compat_ptr(arg));
+
+ default:
+ return adf_file_ioctl(file, cmd, arg);
+ }
+}
diff --git a/drivers/video/adf/adf_fops32.h b/drivers/video/adf/adf_fops32.h
new file mode 100644
index 000000000000..53d43f010208
--- /dev/null
+++ b/drivers/video/adf/adf_fops32.h
@@ -0,0 +1,78 @@
+#ifndef __VIDEO_ADF_ADF_FOPS32_H
+#define __VIDEO_ADF_ADF_FOPS32_H
+
+#include <linux/compat.h>
+#include <linux/ioctl.h>
+
+#include <video/adf.h>
+
+#define ADF_POST_CONFIG32 \
+ _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config32)
+#define ADF_GET_DEVICE_DATA32 \
+ _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data32)
+#define ADF_GET_INTERFACE_DATA32 \
+ _IOR(ADF_IOCTL_TYPE, 5, struct adf_interface_data32)
+#define ADF_GET_OVERLAY_ENGINE_DATA32 \
+ _IOR(ADF_IOCTL_TYPE, 6, struct adf_overlay_engine_data32)
+
+struct adf_post_config32 {
+ compat_size_t n_interfaces;
+ compat_uptr_t interfaces;
+
+ compat_size_t n_bufs;
+ compat_uptr_t bufs;
+
+ compat_size_t custom_data_size;
+ compat_uptr_t custom_data;
+
+ __s64 complete_fence;
+};
+
+struct adf_device_data32 {
+ char name[ADF_NAME_LEN];
+
+ compat_size_t n_attachments;
+ compat_uptr_t attachments;
+
+ compat_size_t n_allowed_attachments;
+ compat_uptr_t allowed_attachments;
+
+ compat_size_t custom_data_size;
+ compat_uptr_t custom_data;
+};
+
+struct adf_interface_data32 {
+ char name[ADF_NAME_LEN];
+
+ __u8 type;
+ __u32 id;
+ /* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */
+ __u32 flags;
+
+ __u8 dpms_state;
+ __u8 hotplug_detect;
+ __u16 width_mm;
+ __u16 height_mm;
+
+ struct drm_mode_modeinfo current_mode;
+ compat_size_t n_available_modes;
+ compat_uptr_t available_modes;
+
+ compat_size_t custom_data_size;
+ compat_uptr_t custom_data;
+};
+
+struct adf_overlay_engine_data32 {
+ char name[ADF_NAME_LEN];
+
+ compat_size_t n_supported_formats;
+ compat_uptr_t supported_formats;
+
+ compat_size_t custom_data_size;
+ compat_uptr_t custom_data;
+};
+
+long adf_file_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+
+#endif /* __VIDEO_ADF_ADF_FOPS32_H */
diff --git a/drivers/video/adf/adf_format.c b/drivers/video/adf/adf_format.c
new file mode 100644
index 000000000000..e3f22c7c85d9
--- /dev/null
+++ b/drivers/video/adf/adf_format.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ * modified from drivers/gpu/drm/drm_crtc.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/export.h>
+#include <linux/kernel.h>
+#include <drm/drm_fourcc.h>
+#include <video/adf_format.h>
+
+bool adf_format_is_standard(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_C8:
+ case DRM_FORMAT_RGB332:
+ case DRM_FORMAT_BGR233:
+ case DRM_FORMAT_XRGB4444:
+ case DRM_FORMAT_XBGR4444:
+ case DRM_FORMAT_RGBX4444:
+ case DRM_FORMAT_BGRX4444:
+ case DRM_FORMAT_ARGB4444:
+ case DRM_FORMAT_ABGR4444:
+ case DRM_FORMAT_RGBA4444:
+ case DRM_FORMAT_BGRA4444:
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_BGRX5551:
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_BGRA5551:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_BGR888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_AYUV:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ case DRM_FORMAT_YUV411:
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV444:
+ case DRM_FORMAT_YVU444:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL(adf_format_is_standard);
+
+bool adf_format_is_rgb(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_C8:
+ case DRM_FORMAT_RGB332:
+ case DRM_FORMAT_BGR233:
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_BGRX5551:
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_BGRA5551:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_BGR888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ return true;
+
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL(adf_format_is_rgb);
+
+u8 adf_format_num_planes(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ case DRM_FORMAT_YUV411:
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV444:
+ case DRM_FORMAT_YVU444:
+ return 3;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ return 2;
+ default:
+ return 1;
+ }
+}
+EXPORT_SYMBOL(adf_format_num_planes);
+
+u8 adf_format_bpp(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_C8:
+ case DRM_FORMAT_RGB332:
+ case DRM_FORMAT_BGR233:
+ return 8;
+
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_BGRX5551:
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_BGRA5551:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ return 16;
+
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_BGR888:
+ return 24;
+
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ return 32;
+
+ default:
+ pr_debug("%s: unsupported pixel format %u\n", __func__, format);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(adf_format_bpp);
+
+u8 adf_format_plane_cpp(u32 format, int plane)
+{
+ if (plane >= adf_format_num_planes(format))
+ return 0;
+
+ switch (format) {
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ return 2;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ return plane ? 2 : 1;
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ case DRM_FORMAT_YUV411:
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV444:
+ case DRM_FORMAT_YVU444:
+ return 1;
+ default:
+ return adf_format_bpp(format) / 8;
+ }
+}
+EXPORT_SYMBOL(adf_format_plane_cpp);
+
+u8 adf_format_horz_chroma_subsampling(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_YUV411:
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ return 4;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ return 2;
+ default:
+ return 1;
+ }
+}
+EXPORT_SYMBOL(adf_format_horz_chroma_subsampling);
+
+u8 adf_format_vert_chroma_subsampling(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YVU410:
+ return 4;
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ return 2;
+ default:
+ return 1;
+ }
+}
+EXPORT_SYMBOL(adf_format_vert_chroma_subsampling);
diff --git a/drivers/video/adf/adf_memblock.c b/drivers/video/adf/adf_memblock.c
new file mode 100644
index 000000000000..3c99f27388db
--- /dev/null
+++ b/drivers/video/adf/adf_memblock.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/dma-buf.h>
+#include <linux/highmem.h>
+#include <linux/memblock.h>
+#include <linux/slab.h>
+
+struct adf_memblock_pdata {
+ phys_addr_t base;
+};
+
+static struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach,
+ enum dma_data_direction direction)
+{
+ struct adf_memblock_pdata *pdata = attach->dmabuf->priv;
+ unsigned long pfn = PFN_DOWN(pdata->base);
+ struct page *page = pfn_to_page(pfn);
+ struct sg_table *table;
+ int ret;
+
+ table = kzalloc(sizeof(*table), GFP_KERNEL);
+ if (!table)
+ return ERR_PTR(-ENOMEM);
+
+ ret = sg_alloc_table(table, 1, GFP_KERNEL);
+ if (ret < 0)
+ goto err;
+
+ sg_set_page(table->sgl, page, attach->dmabuf->size, 0);
+ return table;
+
+err:
+ kfree(table);
+ return ERR_PTR(ret);
+}
+
+static void adf_memblock_unmap(struct dma_buf_attachment *attach,
+ struct sg_table *table, enum dma_data_direction direction)
+{
+ sg_free_table(table);
+}
+
+static void __init_memblock adf_memblock_release(struct dma_buf *buf)
+{
+ struct adf_memblock_pdata *pdata = buf->priv;
+ int err = memblock_free(pdata->base, buf->size);
+
+ if (err < 0)
+ pr_warn("%s: freeing memblock failed: %d\n", __func__, err);
+ kfree(pdata);
+}
+
+static void *adf_memblock_do_kmap(struct dma_buf *buf, unsigned long pgoffset,
+ bool atomic)
+{
+ struct adf_memblock_pdata *pdata = buf->priv;
+ unsigned long pfn = PFN_DOWN(pdata->base) + pgoffset;
+ struct page *page = pfn_to_page(pfn);
+
+ if (atomic)
+ return kmap_atomic(page);
+ else
+ return kmap(page);
+}
+
+static void *adf_memblock_kmap_atomic(struct dma_buf *buf,
+ unsigned long pgoffset)
+{
+ return adf_memblock_do_kmap(buf, pgoffset, true);
+}
+
+static void adf_memblock_kunmap_atomic(struct dma_buf *buf,
+ unsigned long pgoffset, void *vaddr)
+{
+ kunmap_atomic(vaddr);
+}
+
+static void *adf_memblock_kmap(struct dma_buf *buf, unsigned long pgoffset)
+{
+ return adf_memblock_do_kmap(buf, pgoffset, false);
+}
+
+static void adf_memblock_kunmap(struct dma_buf *buf, unsigned long pgoffset,
+ void *vaddr)
+{
+ kunmap(vaddr);
+}
+
+static int adf_memblock_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+ struct adf_memblock_pdata *pdata = buf->priv;
+
+ return remap_pfn_range(vma, vma->vm_start, PFN_DOWN(pdata->base),
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+struct dma_buf_ops adf_memblock_ops = {
+ .map_dma_buf = adf_memblock_map,
+ .unmap_dma_buf = adf_memblock_unmap,
+ .release = adf_memblock_release,
+ .kmap_atomic = adf_memblock_kmap_atomic,
+ .kunmap_atomic = adf_memblock_kunmap_atomic,
+ .kmap = adf_memblock_kmap,
+ .kunmap = adf_memblock_kunmap,
+ .mmap = adf_memblock_mmap,
+};
+
+/**
+ * adf_memblock_export - export a memblock reserved area as a dma-buf
+ *
+ * @base: base physical address
+ * @size: memblock size
+ * @flags: mode flags for the dma-buf's file
+ *
+ * @base and @size must be page-aligned.
+ *
+ * Returns a dma-buf on success or ERR_PTR(-errno) on failure.
+ */
+struct dma_buf *adf_memblock_export(phys_addr_t base, size_t size, int flags)
+{
+ struct adf_memblock_pdata *pdata;
+ struct dma_buf *buf;
+
+ if (PAGE_ALIGN(base) != base || PAGE_ALIGN(size) != size)
+ return ERR_PTR(-EINVAL);
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->base = base;
+ buf = dma_buf_export(pdata, &adf_memblock_ops, size, flags);
+ if (IS_ERR(buf))
+ kfree(pdata);
+
+ return buf;
+}
diff --git a/drivers/video/adf/adf_sysfs.c b/drivers/video/adf/adf_sysfs.c
new file mode 100644
index 000000000000..8c659c71ffa8
--- /dev/null
+++ b/drivers/video/adf/adf_sysfs.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <video/adf_client.h>
+
+#include "adf.h"
+#include "adf_fops.h"
+#include "adf_sysfs.h"
+
+static struct class *adf_class;
+static int adf_major;
+static DEFINE_IDR(adf_minors);
+
+#define dev_to_adf_interface(p) \
+ adf_obj_to_interface(container_of(p, struct adf_obj, dev))
+
+static ssize_t dpms_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ adf_interface_dpms_state(intf));
+}
+
+static ssize_t dpms_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ u8 dpms_state;
+ int err;
+
+ err = kstrtou8(buf, 0, &dpms_state);
+ if (err < 0)
+ return err;
+
+ err = adf_interface_blank(intf, dpms_state);
+ if (err < 0)
+ return err;
+
+ return count;
+}
+
+static ssize_t current_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ struct drm_mode_modeinfo mode;
+
+ adf_interface_current_mode(intf, &mode);
+
+ if (mode.name[0]) {
+ return scnprintf(buf, PAGE_SIZE, "%s\n", mode.name);
+ } else {
+ bool interlaced = !!(mode.flags & DRM_MODE_FLAG_INTERLACE);
+ return scnprintf(buf, PAGE_SIZE, "%ux%u%s\n", mode.hdisplay,
+ mode.vdisplay, interlaced ? "i" : "");
+ }
+}
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ adf_interface_type_str(intf));
+}
+
+static ssize_t vsync_timestamp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ ktime_t timestamp;
+ unsigned long flags;
+
+ read_lock_irqsave(&intf->vsync_lock, flags);
+ memcpy(&timestamp, &intf->vsync_timestamp, sizeof(timestamp));
+ read_unlock_irqrestore(&intf->vsync_lock, flags);
+
+ return scnprintf(buf, PAGE_SIZE, "%llu\n", ktime_to_ns(timestamp));
+}
+
+static ssize_t hotplug_detect_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adf_interface *intf = dev_to_adf_interface(dev);
+ return scnprintf(buf, PAGE_SIZE, "%u\n", intf->hotplug_detect);
+}
+
+static struct device_attribute adf_interface_attrs[] = {
+ __ATTR(dpms_state, S_IRUGO|S_IWUSR, dpms_state_show, dpms_state_store),
+ __ATTR_RO(current_mode),
+ __ATTR_RO(hotplug_detect),
+ __ATTR_RO(type),
+ __ATTR_RO(vsync_timestamp),
+};
+
+int adf_obj_sysfs_init(struct adf_obj *obj, struct device *parent)
+{
+ int ret = idr_alloc(&adf_minors, obj, 0, 0, GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("%s: allocating adf minor failed: %d\n", __func__,
+ ret);
+ return ret;
+ }
+
+ obj->minor = ret;
+ obj->dev.parent = parent;
+ obj->dev.class = adf_class;
+ obj->dev.devt = MKDEV(adf_major, obj->minor);
+
+ ret = device_register(&obj->dev);
+ if (ret < 0) {
+ pr_err("%s: registering adf object failed: %d\n", __func__,
+ ret);
+ goto err_device_register;
+ }
+
+ return 0;
+
+err_device_register:
+ idr_remove(&adf_minors, obj->minor);
+ return ret;
+}
+
+static char *adf_device_devnode(struct device *dev, umode_t *mode,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct adf_obj *obj = container_of(dev, struct adf_obj, dev);
+ return kasprintf(GFP_KERNEL, "adf%d", obj->id);
+}
+
+static char *adf_interface_devnode(struct device *dev, umode_t *mode,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct adf_obj *obj = container_of(dev, struct adf_obj, dev);
+ struct adf_interface *intf = adf_obj_to_interface(obj);
+ struct adf_device *parent = adf_interface_parent(intf);
+ return kasprintf(GFP_KERNEL, "adf-interface%d.%d",
+ parent->base.id, intf->base.id);
+}
+
+static char *adf_overlay_engine_devnode(struct device *dev, umode_t *mode,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct adf_obj *obj = container_of(dev, struct adf_obj, dev);
+ struct adf_overlay_engine *eng = adf_obj_to_overlay_engine(obj);
+ struct adf_device *parent = adf_overlay_engine_parent(eng);
+ return kasprintf(GFP_KERNEL, "adf-overlay-engine%d.%d",
+ parent->base.id, eng->base.id);
+}
+
+static void adf_noop_release(struct device *dev)
+{
+}
+
+static struct device_type adf_device_type = {
+ .name = "adf_device",
+ .devnode = adf_device_devnode,
+ .release = adf_noop_release,
+};
+
+static struct device_type adf_interface_type = {
+ .name = "adf_interface",
+ .devnode = adf_interface_devnode,
+ .release = adf_noop_release,
+};
+
+static struct device_type adf_overlay_engine_type = {
+ .name = "adf_overlay_engine",
+ .devnode = adf_overlay_engine_devnode,
+ .release = adf_noop_release,
+};
+
+int adf_device_sysfs_init(struct adf_device *dev)
+{
+ dev->base.dev.type = &adf_device_type;
+ dev_set_name(&dev->base.dev, "%s", dev->base.name);
+ return adf_obj_sysfs_init(&dev->base, dev->dev);
+}
+
+int adf_interface_sysfs_init(struct adf_interface *intf)
+{
+ struct adf_device *parent = adf_interface_parent(intf);
+ size_t i, j;
+ int ret;
+
+ intf->base.dev.type = &adf_interface_type;
+ dev_set_name(&intf->base.dev, "%s-interface%d", parent->base.name,
+ intf->base.id);
+
+ ret = adf_obj_sysfs_init(&intf->base, &parent->base.dev);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++) {
+ ret = device_create_file(&intf->base.dev,
+ &adf_interface_attrs[i]);
+ if (ret < 0) {
+ dev_err(&intf->base.dev, "creating sysfs attribute %s failed: %d\n",
+ adf_interface_attrs[i].attr.name, ret);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ for (j = 0; j < i; j++)
+ device_remove_file(&intf->base.dev, &adf_interface_attrs[j]);
+ return ret;
+}
+
+int adf_overlay_engine_sysfs_init(struct adf_overlay_engine *eng)
+{
+ struct adf_device *parent = adf_overlay_engine_parent(eng);
+
+ eng->base.dev.type = &adf_overlay_engine_type;
+ dev_set_name(&eng->base.dev, "%s-overlay-engine%d", parent->base.name,
+ eng->base.id);
+
+ return adf_obj_sysfs_init(&eng->base, &parent->base.dev);
+}
+
+struct adf_obj *adf_obj_sysfs_find(int minor)
+{
+ return idr_find(&adf_minors, minor);
+}
+
+void adf_obj_sysfs_destroy(struct adf_obj *obj)
+{
+ idr_remove(&adf_minors, obj->minor);
+ device_unregister(&obj->dev);
+}
+
+void adf_device_sysfs_destroy(struct adf_device *dev)
+{
+ adf_obj_sysfs_destroy(&dev->base);
+}
+
+void adf_interface_sysfs_destroy(struct adf_interface *intf)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++)
+ device_remove_file(&intf->base.dev, &adf_interface_attrs[i]);
+ adf_obj_sysfs_destroy(&intf->base);
+}
+
+void adf_overlay_engine_sysfs_destroy(struct adf_overlay_engine *eng)
+{
+ adf_obj_sysfs_destroy(&eng->base);
+}
+
+int adf_sysfs_init(void)
+{
+ struct class *class;
+ int ret;
+
+ class = class_create(THIS_MODULE, "adf");
+ if (IS_ERR(class)) {
+ ret = PTR_ERR(class);
+ pr_err("%s: creating class failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = register_chrdev(0, "adf", &adf_fops);
+ if (ret < 0) {
+ pr_err("%s: registering device failed: %d\n", __func__, ret);
+ goto err_chrdev;
+ }
+
+ adf_class = class;
+ adf_major = ret;
+ return 0;
+
+err_chrdev:
+ class_destroy(adf_class);
+ return ret;
+}
+
+void adf_sysfs_destroy(void)
+{
+ idr_destroy(&adf_minors);
+ class_destroy(adf_class);
+}
diff --git a/drivers/video/adf/adf_sysfs.h b/drivers/video/adf/adf_sysfs.h
new file mode 100644
index 000000000000..0613ac364f8d
--- /dev/null
+++ b/drivers/video/adf/adf_sysfs.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __VIDEO_ADF_ADF_SYSFS_H
+#define __VIDEO_ADF_ADF_SYSFS_H
+
+struct adf_device;
+struct adf_interface;
+struct adf_overlay_engine;
+
+int adf_device_sysfs_init(struct adf_device *dev);
+void adf_device_sysfs_destroy(struct adf_device *dev);
+int adf_interface_sysfs_init(struct adf_interface *intf);
+void adf_interface_sysfs_destroy(struct adf_interface *intf);
+int adf_overlay_engine_sysfs_init(struct adf_overlay_engine *eng);
+void adf_overlay_engine_sysfs_destroy(struct adf_overlay_engine *eng);
+struct adf_obj *adf_obj_sysfs_find(int minor);
+
+int adf_sysfs_init(void);
+void adf_sysfs_destroy(void);
+
+#endif /* __VIDEO_ADF_ADF_SYSFS_H */
diff --git a/drivers/video/adf/adf_trace.h b/drivers/video/adf/adf_trace.h
new file mode 100644
index 000000000000..3cb2a84d728c
--- /dev/null
+++ b/drivers/video/adf/adf_trace.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM adf
+
+#if !defined(__VIDEO_ADF_ADF_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __VIDEO_ADF_ADF_TRACE_H
+
+#include <linux/tracepoint.h>
+#include <video/adf.h>
+
+TRACE_EVENT(adf_event,
+ TP_PROTO(struct adf_obj *obj, enum adf_event_type type),
+ TP_ARGS(obj, type),
+
+ TP_STRUCT__entry(
+ __string(name, obj->name)
+ __field(enum adf_event_type, type)
+ __array(char, type_str, 32)
+ ),
+ TP_fast_assign(
+ __assign_str(name, obj->name);
+ __entry->type = type;
+ strlcpy(__entry->type_str, adf_event_type_str(obj, type),
+ sizeof(__entry->type_str));
+ ),
+ TP_printk("obj=%s type=%u (%s)",
+ __get_str(name),
+ __entry->type,
+ __entry->type_str)
+);
+
+TRACE_EVENT(adf_event_enable,
+ TP_PROTO(struct adf_obj *obj, enum adf_event_type type),
+ TP_ARGS(obj, type),
+
+ TP_STRUCT__entry(
+ __string(name, obj->name)
+ __field(enum adf_event_type, type)
+ __array(char, type_str, 32)
+ ),
+ TP_fast_assign(
+ __assign_str(name, obj->name);
+ __entry->type = type;
+ strlcpy(__entry->type_str, adf_event_type_str(obj, type),
+ sizeof(__entry->type_str));
+ ),
+ TP_printk("obj=%s type=%u (%s)",
+ __get_str(name),
+ __entry->type,
+ __entry->type_str)
+);
+
+TRACE_EVENT(adf_event_disable,
+ TP_PROTO(struct adf_obj *obj, enum adf_event_type type),
+ TP_ARGS(obj, type),
+
+ TP_STRUCT__entry(
+ __string(name, obj->name)
+ __field(enum adf_event_type, type)
+ __array(char, type_str, 32)
+ ),
+ TP_fast_assign(
+ __assign_str(name, obj->name);
+ __entry->type = type;
+ strlcpy(__entry->type_str, adf_event_type_str(obj, type),
+ sizeof(__entry->type_str));
+ ),
+ TP_printk("obj=%s type=%u (%s)",
+ __get_str(name),
+ __entry->type,
+ __entry->type_str)
+);
+
+#endif /* __VIDEO_ADF_ADF_TRACE_H */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE adf_trace
+#include <trace/define_trace.h>
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index e911b9c96e19..0202a380bd56 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -298,6 +298,21 @@ config PLAT_VERSATILE_CLCD
depends on ARM
depends on FB_ARMCLCD && FB=y
+config FB_ARMHDLCD
+ tristate "ARM High Definition LCD support"
+ depends on FB && ARM
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This framebuffer device driver is for the ARM High Definition
+ Colour LCD controller.
+
+ If you want to compile this as a module (=code which can be
+ inserted into and removed from the running kernel), say M
+ here and read <file:Documentation/kbuild/modules.txt>. The module
+ will be called arm-hdlcd.
+
config FB_ACORN
bool "Acorn VIDC support"
depends on (FB = y) && ARM && ARCH_ACORN
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index 1979afffccfe..5c967782ac56 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_FB_PVR2) += pvr2fb.o
obj-$(CONFIG_FB_VOODOO1) += sstfb.o
obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
obj-$(CONFIG_PLAT_VERSATILE_CLCD) += amba-clcd-versatile.o
+obj-$(CONFIG_FB_ARMHDLCD) += arm-hdlcd.o
obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o
obj-$(CONFIG_FB_68328) += 68328fb.o
obj-$(CONFIG_FB_GBE) += gbefb.o
diff --git a/drivers/video/fbdev/arm-hdlcd.c b/drivers/video/fbdev/arm-hdlcd.c
new file mode 100644
index 000000000000..fa67ed1f3bd4
--- /dev/null
+++ b/drivers/video/fbdev/arm-hdlcd.c
@@ -0,0 +1,844 @@
+/*
+ * drivers/video/fbdev/arm-hdlcd.c
+ *
+ * Copyright (C) 2011 ARM Limited
+ *
+ * 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.
+ *
+ * ARM HDLCD Controller
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/memblock.h>
+#include <linux/arm-hdlcd.h>
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#endif
+
+#include "edid.h"
+
+#ifdef CONFIG_SERIAL_AMBA_PCU_UART
+int get_edid(u8 *msgbuf);
+#else
+#endif
+
+#define to_hdlcd_device(info) container_of(info, struct hdlcd_device, fb)
+
+static struct of_device_id hdlcd_of_matches[] = {
+ { .compatible = "arm,hdlcd" },
+ {},
+};
+
+/* Framebuffer size. */
+static unsigned long framebuffer_size;
+
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+static unsigned long buffer_underrun_events;
+static DEFINE_SPINLOCK(hdlcd_underrun_lock);
+
+static void hdlcd_underrun_set(unsigned long val)
+{
+ spin_lock(&hdlcd_underrun_lock);
+ buffer_underrun_events = val;
+ spin_unlock(&hdlcd_underrun_lock);
+}
+
+static unsigned long hdlcd_underrun_get(void)
+{
+ unsigned long val;
+ spin_lock(&hdlcd_underrun_lock);
+ val = buffer_underrun_events;
+ spin_unlock(&hdlcd_underrun_lock);
+ return val;
+}
+
+#ifdef CONFIG_PROC_FS
+static int hdlcd_underrun_show(struct seq_file *m, void *v)
+{
+ unsigned char underrun_string[32];
+ snprintf(underrun_string, 32, "%lu\n", hdlcd_underrun_get());
+ seq_puts(m, underrun_string);
+ return 0;
+}
+
+static int proc_hdlcd_underrun_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hdlcd_underrun_show, NULL);
+}
+
+static const struct file_operations proc_hdlcd_underrun_operations = {
+ .open = proc_hdlcd_underrun_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int hdlcd_underrun_init(void)
+{
+ hdlcd_underrun_set(0);
+ proc_create("hdlcd_underrun", 0, NULL, &proc_hdlcd_underrun_operations);
+ return 0;
+}
+static void hdlcd_underrun_close(void)
+{
+ remove_proc_entry("hdlcd_underrun", NULL);
+}
+#else
+static int hdlcd_underrun_init(void) { return 0; }
+static void hdlcd_underrun_close(void) { }
+#endif
+#endif
+
+static char *fb_mode = "1680x1050-32@60\0\0\0\0\0";
+
+static struct fb_var_screeninfo cached_var_screeninfo;
+
+static struct fb_videomode hdlcd_default_mode = {
+ .refresh = 60,
+ .xres = 1680,
+ .yres = 1050,
+ .pixclock = 8403,
+ .left_margin = 80,
+ .right_margin = 48,
+ .upper_margin = 21,
+ .lower_margin = 3,
+ .hsync_len = 32,
+ .vsync_len = 6,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+};
+
+static inline void hdlcd_enable(struct hdlcd_device *hdlcd)
+{
+ dev_dbg(hdlcd->dev, "HDLCD: output enabled\n");
+ writel(1, hdlcd->base + HDLCD_REG_COMMAND);
+}
+
+static inline void hdlcd_disable(struct hdlcd_device *hdlcd)
+{
+ dev_dbg(hdlcd->dev, "HDLCD: output disabled\n");
+ writel(0, hdlcd->base + HDLCD_REG_COMMAND);
+}
+
+static int hdlcd_set_bitfields(struct hdlcd_device *hdlcd,
+ struct fb_var_screeninfo *var)
+{
+ int ret = 0;
+
+ memset(&var->transp, 0, sizeof(var->transp));
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->blue.offset = 0;
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ /* pseudocolor */
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ break;
+ case 16:
+ /* 565 format */
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ break;
+ case 32:
+ var->transp.length = 8;
+ case 24:
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!ret) {
+ if(var->bits_per_pixel != 32)
+ {
+ var->green.offset = var->blue.length;
+ var->red.offset = var->green.offset + var->green.length;
+ }
+ else
+ {
+ /* Previously, the byte ordering for 32-bit color was
+ * (msb)<alpha><red><green><blue>(lsb)
+ * but this does not match what android expects and
+ * the colors are odd. Instead, use
+ * <alpha><blue><green><red>
+ * Since we tell fb what we are doing, console
+ * , X and directfb access should work fine.
+ */
+ var->green.offset = var->red.length;
+ var->blue.offset = var->green.offset + var->green.length;
+ var->transp.offset = var->blue.offset + var->blue.length;
+ }
+ }
+
+ return ret;
+}
+
+static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+ int bytes_per_pixel = var->bits_per_pixel / 8;
+
+#ifdef HDLCD_NO_VIRTUAL_SCREEN
+ var->yres_virtual = var->yres;
+#else
+ var->yres_virtual = 2 * var->yres;
+#endif
+
+ if ((var->xres_virtual * bytes_per_pixel * var->yres_virtual) > hdlcd->fb.fix.smem_len)
+ return -ENOMEM;
+
+ if (var->xres > HDLCD_MAX_XRES || var->yres > HDLCD_MAX_YRES)
+ return -EINVAL;
+
+ /* make sure the bitfields are set appropriately */
+ return hdlcd_set_bitfields(hdlcd, var);
+}
+
+/* prototype */
+static int hdlcd_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+
+#define WRITE_HDLCD_REG(reg, value) writel((value), hdlcd->base + (reg))
+#define READ_HDLCD_REG(reg) readl(hdlcd->base + (reg))
+
+static int hdlcd_set_par(struct fb_info *info)
+{
+ struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+ int bytes_per_pixel = hdlcd->fb.var.bits_per_pixel / 8;
+ int polarities;
+ int old_yoffset;
+
+ /* check for shortcuts */
+ old_yoffset = cached_var_screeninfo.yoffset;
+ cached_var_screeninfo.yoffset = info->var.yoffset;
+ if (!memcmp(&info->var, &cached_var_screeninfo,
+ sizeof(struct fb_var_screeninfo))) {
+ if(old_yoffset != info->var.yoffset) {
+ /* we only changed yoffset, and we already
+ * already recorded it a couple lines up
+ */
+ hdlcd_pan_display(&info->var, info);
+ }
+ /* or no change */
+ return 0;
+ }
+
+ hdlcd->fb.fix.line_length = hdlcd->fb.var.xres * bytes_per_pixel;
+
+ if (hdlcd->fb.var.bits_per_pixel >= 16)
+ hdlcd->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+ else
+ hdlcd->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+
+ memcpy(&cached_var_screeninfo, &info->var, sizeof(struct fb_var_screeninfo));
+
+ polarities = HDLCD_POLARITY_DATAEN |
+#ifndef CONFIG_ARCH_TUSCAN
+ HDLCD_POLARITY_PIXELCLK |
+#endif
+ HDLCD_POLARITY_DATA;
+ polarities |= (hdlcd->fb.var.sync & FB_SYNC_HOR_HIGH_ACT) ? HDLCD_POLARITY_HSYNC : 0;
+ polarities |= (hdlcd->fb.var.sync & FB_SYNC_VERT_HIGH_ACT) ? HDLCD_POLARITY_VSYNC : 0;
+
+ hdlcd_disable(hdlcd);
+
+ WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_LENGTH, hdlcd->fb.var.xres * bytes_per_pixel);
+ WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_PITCH, hdlcd->fb.var.xres * bytes_per_pixel);
+ WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_COUNT, hdlcd->fb.var.yres - 1);
+ WRITE_HDLCD_REG(HDLCD_REG_V_SYNC, hdlcd->fb.var.vsync_len - 1);
+ WRITE_HDLCD_REG(HDLCD_REG_V_BACK_PORCH, hdlcd->fb.var.upper_margin - 1);
+ WRITE_HDLCD_REG(HDLCD_REG_V_DATA, hdlcd->fb.var.yres - 1);
+ WRITE_HDLCD_REG(HDLCD_REG_V_FRONT_PORCH, hdlcd->fb.var.lower_margin - 1);
+ WRITE_HDLCD_REG(HDLCD_REG_H_SYNC, hdlcd->fb.var.hsync_len - 1);
+ WRITE_HDLCD_REG(HDLCD_REG_H_BACK_PORCH, hdlcd->fb.var.left_margin - 1);
+ WRITE_HDLCD_REG(HDLCD_REG_H_DATA, hdlcd->fb.var.xres - 1);
+ WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1);
+ WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities);
+ WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bytes_per_pixel - 1) << 3);
+#ifdef HDLCD_RED_DEFAULT_COLOUR
+ WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, (0x00ff0000 | (hdlcd->fb.var.red.length & 0xf) << 8) \
+ | hdlcd->fb.var.red.offset);
+#else
+ WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, ((hdlcd->fb.var.red.length & 0xf) << 8) | hdlcd->fb.var.red.offset);
+#endif
+ WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset);
+ WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset);
+
+ clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000);
+ clk_enable(hdlcd->clk);
+
+ hdlcd_enable(hdlcd);
+
+ return 0;
+}
+
+static int hdlcd_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
+ unsigned int blue, unsigned int transp, struct fb_info *info)
+{
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+
+ pal[regno] = ((red >> 8) << info->var.red.offset) |
+ ((green >> 8) << info->var.green.offset) |
+ ((blue >> 8) << info->var.blue.offset);
+ }
+
+ return 0;
+}
+
+static irqreturn_t hdlcd_irq(int irq, void *data)
+{
+ struct hdlcd_device *hdlcd = data;
+ unsigned long irq_mask, irq_status;
+
+ irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
+ irq_status = READ_HDLCD_REG(HDLCD_REG_INT_STATUS);
+
+ /* acknowledge interrupt(s) */
+ WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status);
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+ if (irq_status & HDLCD_INTERRUPT_UNDERRUN) {
+ /* increment the count */
+ hdlcd_underrun_set(hdlcd_underrun_get() + 1);
+ }
+#endif
+ if (irq_status & HDLCD_INTERRUPT_VSYNC) {
+ /* disable future VSYNC interrupts */
+ WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC);
+
+ complete(&hdlcd->vsync_completion);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int hdlcd_wait_for_vsync(struct fb_info *info)
+{
+ struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+ unsigned long irq_mask;
+ int err;
+
+ /* enable VSYNC interrupt */
+ irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
+ WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask | HDLCD_INTERRUPT_VSYNC);
+
+ err = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
+ msecs_to_jiffies(100));
+
+ if (!err)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int hdlcd_blank(int blank_mode, struct fb_info *info)
+{
+ struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+
+ switch (blank_mode) {
+ case FB_BLANK_POWERDOWN:
+ clk_disable(hdlcd->clk);
+ case FB_BLANK_NORMAL:
+ hdlcd_disable(hdlcd);
+ break;
+ case FB_BLANK_UNBLANK:
+ clk_enable(hdlcd->clk);
+ hdlcd_enable(hdlcd);
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+static void hdlcd_mmap_open(struct vm_area_struct *vma)
+{
+}
+
+static void hdlcd_mmap_close(struct vm_area_struct *vma)
+{
+}
+
+static struct vm_operations_struct hdlcd_mmap_ops = {
+ .open = hdlcd_mmap_open,
+ .close = hdlcd_mmap_close,
+};
+
+static int hdlcd_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+ unsigned long off;
+ unsigned long start;
+ unsigned long len = hdlcd->fb.fix.smem_len;
+
+ if (vma->vm_end - vma->vm_start == 0)
+ return 0;
+ if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+ return -EINVAL;
+
+ off = vma->vm_pgoff << PAGE_SHIFT;
+ if ((off >= len) || (vma->vm_end - vma->vm_start + off) > len)
+ return -EINVAL;
+
+ start = hdlcd->fb.fix.smem_start;
+ off += start;
+
+ vma->vm_pgoff = off >> PAGE_SHIFT;
+ vma->vm_flags |= VM_IO;
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ vma->vm_ops = &hdlcd_mmap_ops;
+ if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int hdlcd_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+
+ hdlcd->fb.var.yoffset = var->yoffset;
+ WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start +
+ (var->yoffset * hdlcd->fb.fix.line_length));
+
+ hdlcd_wait_for_vsync(info);
+
+ return 0;
+}
+
+static int hdlcd_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+ int err;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ err = hdlcd_wait_for_vsync(info);
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ break;
+ }
+
+ return err;
+}
+
+static struct fb_ops hdlcd_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = hdlcd_check_var,
+ .fb_set_par = hdlcd_set_par,
+ .fb_setcolreg = hdlcd_setcolreg,
+ .fb_blank = hdlcd_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_mmap = hdlcd_mmap,
+ .fb_pan_display = hdlcd_pan_display,
+ .fb_ioctl = hdlcd_ioctl,
+ .fb_compat_ioctl = hdlcd_ioctl
+};
+
+static int hdlcd_setup(struct hdlcd_device *hdlcd)
+{
+ u32 version;
+ int err = -EFAULT;
+
+ hdlcd->fb.device = hdlcd->dev;
+
+ hdlcd->clk = clk_get(hdlcd->dev, NULL);
+ if (IS_ERR(hdlcd->clk)) {
+ dev_err(hdlcd->dev, "HDLCD: unable to find clock data\n");
+ return PTR_ERR(hdlcd->clk);
+ }
+
+ err = clk_prepare(hdlcd->clk);
+ if (err)
+ goto clk_prepare_err;
+
+ hdlcd->base = ioremap_nocache(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
+ if (!hdlcd->base) {
+ dev_err(hdlcd->dev, "HDLCD: unable to map registers\n");
+ goto remap_err;
+ }
+
+ hdlcd->fb.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
+ if (!hdlcd->fb.pseudo_palette) {
+ dev_err(hdlcd->dev, "HDLCD: unable to allocate pseudo_palette memory\n");
+ err = -ENOMEM;
+ goto kmalloc_err;
+ }
+
+ version = readl(hdlcd->base + HDLCD_REG_VERSION);
+ if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) {
+ dev_err(hdlcd->dev, "HDLCD: unknown product id: 0x%x\n", version);
+ err = -EINVAL;
+ goto kmalloc_err;
+ }
+ dev_info(hdlcd->dev, "HDLCD: found ARM HDLCD version r%dp%d\n",
+ (version & HDLCD_VERSION_MAJOR_MASK) >> 8,
+ version & HDLCD_VERSION_MINOR_MASK);
+
+ strcpy(hdlcd->fb.fix.id, "hdlcd");
+ hdlcd->fb.fbops = &hdlcd_ops;
+ hdlcd->fb.flags = FBINFO_FLAG_DEFAULT/* | FBINFO_VIRTFB*/;
+
+ hdlcd->fb.fix.type = FB_TYPE_PACKED_PIXELS;
+ hdlcd->fb.fix.type_aux = 0;
+ hdlcd->fb.fix.xpanstep = 0;
+ hdlcd->fb.fix.ypanstep = 1;
+ hdlcd->fb.fix.ywrapstep = 0;
+ hdlcd->fb.fix.accel = FB_ACCEL_NONE;
+
+ hdlcd->fb.var.nonstd = 0;
+ hdlcd->fb.var.activate = FB_ACTIVATE_NOW;
+ hdlcd->fb.var.height = -1;
+ hdlcd->fb.var.width = -1;
+ hdlcd->fb.var.accel_flags = 0;
+
+ init_completion(&hdlcd->vsync_completion);
+
+ if (hdlcd->edid) {
+ /* build modedb from EDID */
+ fb_edid_to_monspecs(hdlcd->edid, &hdlcd->fb.monspecs);
+ fb_videomode_to_modelist(hdlcd->fb.monspecs.modedb,
+ hdlcd->fb.monspecs.modedb_len,
+ &hdlcd->fb.modelist);
+ fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode,
+ hdlcd->fb.monspecs.modedb,
+ hdlcd->fb.monspecs.modedb_len,
+ &hdlcd_default_mode, 32);
+ } else {
+ hdlcd->fb.monspecs.hfmin = 0;
+ hdlcd->fb.monspecs.hfmax = 100000;
+ hdlcd->fb.monspecs.vfmin = 0;
+ hdlcd->fb.monspecs.vfmax = 400;
+ hdlcd->fb.monspecs.dclkmin = 1000000;
+ hdlcd->fb.monspecs.dclkmax = 100000000;
+ fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode, NULL, 0, &hdlcd_default_mode, 32);
+ }
+
+ dev_info(hdlcd->dev, "using %dx%d-%d@%d mode\n", hdlcd->fb.var.xres,
+ hdlcd->fb.var.yres, hdlcd->fb.var.bits_per_pixel,
+ hdlcd->fb.mode ? hdlcd->fb.mode->refresh : 60);
+ hdlcd->fb.var.xres_virtual = hdlcd->fb.var.xres;
+#ifdef HDLCD_NO_VIRTUAL_SCREEN
+ hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres;
+#else
+ hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres * 2;
+#endif
+
+ /* initialise and set the palette */
+ if (fb_alloc_cmap(&hdlcd->fb.cmap, NR_PALETTE, 0)) {
+ dev_err(hdlcd->dev, "failed to allocate cmap memory\n");
+ err = -ENOMEM;
+ goto setup_err;
+ }
+ fb_set_cmap(&hdlcd->fb.cmap, &hdlcd->fb);
+
+ /* Allow max number of outstanding requests with the largest beat burst */
+ WRITE_HDLCD_REG(HDLCD_REG_BUS_OPTIONS, HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);
+ /* Set the framebuffer base to start of allocated memory */
+ WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start);
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+ /* turn on underrun interrupt for counting */
+ WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, HDLCD_INTERRUPT_UNDERRUN);
+#else
+ /* Ensure interrupts are disabled */
+ WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0);
+#endif
+ fb_set_var(&hdlcd->fb, &hdlcd->fb.var);
+
+ if (!register_framebuffer(&hdlcd->fb)) {
+ return 0;
+ }
+
+ dev_err(hdlcd->dev, "HDLCD: cannot register framebuffer\n");
+
+ fb_dealloc_cmap(&hdlcd->fb.cmap);
+setup_err:
+ iounmap(hdlcd->base);
+kmalloc_err:
+ kfree(hdlcd->fb.pseudo_palette);
+remap_err:
+ clk_unprepare(hdlcd->clk);
+clk_prepare_err:
+ clk_put(hdlcd->clk);
+ return err;
+}
+
+static inline unsigned char atohex(u8 data)
+{
+ if (!isxdigit(data))
+ return 0;
+ /* truncate the upper nibble and add 9 to non-digit values */
+ return (data > 0x39) ? ((data & 0xf) + 9) : (data & 0xf);
+}
+
+/* EDID data is passed from devicetree in a literal string that can contain spaces and
+ the hexadecimal dump of the data */
+static int parse_edid_data(struct hdlcd_device *hdlcd, const u8 *edid_data, int data_len)
+{
+ int i, j;
+
+ if (!edid_data)
+ return -EINVAL;
+
+ hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!hdlcd->edid)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < data_len; i++) {
+ if (isspace(edid_data[i]))
+ continue;
+ hdlcd->edid[j++] = atohex(edid_data[i]);
+ if (j >= EDID_LENGTH)
+ break;
+ }
+
+ if (j < EDID_LENGTH) {
+ kfree(hdlcd->edid);
+ hdlcd->edid = NULL;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hdlcd_probe(struct platform_device *pdev)
+{
+ int err = 0, i;
+ struct hdlcd_device *hdlcd;
+ struct resource *mem;
+#ifdef CONFIG_OF
+ struct device_node *of_node;
+#endif
+
+ memset(&cached_var_screeninfo, 0, sizeof(struct fb_var_screeninfo));
+
+ dev_dbg(&pdev->dev, "HDLCD: probing\n");
+
+ hdlcd = kzalloc(sizeof(*hdlcd), GFP_KERNEL);
+ if (!hdlcd)
+ return -ENOMEM;
+
+#ifdef CONFIG_OF
+ of_node = pdev->dev.of_node;
+ if (of_node) {
+ int len;
+ const u8 *edid;
+ const __be32 *prop = of_get_property(of_node, "mode", &len);
+ if (prop)
+ strncpy(fb_mode, (char *)prop, len);
+ prop = of_get_property(of_node, "framebuffer", &len);
+ if (prop) {
+ hdlcd->fb.fix.smem_start = of_read_ulong(prop,
+ of_n_addr_cells(of_node));
+ prop += of_n_addr_cells(of_node);
+ framebuffer_size = of_read_ulong(prop,
+ of_n_size_cells(of_node));
+ if (framebuffer_size > HDLCD_MAX_FRAMEBUFFER_SIZE)
+ framebuffer_size = HDLCD_MAX_FRAMEBUFFER_SIZE;
+ dev_dbg(&pdev->dev, "HDLCD: phys_addr = 0x%lx, size = 0x%lx\n",
+ hdlcd->fb.fix.smem_start, framebuffer_size);
+ }
+ edid = of_get_property(of_node, "edid", &len);
+ if (edid) {
+ err = parse_edid_data(hdlcd, edid, len);
+#ifdef CONFIG_SERIAL_AMBA_PCU_UART
+ } else {
+ /* ask the firmware to fetch the EDID */
+ dev_dbg(&pdev->dev, "HDLCD: Requesting EDID data\n");
+ hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!hdlcd->edid)
+ return -ENOMEM;
+ err = get_edid(hdlcd->edid);
+#endif /* CONFIG_SERIAL_AMBA_PCU_UART */
+ }
+ if (err)
+ dev_info(&pdev->dev, "HDLCD: Failed to parse EDID data\n");
+ }
+#endif /* CONFIG_OF */
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "HDLCD: cannot get platform resources\n");
+ err = -EINVAL;
+ goto resource_err;
+ }
+
+ i = platform_get_irq(pdev, 0);
+ if (i < 0) {
+ dev_err(&pdev->dev, "HDLCD: no irq defined for vsync\n");
+ err = -ENOENT;
+ goto resource_err;
+ } else {
+ err = request_irq(i, hdlcd_irq, 0, dev_name(&pdev->dev), hdlcd);
+ if (err) {
+ dev_err(&pdev->dev, "HDLCD: unable to request irq\n");
+ goto resource_err;
+ }
+ hdlcd->irq = i;
+ }
+
+ if (!request_mem_region(mem->start, resource_size(mem), dev_name(&pdev->dev))) {
+ err = -ENXIO;
+ goto request_err;
+ }
+
+ if (!hdlcd->fb.fix.smem_start) {
+ dev_err(&pdev->dev, "platform did not allocate frame buffer memory\n");
+ err = -ENOMEM;
+ goto memalloc_err;
+ }
+ hdlcd->fb.screen_base = ioremap_wc(hdlcd->fb.fix.smem_start, framebuffer_size);
+ if (!hdlcd->fb.screen_base) {
+ dev_err(&pdev->dev, "unable to ioremap framebuffer\n");
+ err = -ENOMEM;
+ goto probe_err;
+ }
+
+ hdlcd->fb.screen_size = framebuffer_size;
+ hdlcd->fb.fix.smem_len = framebuffer_size;
+ hdlcd->fb.fix.mmio_start = mem->start;
+ hdlcd->fb.fix.mmio_len = resource_size(mem);
+
+ /* Clear the framebuffer */
+ memset(hdlcd->fb.screen_base, 0, framebuffer_size);
+
+ hdlcd->dev = &pdev->dev;
+
+ dev_dbg(&pdev->dev, "HDLCD: framebuffer virt base %p, phys base 0x%lX\n",
+ hdlcd->fb.screen_base, (unsigned long)hdlcd->fb.fix.smem_start);
+
+ err = hdlcd_setup(hdlcd);
+
+ if (err)
+ goto probe_err;
+
+ platform_set_drvdata(pdev, hdlcd);
+ return 0;
+
+probe_err:
+ iounmap(hdlcd->fb.screen_base);
+ memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
+
+memalloc_err:
+ release_mem_region(mem->start, resource_size(mem));
+
+request_err:
+ free_irq(hdlcd->irq, hdlcd);
+
+resource_err:
+ kfree(hdlcd);
+
+ return err;
+}
+
+static int hdlcd_remove(struct platform_device *pdev)
+{
+ struct hdlcd_device *hdlcd = platform_get_drvdata(pdev);
+
+ clk_disable(hdlcd->clk);
+ clk_unprepare(hdlcd->clk);
+ clk_put(hdlcd->clk);
+
+ /* unmap memory */
+ iounmap(hdlcd->fb.screen_base);
+ iounmap(hdlcd->base);
+
+ /* deallocate fb memory */
+ fb_dealloc_cmap(&hdlcd->fb.cmap);
+ kfree(hdlcd->fb.pseudo_palette);
+ memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
+ release_mem_region(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
+
+ free_irq(hdlcd->irq, NULL);
+ kfree(hdlcd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int hdlcd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ /* not implemented yet */
+ return 0;
+}
+
+static int hdlcd_resume(struct platform_device *pdev)
+{
+ /* not implemented yet */
+ return 0;
+}
+#else
+#define hdlcd_suspend NULL
+#define hdlcd_resume NULL
+#endif
+
+static struct platform_driver hdlcd_driver = {
+ .probe = hdlcd_probe,
+ .remove = hdlcd_remove,
+ .suspend = hdlcd_suspend,
+ .resume = hdlcd_resume,
+ .driver = {
+ .name = "hdlcd",
+ .owner = THIS_MODULE,
+ .of_match_table = hdlcd_of_matches,
+ },
+};
+
+static int __init hdlcd_init(void)
+{
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+ int err = platform_driver_register(&hdlcd_driver);
+ if (!err)
+ hdlcd_underrun_init();
+ return err;
+#else
+ return platform_driver_register(&hdlcd_driver);
+#endif
+}
+
+void __exit hdlcd_exit(void)
+{
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+ hdlcd_underrun_close();
+#endif
+ platform_driver_unregister(&hdlcd_driver);
+}
+
+module_init(hdlcd_init);
+module_exit(hdlcd_exit);
+
+MODULE_AUTHOR("Liviu Dudau");
+MODULE_DESCRIPTION("ARM HDLCD core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c
index be053aa80880..2d4b642abf4e 100644
--- a/drivers/video/fbdev/omap2/dss/dispc.c
+++ b/drivers/video/fbdev/omap2/dss/dispc.c
@@ -238,12 +238,12 @@ static unsigned long dispc_plane_lclk_rate(enum omap_plane plane);
static inline void dispc_write_reg(const u16 idx, u32 val)
{
- __raw_writel(val, dispc.base + idx);
+ writel_relaxed(val, dispc.base + idx);
}
static inline u32 dispc_read_reg(const u16 idx)
{
- return __raw_readl(dispc.base + idx);
+ return readl_relaxed(dispc.base + idx);
}
static u32 mgr_fld_read(enum omap_channel channel, enum mgr_reg_fields regfld)
diff --git a/drivers/video/fbdev/omap2/dss/dsi.c b/drivers/video/fbdev/omap2/dss/dsi.c
index 56b92444c54f..109110f8695f 100644
--- a/drivers/video/fbdev/omap2/dss/dsi.c
+++ b/drivers/video/fbdev/omap2/dss/dsi.c
@@ -446,7 +446,7 @@ static inline void dsi_write_reg(struct platform_device *dsidev,
default: return;
}
- __raw_writel(val, base + idx.idx);
+ writel_relaxed(val, base + idx.idx);
}
static inline u32 dsi_read_reg(struct platform_device *dsidev,
@@ -462,7 +462,7 @@ static inline u32 dsi_read_reg(struct platform_device *dsidev,
default: return 0;
}
- return __raw_readl(base + idx.idx);
+ return readl_relaxed(base + idx.idx);
}
static void dsi_bus_lock(struct omap_dss_device *dssdev)
diff --git a/drivers/video/fbdev/omap2/dss/dss.c b/drivers/video/fbdev/omap2/dss/dss.c
index 6daeb7ed44c6..e7d7dc36f37b 100644
--- a/drivers/video/fbdev/omap2/dss/dss.c
+++ b/drivers/video/fbdev/omap2/dss/dss.c
@@ -105,12 +105,12 @@ static const char * const dss_generic_clk_source_names[] = {
static inline void dss_write_reg(const struct dss_reg idx, u32 val)
{
- __raw_writel(val, dss.base + idx.idx);
+ writel_relaxed(val, dss.base + idx.idx);
}
static inline u32 dss_read_reg(const struct dss_reg idx)
{
- return __raw_readl(dss.base + idx.idx);
+ return readl_relaxed(dss.base + idx.idx);
}
#define SR(reg) \
diff --git a/drivers/video/fbdev/omap2/dss/hdmi.h b/drivers/video/fbdev/omap2/dss/hdmi.h
index 262771b9b76b..dce992b495ec 100644
--- a/drivers/video/fbdev/omap2/dss/hdmi.h
+++ b/drivers/video/fbdev/omap2/dss/hdmi.h
@@ -271,12 +271,12 @@ struct hdmi_core_data {
static inline void hdmi_write_reg(void __iomem *base_addr, const u32 idx,
u32 val)
{
- __raw_writel(val, base_addr + idx);
+ writel_relaxed(val, base_addr + idx);
}
static inline u32 hdmi_read_reg(void __iomem *base_addr, const u32 idx)
{
- return __raw_readl(base_addr + idx);
+ return readl_relaxed(base_addr + idx);
}
#define REG_FLD_MOD(base, idx, val, start, end) \
diff --git a/drivers/video/fbdev/omap2/dss/venc.c b/drivers/video/fbdev/omap2/dss/venc.c
index 21d81113962b..8095293562ef 100644
--- a/drivers/video/fbdev/omap2/dss/venc.c
+++ b/drivers/video/fbdev/omap2/dss/venc.c
@@ -310,12 +310,12 @@ static struct {
static inline void venc_write_reg(int idx, u32 val)
{
- __raw_writel(val, venc.base + idx);
+ writel_relaxed(val, venc.base + idx);
}
static inline u32 venc_read_reg(int idx)
{
- u32 l = __raw_readl(venc.base + idx);
+ u32 l = readl_relaxed(venc.base + idx);
return l;
}
diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c
index ec2d132c782d..5f345413d3e6 100644
--- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c
@@ -82,18 +82,18 @@ static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color)
g = g * 64 / 256;
b = b * 32 / 256;
- __raw_writew((r << 11) | (g << 5) | (b << 0), p);
+ writew_relaxed((r << 11) | (g << 5) | (b << 0), p);
} else if (var->bits_per_pixel == 24) {
u8 __iomem *p = (u8 __iomem *)addr;
p += (y * line_len + x) * 3;
- __raw_writeb(b, p + 0);
- __raw_writeb(g, p + 1);
- __raw_writeb(r, p + 2);
+ writeb_relaxed(b, p + 0);
+ writeb_relaxed(g, p + 1);
+ writeb_relaxed(r, p + 2);
} else if (var->bits_per_pixel == 32) {
u32 __iomem *p = (u32 __iomem *)addr;
p += y * line_len + x;
- __raw_writel(color, p);
+ writel_relaxed(color, p);
}
}
diff --git a/drivers/video/fbdev/omap2/vrfb.c b/drivers/video/fbdev/omap2/vrfb.c
index f346b02eee1d..032469079bfe 100644
--- a/drivers/video/fbdev/omap2/vrfb.c
+++ b/drivers/video/fbdev/omap2/vrfb.c
@@ -82,17 +82,17 @@ static bool vrfb_loaded;
static void omap2_sms_write_rot_control(u32 val, unsigned ctx)
{
- __raw_writel(val, vrfb_base + SMS_ROT_CONTROL(ctx));
+ writel_relaxed(val, vrfb_base + SMS_ROT_CONTROL(ctx));
}
static void omap2_sms_write_rot_size(u32 val, unsigned ctx)
{
- __raw_writel(val, vrfb_base + SMS_ROT_SIZE(ctx));
+ writel_relaxed(val, vrfb_base + SMS_ROT_SIZE(ctx));
}
static void omap2_sms_write_rot_physical_ba(u32 val, unsigned ctx)
{
- __raw_writel(val, vrfb_base + SMS_ROT_PHYSICAL_BA(ctx));
+ writel_relaxed(val, vrfb_base + SMS_ROT_PHYSICAL_BA(ctx));
}
static inline void restore_hw_context(int ctx)
diff --git a/drivers/video/vexpress-dvi.c b/drivers/video/vexpress-dvi.c
new file mode 100644
index 000000000000..b656b9aeffcc
--- /dev/null
+++ b/drivers/video/vexpress-dvi.c
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2012 ARM Limited
+ */
+
+#define pr_fmt(fmt) "vexpress-dvi: " fmt
+
+#include <linux/fb.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/vexpress.h>
+
+
+static struct regmap *vexpress_dvimode_reg;
+
+static struct {
+ u32 xres, yres, mode;
+} vexpress_dvi_dvimodes[] = {
+ { 640, 480, 0 }, /* VGA */
+ { 800, 600, 1 }, /* SVGA */
+ { 1024, 768, 2 }, /* XGA */
+ { 1280, 1024, 3 }, /* SXGA */
+ { 1600, 1200, 4 }, /* UXGA */
+ { 1920, 1080, 5 }, /* HD1080 */
+};
+
+static void vexpress_dvi_mode_set(struct fb_info *info, u32 xres, u32 yres)
+{
+ int err = -ENOENT;
+ int i;
+
+ if (!vexpress_dvimode_reg)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(vexpress_dvi_dvimodes); i++) {
+ if (vexpress_dvi_dvimodes[i].xres == xres &&
+ vexpress_dvi_dvimodes[i].yres == yres) {
+ pr_debug("mode: %ux%u = %d\n", xres, yres,
+ vexpress_dvi_dvimodes[i].mode);
+ err = regmap_write(vexpress_dvimode_reg, 0,
+ vexpress_dvi_dvimodes[i].mode);
+ break;
+ }
+ }
+
+ if (err)
+ pr_warn("Failed to set %ux%u mode! (%d)\n", xres, yres, err);
+}
+
+
+static struct regmap *vexpress_muxfpga_reg;
+static int vexpress_dvi_fb = -1;
+
+static int vexpress_dvi_mux_set(struct fb_info *info)
+{
+ int err;
+ u32 site;
+
+ if (!info->device->of_node)
+ site = vexpress_config_get_master();
+ else {
+ u32 position, dcc;
+ err = vexpress_config_get_topo(info->device->of_node, &site, &position, &dcc);
+ if (err)
+ return err;
+ }
+
+ if (!vexpress_muxfpga_reg)
+ return -ENXIO;
+
+ err = regmap_write(vexpress_muxfpga_reg, 0, site);
+ if (!err) {
+ pr_debug("Selected MUXFPGA input %d (fb%d)\n", site,
+ info->node);
+ vexpress_dvi_fb = info->node;
+ vexpress_dvi_mode_set(info, info->var.xres,
+ info->var.yres);
+ } else {
+ pr_warn("Failed to select MUXFPGA input %d (fb%d)! (%d)\n",
+ site, info->node, err);
+ }
+
+ return err;
+}
+
+static int vexpress_dvi_fb_select(int fb)
+{
+ int err;
+ struct fb_info *info;
+
+ /* fb0 is the default */
+ if (fb < 0)
+ fb = 0;
+
+ info = registered_fb[fb];
+ if (!info || !lock_fb_info(info))
+ return -ENODEV;
+
+ err = vexpress_dvi_mux_set(info);
+
+ unlock_fb_info(info);
+
+ return err;
+}
+
+static ssize_t vexpress_dvi_fb_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", vexpress_dvi_fb);
+}
+
+static ssize_t vexpress_dvi_fb_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ long value;
+ int err = kstrtol(buf, 0, &value);
+
+ if (!err)
+ err = vexpress_dvi_fb_select(value);
+
+ return err ? err : count;
+}
+
+DEVICE_ATTR(fb, S_IRUGO | S_IWUSR, vexpress_dvi_fb_show,
+ vexpress_dvi_fb_store);
+
+
+static int vexpress_dvi_fb_event_notify(struct notifier_block *self,
+ unsigned long action, void *data)
+{
+ struct fb_event *event = data;
+ struct fb_info *info = event->info;
+ struct fb_videomode *mode = event->data;
+
+ switch (action) {
+ case FB_EVENT_FB_REGISTERED:
+ if (vexpress_dvi_fb < 0)
+ vexpress_dvi_mux_set(info);
+ break;
+ case FB_EVENT_MODE_CHANGE:
+ case FB_EVENT_MODE_CHANGE_ALL:
+ if (info->node == vexpress_dvi_fb)
+ vexpress_dvi_mode_set(info, mode->xres, mode->yres);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block vexpress_dvi_fb_notifier = {
+ .notifier_call = vexpress_dvi_fb_event_notify,
+};
+static bool vexpress_dvi_fb_notifier_registered;
+
+
+enum vexpress_dvi_func { FUNC_MUXFPGA, FUNC_DVIMODE };
+
+static struct of_device_id vexpress_dvi_of_match[] = {
+ {
+ .compatible = "arm,vexpress-muxfpga",
+ .data = (void *)FUNC_MUXFPGA,
+ }, {
+ .compatible = "arm,vexpress-dvimode",
+ .data = (void *)FUNC_DVIMODE,
+ },
+ {}
+};
+
+static int vexpress_dvi_probe(struct platform_device *pdev)
+{
+ enum vexpress_dvi_func func;
+ const struct of_device_id *match =
+ of_match_device(vexpress_dvi_of_match, &pdev->dev);
+
+ if (match)
+ func = (enum vexpress_dvi_func)match->data;
+ else
+ func = pdev->id_entry->driver_data;
+
+ switch (func) {
+ case FUNC_MUXFPGA:
+ vexpress_muxfpga_reg =
+ devm_regmap_init_vexpress_config(&pdev->dev);
+ device_create_file(&pdev->dev, &dev_attr_fb);
+ break;
+ case FUNC_DVIMODE:
+ vexpress_dvimode_reg =
+ devm_regmap_init_vexpress_config(&pdev->dev);
+ break;
+ }
+
+ if (!vexpress_dvi_fb_notifier_registered) {
+ fb_register_client(&vexpress_dvi_fb_notifier);
+ vexpress_dvi_fb_notifier_registered = true;
+ }
+
+ vexpress_dvi_fb_select(vexpress_dvi_fb);
+
+ return 0;
+}
+
+static const struct platform_device_id vexpress_dvi_id_table[] = {
+ { .name = "vexpress-muxfpga", .driver_data = FUNC_MUXFPGA, },
+ { .name = "vexpress-dvimode", .driver_data = FUNC_DVIMODE, },
+ {}
+};
+
+static struct platform_driver vexpress_dvi_driver = {
+ .probe = vexpress_dvi_probe,
+ .driver = {
+ .name = "vexpress-dvi",
+ .of_match_table = vexpress_dvi_of_match,
+ },
+ .id_table = vexpress_dvi_id_table,
+};
+
+static int __init vexpress_dvi_init(void)
+{
+ return platform_driver_register(&vexpress_dvi_driver);
+}
+device_initcall(vexpress_dvi_init);
diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c
index e76a9b39abb2..5e505d496f92 100644
--- a/drivers/w1/masters/ds2482.c
+++ b/drivers/w1/masters/ds2482.c
@@ -18,6 +18,8 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/platform_data/ds2482.h>
#include <asm/delay.h>
#include "../w1.h"
@@ -84,7 +86,8 @@ static const u8 ds2482_chan_rd[8] =
static int ds2482_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int ds2482_remove(struct i2c_client *client);
-
+static int ds2482_suspend(struct device *dev);
+static int ds2482_resume(struct device *dev);
/**
* Driver data (common to all clients)
@@ -94,10 +97,16 @@ static const struct i2c_device_id ds2482_id[] = {
{ }
};
+static const struct dev_pm_ops ds2482_pm_ops = {
+ .suspend = ds2482_suspend,
+ .resume = ds2482_resume,
+};
+
static struct i2c_driver ds2482_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "ds2482",
+ .pm = &ds2482_pm_ops,
},
.probe = ds2482_probe,
.remove = ds2482_remove,
@@ -119,6 +128,7 @@ struct ds2482_w1_chan {
struct ds2482_data {
struct i2c_client *client;
struct mutex access_lock;
+ int slpz_gpio;
/* 1-wire interface(s) */
int w1_count; /* 1 or 8 */
@@ -444,11 +454,31 @@ static u8 ds2482_w1_set_pullup(void *data, int delay)
return retval;
}
+static int ds2482_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds2482_data *data = i2c_get_clientdata(client);
+
+ if (data->slpz_gpio >= 0)
+ gpio_set_value(data->slpz_gpio, 0);
+ return 0;
+}
+
+static int ds2482_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds2482_data *data = i2c_get_clientdata(client);
+
+ if (data->slpz_gpio >= 0)
+ gpio_set_value(data->slpz_gpio, 1);
+ return 0;
+}
static int ds2482_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ds2482_data *data;
+ struct ds2482_platform_data *pdata;
int err = -ENODEV;
int temp1;
int idx;
@@ -515,6 +545,16 @@ static int ds2482_probe(struct i2c_client *client,
}
}
+ pdata = client->dev.platform_data;
+ data->slpz_gpio = pdata ? pdata->slpz_gpio : -1;
+
+ if (data->slpz_gpio >= 0) {
+ err = gpio_request_one(data->slpz_gpio, GPIOF_OUT_INIT_HIGH,
+ "ds2482.slpz");
+ if (err < 0)
+ goto exit_w1_remove;
+ }
+
return 0;
exit_w1_remove:
@@ -539,6 +579,11 @@ static int ds2482_remove(struct i2c_client *client)
w1_remove_master_device(&data->w1_ch[idx].w1_bm);
}
+ if (data->slpz_gpio >= 0) {
+ gpio_set_value(data->slpz_gpio, 0);
+ gpio_free(data->slpz_gpio);
+ }
+
/* Free the memory */
kfree(data);
return 0;
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index b10b48c2a7af..537ed0d1af2e 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -34,6 +34,7 @@
#include <linux/mutex.h>
#include <linux/anon_inodes.h>
#include <linux/device.h>
+#include <linux/freezer.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/mman.h>
@@ -1637,7 +1638,8 @@ fetch_events:
}
spin_unlock_irqrestore(&ep->lock, flags);
- if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
+ if (!freezable_schedule_hrtimeout_range(to, slack,
+ HRTIMER_MODE_ABS))
timed_out = 1;
spin_lock_irqsave(&ep->lock, flags);
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index ef9bef118342..aca4be5046dc 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -1157,7 +1157,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
if ((inode->i_state & flags) == flags)
return;
- if (unlikely(block_dump))
+ if (unlikely(block_dump > 1))
block_dump___mark_inode_dirty(inode);
spin_lock(&inode->i_lock);
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index ca887314aba9..54792719c7ea 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -20,6 +20,7 @@
#include <linux/swap.h>
#include <linux/splice.h>
#include <linux/aio.h>
+#include <linux/freezer.h>
MODULE_ALIAS_MISCDEV(FUSE_MINOR);
MODULE_ALIAS("devname:fuse");
@@ -464,7 +465,10 @@ __acquires(fc->lock)
* Wait it out.
*/
spin_unlock(&fc->lock);
- wait_event(req->waitq, req->state == FUSE_REQ_FINISHED);
+
+ while (req->state != FUSE_REQ_FINISHED)
+ wait_event_freezable(req->waitq,
+ req->state == FUSE_REQ_FINISHED);
spin_lock(&fc->lock);
if (!req->aborted)
diff --git a/fs/locks.c b/fs/locks.c
index cb66fb05ad4a..bb08857f90b5 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1619,7 +1619,7 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
smp_mb();
error = check_conflicting_open(dentry, arg);
if (error)
- locks_unlink_lock(flp);
+ locks_unlink_lock(before);
out:
if (is_deleg)
mutex_unlock(&inode->i_mutex);
diff --git a/fs/proc/base.c b/fs/proc/base.c
index baf852b648ad..916e557d3737 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -135,6 +135,12 @@ struct pid_entry {
NULL, &proc_single_file_operations, \
{ .proc_show = show } )
+/* ANDROID is for special files in /proc. */
+#define ANDROID(NAME, MODE, OTYPE) \
+ NOD(NAME, (S_IFREG|(MODE)), \
+ &proc_##OTYPE##_inode_operations, \
+ &proc_##OTYPE##_operations, {})
+
/*
* Count the number of hardlinks for the pid_entry table, excluding the .
* and .. links.
@@ -933,6 +939,37 @@ out:
return err < 0 ? err : count;
}
+static int oom_adjust_permission(struct inode *inode, int mask)
+{
+ kuid_t uid;
+ struct task_struct *p;
+ kuid_t system_server_uid = KUIDT_INIT(1000);
+
+ p = get_proc_task(inode);
+ if(p) {
+ uid = task_uid(p);
+ put_task_struct(p);
+ }
+
+ /*
+ * System Server (uid == 1000) is granted access to oom_adj of all
+ * android applications (uid > 10000) as and services (uid >= 1000)
+ */
+ if (p && (uid_eq(current_fsuid(), system_server_uid) &&
+ uid_gt(uid, system_server_uid))) {
+ if (inode->i_mode >> 6 & mask) {
+ return 0;
+ }
+ }
+
+ /* Fall back to default. */
+ return generic_permission(inode, mask);
+}
+
+static const struct inode_operations proc_oom_adj_inode_operations = {
+ .permission = oom_adjust_permission,
+};
+
static const struct file_operations proc_oom_adj_operations = {
.read = oom_adj_read,
.write = oom_adj_write,
@@ -2579,7 +2616,7 @@ static const struct pid_entry tgid_base_stuff[] = {
REG("cgroup", S_IRUGO, proc_cgroup_operations),
#endif
ONE("oom_score", S_IRUGO, proc_oom_score),
- REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adj_operations),
+ ANDROID("oom_adj", S_IRUGO|S_IWUSR, oom_adj),
REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),
#ifdef CONFIG_AUDITSYSCALL
REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index dfc791c42d64..18bc8669977d 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -129,6 +129,56 @@ static void release_task_mempolicy(struct proc_maps_private *priv)
}
#endif
+static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma)
+{
+ const char __user *name = vma_get_anon_name(vma);
+ struct mm_struct *mm = vma->vm_mm;
+
+ unsigned long page_start_vaddr;
+ unsigned long page_offset;
+ unsigned long num_pages;
+ unsigned long max_len = NAME_MAX;
+ int i;
+
+ page_start_vaddr = (unsigned long)name & PAGE_MASK;
+ page_offset = (unsigned long)name - page_start_vaddr;
+ num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE);
+
+ seq_puts(m, "[anon:");
+
+ for (i = 0; i < num_pages; i++) {
+ int len;
+ int write_len;
+ const char *kaddr;
+ long pages_pinned;
+ struct page *page;
+
+ pages_pinned = get_user_pages(current, mm, page_start_vaddr,
+ 1, 0, 0, &page, NULL);
+ if (pages_pinned < 1) {
+ seq_puts(m, "<fault>]");
+ return;
+ }
+
+ kaddr = (const char *)kmap(page);
+ len = min(max_len, PAGE_SIZE - page_offset);
+ write_len = strnlen(kaddr + page_offset, len);
+ seq_write(m, kaddr + page_offset, write_len);
+ kunmap(page);
+ put_page(page);
+
+ /* if strnlen hit a null terminator then we're done */
+ if (write_len != len)
+ break;
+
+ max_len -= len;
+ page_offset = 0;
+ page_start_vaddr += PAGE_SIZE;
+ }
+
+ seq_putc(m, ']');
+}
+
static void vma_stop(struct proc_maps_private *priv, struct vm_area_struct *vma)
{
if (vma && vma != priv->tail_vma) {
@@ -336,6 +386,12 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
seq_pad(m, ' ');
seq_printf(m, "[stack:%d]", tid);
}
+ goto done;
+ }
+
+ if (vma_get_anon_name(vma)) {
+ seq_pad(m, ' ');
+ seq_print_vma_name(m, vma);
}
}
@@ -638,6 +694,12 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
show_smap_vma_flags(m, vma);
+ if (vma_get_anon_name(vma)) {
+ seq_puts(m, "Name: ");
+ seq_print_vma_name(m, vma);
+ seq_putc(m, '\n');
+ }
+
if (m->count < m->size) /* vma is copied successfully */
m->version = (vma != get_gate_vma(task->mm))
? vma->vm_start : 0;
diff --git a/include/dt-bindings/clock/bcm281xx.h b/include/dt-bindings/clock/bcm281xx.h
index a763460cf1af..99f4aade97ef 100644
--- a/include/dt-bindings/clock/bcm281xx.h
+++ b/include/dt-bindings/clock/bcm281xx.h
@@ -72,6 +72,7 @@
#define BCM281XX_SLAVE_CCU_BSC2 7
#define BCM281XX_SLAVE_CCU_BSC3 8
#define BCM281XX_SLAVE_CCU_PWM 9
-#define BCM281XX_SLAVE_CCU_CLOCK_COUNT 10
+#define BCM281XX_SLAVE_CCU_BSC3_APB 10
+#define BCM281XX_SLAVE_CCU_CLOCK_COUNT 11
#endif /* _CLOCK_BCM281XX_H */
diff --git a/include/dt-bindings/clock/hix5hd2-clock.h b/include/dt-bindings/clock/hix5hd2-clock.h
index aad579a75802..fd29c174ba63 100644
--- a/include/dt-bindings/clock/hix5hd2-clock.h
+++ b/include/dt-bindings/clock/hix5hd2-clock.h
@@ -46,6 +46,7 @@
#define HIX5HD2_SFC_MUX 64
#define HIX5HD2_MMC_MUX 65
#define HIX5HD2_FEPHY_MUX 66
+#define HIX5HD2_SD_MUX 67
/* gate clocks */
#define HIX5HD2_SFC_RST 128
@@ -53,6 +54,32 @@
#define HIX5HD2_MMC_CIU_CLK 130
#define HIX5HD2_MMC_BIU_CLK 131
#define HIX5HD2_MMC_CIU_RST 132
+#define HIX5HD2_FWD_BUS_CLK 133
+#define HIX5HD2_FWD_SYS_CLK 134
+#define HIX5HD2_MAC0_PHY_CLK 135
+#define HIX5HD2_SD_CIU_CLK 136
+#define HIX5HD2_SD_BIU_CLK 137
+#define HIX5HD2_SD_CIU_RST 138
+#define HIX5HD2_WDG0_CLK 139
+#define HIX5HD2_WDG0_RST 140
+#define HIX5HD2_I2C0_CLK 141
+#define HIX5HD2_I2C0_RST 142
+#define HIX5HD2_I2C1_CLK 143
+#define HIX5HD2_I2C1_RST 144
+#define HIX5HD2_I2C2_CLK 145
+#define HIX5HD2_I2C2_RST 146
+#define HIX5HD2_I2C3_CLK 147
+#define HIX5HD2_I2C3_RST 148
+#define HIX5HD2_I2C4_CLK 149
+#define HIX5HD2_I2C4_RST 150
+#define HIX5HD2_I2C5_CLK 151
+#define HIX5HD2_I2C5_RST 152
+
+/* complex */
+#define HIX5HD2_MAC0_CLK 192
+#define HIX5HD2_MAC1_CLK 193
+#define HIX5HD2_SATA_CLK 194
+#define HIX5HD2_USB_CLK 195
#define HIX5HD2_NR_CLKS 256
#endif /* __DTS_HIX5HD2_CLOCK_H */
diff --git a/include/dt-bindings/mfd/qcom,rpm.h b/include/dt-bindings/mfd/qcom,rpm.h
new file mode 100644
index 000000000000..277e7891e73b
--- /dev/null
+++ b/include/dt-bindings/mfd/qcom,rpm.h
@@ -0,0 +1,142 @@
+/*
+ * This header provides constants for the Qualcomm RPM bindings.
+ */
+
+#ifndef _DT_BINDINGS_MFD_QCOM_RPM_H
+#define _DT_BINDINGS_MFD_QCOM_RPM_H
+
+#define QCOM_RPM_APPS_FABRIC_ARB 1
+#define QCOM_RPM_APPS_FABRIC_CLK 2
+#define QCOM_RPM_APPS_FABRIC_HALT 3
+#define QCOM_RPM_APPS_FABRIC_IOCTL 4
+#define QCOM_RPM_APPS_FABRIC_MODE 5
+#define QCOM_RPM_APPS_L2_CACHE_CTL 6
+#define QCOM_RPM_CFPB_CLK 7
+#define QCOM_RPM_CXO_BUFFERS 8
+#define QCOM_RPM_CXO_CLK 9
+#define QCOM_RPM_DAYTONA_FABRIC_CLK 10
+#define QCOM_RPM_DDR_DMM 11
+#define QCOM_RPM_EBI1_CLK 12
+#define QCOM_RPM_HDMI_SWITCH 13
+#define QCOM_RPM_MMFPB_CLK 14
+#define QCOM_RPM_MM_FABRIC_ARB 15
+#define QCOM_RPM_MM_FABRIC_CLK 16
+#define QCOM_RPM_MM_FABRIC_HALT 17
+#define QCOM_RPM_MM_FABRIC_IOCTL 18
+#define QCOM_RPM_MM_FABRIC_MODE 19
+#define QCOM_RPM_PLL_4 20
+#define QCOM_RPM_PM8058_LDO0 21
+#define QCOM_RPM_PM8058_LDO1 22
+#define QCOM_RPM_PM8058_LDO2 23
+#define QCOM_RPM_PM8058_LDO3 24
+#define QCOM_RPM_PM8058_LDO4 25
+#define QCOM_RPM_PM8058_LDO5 26
+#define QCOM_RPM_PM8058_LDO6 27
+#define QCOM_RPM_PM8058_LDO7 28
+#define QCOM_RPM_PM8058_LDO8 29
+#define QCOM_RPM_PM8058_LDO9 30
+#define QCOM_RPM_PM8058_LDO10 31
+#define QCOM_RPM_PM8058_LDO11 32
+#define QCOM_RPM_PM8058_LDO12 33
+#define QCOM_RPM_PM8058_LDO13 34
+#define QCOM_RPM_PM8058_LDO14 35
+#define QCOM_RPM_PM8058_LDO15 36
+#define QCOM_RPM_PM8058_LDO16 37
+#define QCOM_RPM_PM8058_LDO17 38
+#define QCOM_RPM_PM8058_LDO18 39
+#define QCOM_RPM_PM8058_LDO19 40
+#define QCOM_RPM_PM8058_LDO20 41
+#define QCOM_RPM_PM8058_LDO21 42
+#define QCOM_RPM_PM8058_LDO22 43
+#define QCOM_RPM_PM8058_LDO23 44
+#define QCOM_RPM_PM8058_LDO24 45
+#define QCOM_RPM_PM8058_LDO25 46
+#define QCOM_RPM_PM8058_LVS0 47
+#define QCOM_RPM_PM8058_LVS1 48
+#define QCOM_RPM_PM8058_NCP 49
+#define QCOM_RPM_PM8058_SMPS0 50
+#define QCOM_RPM_PM8058_SMPS1 51
+#define QCOM_RPM_PM8058_SMPS2 52
+#define QCOM_RPM_PM8058_SMPS3 53
+#define QCOM_RPM_PM8058_SMPS4 54
+#define QCOM_RPM_PM8821_L1 55
+#define QCOM_RPM_PM8821_S1 56
+#define QCOM_RPM_PM8821_S2 57
+#define QCOM_RPM_PM8901_LDO0 58
+#define QCOM_RPM_PM8901_LDO1 59
+#define QCOM_RPM_PM8901_LDO2 60
+#define QCOM_RPM_PM8901_LDO3 61
+#define QCOM_RPM_PM8901_LDO4 62
+#define QCOM_RPM_PM8901_LDO5 63
+#define QCOM_RPM_PM8901_LDO6 64
+#define QCOM_RPM_PM8901_LVS0 65
+#define QCOM_RPM_PM8901_LVS1 66
+#define QCOM_RPM_PM8901_LVS2 67
+#define QCOM_RPM_PM8901_LVS3 68
+#define QCOM_RPM_PM8901_MVS 69
+#define QCOM_RPM_PM8901_SMPS0 70
+#define QCOM_RPM_PM8901_SMPS1 71
+#define QCOM_RPM_PM8901_SMPS2 72
+#define QCOM_RPM_PM8901_SMPS3 73
+#define QCOM_RPM_PM8901_SMPS4 74
+#define QCOM_RPM_PM8921_CLK1 75
+#define QCOM_RPM_PM8921_CLK2 76
+#define QCOM_RPM_PM8921_L1 77
+#define QCOM_RPM_PM8921_L2 78
+#define QCOM_RPM_PM8921_L3 79
+#define QCOM_RPM_PM8921_L4 80
+#define QCOM_RPM_PM8921_L5 81
+#define QCOM_RPM_PM8921_L6 82
+#define QCOM_RPM_PM8921_L7 83
+#define QCOM_RPM_PM8921_L8 84
+#define QCOM_RPM_PM8921_L9 85
+#define QCOM_RPM_PM8921_L10 86
+#define QCOM_RPM_PM8921_L11 87
+#define QCOM_RPM_PM8921_L12 88
+#define QCOM_RPM_PM8921_L13 89
+#define QCOM_RPM_PM8921_L14 90
+#define QCOM_RPM_PM8921_L15 91
+#define QCOM_RPM_PM8921_L16 92
+#define QCOM_RPM_PM8921_L17 93
+#define QCOM_RPM_PM8921_L18 94
+#define QCOM_RPM_PM8921_L19 95
+#define QCOM_RPM_PM8921_L20 96
+#define QCOM_RPM_PM8921_L21 97
+#define QCOM_RPM_PM8921_L22 98
+#define QCOM_RPM_PM8921_L23 99
+#define QCOM_RPM_PM8921_L24 100
+#define QCOM_RPM_PM8921_L25 101
+#define QCOM_RPM_PM8921_L26 102
+#define QCOM_RPM_PM8921_L27 103
+#define QCOM_RPM_PM8921_L28 104
+#define QCOM_RPM_PM8921_L29 105
+#define QCOM_RPM_PM8921_LVS1 106
+#define QCOM_RPM_PM8921_LVS2 107
+#define QCOM_RPM_PM8921_LVS3 108
+#define QCOM_RPM_PM8921_LVS4 109
+#define QCOM_RPM_PM8921_LVS5 110
+#define QCOM_RPM_PM8921_LVS6 111
+#define QCOM_RPM_PM8921_LVS7 112
+#define QCOM_RPM_PM8921_MVS 113
+#define QCOM_RPM_PM8921_NCP 114
+#define QCOM_RPM_PM8921_S1 115
+#define QCOM_RPM_PM8921_S2 116
+#define QCOM_RPM_PM8921_S3 117
+#define QCOM_RPM_PM8921_S4 118
+#define QCOM_RPM_PM8921_S5 119
+#define QCOM_RPM_PM8921_S6 120
+#define QCOM_RPM_PM8921_S7 121
+#define QCOM_RPM_PM8921_S8 122
+#define QCOM_RPM_PXO_CLK 123
+#define QCOM_RPM_QDSS_CLK 124
+#define QCOM_RPM_SFPB_CLK 125
+#define QCOM_RPM_SMI_CLK 126
+#define QCOM_RPM_SYS_FABRIC_ARB 127
+#define QCOM_RPM_SYS_FABRIC_CLK 128
+#define QCOM_RPM_SYS_FABRIC_HALT 129
+#define QCOM_RPM_SYS_FABRIC_IOCTL 130
+#define QCOM_RPM_SYS_FABRIC_MODE 131
+#define QCOM_RPM_USB_OTG_SWITCH 132
+#define QCOM_RPM_VDDMIN_GPIO 133
+
+#endif
diff --git a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
new file mode 100644
index 000000000000..b208fdd8f1c4
--- /dev/null
+++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
@@ -0,0 +1,142 @@
+/*
+ * This header provides constants for the Qualcomm PMIC gpio binding.
+ */
+
+#ifndef _DT_BINDINGS_PINCTRL_QCOM_PMIC_GPIO_H
+#define _DT_BINDINGS_PINCTRL_QCOM_PMIC_GPIO_H
+
+#define PMIC_GPIO_PULL_UP_30 1
+#define PMIC_GPIO_PULL_UP_1P5 2
+#define PMIC_GPIO_PULL_UP_31P5 3
+#define PMIC_GPIO_PULL_UP_1P5_30 4
+
+#define PMIC_GPIO_STRENGTH_NO 0
+#define PMIC_GPIO_STRENGTH_HIGH 1
+#define PMIC_GPIO_STRENGTH_MED 2
+#define PMIC_GPIO_STRENGTH_LOW 3
+
+/*
+ * Note: PM8018 GPIO3 and GPIO4 are supporting
+ * only S3 and L2 options (1.8V)
+ */
+#define PM8018_GPIO_L6 0
+#define PM8018_GPIO_L5 1
+#define PM8018_GPIO_S3 2
+#define PM8018_GPIO_L14 3
+#define PM8018_GPIO_L2 4
+#define PM8018_GPIO_L4 5
+#define PM8018_GPIO_VDD 6
+
+/*
+ * Note: PM8038 GPIO7 and GPIO8 are supporting
+ * only L11 and L4 options (1.8V)
+ */
+#define PM8038_GPIO_VPH 0
+#define PM8038_GPIO_BB 1
+#define PM8038_GPIO_L11 2
+#define PM8038_GPIO_L15 3
+#define PM8038_GPIO_L4 4
+#define PM8038_GPIO_L3 5
+#define PM8038_GPIO_L17 6
+
+#define PM8058_GPIO_VPH 0
+#define PM8058_GPIO_BB 1
+#define PM8058_GPIO_S3 2
+#define PM8058_GPIO_L3 3
+#define PM8058_GPIO_L7 4
+#define PM8058_GPIO_L6 5
+#define PM8058_GPIO_L5 6
+#define PM8058_GPIO_L2 7
+
+#define PM8917_GPIO_VPH 0
+#define PM8917_GPIO_S4 2
+#define PM8917_GPIO_L15 3
+#define PM8917_GPIO_L4 4
+#define PM8917_GPIO_L3 5
+#define PM8917_GPIO_L17 6
+
+#define PM8921_GPIO_VPH 0
+#define PM8921_GPIO_BB 1
+#define PM8921_GPIO_S4 2
+#define PM8921_GPIO_L15 3
+#define PM8921_GPIO_L4 4
+#define PM8921_GPIO_L3 5
+#define PM8921_GPIO_L17 6
+
+/*
+ * Note: PM8941 gpios from 15 to 18 are supporting
+ * only S3 and L6 options (1.8V)
+ */
+#define PM8941_GPIO_VPH 0
+#define PM8941_GPIO_L1 1
+#define PM8941_GPIO_S3 2
+#define PM8941_GPIO_L6 3
+
+/*
+ * Note: PMA8084 gpios from 15 to 18 are supporting
+ * only S4 and L6 options (1.8V)
+ */
+#define PMA8084_GPIO_VPH 0
+#define PMA8084_GPIO_L1 1
+#define PMA8084_GPIO_S4 2
+#define PMA8084_GPIO_L6 3
+
+/* To be used with "function = " */
+#define PMIC_GPIO_FUNC_NORMAL "normal"
+#define PMIC_GPIO_FUNC_PAIRED "paired"
+#define PMIC_GPIO_FUNC_FUNC1 "func1"
+#define PMIC_GPIO_FUNC_FUNC2 "func2"
+#define PMIC_GPIO_FUNC_DTEST1 "dtest1"
+#define PMIC_GPIO_FUNC_DTEST2 "dtest2"
+#define PMIC_GPIO_FUNC_DTEST3 "dtest3"
+#define PMIC_GPIO_FUNC_DTEST4 "dtest4"
+
+#define PM8038_GPIO1_2_LPG_DRV PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO3_5V_BOOST_EN PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO4_SSBI_ALT_CLK PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO5_6_EXT_REG_EN PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO10_11_EXT_REG_EN PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO6_7_CLK PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO9_BAT_ALRM_OUT PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO6_12_KYPD_DRV PMIC_GPIO_FUNC_FUNC2
+
+#define PM8058_GPIO7_8_MP3_CLK PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO7_8_BCLK_19P2MHZ PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO9_26_KYPD_DRV PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO21_23_UART_TX PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO24_26_LPG_DRV PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO33_BCLK_19P2MHZ PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO34_35_MP3_CLK PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO36_BCLK_19P2MHZ PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO37_UPL_OUT PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO37_UART_M_RX PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO38_XO_SLEEP_CLK PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO38_39_CLK_32KHZ PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO39_MP3_CLK PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO40_EXT_BB_EN PMIC_GPIO_FUNC_FUNC1
+
+#define PM8917_GPIO9_18_KEYP_DRV PMIC_GPIO_FUNC_FUNC1
+#define PM8917_GPIO20_BAT_ALRM_OUT PMIC_GPIO_FUNC_FUNC1
+#define PM8917_GPIO21_23_UART_TX PMIC_GPIO_FUNC_FUNC2
+#define PM8917_GPIO25_26_EXT_REG_EN PMIC_GPIO_FUNC_FUNC1
+#define PM8917_GPIO37_38_XO_SLEEP_CLK PMIC_GPIO_FUNC_FUNC1
+#define PM8917_GPIO37_38_MP3_CLK PMIC_GPIO_FUNC_FUNC2
+
+#define PM8941_GPIO9_14_KYPD_DRV PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO15_18_DIV_CLK PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO15_18_SLEEP_CLK PMIC_GPIO_FUNC_FUNC2
+#define PM8941_GPIO23_26_KYPD_DRV PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO23_26_LPG_DRV_HI PMIC_GPIO_FUNC_FUNC2
+#define PM8941_GPIO31_BAT_ALRM_OUT PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO33_36_LPG_DRV_3D PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO33_36_LPG_DRV_HI PMIC_GPIO_FUNC_FUNC2
+
+#define PMA8084_GPIO4_5_LPG_DRV PMIC_GPIO_FUNC_FUNC1
+#define PMA8084_GPIO7_10_LPG_DRV PMIC_GPIO_FUNC_FUNC1
+#define PMA8084_GPIO5_14_KEYP_DRV PMIC_GPIO_FUNC_FUNC2
+#define PMA8084_GPIO19_21_KEYP_DRV PMIC_GPIO_FUNC_FUNC2
+#define PMA8084_GPIO15_18_DIV_CLK PMIC_GPIO_FUNC_FUNC1
+#define PMA8084_GPIO15_18_SLEEP_CLK PMIC_GPIO_FUNC_FUNC2
+#define PMA8084_GPIO22_BAT_ALRM_OUT PMIC_GPIO_FUNC_FUNC1
+
+#endif
diff --git a/include/dt-bindings/pinctrl/qcom,pmic-mpp.h b/include/dt-bindings/pinctrl/qcom,pmic-mpp.h
new file mode 100644
index 000000000000..1bda99d9ca83
--- /dev/null
+++ b/include/dt-bindings/pinctrl/qcom,pmic-mpp.h
@@ -0,0 +1,70 @@
+/*
+ * This header provides constants for the Qualcomm PMIC's
+ * Multi-Purpose Pin binding.
+ */
+
+#ifndef _DT_BINDINGS_PINCTRL_QCOM_PMIC_MPP_H
+#define _DT_BINDINGS_PINCTRL_QCOM_PMIC_MPP_H
+
+/* power-source */
+#define PM8841_MPP_VPH 0
+#define PM8841_MPP_S3 2
+
+#define PM8941_MPP_VPH 0
+#define PM8941_MPP_L1 1
+#define PM8941_MPP_S3 2
+#define PM8941_MPP_L6 3
+
+#define PMA8084_MPP_VPH 0
+#define PMA8084_MPP_L1 1
+#define PMA8084_MPP_S4 2
+#define PMA8084_MPP_L6 3
+
+/*
+ * Analog Output - Set the analog output voltage reference.
+ * To be used with "qcom,vrefence = <>"
+ */
+#define PMIC_MPP_VREFERENCE_1V25 0
+#define PMIC_MPP_VREFERENCE_0V625 1
+#define PMIC_MPP_VREFERENCE_0V3125 2
+#define PMIC_MPP_VREFERENCE_PAIRED_MPP 3
+#define PMIC_MPP_VREFERENCE_ABUS1 4
+#define PMIC_MPP_VREFERENCE_ABUS2 5
+#define PMIC_MPP_VREFERENCE_ABUS3 6
+#define PMIC_MPP_VREFERENCE_ABUS4 7
+
+/*
+ * Analog Input - Set the source for analog input.
+ * To be used with "qcom,amux-route = <>"
+ */
+#define PMIC_MPP_AMUX_ROUTE_CH5 0
+#define PMIC_MPP_AMUX_ROUTE_CH6 1
+#define PMIC_MPP_AMUX_ROUTE_CH7 2
+#define PMIC_MPP_AMUX_ROUTE_CH8 3
+#define PMIC_MPP_AMUX_ROUTE_ABUS1 4
+#define PMIC_MPP_AMUX_ROUTE_ABUS2 5
+#define PMIC_MPP_AMUX_ROUTE_ABUS3 6
+#define PMIC_MPP_AMUX_ROUTE_ABUS4 7
+
+/*
+ * Mode select - indicates whether the pin should be digital input, output, both
+ * or analog input, output or current sink. To be used with "qcom,mode = <>"
+ */
+#define PMIC_MPP_MODE_DI 0
+#define PMIC_MPP_MODE_DO 1
+#define PMIC_MPP_MODE_DIO 2
+#define PMIC_MPP_MODE_AIO 3
+#define PMIC_MPP_MODE_AI 4
+#define PMIC_MPP_MODE_AO 5
+#define PMIC_MPP_MODE_CS 6
+
+/* To be used with "function = " */
+#define PMIC_MPP_FUNC_NORMAL "normal"
+#define PMIC_MPP_FUNC_PAIRED "paired"
+#define PMIC_MPP_FUNC_DTEST1 "dtest1"
+#define PMIC_MPP_FUNC_DTEST2 "dtest2"
+#define PMIC_MPP_FUNC_DTEST3 "dtest3"
+#define PMIC_MPP_FUNC_DTEST4 "dtest4"
+
+#endif
+
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
new file mode 100644
index 000000000000..a4608897a5f3
--- /dev/null
+++ b/include/linux/Kbuild
@@ -0,0 +1,2 @@
+header-y += if_pppolac.h
+header-y += if_pppopns.h
diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h
index 8c98113069ce..420099839252 100644
--- a/include/linux/amba/mmci.h
+++ b/include/linux/amba/mmci.h
@@ -5,6 +5,15 @@
#define AMBA_MMCI_H
#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+
+struct embedded_sdio_data {
+ struct sdio_cis cis;
+ struct sdio_cccr cccr;
+ struct sdio_embedded_func *funcs;
+ int num_funcs;
+};
/**
* struct mmci_platform_data - platform configuration for the MMCI
@@ -31,6 +40,9 @@ struct mmci_platform_data {
int gpio_wp;
int gpio_cd;
bool cd_invert;
+ unsigned int status_irq;
+ struct embedded_sdio_data *embedded_sdio;
+ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
};
#endif
diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h
new file mode 100644
index 000000000000..6f1fa1792dfc
--- /dev/null
+++ b/include/linux/android_aid.h
@@ -0,0 +1,28 @@
+/* include/linux/android_aid.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_ANDROID_AID_H
+#define _LINUX_ANDROID_AID_H
+
+/* AIDs that the kernel treats differently */
+#define AID_OBSOLETE_000 KGIDT_INIT(3001) /* was NET_BT_ADMIN */
+#define AID_OBSOLETE_001 KGIDT_INIT(3002) /* was NET_BT */
+#define AID_INET KGIDT_INIT(3003)
+#define AID_NET_RAW KGIDT_INIT(3004)
+#define AID_NET_ADMIN KGIDT_INIT(3005)
+#define AID_NET_BW_STATS KGIDT_INIT(3006) /* read bandwidth statistics */
+#define AID_NET_BW_ACCT KGIDT_INIT(3007) /* change bandwidth statistics accounting */
+
+#endif
diff --git a/include/linux/arm-hdlcd.h b/include/linux/arm-hdlcd.h
new file mode 100644
index 000000000000..939f3a81d56b
--- /dev/null
+++ b/include/linux/arm-hdlcd.h
@@ -0,0 +1,122 @@
+/*
+ * include/linux/arm-hdlcd.h
+ *
+ * Copyright (C) 2011 ARM Limited
+ *
+ * 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.
+ *
+ * ARM HDLCD Controller register definition
+ */
+
+#include <linux/fb.h>
+#include <linux/completion.h>
+
+/* register offsets */
+#define HDLCD_REG_VERSION 0x0000 /* ro */
+#define HDLCD_REG_INT_RAWSTAT 0x0010 /* rw */
+#define HDLCD_REG_INT_CLEAR 0x0014 /* wo */
+#define HDLCD_REG_INT_MASK 0x0018 /* rw */
+#define HDLCD_REG_INT_STATUS 0x001c /* ro */
+#define HDLCD_REG_USER_OUT 0x0020 /* rw */
+#define HDLCD_REG_FB_BASE 0x0100 /* rw */
+#define HDLCD_REG_FB_LINE_LENGTH 0x0104 /* rw */
+#define HDLCD_REG_FB_LINE_COUNT 0x0108 /* rw */
+#define HDLCD_REG_FB_LINE_PITCH 0x010c /* rw */
+#define HDLCD_REG_BUS_OPTIONS 0x0110 /* rw */
+#define HDLCD_REG_V_SYNC 0x0200 /* rw */
+#define HDLCD_REG_V_BACK_PORCH 0x0204 /* rw */
+#define HDLCD_REG_V_DATA 0x0208 /* rw */
+#define HDLCD_REG_V_FRONT_PORCH 0x020c /* rw */
+#define HDLCD_REG_H_SYNC 0x0210 /* rw */
+#define HDLCD_REG_H_BACK_PORCH 0x0214 /* rw */
+#define HDLCD_REG_H_DATA 0x0218 /* rw */
+#define HDLCD_REG_H_FRONT_PORCH 0x021c /* rw */
+#define HDLCD_REG_POLARITIES 0x0220 /* rw */
+#define HDLCD_REG_COMMAND 0x0230 /* rw */
+#define HDLCD_REG_PIXEL_FORMAT 0x0240 /* rw */
+#define HDLCD_REG_BLUE_SELECT 0x0244 /* rw */
+#define HDLCD_REG_GREEN_SELECT 0x0248 /* rw */
+#define HDLCD_REG_RED_SELECT 0x024c /* rw */
+
+/* version */
+#define HDLCD_PRODUCT_ID 0x1CDC0000
+#define HDLCD_PRODUCT_MASK 0xFFFF0000
+#define HDLCD_VERSION_MAJOR_MASK 0x0000FF00
+#define HDLCD_VERSION_MINOR_MASK 0x000000FF
+
+/* interrupts */
+#define HDLCD_INTERRUPT_DMA_END (1 << 0)
+#define HDLCD_INTERRUPT_BUS_ERROR (1 << 1)
+#define HDLCD_INTERRUPT_VSYNC (1 << 2)
+#define HDLCD_INTERRUPT_UNDERRUN (1 << 3)
+
+/* polarity */
+#define HDLCD_POLARITY_VSYNC (1 << 0)
+#define HDLCD_POLARITY_HSYNC (1 << 1)
+#define HDLCD_POLARITY_DATAEN (1 << 2)
+#define HDLCD_POLARITY_DATA (1 << 3)
+#define HDLCD_POLARITY_PIXELCLK (1 << 4)
+
+/* commands */
+#define HDLCD_COMMAND_DISABLE (0 << 0)
+#define HDLCD_COMMAND_ENABLE (1 << 0)
+
+/* pixel format */
+#define HDLCD_PIXEL_FMT_LITTLE_ENDIAN (0 << 31)
+#define HDLCD_PIXEL_FMT_BIG_ENDIAN (1 << 31)
+#define HDLCD_BYTES_PER_PIXEL_MASK (3 << 3)
+
+/* bus options */
+#define HDLCD_BUS_BURST_MASK 0x01f
+#define HDLCD_BUS_MAX_OUTSTAND 0xf00
+#define HDLCD_BUS_BURST_NONE (0 << 0)
+#define HDLCD_BUS_BURST_1 (1 << 0)
+#define HDLCD_BUS_BURST_2 (1 << 1)
+#define HDLCD_BUS_BURST_4 (1 << 2)
+#define HDLCD_BUS_BURST_8 (1 << 3)
+#define HDLCD_BUS_BURST_16 (1 << 4)
+
+/* Max resolution supported is 4096x4096, 8 bit per color component,
+ 8 bit alpha, but we are going to choose the usual hardware default
+ (2048x2048, 32 bpp) and enable double buffering */
+#define HDLCD_MAX_XRES 2048
+#define HDLCD_MAX_YRES 2048
+#define HDLCD_MAX_FRAMEBUFFER_SIZE (HDLCD_MAX_XRES * HDLCD_MAX_YRES << 2)
+
+#define HDLCD_MEM_BASE (CONFIG_PAGE_OFFSET - 0x1000000)
+
+#define NR_PALETTE 256
+
+/* OEMs using HDLCD may wish to enable these settings if
+ * display disruption is apparent and you suspect HDLCD
+ * access to RAM may be starved.
+ */
+/* Turn HDLCD default color red instead of black so
+ * that it's easy to see pixel clock data underruns
+ * (compared to other visual disruption)
+ */
+//#define HDLCD_RED_DEFAULT_COLOUR
+/* Add a counter in the IRQ handler to count buffer underruns
+ * and /proc/hdlcd_underrun to read the counter
+ */
+//#define HDLCD_COUNT_BUFFERUNDERRUNS
+/* Restrict height to 1x screen size
+ *
+ */
+//#define HDLCD_NO_VIRTUAL_SCREEN
+
+#ifdef CONFIG_ANDROID
+#define HDLCD_NO_VIRTUAL_SCREEN
+#endif
+
+struct hdlcd_device {
+ struct fb_info fb;
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *base;
+ int irq;
+ struct completion vsync_completion;
+ unsigned char *edid;
+};
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index b5223c570eba..e68c02af2b81 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -627,6 +627,8 @@ struct cgroup_subsys {
void (*css_free)(struct cgroup_subsys_state *css);
void (*css_reset)(struct cgroup_subsys_state *css);
+ int (*allow_attach)(struct cgroup_subsys_state *css,
+ struct cgroup_taskset *tset);
int (*can_attach)(struct cgroup_subsys_state *css,
struct cgroup_taskset *tset);
void (*cancel_attach)(struct cgroup_subsys_state *css,
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 95978ad7fcdd..e899a9200a9f 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -265,4 +265,11 @@ void arch_cpu_idle_enter(void);
void arch_cpu_idle_exit(void);
void arch_cpu_idle_dead(void);
+#define IDLE_START 1
+#define IDLE_END 2
+
+void idle_notifier_register(struct notifier_block *n);
+void idle_notifier_unregister(struct notifier_block *n);
+void idle_notifier_call_chain(unsigned long val);
+
#endif /* _LINUX_CPU_H_ */
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 7d1955afa62c..acdd2a81ce08 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -476,6 +476,9 @@ extern struct cpufreq_governor cpufreq_gov_ondemand;
#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE)
extern struct cpufreq_governor cpufreq_gov_conservative;
#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_conservative)
+#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE)
+extern struct cpufreq_governor cpufreq_gov_interactive;
+#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactive)
#endif
/*********************************************************************
diff --git a/include/linux/gpio_event.h b/include/linux/gpio_event.h
new file mode 100644
index 000000000000..2613fc5e4a93
--- /dev/null
+++ b/include/linux/gpio_event.h
@@ -0,0 +1,170 @@
+/* include/linux/gpio_event.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_GPIO_EVENT_H
+#define _LINUX_GPIO_EVENT_H
+
+#include <linux/input.h>
+
+struct gpio_event_input_devs {
+ int count;
+ struct input_dev *dev[];
+};
+enum {
+ GPIO_EVENT_FUNC_UNINIT = 0x0,
+ GPIO_EVENT_FUNC_INIT = 0x1,
+ GPIO_EVENT_FUNC_SUSPEND = 0x2,
+ GPIO_EVENT_FUNC_RESUME = 0x3,
+};
+struct gpio_event_info {
+ int (*func)(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info,
+ void **data, int func);
+ int (*event)(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info,
+ void **data, unsigned int dev, unsigned int type,
+ unsigned int code, int value); /* out events */
+ bool no_suspend;
+};
+
+struct gpio_event_platform_data {
+ const char *name;
+ struct gpio_event_info **info;
+ size_t info_count;
+ int (*power)(const struct gpio_event_platform_data *pdata, bool on);
+ const char *names[]; /* If name is NULL, names contain a NULL */
+ /* terminated list of input devices to create */
+};
+
+#define GPIO_EVENT_DEV_NAME "gpio-event"
+
+/* Key matrix */
+
+enum gpio_event_matrix_flags {
+ /* unset: drive active output low, set: drive active output high */
+ GPIOKPF_ACTIVE_HIGH = 1U << 0,
+ GPIOKPF_DEBOUNCE = 1U << 1,
+ GPIOKPF_REMOVE_SOME_PHANTOM_KEYS = 1U << 2,
+ GPIOKPF_REMOVE_PHANTOM_KEYS = GPIOKPF_REMOVE_SOME_PHANTOM_KEYS |
+ GPIOKPF_DEBOUNCE,
+ GPIOKPF_DRIVE_INACTIVE = 1U << 3,
+ GPIOKPF_LEVEL_TRIGGERED_IRQ = 1U << 4,
+ GPIOKPF_PRINT_UNMAPPED_KEYS = 1U << 16,
+ GPIOKPF_PRINT_MAPPED_KEYS = 1U << 17,
+ GPIOKPF_PRINT_PHANTOM_KEYS = 1U << 18,
+};
+
+#define MATRIX_CODE_BITS (10)
+#define MATRIX_KEY_MASK ((1U << MATRIX_CODE_BITS) - 1)
+#define MATRIX_KEY(dev, code) \
+ (((dev) << MATRIX_CODE_BITS) | (code & MATRIX_KEY_MASK))
+
+extern int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func);
+struct gpio_event_matrix_info {
+ /* initialize to gpio_event_matrix_func */
+ struct gpio_event_info info;
+ /* size must be ninputs * noutputs */
+ const unsigned short *keymap;
+ unsigned int *input_gpios;
+ unsigned int *output_gpios;
+ unsigned int ninputs;
+ unsigned int noutputs;
+ /* time to wait before reading inputs after driving each output */
+ ktime_t settle_time;
+ /* time to wait before scanning the keypad a second time */
+ ktime_t debounce_delay;
+ ktime_t poll_time;
+ unsigned flags;
+};
+
+/* Directly connected inputs and outputs */
+
+enum gpio_event_direct_flags {
+ GPIOEDF_ACTIVE_HIGH = 1U << 0,
+/* GPIOEDF_USE_DOWN_IRQ = 1U << 1, */
+/* GPIOEDF_USE_IRQ = (1U << 2) | GPIOIDF_USE_DOWN_IRQ, */
+ GPIOEDF_PRINT_KEYS = 1U << 8,
+ GPIOEDF_PRINT_KEY_DEBOUNCE = 1U << 9,
+ GPIOEDF_PRINT_KEY_UNSTABLE = 1U << 10,
+};
+
+struct gpio_event_direct_entry {
+ uint32_t gpio:16;
+ uint32_t code:10;
+ uint32_t dev:6;
+};
+
+/* inputs */
+extern int gpio_event_input_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func);
+struct gpio_event_input_info {
+ /* initialize to gpio_event_input_func */
+ struct gpio_event_info info;
+ ktime_t debounce_time;
+ ktime_t poll_time;
+ uint16_t flags;
+ uint16_t type;
+ const struct gpio_event_direct_entry *keymap;
+ size_t keymap_size;
+};
+
+/* outputs */
+extern int gpio_event_output_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func);
+extern int gpio_event_output_event(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data,
+ unsigned int dev, unsigned int type,
+ unsigned int code, int value);
+struct gpio_event_output_info {
+ /* initialize to gpio_event_output_func and gpio_event_output_event */
+ struct gpio_event_info info;
+ uint16_t flags;
+ uint16_t type;
+ const struct gpio_event_direct_entry *keymap;
+ size_t keymap_size;
+};
+
+
+/* axes */
+
+enum gpio_event_axis_flags {
+ GPIOEAF_PRINT_UNKNOWN_DIRECTION = 1U << 16,
+ GPIOEAF_PRINT_RAW = 1U << 17,
+ GPIOEAF_PRINT_EVENT = 1U << 18,
+};
+
+extern int gpio_event_axis_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func);
+struct gpio_event_axis_info {
+ /* initialize to gpio_event_axis_func */
+ struct gpio_event_info info;
+ uint8_t count; /* number of gpios for this axis */
+ uint8_t dev; /* device index when using multiple input devices */
+ uint8_t type; /* EV_REL or EV_ABS */
+ uint16_t code;
+ uint16_t decoded_size;
+ uint16_t (*map)(struct gpio_event_axis_info *info, uint16_t in);
+ uint32_t *gpio;
+ uint32_t flags;
+};
+#define gpio_axis_2bit_gray_map gpio_axis_4bit_gray_map
+#define gpio_axis_3bit_gray_map gpio_axis_4bit_gray_map
+uint16_t gpio_axis_4bit_gray_map(
+ struct gpio_event_axis_info *info, uint16_t in);
+uint16_t gpio_axis_5bit_singletrack_map(
+ struct gpio_event_axis_info *info, uint16_t in);
+
+#endif
diff --git a/include/linux/hid.h b/include/linux/hid.h
index f53c4a9cca1d..edf10bcf22aa 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -667,8 +667,8 @@ struct hid_driver {
int (*input_mapped)(struct hid_device *hdev,
struct hid_input *hidinput, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max);
- void (*input_configured)(struct hid_device *hdev,
- struct hid_input *hidinput);
+ int (*input_configured)(struct hid_device *hdev,
+ struct hid_input *hidinput);
void (*feature_mapping)(struct hid_device *hdev,
struct hid_field *field,
struct hid_usage *usage);
diff --git a/include/linux/if_pppolac.h b/include/linux/if_pppolac.h
new file mode 100644
index 000000000000..e40aa1075a30
--- /dev/null
+++ b/include/linux/if_pppolac.h
@@ -0,0 +1,23 @@
+/* include/linux/if_pppolac.h
+ *
+ * Header for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661)
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Chia-chi Yeh <chiachi@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_IF_PPPOLAC_H
+#define __LINUX_IF_PPPOLAC_H
+
+#include <uapi/linux/if_pppolac.h>
+
+#endif /* __LINUX_IF_PPPOLAC_H */
diff --git a/include/linux/if_pppopns.h b/include/linux/if_pppopns.h
new file mode 100644
index 000000000000..4ac621a9ce7c
--- /dev/null
+++ b/include/linux/if_pppopns.h
@@ -0,0 +1,23 @@
+/* include/linux/if_pppopns.h
+ *
+ * Header for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637)
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Chia-chi Yeh <chiachi@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_IF_PPPOPNS_H
+#define __LINUX_IF_PPPOPNS_H
+
+#include <uapi/linux/if_pppopns.h>
+
+#endif /* __LINUX_IF_PPPOPNS_H */
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index aff7ad8a4ea3..dccd621d6377 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -41,6 +41,25 @@ struct pptp_opt {
u32 seq_sent, seq_recv;
int ppp_flags;
};
+
+struct pppolac_opt {
+ __u32 local;
+ __u32 remote;
+ __u32 recv_sequence;
+ __u32 xmit_sequence;
+ atomic_t sequencing;
+ int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb);
+};
+
+struct pppopns_opt {
+ __u16 local;
+ __u16 remote;
+ __u32 recv_sequence;
+ __u32 xmit_sequence;
+ void (*data_ready)(struct sock *sk_raw, int length);
+ int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb);
+};
+
#include <net/sock.h>
struct pppox_sock {
@@ -51,6 +70,8 @@ struct pppox_sock {
union {
struct pppoe_opt pppoe;
struct pptp_opt pptp;
+ struct pppolac_opt lac;
+ struct pppopns_opt pns;
} proto;
__be16 num;
};
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 95624bed87ef..bb38a26d2d3a 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -838,6 +838,9 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
# define REBUILD_DUE_TO_FTRACE_MCOUNT_RECORD
#endif
+/* To identify board information in panic logs, set this */
+extern char *mach_panic_string;
+
/* Permissions on a sysfs file: you didn't miss the 0 prefix did you? */
#define VERIFY_OCTAL_PERMISSIONS(perms) \
(BUILD_BUG_ON_ZERO((perms) < 0) + \
diff --git a/include/linux/keychord.h b/include/linux/keychord.h
new file mode 100644
index 000000000000..08cf5402102c
--- /dev/null
+++ b/include/linux/keychord.h
@@ -0,0 +1,23 @@
+/*
+ * Key chord input driver
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+*/
+
+#ifndef __LINUX_KEYCHORD_H_
+#define __LINUX_KEYCHORD_H_
+
+#include <uapi/linux/keychord.h>
+
+#endif /* __LINUX_KEYCHORD_H_ */
diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h
new file mode 100644
index 000000000000..a2ac49e5b684
--- /dev/null
+++ b/include/linux/keyreset.h
@@ -0,0 +1,28 @@
+/*
+ * include/linux/keyreset.h - platform data structure for resetkeys driver
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_KEYRESET_H
+#define _LINUX_KEYRESET_H
+
+#define KEYRESET_NAME "keyreset"
+
+struct keyreset_platform_data {
+ int (*reset_fn)(void);
+ int *keys_up;
+ int keys_down[]; /* 0 terminated */
+};
+
+#endif /* _LINUX_KEYRESET_H */
diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h
new file mode 100644
index 000000000000..307d9cab2026
--- /dev/null
+++ b/include/linux/mailbox_client.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013-2014 Linaro Ltd.
+ * Author: Jassi Brar <jassisinghbrar@gmail.com>
+ *
+ * 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 __MAILBOX_CLIENT_H
+#define __MAILBOX_CLIENT_H
+
+#include <linux/of.h>
+#include <linux/device.h>
+
+struct mbox_chan;
+
+/**
+ * struct mbox_client - User of a mailbox
+ * @dev: The client device
+ * @tx_block: If the mbox_send_message should block until data is
+ * transmitted.
+ * @tx_tout: Max block period in ms before TX is assumed failure
+ * @knows_txdone: If the client could run the TX state machine. Usually
+ * if the client receives some ACK packet for transmission.
+ * Unused if the controller already has TX_Done/RTR IRQ.
+ * @rx_callback: Atomic callback to provide client the data received
+ * @tx_done: Atomic callback to tell client of data transmission
+ */
+struct mbox_client {
+ struct device *dev;
+ bool tx_block;
+ unsigned long tx_tout;
+ bool knows_txdone;
+
+ void (*rx_callback)(struct mbox_client *cl, void *mssg);
+ void (*tx_done)(struct mbox_client *cl, void *mssg, int r);
+};
+
+struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);
+int mbox_send_message(struct mbox_chan *chan, void *mssg);
+void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */
+bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */
+void mbox_free_channel(struct mbox_chan *chan); /* may sleep */
+
+#endif /* __MAILBOX_CLIENT_H */
diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
new file mode 100644
index 000000000000..9ee195b02444
--- /dev/null
+++ b/include/linux/mailbox_controller.h
@@ -0,0 +1,135 @@
+/*
+ * 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 __MAILBOX_CONTROLLER_H
+#define __MAILBOX_CONTROLLER_H
+
+#include <linux/of.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/device.h>
+#include <linux/completion.h>
+
+struct mbox_chan;
+
+/**
+ * struct mbox_chan_ops - methods to control mailbox channels
+ * @send_data: The API asks the MBOX controller driver, in atomic
+ * context try to transmit a message on the bus. Returns 0 if
+ * data is accepted for transmission, -EBUSY while rejecting
+ * if the remote hasn't yet read the last data sent. Actual
+ * transmission of data is reported by the controller via
+ * mbox_chan_txdone (if it has some TX ACK irq). It must not
+ * sleep.
+ * @startup: Called when a client requests the chan. The controller
+ * could ask clients for additional parameters of communication
+ * to be provided via client's chan_data. This call may
+ * block. After this call the Controller must forward any
+ * data received on the chan by calling mbox_chan_received_data.
+ * The controller may do stuff that need to sleep.
+ * @shutdown: Called when a client relinquishes control of a chan.
+ * This call may block too. The controller must not forward
+ * any received data anymore.
+ * The controller may do stuff that need to sleep.
+ * @last_tx_done: If the controller sets 'txdone_poll', the API calls
+ * this to poll status of last TX. The controller must
+ * give priority to IRQ method over polling and never
+ * set both txdone_poll and txdone_irq. Only in polling
+ * mode 'send_data' is expected to return -EBUSY.
+ * The controller may do stuff that need to sleep/block.
+ * Used only if txdone_poll:=true && txdone_irq:=false
+ * @peek_data: Atomic check for any received data. Return true if controller
+ * has some data to push to the client. False otherwise.
+ */
+struct mbox_chan_ops {
+ int (*send_data)(struct mbox_chan *chan, void *data);
+ int (*startup)(struct mbox_chan *chan);
+ void (*shutdown)(struct mbox_chan *chan);
+ bool (*last_tx_done)(struct mbox_chan *chan);
+ bool (*peek_data)(struct mbox_chan *chan);
+};
+
+/**
+ * struct mbox_controller - Controller of a class of communication channels
+ * @dev: Device backing this controller
+ * @ops: Operators that work on each communication chan
+ * @chans: Array of channels
+ * @num_chans: Number of channels in the 'chans' array.
+ * @txdone_irq: Indicates if the controller can report to API when
+ * the last transmitted data was read by the remote.
+ * Eg, if it has some TX ACK irq.
+ * @txdone_poll: If the controller can read but not report the TX
+ * done. Ex, some register shows the TX status but
+ * no interrupt rises. Ignored if 'txdone_irq' is set.
+ * @txpoll_period: If 'txdone_poll' is in effect, the API polls for
+ * last TX's status after these many millisecs
+ * @of_xlate: Controller driver specific mapping of channel via DT
+ * @poll: API private. Used to poll for TXDONE on all channels.
+ * @period: API private. Polling period.
+ * @node: API private. To hook into list of controllers.
+ */
+struct mbox_controller {
+ struct device *dev;
+ struct mbox_chan_ops *ops;
+ struct mbox_chan *chans;
+ int num_chans;
+ bool txdone_irq;
+ bool txdone_poll;
+ unsigned txpoll_period;
+ struct mbox_chan *(*of_xlate)(struct mbox_controller *mbox,
+ const struct of_phandle_args *sp);
+ /* Internal to API */
+ struct timer_list poll;
+ unsigned period;
+ struct list_head node;
+};
+
+/*
+ * The length of circular buffer for queuing messages from a client.
+ * 'msg_count' tracks the number of buffered messages while 'msg_free'
+ * is the index where the next message would be buffered.
+ * We shouldn't need it too big because every transfer is interrupt
+ * triggered and if we have lots of data to transfer, the interrupt
+ * latencies are going to be the bottleneck, not the buffer length.
+ * Besides, mbox_send_message could be called from atomic context and
+ * the client could also queue another message from the notifier 'tx_done'
+ * of the last transfer done.
+ * REVISIT: If too many platforms see the "Try increasing MBOX_TX_QUEUE_LEN"
+ * print, it needs to be taken from config option or somesuch.
+ */
+#define MBOX_TX_QUEUE_LEN 20
+
+/**
+ * struct mbox_chan - s/w representation of a communication chan
+ * @mbox: Pointer to the parent/provider of this channel
+ * @txdone_method: Way to detect TXDone chosen by the API
+ * @cl: Pointer to the current owner of this channel
+ * @tx_complete: Transmission completion
+ * @active_req: Currently active request hook
+ * @msg_count: No. of mssg currently queued
+ * @msg_free: Index of next available mssg slot
+ * @msg_data: Hook for data packet
+ * @lock: Serialise access to the channel
+ * @con_priv: Hook for controller driver to attach private data
+ */
+struct mbox_chan {
+ struct mbox_controller *mbox;
+ unsigned txdone_method;
+ struct mbox_client *cl;
+ struct completion tx_complete;
+ void *active_req;
+ unsigned msg_count, msg_free;
+ void *msg_data[MBOX_TX_QUEUE_LEN];
+ spinlock_t lock; /* Serialise access to the channel */
+ void *con_priv;
+};
+
+int mbox_controller_register(struct mbox_controller *mbox); /* can sleep */
+void mbox_controller_unregister(struct mbox_controller *mbox); /* can sleep */
+void mbox_chan_received_data(struct mbox_chan *chan, void *data); /* atomic */
+void mbox_chan_txdone(struct mbox_chan *chan, int r); /* atomic */
+
+#endif /* __MAILBOX_CONTROLLER_H */
diff --git a/include/linux/mfd/hi6421-pmic.h b/include/linux/mfd/hi6421-pmic.h
new file mode 100644
index 000000000000..0b60a9b2a666
--- /dev/null
+++ b/include/linux/mfd/hi6421-pmic.h
@@ -0,0 +1,99 @@
+/*
+ * Header file for device driver Hi6421 PMIC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (C) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * 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 __HI6421_PMIC_H
+#define __HI6421_PMIC_H
+
+#include <linux/irqdomain.h>
+#include <linux/mutex.h>
+#include <linux/time.h>
+
+#define OCP_DEB_CTRL_REG (0x51)
+#define OCP_DEB_SEL_MASK (0x0C)
+#define OCP_DEB_SEL_8MS (0x00)
+#define OCP_DEB_SEL_16MS (0x04)
+#define OCP_DEB_SEL_32MS (0x08)
+#define OCP_DEB_SEL_64MS (0x0C)
+#define OCP_EN_DEBOUNCE_MASK (0x02)
+#define OCP_EN_DEBOUNCE_ENABLE (0x02)
+#define OCP_AUTO_STOP_MASK (0x01)
+#define OCP_AUTO_STOP_ENABLE (0x01)
+#define HI6421_REGS_ENA_PROTECT_TIME (100) /* in microseconds */
+#define HI6421_ECO_MODE_ENABLE (1)
+#define HI6421_ECO_MODE_DISABLE (0)
+
+#define HI6421_NR_IRQ 24
+#define HI6421_MASK_FIELD 0xFF
+#define HI6421_BITS 8
+
+#define HI6421_IRQ_ALARM 0 /* RTC Alarm */
+#define HI6421_IRQ_OTMP 1 /* Temperature too high */
+#define HI6421_IRQ_OCP 2 /* BUCK/LDO overload */
+#define HI6421_IRQ_RESET_IN 3 /* RESETIN_N signal */
+#define HI6421_IRQ_ONKEY_10S 4 /* PWRON key hold for 10s */
+#define HI6421_IRQ_ONKEY_1S 5 /* PWRON key hold for 1s */
+#define HI6421_IRQ_ONKEY_UP 6 /* PWRON key released */
+#define HI6421_IRQ_ONKEY_DOWN 7 /* PWRON key pressed */
+#define HI6421_IRQ_HEADSET_OUT 8 /* Headset plugged out */
+#define HI6421_IRQ_HEADSET_IN 9 /* Headset plugged in */
+#define HI6421_IRQ_COULOMB 10 /* Coulomb Counter */
+#define HI6421_IRQ_VBUS_UP 11 /* VBUS rising */
+#define HI6421_IRQ_VBUS_DOWN 12 /* VBUS falling */
+#define HI6421_IRQ_VBAT_LOW 13 /* VBattery too low */
+#define HI6421_IRQ_VBAT_HIGH 14 /* VBattery overvoltage */
+#define HI6421_IRQ_CHARGE_IN1 15 /* Charger plugged in */
+#define HI6421_IRQ_CHARGE_IN3 16 /* Charger plugged in */
+#define HI6421_IRQ_CHARGE_IN2 17 /* Charger plugged in */
+#define HI6421_IRQ_HS_BTN_DOWN 18 /* Headset button pressed */
+#define HI6421_IRQ_HS_BTN_UP 19 /* Headset button released */
+#define HI6421_IRQ_BATTERY_ON 20 /* Battery inserted */
+#define HI6421_IRQ_RESET 21 /* Reset */
+
+/**
+ * struct hi6421_pmic - Hi6421 PMIC chip-level data structure.
+ *
+ * This struct describes Hi6421 PMIC chip-level data.
+ *
+ * @enable_mutex: Used by regulators, to make sure that only one regulator is
+ * in the turning-on phase at any time. See also @last_enabled
+ * and hi6421_regulator_enable().
+ * @last_enabled: Used by regulators, to make sure enough distance in time
+ * between enabling of any of two regulators.
+ */
+struct hi6421_pmic {
+ struct resource *res;
+ struct device *dev;
+ void __iomem *regs;
+ spinlock_t lock;
+ struct irq_domain *domain;
+ int irq;
+ int gpio;
+ struct mutex enable_mutex;
+ struct timeval last_enabled;
+};
+
+/* Register Access Helpers */
+u32 hi6421_pmic_read(struct hi6421_pmic *pmic, int reg);
+void hi6421_pmic_write(struct hi6421_pmic *pmic, int reg, u32 val);
+void hi6421_pmic_rmw(struct hi6421_pmic *pmic, int reg, u32 mask, u32 bits);
+
+#endif /* __HI6421_PMIC_H */
diff --git a/include/linux/mfd/pm8921-core.h b/include/linux/mfd/pm8921-core.h
new file mode 100644
index 000000000000..77f7cb5a4d68
--- /dev/null
+++ b/include/linux/mfd/pm8921-core.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014, Sony Mobile Communications AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef __MFD_PM8921_CORE_H
+#define __MFD_PM8921_CORE_H
+
+#include <linux/err.h>
+
+#ifdef CONFIG_MFD_PM8921_CORE
+
+int pm8xxx_read_irq_status(int irq);
+
+#else
+
+static inline int pm8xxx_read_irq_status(int irq)
+{
+ return -ENOSYS;
+}
+
+#endif
+
+#endif
diff --git a/include/linux/mfd/qcom_rpm.h b/include/linux/mfd/qcom_rpm.h
new file mode 100644
index 000000000000..a52bc377dfa4
--- /dev/null
+++ b/include/linux/mfd/qcom_rpm.h
@@ -0,0 +1,12 @@
+#ifndef __QCOM_RPM_H__
+#define __QCOM_RPM_H__
+
+#include <linux/types.h>
+
+struct device;
+struct qcom_rpm;
+
+struct qcom_rpm *dev_get_qcom_rpm(struct device *dev);
+int qcom_rpm_write(struct qcom_rpm *rpm, int resource, u32 *buf, size_t count);
+
+#endif
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 8981cc882ed2..9f5f1cec6a63 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1064,6 +1064,7 @@ extern void pagefault_out_of_memory(void);
extern void show_free_areas(unsigned int flags);
extern bool skip_free_areas_node(unsigned int flags, int nid);
+void shmem_set_file(struct vm_area_struct *vma, struct file *file);
int shmem_zero_setup(struct vm_area_struct *);
#ifdef CONFIG_SHMEM
bool shmem_mapping(struct address_space *mapping);
@@ -1767,7 +1768,7 @@ extern int vma_adjust(struct vm_area_struct *vma, unsigned long start,
extern struct vm_area_struct *vma_merge(struct mm_struct *,
struct vm_area_struct *prev, unsigned long addr, unsigned long end,
unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t,
- struct mempolicy *);
+ struct mempolicy *, const char __user *);
extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *);
extern int split_vma(struct mm_struct *,
struct vm_area_struct *, unsigned long addr, int new_below);
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 6e0b286649f1..d64c194ce776 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -274,6 +274,10 @@ struct vm_area_struct {
* For areas with an address space and backing store,
* linkage into the address_space->i_mmap interval tree, or
* linkage of vma in the address_space->i_mmap_nonlinear list.
+ *
+ * For private anonymous mappings, a pointer to a null terminated string
+ * in the user process containing the name given to the vma, or NULL
+ * if unnamed.
*/
union {
struct {
@@ -281,6 +285,7 @@ struct vm_area_struct {
unsigned long rb_subtree_last;
} linear;
struct list_head nonlinear;
+ const char __user *anon_name;
} shared;
/*
@@ -511,6 +516,15 @@ static inline void clear_tlb_flush_pending(struct mm_struct *mm)
}
#endif
+/* Return the name for an anonymous mapping or NULL for a file-backed mapping */
+static inline const char __user *vma_get_anon_name(struct vm_area_struct *vma)
+{
+ if (vma->vm_file)
+ return NULL;
+
+ return vma->shared.anon_name;
+}
+
struct vm_special_mapping
{
const char *name;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 7960424d0bc0..327e69039a03 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -365,6 +365,15 @@ struct mmc_host {
unsigned int slotno; /* used for sdio acpi binding */
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ struct {
+ struct sdio_cis *cis;
+ struct sdio_cccr *cccr;
+ struct sdio_embedded_func *funcs;
+ int num_funcs;
+ } embedded_sdio_data;
+#endif
+
unsigned long private[0] ____cacheline_aligned;
};
@@ -374,6 +383,14 @@ void mmc_remove_host(struct mmc_host *);
void mmc_free_host(struct mmc_host *);
int mmc_of_parse(struct mmc_host *host);
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+extern void mmc_set_embedded_sdio_data(struct mmc_host *host,
+ struct sdio_cis *cis,
+ struct sdio_cccr *cccr,
+ struct sdio_embedded_func *funcs,
+ int num_funcs);
+#endif
+
static inline void *mmc_priv(struct mmc_host *host)
{
return (void *)host->private;
diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h
index 4a139204c20c..6e2d6a135c7e 100644
--- a/include/linux/mmc/pm.h
+++ b/include/linux/mmc/pm.h
@@ -26,5 +26,6 @@ typedef unsigned int mmc_pm_flag_t;
#define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */
#define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */
+#define MMC_PM_IGNORE_PM_NOTIFY (1 << 2) /* ignore mmc pm notify */
#endif /* LINUX_MMC_PM_H */
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index 50f0bc952328..dc680c4b50d4 100644..100755
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -23,6 +23,14 @@ struct sdio_func;
typedef void (sdio_irq_handler_t)(struct sdio_func *);
/*
+ * Structure used to hold embedded SDIO device data from platform layer
+ */
+struct sdio_embedded_func {
+ uint8_t f_class;
+ uint32_t f_maxblksize;
+};
+
+/*
* SDIO function CIS tuple (unknown to the core)
*/
struct sdio_func_tuple {
@@ -130,6 +138,8 @@ extern int sdio_release_irq(struct sdio_func *func);
extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz);
extern u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret);
+extern u8 sdio_readb_ext(struct sdio_func *func, unsigned int addr, int *err_ret,
+ unsigned in);
extern u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret);
extern u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret);
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h
index 5f487d776411..21732b5bc666 100644
--- a/include/linux/mtd/map.h
+++ b/include/linux/mtd/map.h
@@ -397,11 +397,11 @@ static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
map_word r;
if (map_bankwidth_is_1(map))
- r.x[0] = __raw_readb(map->virt + ofs);
+ r.x[0] = readb_relaxed(map->virt + ofs);
else if (map_bankwidth_is_2(map))
- r.x[0] = __raw_readw(map->virt + ofs);
+ r.x[0] = readw_relaxed(map->virt + ofs);
else if (map_bankwidth_is_4(map))
- r.x[0] = __raw_readl(map->virt + ofs);
+ r.x[0] = readl_relaxed(map->virt + ofs);
#if BITS_PER_LONG >= 64
else if (map_bankwidth_is_8(map))
r.x[0] = __raw_readq(map->virt + ofs);
@@ -417,11 +417,11 @@ static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
{
if (map_bankwidth_is_1(map))
- __raw_writeb(datum.x[0], map->virt + ofs);
+ writeb_relaxed(datum.x[0], map->virt + ofs);
else if (map_bankwidth_is_2(map))
- __raw_writew(datum.x[0], map->virt + ofs);
+ writew_relaxed(datum.x[0], map->virt + ofs);
else if (map_bankwidth_is_4(map))
- __raw_writel(datum.x[0], map->virt + ofs);
+ writel_relaxed(datum.x[0], map->virt + ofs);
#if BITS_PER_LONG >= 64
else if (map_bankwidth_is_8(map))
__raw_writeq(datum.x[0], map->virt + ofs);
diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h
new file mode 100644
index 000000000000..ca60fbdec2f3
--- /dev/null
+++ b/include/linux/netfilter/xt_qtaguid.h
@@ -0,0 +1,13 @@
+#ifndef _XT_QTAGUID_MATCH_H
+#define _XT_QTAGUID_MATCH_H
+
+/* For now we just replace the xt_owner.
+ * FIXME: make iptables aware of qtaguid. */
+#include <linux/netfilter/xt_owner.h>
+
+#define XT_QTAGUID_UID XT_OWNER_UID
+#define XT_QTAGUID_GID XT_OWNER_GID
+#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET
+#define xt_qtaguid_match_info xt_owner_match_info
+
+#endif /* _XT_QTAGUID_MATCH_H */
diff --git a/include/linux/netfilter/xt_quota2.h b/include/linux/netfilter/xt_quota2.h
new file mode 100644
index 000000000000..eadc6903314e
--- /dev/null
+++ b/include/linux/netfilter/xt_quota2.h
@@ -0,0 +1,25 @@
+#ifndef _XT_QUOTA_H
+#define _XT_QUOTA_H
+
+enum xt_quota_flags {
+ XT_QUOTA_INVERT = 1 << 0,
+ XT_QUOTA_GROW = 1 << 1,
+ XT_QUOTA_PACKET = 1 << 2,
+ XT_QUOTA_NO_CHANGE = 1 << 3,
+ XT_QUOTA_MASK = 0x0F,
+};
+
+struct xt_quota_counter;
+
+struct xt_quota_mtinfo2 {
+ char name[15];
+ u_int8_t flags;
+
+ /* Comparison-invariant */
+ aligned_u64 quota;
+
+ /* Used internally by the kernel */
+ struct xt_quota_counter *master __attribute__((aligned(8)));
+};
+
+#endif /* _XT_QUOTA_H */
diff --git a/include/linux/nmi.h b/include/linux/nmi.h
index 1d2a6ab6b8bb..879c680b1a5b 100644
--- a/include/linux/nmi.h
+++ b/include/linux/nmi.h
@@ -14,8 +14,11 @@
* may be used to reset the timeout - for code which intentionally
* disables interrupts for a long time. This call is stateless.
*/
-#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR)
+#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR_NMI)
#include <asm/nmi.h>
+#endif
+
+#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR)
extern void touch_nmi_watchdog(void);
#else
static inline void touch_nmi_watchdog(void)
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index fb7b7221e063..cb9479e423e3 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -23,17 +23,8 @@ struct of_pci_range {
#define for_each_of_pci_range(parser, range) \
for (; of_pci_range_parser_one(parser, range);)
-static inline void of_pci_range_to_resource(struct of_pci_range *range,
- struct device_node *np,
- struct resource *res)
-{
- res->flags = range->flags;
- res->start = range->cpu_addr;
- res->end = range->cpu_addr + range->size - 1;
- res->parent = res->child = res->sibling = NULL;
- res->name = np->full_name;
-}
-
+extern int of_pci_range_to_resource(struct of_pci_range *range,
+ struct device_node *np, struct resource *res);
/* Translate a DMA address from device space to CPU space */
extern u64 of_translate_dma_address(struct device_node *dev,
const __be32 *in_addr);
@@ -55,6 +46,7 @@ extern void __iomem *of_iomap(struct device_node *device, int index);
extern const __be32 *of_get_address(struct device_node *dev, int index,
u64 *size, unsigned int *flags);
+extern int pci_register_io_range(phys_addr_t addr, resource_size_t size);
extern unsigned long pci_address_to_pio(phys_addr_t addr);
extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
diff --git a/include/linux/mailbox.h b/include/linux/pl320-ipc.h
index 5161f63ec1c8..5161f63ec1c8 100644
--- a/include/linux/mailbox.h
+++ b/include/linux/pl320-ipc.h
diff --git a/include/linux/platform_data/ds2482.h b/include/linux/platform_data/ds2482.h
new file mode 100644
index 000000000000..5a6879e2a09a
--- /dev/null
+++ b/include/linux/platform_data/ds2482.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __PLATFORM_DATA_DS2482__
+#define __PLATFORM_DATA_DS2482__
+
+struct ds2482_platform_data {
+ int slpz_gpio;
+};
+
+#endif /* __PLATFORM_DATA_DS2482__ */
diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h
index 660c029d694f..16ec262dfcc8 100644
--- a/include/linux/platform_data/mtd-nand-omap2.h
+++ b/include/linux/platform_data/mtd-nand-omap2.h
@@ -21,8 +21,17 @@ enum nand_io {
};
enum omap_ecc {
- /* 1-bit ECC calculation by GPMC, Error detection by Software */
- OMAP_ECC_HAM1_CODE_HW = 0,
+ /*
+ * 1-bit ECC: calculation and correction by SW
+ * ECC stored at end of spare area
+ */
+ OMAP_ECC_HAM1_CODE_SW = 0,
+
+ /*
+ * 1-bit ECC: calculation by GPMC, Error detection by Software
+ * ECC layout compatible with ROM code layout
+ */
+ OMAP_ECC_HAM1_CODE_HW,
/* 4-bit ECC calculation by GPMC, Error detection by Software */
OMAP_ECC_BCH4_CODE_HW_DETECTION_SW,
/* 4-bit ECC calculation by GPMC, Error detection by ELM */
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index f3dea41dbcd2..21ead5cd5cb0 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -146,6 +146,10 @@ enum power_supply_property {
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+ /* Local extensions */
+ POWER_SUPPLY_PROP_USB_HC,
+ POWER_SUPPLY_PROP_USB_OTG,
+ POWER_SUPPLY_PROP_CHARGE_ENABLED,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 5c2c885ee52b..f69367982080 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1878,6 +1878,9 @@ static inline cputime_t task_gtime(struct task_struct *t)
extern void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st);
extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st);
+extern int task_free_register(struct notifier_block *n);
+extern int task_free_unregister(struct notifier_block *n);
+
/*
* Per process flags
*/
diff --git a/include/linux/scpi_protocol.h b/include/linux/scpi_protocol.h
new file mode 100644
index 000000000000..66e5eb3710ab
--- /dev/null
+++ b/include/linux/scpi_protocol.h
@@ -0,0 +1,30 @@
+/*
+ * SCPI Message Protocol driver header
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/types.h>
+
+struct scpi_opp {
+ u32 *freqs;
+ u32 latency; /* in usecs */
+ int count;
+};
+
+unsigned long scpi_clk_get_val(u16 clk_id);
+int scpi_clk_set_val(u16 clk_id, unsigned long rate);
+int scpi_dvfs_get_idx(u8 domain);
+int scpi_dvfs_set_idx(u8 domain, u8 idx);
+struct scpi_opp *scpi_dvfs_get_opps(u8 domain);
diff --git a/include/linux/security.h b/include/linux/security.h
index 623f90e5f38d..089f0abeb3b4 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1441,6 +1441,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
struct security_operations {
char name[SECURITY_NAME_MAX + 1];
+ int (*binder_set_context_mgr) (struct task_struct *mgr);
+ int (*binder_transaction) (struct task_struct *from, struct task_struct *to);
+ int (*binder_transfer_binder) (struct task_struct *from, struct task_struct *to);
+ int (*binder_transfer_file) (struct task_struct *from, struct task_struct *to, struct file *file);
+
int (*ptrace_access_check) (struct task_struct *child, unsigned int mode);
int (*ptrace_traceme) (struct task_struct *parent);
int (*capget) (struct task_struct *target,
@@ -1739,6 +1744,10 @@ extern void __init security_fixup_ops(struct security_operations *ops);
/* Security operations */
+int security_binder_set_context_mgr(struct task_struct *mgr);
+int security_binder_transaction(struct task_struct *from, struct task_struct *to);
+int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to);
+int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file);
int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
int security_ptrace_traceme(struct task_struct *parent);
int security_capget(struct task_struct *target,
@@ -1927,6 +1936,26 @@ static inline int security_init(void)
return 0;
}
+static inline int security_binder_set_context_mgr(struct task_struct *mgr)
+{
+ return 0;
+}
+
+static inline int security_binder_transaction(struct task_struct *from, struct task_struct *to)
+{
+ return 0;
+}
+
+static inline int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to)
+{
+ return 0;
+}
+
+static inline int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file)
+{
+ return 0;
+}
+
static inline int security_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index cf3a1e789bf5..9238fc29b95a 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -66,6 +66,7 @@ struct uart_ops {
void (*set_ldisc)(struct uart_port *, int new);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
+ void (*wake_peer)(struct uart_port *);
/*
* Return a string describing the type of the port
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index e713543336f1..46d188a9947c 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -253,6 +253,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* the device whose settings are being modified.
* @transfer: adds a message to the controller's transfer queue.
* @cleanup: frees controller-specific state
+ * @can_dma: determine whether this master supports DMA
* @queued: whether this master is providing an internal message queue
* @kworker: thread struct for message pump
* @kworker_task: pointer to task for message pump kworker thread
@@ -262,6 +263,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @cur_msg: the currently in-flight message
* @cur_msg_prepared: spi_prepare_message was called for the currently
* in-flight message
+ * @cur_msg_mapped: message has been mapped for DMA
* @xfer_completion: used by core transfer_one_message()
* @busy: message pump is busy
* @running: message pump is running
@@ -299,6 +301,10 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
* number. Any individual value may be -ENOENT for CS lines that
* are not GPIOs (driven by the SPI controller itself).
+ * @dma_tx: DMA transmit channel
+ * @dma_rx: DMA receive channel
+ * @dummy_rx: dummy receive buffer for full-duplex devices
+ * @dummy_tx: dummy transmit buffer for full-duplex devices
*
* Each SPI master controller can communicate with one or more @spi_device
* children. These make a small bus, sharing MOSI, MISO and SCK signals
@@ -632,6 +638,7 @@ struct spi_transfer {
* addresses for each transfer buffer
* @complete: called to report transaction completions
* @context: the argument to complete() when it's called
+ * @frame_length: the total number of bytes in the message
* @actual_length: the total number of bytes that were transferred in all
* successful segments
* @status: zero for success, else negative errno
diff --git a/include/linux/switch.h b/include/linux/switch.h
new file mode 100644
index 000000000000..3e4c748e343a
--- /dev/null
+++ b/include/linux/switch.h
@@ -0,0 +1,53 @@
+/*
+ * Switch class driver
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+*/
+
+#ifndef __LINUX_SWITCH_H__
+#define __LINUX_SWITCH_H__
+
+struct switch_dev {
+ const char *name;
+ struct device *dev;
+ int index;
+ int state;
+
+ ssize_t (*print_name)(struct switch_dev *sdev, char *buf);
+ ssize_t (*print_state)(struct switch_dev *sdev, char *buf);
+};
+
+struct gpio_switch_platform_data {
+ const char *name;
+ unsigned gpio;
+
+ /* if NULL, switch_dev.name will be printed */
+ const char *name_on;
+ const char *name_off;
+ /* if NULL, "0" or "1" will be printed */
+ const char *state_on;
+ const char *state_off;
+};
+
+extern int switch_dev_register(struct switch_dev *sdev);
+extern void switch_dev_unregister(struct switch_dev *sdev);
+
+static inline int switch_get_state(struct switch_dev *sdev)
+{
+ return sdev->state;
+}
+
+extern void switch_set_state(struct switch_dev *sdev, int state);
+
+#endif /* __LINUX_SWITCH_H__ */
diff --git a/include/linux/uid_stat.h b/include/linux/uid_stat.h
new file mode 100644
index 000000000000..6bd6c4e52d17
--- /dev/null
+++ b/include/linux/uid_stat.h
@@ -0,0 +1,29 @@
+/* include/linux/uid_stat.h
+ *
+ * Copyright (C) 2008-2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __uid_stat_h
+#define __uid_stat_h
+
+/* Contains definitions for resource tracking per uid. */
+
+#ifdef CONFIG_UID_STAT
+int uid_stat_tcp_snd(uid_t uid, int size);
+int uid_stat_tcp_rcv(uid_t uid, int size);
+#else
+#define uid_stat_tcp_snd(uid, size) do {} while (0);
+#define uid_stat_tcp_rcv(uid, size) do {} while (0);
+#endif
+
+#endif /* _LINUX_UID_STAT_H */
diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h
new file mode 100644
index 000000000000..ebe3c4d59309
--- /dev/null
+++ b/include/linux/usb/f_accessory.h
@@ -0,0 +1,23 @@
+/*
+ * Gadget Function Driver for Android USB accessories
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_USB_F_ACCESSORY_H
+#define __LINUX_USB_F_ACCESSORY_H
+
+#include <uapi/linux/usb/f_accessory.h>
+
+#endif /* __LINUX_USB_F_ACCESSORY_H */
diff --git a/include/linux/usb/f_mtp.h b/include/linux/usb/f_mtp.h
new file mode 100644
index 000000000000..4e8417791bea
--- /dev/null
+++ b/include/linux/usb/f_mtp.h
@@ -0,0 +1,23 @@
+/*
+ * Gadget Function Driver for MTP
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_USB_F_MTP_H
+#define __LINUX_USB_F_MTP_H
+
+#include <uapi/linux/usb/f_mtp.h>
+
+#endif /* __LINUX_USB_F_MTP_H */
diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h
new file mode 100644
index 000000000000..f4a698a22880
--- /dev/null
+++ b/include/linux/wakelock.h
@@ -0,0 +1,67 @@
+/* include/linux/wakelock.h
+ *
+ * Copyright (C) 2007-2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_WAKELOCK_H
+#define _LINUX_WAKELOCK_H
+
+#include <linux/ktime.h>
+#include <linux/device.h>
+
+/* A wake_lock prevents the system from entering suspend or other low power
+ * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock
+ * prevents a full system suspend.
+ */
+
+enum {
+ WAKE_LOCK_SUSPEND, /* Prevent suspend */
+ WAKE_LOCK_TYPE_COUNT
+};
+
+struct wake_lock {
+ struct wakeup_source ws;
+};
+
+static inline void wake_lock_init(struct wake_lock *lock, int type,
+ const char *name)
+{
+ wakeup_source_init(&lock->ws, name);
+}
+
+static inline void wake_lock_destroy(struct wake_lock *lock)
+{
+ wakeup_source_trash(&lock->ws);
+}
+
+static inline void wake_lock(struct wake_lock *lock)
+{
+ __pm_stay_awake(&lock->ws);
+}
+
+static inline void wake_lock_timeout(struct wake_lock *lock, long timeout)
+{
+ __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout));
+}
+
+static inline void wake_unlock(struct wake_lock *lock)
+{
+ __pm_relax(&lock->ws);
+}
+
+static inline int wake_lock_active(struct wake_lock *lock)
+{
+ return lock->ws.active;
+}
+
+#endif
diff --git a/include/linux/wakeup_reason.h b/include/linux/wakeup_reason.h
new file mode 100644
index 000000000000..7ce50f0debc4
--- /dev/null
+++ b/include/linux/wakeup_reason.h
@@ -0,0 +1,23 @@
+/*
+ * include/linux/wakeup_reason.h
+ *
+ * Logs the reason which caused the kernel to resume
+ * from the suspend mode.
+ *
+ * Copyright (C) 2014 Google, Inc.
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _LINUX_WAKEUP_REASON_H
+#define _LINUX_WAKEUP_REASON_H
+
+void log_wakeup_reason(int irq);
+
+#endif /* _LINUX_WAKEUP_REASON_H */
diff --git a/include/linux/wifi_tiwlan.h b/include/linux/wifi_tiwlan.h
new file mode 100644
index 000000000000..f07e0679fb82
--- /dev/null
+++ b/include/linux/wifi_tiwlan.h
@@ -0,0 +1,27 @@
+/* include/linux/wifi_tiwlan.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#ifndef _LINUX_WIFI_TIWLAN_H_
+#define _LINUX_WIFI_TIWLAN_H_
+
+#include <linux/wlan_plat.h>
+
+#define WMPA_NUMBER_OF_SECTIONS 3
+#define WMPA_NUMBER_OF_BUFFERS 160
+#define WMPA_SECTION_HEADER 24
+#define WMPA_SECTION_SIZE_0 (WMPA_NUMBER_OF_BUFFERS * 64)
+#define WMPA_SECTION_SIZE_1 (WMPA_NUMBER_OF_BUFFERS * 256)
+#define WMPA_SECTION_SIZE_2 (WMPA_NUMBER_OF_BUFFERS * 2048)
+
+#endif
diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h
new file mode 100644
index 000000000000..40ec3482d1ef
--- /dev/null
+++ b/include/linux/wlan_plat.h
@@ -0,0 +1,27 @@
+/* include/linux/wlan_plat.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#ifndef _LINUX_WLAN_PLAT_H_
+#define _LINUX_WLAN_PLAT_H_
+
+struct wifi_platform_data {
+ int (*set_power)(int val);
+ int (*set_reset)(int val);
+ int (*set_carddetect)(int val);
+ void *(*mem_prealloc)(int section, unsigned long size);
+ int (*get_mac_addr)(unsigned char *buf);
+ void *(*get_country_code)(char *ccode);
+};
+
+#endif
diff --git a/include/net/activity_stats.h b/include/net/activity_stats.h
new file mode 100644
index 000000000000..10e4c1506eeb
--- /dev/null
+++ b/include/net/activity_stats.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * Author: Mike Chan (mike@android.com)
+ */
+
+#ifndef __activity_stats_h
+#define __activity_stats_h
+
+#ifdef CONFIG_NET_ACTIVITY_STATS
+void activity_stats_update(void);
+#else
+#define activity_stats_update(void) {}
+#endif
+
+#endif /* _NET_ACTIVITY_STATS_H */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 590e01a476ac..68576d24e95c 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -278,6 +278,7 @@ extern int sysctl_tcp_challenge_ack_limit;
extern unsigned int sysctl_tcp_notsent_lowat;
extern int sysctl_tcp_min_tso_segs;
extern int sysctl_tcp_autocorking;
+extern int sysctl_tcp_default_init_rwnd;
extern atomic_long_t tcp_memory_allocated;
extern struct percpu_counter tcp_sockets_allocated;
@@ -1571,6 +1572,8 @@ static inline bool tcp_stream_memory_free(const struct sock *sk)
return notsent_bytes < tcp_notsent_lowat(tp);
}
+extern int tcp_nuke_addr(struct net *net, struct sockaddr *addr);
+
#ifdef CONFIG_PROC_FS
int tcp4_proc_init(void);
void tcp4_proc_exit(void);
diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h
new file mode 100644
index 000000000000..951e6ca12da8
--- /dev/null
+++ b/include/trace/events/cpufreq_interactive.h
@@ -0,0 +1,112 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cpufreq_interactive
+
+#if !defined(_TRACE_CPUFREQ_INTERACTIVE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_CPUFREQ_INTERACTIVE_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(set,
+ TP_PROTO(u32 cpu_id, unsigned long targfreq,
+ unsigned long actualfreq),
+ TP_ARGS(cpu_id, targfreq, actualfreq),
+
+ TP_STRUCT__entry(
+ __field( u32, cpu_id )
+ __field(unsigned long, targfreq )
+ __field(unsigned long, actualfreq )
+ ),
+
+ TP_fast_assign(
+ __entry->cpu_id = (u32) cpu_id;
+ __entry->targfreq = targfreq;
+ __entry->actualfreq = actualfreq;
+ ),
+
+ TP_printk("cpu=%u targ=%lu actual=%lu",
+ __entry->cpu_id, __entry->targfreq,
+ __entry->actualfreq)
+);
+
+DEFINE_EVENT(set, cpufreq_interactive_setspeed,
+ TP_PROTO(u32 cpu_id, unsigned long targfreq,
+ unsigned long actualfreq),
+ TP_ARGS(cpu_id, targfreq, actualfreq)
+);
+
+DECLARE_EVENT_CLASS(loadeval,
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, cpu_id )
+ __field(unsigned long, load )
+ __field(unsigned long, curtarg )
+ __field(unsigned long, curactual )
+ __field(unsigned long, newtarg )
+ ),
+
+ TP_fast_assign(
+ __entry->cpu_id = cpu_id;
+ __entry->load = load;
+ __entry->curtarg = curtarg;
+ __entry->curactual = curactual;
+ __entry->newtarg = newtarg;
+ ),
+
+ TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu",
+ __entry->cpu_id, __entry->load, __entry->curtarg,
+ __entry->curactual, __entry->newtarg)
+);
+
+DEFINE_EVENT(loadeval, cpufreq_interactive_target,
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
+);
+
+DEFINE_EVENT(loadeval, cpufreq_interactive_already,
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
+);
+
+DEFINE_EVENT(loadeval, cpufreq_interactive_notyet,
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curtarg, unsigned long curactual,
+ unsigned long newtarg),
+ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg)
+);
+
+TRACE_EVENT(cpufreq_interactive_boost,
+ TP_PROTO(const char *s),
+ TP_ARGS(s),
+ TP_STRUCT__entry(
+ __string(s, s)
+ ),
+ TP_fast_assign(
+ __assign_str(s, s);
+ ),
+ TP_printk("%s", __get_str(s))
+);
+
+TRACE_EVENT(cpufreq_interactive_unboost,
+ TP_PROTO(const char *s),
+ TP_ARGS(s),
+ TP_STRUCT__entry(
+ __string(s, s)
+ ),
+ TP_fast_assign(
+ __assign_str(s, s);
+ ),
+ TP_printk("%s", __get_str(s))
+);
+
+#endif /* _TRACE_CPUFREQ_INTERACTIVE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/gpu.h b/include/trace/events/gpu.h
new file mode 100644
index 000000000000..7e15cdfafe5a
--- /dev/null
+++ b/include/trace/events/gpu.h
@@ -0,0 +1,143 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM gpu
+
+#if !defined(_TRACE_GPU_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_GPU_H
+
+#include <linux/tracepoint.h>
+#include <linux/time.h>
+
+#define show_secs_from_ns(ns) \
+ ({ \
+ u64 t = ns + (NSEC_PER_USEC / 2); \
+ do_div(t, NSEC_PER_SEC); \
+ t; \
+ })
+
+#define show_usecs_from_ns(ns) \
+ ({ \
+ u64 t = ns + (NSEC_PER_USEC / 2) ; \
+ u32 rem; \
+ do_div(t, NSEC_PER_USEC); \
+ rem = do_div(t, USEC_PER_SEC); \
+ })
+
+/*
+ * The gpu_sched_switch event indicates that a switch from one GPU context to
+ * another occurred on one of the GPU hardware blocks.
+ *
+ * The gpu_name argument identifies the GPU hardware block. Each independently
+ * scheduled GPU hardware block should have a different name. This may be used
+ * in different ways for different GPUs. For example, if a GPU includes
+ * multiple processing cores it may use names "GPU 0", "GPU 1", etc. If a GPU
+ * includes a separately scheduled 2D and 3D hardware block, it might use the
+ * names "2D" and "3D".
+ *
+ * The timestamp argument is the timestamp at which the switch occurred on the
+ * GPU. These timestamps are in units of nanoseconds and must use
+ * approximately the same time as sched_clock, though they need not come from
+ * any CPU clock. The timestamps for a single hardware block must be
+ * monotonically nondecreasing. This means that if a variable compensation
+ * offset is used to translate from some other clock to the sched_clock, then
+ * care must be taken when increasing that offset, and doing so may result in
+ * multiple events with the same timestamp.
+ *
+ * The next_ctx_id argument identifies the next context that was running on
+ * the GPU hardware block. A value of 0 indicates that the hardware block
+ * will be idle.
+ *
+ * The next_prio argument indicates the priority of the next context at the
+ * time of the event. The exact numeric values may mean different things for
+ * different GPUs, but they should follow the rule that lower values indicate a
+ * higher priority.
+ *
+ * The next_job_id argument identifies the batch of work that the GPU will be
+ * working on. This should correspond to a job_id that was previously traced
+ * as a gpu_job_enqueue event when the batch of work was created.
+ */
+TRACE_EVENT(gpu_sched_switch,
+
+ TP_PROTO(const char *gpu_name, u64 timestamp,
+ u32 next_ctx_id, s32 next_prio, u32 next_job_id),
+
+ TP_ARGS(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id),
+
+ TP_STRUCT__entry(
+ __string( gpu_name, gpu_name )
+ __field( u64, timestamp )
+ __field( u32, next_ctx_id )
+ __field( s32, next_prio )
+ __field( u32, next_job_id )
+ ),
+
+ TP_fast_assign(
+ __assign_str(gpu_name, gpu_name);
+ __entry->timestamp = timestamp;
+ __entry->next_ctx_id = next_ctx_id;
+ __entry->next_prio = next_prio;
+ __entry->next_job_id = next_job_id;
+ ),
+
+ TP_printk("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld "
+ "next_job_id=%lu",
+ __get_str(gpu_name),
+ (unsigned long long)show_secs_from_ns(__entry->timestamp),
+ (unsigned long)show_usecs_from_ns(__entry->timestamp),
+ (unsigned long)__entry->next_ctx_id,
+ (long)__entry->next_prio,
+ (unsigned long)__entry->next_job_id)
+);
+
+/*
+ * The gpu_job_enqueue event indicates that a batch of work has been queued up
+ * to be processed by the GPU. This event is not intended to indicate that
+ * the batch of work has been submitted to the GPU hardware, but rather that
+ * it has been submitted to the GPU kernel driver.
+ *
+ * This event should be traced on the thread that initiated the work being
+ * queued. For example, if a batch of work is submitted to the kernel by a
+ * userland thread, the event should be traced on that thread.
+ *
+ * The ctx_id field identifies the GPU context in which the batch of work
+ * being queued is to be run.
+ *
+ * The job_id field identifies the batch of work being queued within the given
+ * GPU context. The first batch of work submitted for a given GPU context
+ * should have a job_id of 0, and each subsequent batch of work should
+ * increment the job_id by 1.
+ *
+ * The type field identifies the type of the job being enqueued. The job
+ * types may be different for different GPU hardware. For example, a GPU may
+ * differentiate between "2D", "3D", and "compute" jobs.
+ */
+TRACE_EVENT(gpu_job_enqueue,
+
+ TP_PROTO(u32 ctx_id, u32 job_id, const char *type),
+
+ TP_ARGS(ctx_id, job_id, type),
+
+ TP_STRUCT__entry(
+ __field( u32, ctx_id )
+ __field( u32, job_id )
+ __string( type, type )
+ ),
+
+ TP_fast_assign(
+ __entry->ctx_id = ctx_id;
+ __entry->job_id = job_id;
+ __assign_str(type, type);
+ ),
+
+ TP_printk("ctx_id=%lu job_id=%lu type=%s",
+ (unsigned long)__entry->ctx_id,
+ (unsigned long)__entry->job_id,
+ __get_str(type))
+);
+
+#undef show_secs_from_ns
+#undef show_usecs_from_ns
+
+#endif /* _TRACE_GPU_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/mmc.h b/include/trace/events/mmc.h
new file mode 100644
index 000000000000..82b368dbcefc
--- /dev/null
+++ b/include/trace/events/mmc.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mmc
+
+#if !defined(_TRACE_MMC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MMC_H
+
+#include <linux/tracepoint.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/core.h>
+
+/*
+ * Unconditional logging of mmc block erase operations,
+ * including cmd, address, size
+ */
+DECLARE_EVENT_CLASS(mmc_blk_erase_class,
+ TP_PROTO(unsigned int cmd, unsigned int addr, unsigned int size),
+ TP_ARGS(cmd, addr, size),
+ TP_STRUCT__entry(
+ __field(unsigned int, cmd)
+ __field(unsigned int, addr)
+ __field(unsigned int, size)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ __entry->addr = addr;
+ __entry->size = size;
+ ),
+ TP_printk("cmd=%u,addr=0x%08x,size=0x%08x",
+ __entry->cmd, __entry->addr, __entry->size)
+);
+
+DEFINE_EVENT(mmc_blk_erase_class, mmc_blk_erase_start,
+ TP_PROTO(unsigned int cmd, unsigned int addr, unsigned int size),
+ TP_ARGS(cmd, addr, size));
+
+DEFINE_EVENT(mmc_blk_erase_class, mmc_blk_erase_end,
+ TP_PROTO(unsigned int cmd, unsigned int addr, unsigned int size),
+ TP_ARGS(cmd, addr, size));
+
+/*
+ * Logging of start of read or write mmc block operation,
+ * including cmd, address, size
+ */
+DECLARE_EVENT_CLASS(mmc_blk_rw_class,
+ TP_PROTO(unsigned int cmd, unsigned int addr, struct mmc_data *data),
+ TP_ARGS(cmd, addr, data),
+ TP_STRUCT__entry(
+ __field(unsigned int, cmd)
+ __field(unsigned int, addr)
+ __field(unsigned int, size)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ __entry->addr = addr;
+ __entry->size = data->blocks;
+ ),
+ TP_printk("cmd=%u,addr=0x%08x,size=0x%08x",
+ __entry->cmd, __entry->addr, __entry->size)
+);
+
+DEFINE_EVENT_CONDITION(mmc_blk_rw_class, mmc_blk_rw_start,
+ TP_PROTO(unsigned int cmd, unsigned int addr, struct mmc_data *data),
+ TP_ARGS(cmd, addr, data),
+ TP_CONDITION(((cmd == MMC_READ_MULTIPLE_BLOCK) ||
+ (cmd == MMC_WRITE_MULTIPLE_BLOCK)) &&
+ data));
+
+DEFINE_EVENT_CONDITION(mmc_blk_rw_class, mmc_blk_rw_end,
+ TP_PROTO(unsigned int cmd, unsigned int addr, struct mmc_data *data),
+ TP_ARGS(cmd, addr, data),
+ TP_CONDITION(((cmd == MMC_READ_MULTIPLE_BLOCK) ||
+ (cmd == MMC_WRITE_MULTIPLE_BLOCK)) &&
+ data));
+#endif /* _TRACE_MMC_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/power.h b/include/trace/events/power.h
index d19840b0cac8..274021214b4e 100644
--- a/include/trace/events/power.h
+++ b/include/trace/events/power.h
@@ -264,6 +264,25 @@ DEFINE_EVENT(clock, clock_set_rate,
TP_ARGS(name, state, cpu_id)
);
+TRACE_EVENT(clock_set_parent,
+
+ TP_PROTO(const char *name, const char *parent_name),
+
+ TP_ARGS(name, parent_name),
+
+ TP_STRUCT__entry(
+ __string( name, name )
+ __string( parent_name, parent_name )
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, name);
+ __assign_str(parent_name, parent_name);
+ ),
+
+ TP_printk("%s parent=%s", __get_str(name), __get_str(parent_name))
+);
+
/*
* The power domain events are used for power domains transitions
*/
diff --git a/include/uapi/linux/if_pppolac.h b/include/uapi/linux/if_pppolac.h
new file mode 100644
index 000000000000..b7eb8153ef66
--- /dev/null
+++ b/include/uapi/linux/if_pppolac.h
@@ -0,0 +1,33 @@
+/* include/uapi/linux/if_pppolac.h
+ *
+ * Header for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661)
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Chia-chi Yeh <chiachi@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _UAPI_LINUX_IF_PPPOLAC_H
+#define _UAPI_LINUX_IF_PPPOLAC_H
+
+#include <linux/socket.h>
+#include <linux/types.h>
+
+struct sockaddr_pppolac {
+ sa_family_t sa_family; /* AF_PPPOX */
+ unsigned int sa_protocol; /* PX_PROTO_OLAC */
+ int udp_socket;
+ struct __attribute__((packed)) {
+ __u16 tunnel, session;
+ } local, remote;
+} __attribute__((packed));
+
+#endif /* _UAPI_LINUX_IF_PPPOLAC_H */
diff --git a/include/uapi/linux/if_pppopns.h b/include/uapi/linux/if_pppopns.h
new file mode 100644
index 000000000000..a392b52ea6ec
--- /dev/null
+++ b/include/uapi/linux/if_pppopns.h
@@ -0,0 +1,32 @@
+/* include/uapi/linux/if_pppopns.h
+ *
+ * Header for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637)
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Chia-chi Yeh <chiachi@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _UAPI_LINUX_IF_PPPOPNS_H
+#define _UAPI_LINUX_IF_PPPOPNS_H
+
+#include <linux/socket.h>
+#include <linux/types.h>
+
+struct sockaddr_pppopns {
+ sa_family_t sa_family; /* AF_PPPOX */
+ unsigned int sa_protocol; /* PX_PROTO_OPNS */
+ int tcp_socket;
+ __u16 local;
+ __u16 remote;
+} __attribute__((packed));
+
+#endif /* _UAPI_LINUX_IF_PPPOPNS_H */
diff --git a/include/uapi/linux/if_pppox.h b/include/uapi/linux/if_pppox.h
index e128769331b5..5861d45bc51d 100644
--- a/include/uapi/linux/if_pppox.h
+++ b/include/uapi/linux/if_pppox.h
@@ -23,6 +23,8 @@
#include <linux/socket.h>
#include <linux/if_ether.h>
#include <linux/if_pppol2tp.h>
+#include <linux/if_pppolac.h>
+#include <linux/if_pppopns.h>
/* For user-space programs to pick up these definitions
* which they wouldn't get otherwise without defining __KERNEL__
@@ -56,7 +58,9 @@ struct pptp_addr {
#define PX_PROTO_OE 0 /* Currently just PPPoE */
#define PX_PROTO_OL2TP 1 /* Now L2TP also */
#define PX_PROTO_PPTP 2
-#define PX_MAX_PROTO 3
+#define PX_PROTO_OLAC 3
+#define PX_PROTO_OPNS 4
+#define PX_MAX_PROTO 5
struct sockaddr_pppox {
__kernel_sa_family_t sa_family; /* address family, AF_PPPOX */
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 19df18c9b8be..a24fb643fe8e 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -152,7 +152,12 @@ struct input_keymap_entry {
#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */
#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
-#define EVIOCREVOKE _IOW('E', 0x91, int) /* Revoke device access */
+
+/* HACK: disable conflicting EVIOCREVOKE until Android userspace stops using EVIOCSSUSPENDBLOCK */
+/*#define EVIOCREVOKE _IOW('E', 0x91, int)*/ /* Revoke device access */
+
+#define EVIOCGSUSPENDBLOCK _IOR('E', 0x91, int) /* get suspend block enable */
+#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) /* set suspend block enable */
#define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */
diff --git a/include/uapi/linux/keychord.h b/include/uapi/linux/keychord.h
new file mode 100644
index 000000000000..ea7cf4d27bbd
--- /dev/null
+++ b/include/uapi/linux/keychord.h
@@ -0,0 +1,52 @@
+/*
+ * Key chord input driver
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+*/
+
+#ifndef _UAPI_LINUX_KEYCHORD_H_
+#define _UAPI_LINUX_KEYCHORD_H_
+
+#include <linux/input.h>
+
+#define KEYCHORD_VERSION 1
+
+/*
+ * One or more input_keychord structs are written to /dev/keychord
+ * at once to specify the list of keychords to monitor.
+ * Reading /dev/keychord returns the id of a keychord when the
+ * keychord combination is pressed. A keychord is signalled when
+ * all of the keys in the keycode list are in the pressed state.
+ * The order in which the keys are pressed does not matter.
+ * The keychord will not be signalled if keys not in the keycode
+ * list are pressed.
+ * Keychords will not be signalled on key release events.
+ */
+struct input_keychord {
+ /* should be KEYCHORD_VERSION */
+ __u16 version;
+ /*
+ * client specified ID, returned from read()
+ * when this keychord is pressed.
+ */
+ __u16 id;
+
+ /* number of keycodes in this keychord */
+ __u16 count;
+
+ /* variable length array of keycodes */
+ __u16 keycodes[];
+};
+
+#endif /* _UAPI_LINUX_KEYCHORD_H_ */
diff --git a/include/uapi/linux/netfilter/xt_IDLETIMER.h b/include/uapi/linux/netfilter/xt_IDLETIMER.h
index 208ae9387331..faaa28b3d061 100644
--- a/include/uapi/linux/netfilter/xt_IDLETIMER.h
+++ b/include/uapi/linux/netfilter/xt_IDLETIMER.h
@@ -4,6 +4,7 @@
* Header file for Xtables timer target module.
*
* Copyright (C) 2004, 2010 Nokia Corporation
+ *
* Written by Timo Teras <ext-timo.teras@nokia.com>
*
* Converted to x_tables and forward-ported to 2.6.34
@@ -32,12 +33,19 @@
#include <linux/types.h>
#define MAX_IDLETIMER_LABEL_SIZE 28
+#define NLMSG_MAX_SIZE 64
+
+#define NL_EVENT_TYPE_INACTIVE 0
+#define NL_EVENT_TYPE_ACTIVE 1
struct idletimer_tg_info {
__u32 timeout;
char label[MAX_IDLETIMER_LABEL_SIZE];
+ /* Use netlink messages for notification in addition to sysfs */
+ __u8 send_nl_msg;
+
/* for kernel module internal use only */
struct idletimer_tg *timer __attribute__((aligned(8)));
};
diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h
index 6315e2ac3474..55076a32bdfd 100644
--- a/include/uapi/linux/netfilter/xt_socket.h
+++ b/include/uapi/linux/netfilter/xt_socket.h
@@ -18,4 +18,9 @@ struct xt_socket_mtinfo2 {
};
#define XT_SOCKET_FLAGS_V2 (XT_SOCKET_TRANSPARENT | XT_SOCKET_NOWILDCARD)
+struct sock *xt_socket_get4_sk(const struct sk_buff *skb,
+ struct xt_action_param *par);
+struct sock *xt_socket_get6_sk(const struct sk_buff *skb,
+ struct xt_action_param *par);
+
#endif /* _XT_SOCKET_H */
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 58afc04c107e..cae5524ef77c 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -149,6 +149,8 @@
#define PR_GET_TID_ADDRESS 40
+#define PR_SET_VMA 0x53564d41
+# define PR_SET_VMA_ANON_NAME 0
#define PR_SET_THP_DISABLE 41
#define PR_GET_THP_DISABLE 42
diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h
index e888b1aed69f..623e9aab645e 100644
--- a/include/uapi/linux/sockios.h
+++ b/include/uapi/linux/sockios.h
@@ -65,6 +65,7 @@
#define SIOCDIFADDR 0x8936 /* delete PA address */
#define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */
#define SIOCGIFCOUNT 0x8938 /* get number of devices */
+#define SIOCKILLADDR 0x8939 /* kill sockets with this local addr */
#define SIOCGIFBR 0x8940 /* Bridging support */
#define SIOCSIFBR 0x8941 /* Set bridging options */
diff --git a/include/uapi/linux/usb/f_accessory.h b/include/uapi/linux/usb/f_accessory.h
new file mode 100644
index 000000000000..0baeb7d0d74c
--- /dev/null
+++ b/include/uapi/linux/usb/f_accessory.h
@@ -0,0 +1,146 @@
+/*
+ * Gadget Function Driver for Android USB accessories
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H
+#define _UAPI_LINUX_USB_F_ACCESSORY_H
+
+/* Use Google Vendor ID when in accessory mode */
+#define USB_ACCESSORY_VENDOR_ID 0x18D1
+
+
+/* Product ID to use when in accessory mode */
+#define USB_ACCESSORY_PRODUCT_ID 0x2D00
+
+/* Product ID to use when in accessory mode and adb is enabled */
+#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
+
+/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */
+#define ACCESSORY_STRING_MANUFACTURER 0
+#define ACCESSORY_STRING_MODEL 1
+#define ACCESSORY_STRING_DESCRIPTION 2
+#define ACCESSORY_STRING_VERSION 3
+#define ACCESSORY_STRING_URI 4
+#define ACCESSORY_STRING_SERIAL 5
+
+/* Control request for retrieving device's protocol version
+ *
+ * requestType: USB_DIR_IN | USB_TYPE_VENDOR
+ * request: ACCESSORY_GET_PROTOCOL
+ * value: 0
+ * index: 0
+ * data version number (16 bits little endian)
+ * 1 for original accessory support
+ * 2 adds HID and device to host audio support
+ */
+#define ACCESSORY_GET_PROTOCOL 51
+
+/* Control request for host to send a string to the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_STRING
+ * value: 0
+ * index: string ID
+ * data zero terminated UTF8 string
+ *
+ * The device can later retrieve these strings via the
+ * ACCESSORY_GET_STRING_* ioctls
+ */
+#define ACCESSORY_SEND_STRING 52
+
+/* Control request for starting device in accessory mode.
+ * The host sends this after setting all its strings to the device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_START
+ * value: 0
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_START 53
+
+/* Control request for registering a HID device.
+ * Upon registering, a unique ID is sent by the accessory in the
+ * value parameter. This ID will be used for future commands for
+ * the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID_DEVICE
+ * value: Accessory assigned ID for the HID device
+ * index: total length of the HID report descriptor
+ * data none
+ */
+#define ACCESSORY_REGISTER_HID 54
+
+/* Control request for unregistering a HID device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_UNREGISTER_HID 55
+
+/* Control request for sending the HID report descriptor.
+ * If the HID descriptor is longer than the endpoint zero max packet size,
+ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
+ * commands. The data for the descriptor must be sent sequentially
+ * if multiple packets are needed.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_HID_REPORT_DESC
+ * value: Accessory assigned ID for the HID device
+ * index: offset of data in descriptor
+ * (needed when HID descriptor is too big for one packet)
+ * data the HID report descriptor
+ */
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+
+/* Control request for sending HID events.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_HID_EVENT
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data the HID report for the event
+ */
+#define ACCESSORY_SEND_HID_EVENT 57
+
+/* Control request for setting the audio mode.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_AUDIO_MODE
+ * value: 0 - no audio
+ * 1 - device to host, 44100 16-bit stereo PCM
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_SET_AUDIO_MODE 58
+
+/* ioctls for retrieving strings set by the host */
+#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
+#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
+#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
+#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
+#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
+#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
+/* returns 1 if there is a start request pending */
+#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
+
+#endif /* _UAPI_LINUX_USB_F_ACCESSORY_H */
diff --git a/include/uapi/linux/usb/f_mtp.h b/include/uapi/linux/usb/f_mtp.h
new file mode 100644
index 000000000000..503291855abd
--- /dev/null
+++ b/include/uapi/linux/usb/f_mtp.h
@@ -0,0 +1,61 @@
+/*
+ * Gadget Function Driver for MTP
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _UAPI_LINUX_USB_F_MTP_H
+#define _UAPI_LINUX_USB_F_MTP_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+struct mtp_file_range {
+ /* file descriptor for file to transfer */
+ int fd;
+ /* offset in file for start of transfer */
+ loff_t offset;
+ /* number of bytes to transfer */
+ int64_t length;
+ /* MTP command ID for data header,
+ * used only for MTP_SEND_FILE_WITH_HEADER
+ */
+ uint16_t command;
+ /* MTP transaction ID for data header,
+ * used only for MTP_SEND_FILE_WITH_HEADER
+ */
+ uint32_t transaction_id;
+};
+
+struct mtp_event {
+ /* size of the event */
+ size_t length;
+ /* event data to send */
+ void *data;
+};
+
+/* Sends the specified file range to the host */
+#define MTP_SEND_FILE _IOW('M', 0, struct mtp_file_range)
+/* Receives data from the host and writes it to a file.
+ * The file is created if it does not exist.
+ */
+#define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range)
+/* Sends an event to the host via the interrupt endpoint */
+#define MTP_SEND_EVENT _IOW('M', 3, struct mtp_event)
+/* Sends the specified file range to the host,
+ * with a 12 byte MTP data packet header at the beginning.
+ */
+#define MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, struct mtp_file_range)
+
+#endif /* _UAPI_LINUX_USB_F_MTP_H */
diff --git a/include/uapi/video/adf.h b/include/uapi/video/adf.h
new file mode 100644
index 000000000000..b703bf9e9a66
--- /dev/null
+++ b/include/uapi/video/adf.h
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _UAPI_VIDEO_ADF_H_
+#define _UAPI_VIDEO_ADF_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_mode.h>
+
+#define ADF_NAME_LEN 32
+#define ADF_MAX_CUSTOM_DATA_SIZE 4096
+
+enum adf_interface_type {
+ ADF_INTF_DSI = 0,
+ ADF_INTF_eDP = 1,
+ ADF_INTF_DPI = 2,
+ ADF_INTF_VGA = 3,
+ ADF_INTF_DVI = 4,
+ ADF_INTF_HDMI = 5,
+ ADF_INTF_MEMORY = 6,
+ ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
+ ADF_INTF_TYPE_MAX = (~(__u32)0),
+};
+
+#define ADF_INTF_FLAG_PRIMARY (1 << 0)
+#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
+
+enum adf_event_type {
+ ADF_EVENT_VSYNC = 0,
+ ADF_EVENT_HOTPLUG = 1,
+ ADF_EVENT_DEVICE_CUSTOM = 128,
+ ADF_EVENT_TYPE_MAX = 255,
+};
+
+/**
+ * struct adf_set_event - start or stop subscribing to ADF events
+ *
+ * @type: the type of event to (un)subscribe
+ * @enabled: subscribe or unsubscribe
+ *
+ * After subscribing to an event, userspace may poll() the ADF object's fd
+ * to wait for events or read() to consume the event's data.
+ *
+ * ADF reserves event types 0 to %ADF_EVENT_DEVICE_CUSTOM-1 for its own events.
+ * Devices may use event types %ADF_EVENT_DEVICE_CUSTOM to %ADF_EVENT_TYPE_MAX-1
+ * for driver-private events.
+ */
+struct adf_set_event {
+ __u8 type;
+ __u8 enabled;
+};
+
+/**
+ * struct adf_event - common header for ADF event data
+ *
+ * @type: event type
+ * @length: total size of event data, header inclusive
+ */
+struct adf_event {
+ __u8 type;
+ __u32 length;
+};
+
+/**
+ * struct adf_vsync_event - ADF vsync event
+ *
+ * @base: event header (see &struct adf_event)
+ * @timestamp: time of vsync event, in nanoseconds
+ */
+struct adf_vsync_event {
+ struct adf_event base;
+ __u64 timestamp;
+};
+
+/**
+ * struct adf_vsync_event - ADF display hotplug event
+ *
+ * @base: event header (see &struct adf_event)
+ * @connected: whether a display is now connected to the interface
+ */
+struct adf_hotplug_event {
+ struct adf_event base;
+ __u8 connected;
+};
+
+#define ADF_MAX_PLANES 4
+/**
+ * struct adf_buffer_config - description of buffer displayed by adf_post_config
+ *
+ * @overlay_engine: id of the target overlay engine
+ * @w: width of display region in pixels
+ * @h: height of display region in pixels
+ * @format: DRM-style fourcc, see drm_fourcc.h for standard formats
+ * @fd: dma_buf fd for each plane
+ * @offset: location of first pixel to scan out, in bytes
+ * @pitch: stride (i.e. length of a scanline including padding) in bytes
+ * @n_planes: number of planes in buffer
+ * @acquire_fence: sync_fence fd which will clear when the buffer is
+ * ready for display, or <0 if the buffer is already ready
+ */
+struct adf_buffer_config {
+ __u32 overlay_engine;
+
+ __u32 w;
+ __u32 h;
+ __u32 format;
+
+ __s64 fd[ADF_MAX_PLANES];
+ __u32 offset[ADF_MAX_PLANES];
+ __u32 pitch[ADF_MAX_PLANES];
+ __u8 n_planes;
+
+ __s64 acquire_fence;
+};
+#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
+
+/**
+ * struct adf_post_config - request to flip to a new set of buffers
+ *
+ * @n_interfaces: number of interfaces targeted by the flip (input)
+ * @interfaces: ids of interfaces targeted by the flip (input)
+ * @n_bufs: number of buffers displayed (input)
+ * @bufs: description of buffers displayed (input)
+ * @custom_data_size: size of driver-private data (input)
+ * @custom_data: driver-private data (input)
+ * @complete_fence: sync_fence fd which will clear when this
+ * configuration has left the screen (output)
+ */
+struct adf_post_config {
+ size_t n_interfaces;
+ __u32 __user *interfaces;
+
+ size_t n_bufs;
+ struct adf_buffer_config __user *bufs;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+
+ __s64 complete_fence;
+};
+#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
+
+/**
+ * struct adf_simple_buffer_allocate - request to allocate a "simple" buffer
+ *
+ * @w: width of buffer in pixels (input)
+ * @h: height of buffer in pixels (input)
+ * @format: DRM-style fourcc (input)
+ *
+ * @fd: dma_buf fd (output)
+ * @offset: location of first pixel, in bytes (output)
+ * @pitch: length of a scanline including padding, in bytes (output)
+ *
+ * Simple buffers are analogous to DRM's "dumb" buffers. They have a single
+ * plane of linear RGB data which can be allocated and scanned out without
+ * any driver-private ioctls or data.
+ *
+ * @format must be a standard RGB format defined in drm_fourcc.h.
+ *
+ * ADF clients must NOT assume that an interface can scan out a simple buffer
+ * allocated by a different ADF interface, even if the two interfaces belong to
+ * the same ADF device.
+ */
+struct adf_simple_buffer_alloc {
+ __u16 w;
+ __u16 h;
+ __u32 format;
+
+ __s64 fd;
+ __u32 offset;
+ __u32 pitch;
+};
+
+/**
+ * struct adf_simple_post_config - request to flip to a single buffer without
+ * driver-private data
+ *
+ * @buf: description of buffer displayed (input)
+ * @complete_fence: sync_fence fd which will clear when this buffer has left the
+ * screen (output)
+ */
+struct adf_simple_post_config {
+ struct adf_buffer_config buf;
+ __s64 complete_fence;
+};
+
+/**
+ * struct adf_attachment_config - description of attachment between an overlay
+ * engine and an interface
+ *
+ * @overlay_engine: id of the overlay engine
+ * @interface: id of the interface
+ */
+struct adf_attachment_config {
+ __u32 overlay_engine;
+ __u32 interface;
+};
+
+/**
+ * struct adf_device_data - describes a display device
+ *
+ * @name: display device's name
+ * @n_attachments: the number of current attachments
+ * @attachments: list of current attachments
+ * @n_allowed_attachments: the number of allowed attachments
+ * @allowed_attachments: list of allowed attachments
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_device_data {
+ char name[ADF_NAME_LEN];
+
+ size_t n_attachments;
+ struct adf_attachment_config __user *attachments;
+
+ size_t n_allowed_attachments;
+ struct adf_attachment_config __user *allowed_attachments;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config))
+
+/**
+ * struct adf_device_data - describes a display interface
+ *
+ * @name: display interface's name
+ * @type: interface type (see enum @adf_interface_type)
+ * @id: which interface of type @type;
+ * e.g. interface DSI.1 -> @type=@ADF_INTF_TYPE_DSI, @id=1
+ * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values)
+ * @dpms_state: DPMS state (one of @DRM_MODE_DPMS_* defined in drm_mode.h)
+ * @hotplug_detect: whether a display is plugged in
+ * @width_mm: screen width in millimeters, or 0 if unknown
+ * @height_mm: screen height in millimeters, or 0 if unknown
+ * @current_mode: current display mode
+ * @n_available_modes: the number of hardware display modes
+ * @available_modes: list of hardware display modes
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_interface_data {
+ char name[ADF_NAME_LEN];
+
+ __u32 type;
+ __u32 id;
+ /* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */
+ __u32 flags;
+
+ __u8 dpms_state;
+ __u8 hotplug_detect;
+ __u16 width_mm;
+ __u16 height_mm;
+
+ struct drm_mode_modeinfo current_mode;
+ size_t n_available_modes;
+ struct drm_mode_modeinfo __user *available_modes;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo))
+
+/**
+ * struct adf_overlay_engine_data - describes an overlay engine
+ *
+ * @name: overlay engine's name
+ * @n_supported_formats: number of supported formats
+ * @supported_formats: list of supported formats
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_overlay_engine_data {
+ char name[ADF_NAME_LEN];
+
+ size_t n_supported_formats;
+ __u32 __user *supported_formats;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
+
+#define ADF_IOCTL_TYPE 'D'
+#define ADF_IOCTL_NR_CUSTOM 128
+
+#define ADF_SET_EVENT _IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
+#define ADF_BLANK _IOW(ADF_IOCTL_TYPE, 1, __u8)
+#define ADF_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
+#define ADF_SET_MODE _IOW(ADF_IOCTL_TYPE, 3, \
+ struct drm_mode_modeinfo)
+#define ADF_GET_DEVICE_DATA _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
+#define ADF_GET_INTERFACE_DATA _IOR(ADF_IOCTL_TYPE, 5, \
+ struct adf_interface_data)
+#define ADF_GET_OVERLAY_ENGINE_DATA \
+ _IOR(ADF_IOCTL_TYPE, 6, \
+ struct adf_overlay_engine_data)
+#define ADF_SIMPLE_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 7, \
+ struct adf_simple_post_config)
+#define ADF_SIMPLE_BUFFER_ALLOC _IOW(ADF_IOCTL_TYPE, 8, \
+ struct adf_simple_buffer_alloc)
+#define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, \
+ struct adf_attachment_config)
+#define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, \
+ struct adf_attachment_config)
+
+#endif /* _UAPI_VIDEO_ADF_H_ */
diff --git a/include/video/adf.h b/include/video/adf.h
new file mode 100644
index 000000000000..2b742ab463dd
--- /dev/null
+++ b/include/video/adf.h
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _VIDEO_ADF_H
+#define _VIDEO_ADF_H
+
+#include <linux/device.h>
+#include <linux/dma-buf.h>
+#include <linux/idr.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/ktime.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <uapi/video/adf.h>
+#include "sync.h"
+
+struct adf_obj;
+struct adf_obj_ops;
+struct adf_device;
+struct adf_device_ops;
+struct adf_interface;
+struct adf_interface_ops;
+struct adf_overlay_engine;
+struct adf_overlay_engine_ops;
+
+/**
+ * struct adf_buffer - buffer displayed by adf_post
+ *
+ * @overlay_engine: target overlay engine
+ * @w: width of display region in pixels
+ * @h: height of display region in pixels
+ * @format: DRM-style fourcc, see drm_fourcc.h for standard formats
+ * @dma_bufs: dma_buf for each plane
+ * @offset: location of first pixel to scan out, in bytes
+ * @pitch: length of a scanline including padding, in bytes
+ * @n_planes: number of planes in buffer
+ * @acquire_fence: sync_fence which will clear when the buffer is
+ * ready for display
+ *
+ * &struct adf_buffer is the in-kernel counterpart to the userspace-facing
+ * &struct adf_buffer_config.
+ */
+struct adf_buffer {
+ struct adf_overlay_engine *overlay_engine;
+
+ u32 w;
+ u32 h;
+ u32 format;
+
+ struct dma_buf *dma_bufs[ADF_MAX_PLANES];
+ u32 offset[ADF_MAX_PLANES];
+ u32 pitch[ADF_MAX_PLANES];
+ u8 n_planes;
+
+ struct sync_fence *acquire_fence;
+};
+
+/**
+ * struct adf_buffer_mapping - state for mapping a &struct adf_buffer into the
+ * display device
+ *
+ * @attachments: dma-buf attachment for each plane
+ * @sg_tables: SG tables for each plane
+ */
+struct adf_buffer_mapping {
+ struct dma_buf_attachment *attachments[ADF_MAX_PLANES];
+ struct sg_table *sg_tables[ADF_MAX_PLANES];
+};
+
+/**
+ * struct adf_post - request to flip to a new set of buffers
+ *
+ * @n_bufs: number of buffers displayed
+ * @bufs: buffers displayed
+ * @mappings: in-device mapping state for each buffer
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ *
+ * &struct adf_post is the in-kernel counterpart to the userspace-facing
+ * &struct adf_post_config.
+ */
+struct adf_post {
+ size_t n_bufs;
+ struct adf_buffer *bufs;
+ struct adf_buffer_mapping *mappings;
+
+ size_t custom_data_size;
+ void *custom_data;
+};
+
+/**
+ * struct adf_attachment - description of attachment between an overlay engine
+ * and an interface
+ *
+ * @overlay_engine: the overlay engine
+ * @interface: the interface
+ *
+ * &struct adf_attachment is the in-kernel counterpart to the userspace-facing
+ * &struct adf_attachment_config.
+ */
+struct adf_attachment {
+ struct adf_overlay_engine *overlay_engine;
+ struct adf_interface *interface;
+};
+
+struct adf_pending_post {
+ struct list_head head;
+ struct adf_post config;
+ void *state;
+};
+
+enum adf_obj_type {
+ ADF_OBJ_OVERLAY_ENGINE = 0,
+ ADF_OBJ_INTERFACE = 1,
+ ADF_OBJ_DEVICE = 2,
+};
+
+/**
+ * struct adf_obj_ops - common ADF object implementation ops
+ *
+ * @open: handle opening the object's device node
+ * @release: handle releasing an open file
+ * @ioctl: handle custom ioctls
+ *
+ * @supports_event: return whether the object supports generating events of type
+ * @type
+ * @set_event: enable or disable events of type @type
+ * @event_type_str: return a string representation of custom event @type
+ * (@type >= %ADF_EVENT_DEVICE_CUSTOM).
+ *
+ * @custom_data: copy up to %ADF_MAX_CUSTOM_DATA_SIZE bytes of driver-private
+ * data into @data (allocated by ADF) and return the number of copied bytes
+ * in @size. Return 0 on success or an error code (<0) on failure.
+ */
+struct adf_obj_ops {
+ /* optional */
+ int (*open)(struct adf_obj *obj, struct inode *inode,
+ struct file *file);
+ /* optional */
+ void (*release)(struct adf_obj *obj, struct inode *inode,
+ struct file *file);
+ /* optional */
+ long (*ioctl)(struct adf_obj *obj, unsigned int cmd, unsigned long arg);
+
+ /* optional */
+ bool (*supports_event)(struct adf_obj *obj, enum adf_event_type type);
+ /* required if supports_event is implemented */
+ void (*set_event)(struct adf_obj *obj, enum adf_event_type type,
+ bool enabled);
+ /* optional */
+ const char *(*event_type_str)(struct adf_obj *obj,
+ enum adf_event_type type);
+
+ /* optional */
+ int (*custom_data)(struct adf_obj *obj, void *data, size_t *size);
+};
+
+struct adf_obj {
+ enum adf_obj_type type;
+ char name[ADF_NAME_LEN];
+ struct adf_device *parent;
+
+ const struct adf_obj_ops *ops;
+
+ struct device dev;
+
+ struct spinlock file_lock;
+ struct list_head file_list;
+
+ struct mutex event_lock;
+ struct rb_root event_refcount;
+
+ int id;
+ int minor;
+};
+
+/**
+ * struct adf_device_ops - display device implementation ops
+ *
+ * @owner: device's module
+ * @base: common operations (see &struct adf_obj_ops)
+ *
+ * @attach: attach overlay engine @eng to interface @intf. Return 0 on success
+ * or error code (<0) on failure.
+ * @detach: detach overlay engine @eng from interface @intf. Return 0 on
+ * success or error code (<0) on failure.
+ *
+ * @validate_custom_format: validate the number and size of planes
+ * in buffers with a custom format (i.e., not one of the @DRM_FORMAT_*
+ * types defined in drm/drm_fourcc.h). Return 0 if the buffer is valid or
+ * an error code (<0) otherwise.
+ *
+ * @validate: validate that the proposed configuration @cfg is legal. The
+ * driver may optionally allocate and return some driver-private state in
+ * @driver_state, which will be passed to the corresponding post(). The
+ * driver may NOT commit any changes to hardware. Return 0 if @cfg is
+ * valid or an error code (<0) otherwise.
+ * @complete_fence: create a hardware-backed sync fence to be signaled when
+ * @cfg is removed from the screen. If unimplemented, ADF automatically
+ * creates an sw_sync fence. Return the sync fence on success or a
+ * PTR_ERR() on failure.
+ * @post: flip @cfg onto the screen. Wait for the display to begin scanning out
+ * @cfg before returning.
+ * @advance_timeline: signal the sync fence for the last configuration to leave
+ * the display. If unimplemented, ADF automatically advances an sw_sync
+ * timeline.
+ * @state_free: free driver-private state allocated during validate()
+ */
+struct adf_device_ops {
+ /* required */
+ struct module *owner;
+ const struct adf_obj_ops base;
+
+ /* optional */
+ int (*attach)(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+ /* optional */
+ int (*detach)(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+
+ /* required if any of the device's overlay engines supports at least one
+ custom format */
+ int (*validate_custom_format)(struct adf_device *dev,
+ struct adf_buffer *buf);
+
+ /* required */
+ int (*validate)(struct adf_device *dev, struct adf_post *cfg,
+ void **driver_state);
+ /* optional */
+ struct sync_fence *(*complete_fence)(struct adf_device *dev,
+ struct adf_post *cfg, void *driver_state);
+ /* required */
+ void (*post)(struct adf_device *dev, struct adf_post *cfg,
+ void *driver_state);
+ /* required if complete_fence is implemented */
+ void (*advance_timeline)(struct adf_device *dev,
+ struct adf_post *cfg, void *driver_state);
+ /* required if validate allocates driver state */
+ void (*state_free)(struct adf_device *dev, void *driver_state);
+};
+
+struct adf_attachment_list {
+ struct adf_attachment attachment;
+ struct list_head head;
+};
+
+struct adf_device {
+ struct adf_obj base;
+ struct device *dev;
+
+ const struct adf_device_ops *ops;
+
+ struct mutex client_lock;
+
+ struct idr interfaces;
+ size_t n_interfaces;
+ struct idr overlay_engines;
+
+ struct list_head post_list;
+ struct mutex post_lock;
+ struct kthread_worker post_worker;
+ struct task_struct *post_thread;
+ struct kthread_work post_work;
+
+ struct list_head attached;
+ size_t n_attached;
+ struct list_head attach_allowed;
+ size_t n_attach_allowed;
+
+ struct adf_pending_post *onscreen;
+
+ struct sw_sync_timeline *timeline;
+ int timeline_max;
+};
+
+/**
+ * struct adf_interface_ops - display interface implementation ops
+ *
+ * @base: common operations (see &struct adf_obj_ops)
+ *
+ * @blank: change the display's DPMS state. Return 0 on success or error
+ * code (<0) on failure.
+ *
+ * @alloc_simple_buffer: allocate a buffer with the specified @w, @h, and
+ * @format. @format will be a standard RGB format (i.e.,
+ * adf_format_is_rgb(@format) == true). Return 0 on success or error code
+ * (<0) on failure. On success, return the buffer, offset, and pitch in
+ * @dma_buf, @offset, and @pitch respectively.
+ * @describe_simple_post: provide driver-private data needed to post a single
+ * buffer @buf. Copy up to ADF_MAX_CUSTOM_DATA_SIZE bytes into @data
+ * (allocated by ADF) and return the number of bytes in @size. Return 0 on
+ * success or error code (<0) on failure.
+ *
+ * @modeset: change the interface's mode. @mode is not necessarily part of the
+ * modelist passed to adf_hotplug_notify_connected(); the driver may
+ * accept or reject custom modes at its discretion. Return 0 on success or
+ * error code (<0) if the mode could not be set.
+ *
+ * @screen_size: copy the screen dimensions in millimeters into @width_mm
+ * and @height_mm. Return 0 on success or error code (<0) if the display
+ * dimensions are unknown.
+ *
+ * @type_str: return a string representation of custom @intf->type
+ * (@intf->type >= @ADF_INTF_TYPE_DEVICE_CUSTOM).
+ */
+struct adf_interface_ops {
+ const struct adf_obj_ops base;
+
+ /* optional */
+ int (*blank)(struct adf_interface *intf, u8 state);
+
+ /* optional */
+ int (*alloc_simple_buffer)(struct adf_interface *intf,
+ u16 w, u16 h, u32 format,
+ struct dma_buf **dma_buf, u32 *offset, u32 *pitch);
+ /* optional */
+ int (*describe_simple_post)(struct adf_interface *intf,
+ struct adf_buffer *fb, void *data, size_t *size);
+
+ /* optional */
+ int (*modeset)(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode);
+
+ /* optional */
+ int (*screen_size)(struct adf_interface *intf, u16 *width_mm,
+ u16 *height_mm);
+
+ /* optional */
+ const char *(*type_str)(struct adf_interface *intf);
+};
+
+struct adf_interface {
+ struct adf_obj base;
+ const struct adf_interface_ops *ops;
+
+ struct drm_mode_modeinfo current_mode;
+
+ enum adf_interface_type type;
+ u32 idx;
+ u32 flags;
+
+ wait_queue_head_t vsync_wait;
+ ktime_t vsync_timestamp;
+ rwlock_t vsync_lock;
+
+ u8 dpms_state;
+
+ bool hotplug_detect;
+ struct drm_mode_modeinfo *modelist;
+ size_t n_modes;
+ rwlock_t hotplug_modelist_lock;
+};
+
+/**
+ * struct adf_interface_ops - overlay engine implementation ops
+ *
+ * @base: common operations (see &struct adf_obj_ops)
+ *
+ * @supported_formats: list of fourccs the overlay engine can scan out
+ * @n_supported_formats: length of supported_formats, up to
+ * ADF_MAX_SUPPORTED_FORMATS
+ */
+struct adf_overlay_engine_ops {
+ const struct adf_obj_ops base;
+
+ /* required */
+ const u32 *supported_formats;
+ /* required */
+ const size_t n_supported_formats;
+};
+
+struct adf_overlay_engine {
+ struct adf_obj base;
+
+ const struct adf_overlay_engine_ops *ops;
+};
+
+#define adf_obj_to_device(ptr) \
+ container_of((ptr), struct adf_device, base)
+
+#define adf_obj_to_interface(ptr) \
+ container_of((ptr), struct adf_interface, base)
+
+#define adf_obj_to_overlay_engine(ptr) \
+ container_of((ptr), struct adf_overlay_engine, base)
+
+int __printf(4, 5) adf_device_init(struct adf_device *dev,
+ struct device *parent, const struct adf_device_ops *ops,
+ const char *fmt, ...);
+void adf_device_destroy(struct adf_device *dev);
+int __printf(7, 8) adf_interface_init(struct adf_interface *intf,
+ struct adf_device *dev, enum adf_interface_type type, u32 idx,
+ u32 flags, const struct adf_interface_ops *ops, const char *fmt,
+ ...);
+void adf_interface_destroy(struct adf_interface *intf);
+static inline struct adf_device *adf_interface_parent(
+ struct adf_interface *intf)
+{
+ return intf->base.parent;
+}
+int __printf(4, 5) adf_overlay_engine_init(struct adf_overlay_engine *eng,
+ struct adf_device *dev,
+ const struct adf_overlay_engine_ops *ops, const char *fmt, ...);
+void adf_overlay_engine_destroy(struct adf_overlay_engine *eng);
+static inline struct adf_device *adf_overlay_engine_parent(
+ struct adf_overlay_engine *eng)
+{
+ return eng->base.parent;
+}
+
+int adf_attachment_allow(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+
+const char *adf_obj_type_str(enum adf_obj_type type);
+const char *adf_interface_type_str(struct adf_interface *intf);
+const char *adf_event_type_str(struct adf_obj *obj, enum adf_event_type type);
+
+#define ADF_FORMAT_STR_SIZE 5
+void adf_format_str(u32 format, char buf[ADF_FORMAT_STR_SIZE]);
+int adf_format_validate_yuv(struct adf_device *dev, struct adf_buffer *buf,
+ u8 num_planes, u8 hsub, u8 vsub, u8 cpp[]);
+/**
+ * adf_format_validate_rgb - validate the number and size of planes in buffers
+ * with a custom RGB format.
+ *
+ * @dev: ADF device performing the validation
+ * @buf: buffer to validate
+ * @cpp: expected bytes per pixel
+ *
+ * adf_format_validate_rgb() is intended to be called as a helper from @dev's
+ * validate_custom_format() op. @buf must have a single RGB plane.
+ *
+ * Returns 0 if @buf has a single plane with sufficient size, or -EINVAL
+ * otherwise.
+ */
+static inline int adf_format_validate_rgb(struct adf_device *dev,
+ struct adf_buffer *buf, u8 cpp)
+{
+ return adf_format_validate_yuv(dev, buf, 1, 1, 1, &cpp);
+}
+
+int adf_event_get(struct adf_obj *obj, enum adf_event_type type);
+int adf_event_put(struct adf_obj *obj, enum adf_event_type type);
+int adf_event_notify(struct adf_obj *obj, struct adf_event *event);
+
+static inline void adf_vsync_get(struct adf_interface *intf)
+{
+ adf_event_get(&intf->base, ADF_EVENT_VSYNC);
+}
+
+static inline void adf_vsync_put(struct adf_interface *intf)
+{
+ adf_event_put(&intf->base, ADF_EVENT_VSYNC);
+}
+
+int adf_vsync_wait(struct adf_interface *intf, long timeout);
+void adf_vsync_notify(struct adf_interface *intf, ktime_t timestamp);
+
+int adf_hotplug_notify_connected(struct adf_interface *intf,
+ struct drm_mode_modeinfo *modelist, size_t n_modes);
+void adf_hotplug_notify_disconnected(struct adf_interface *intf);
+
+void adf_modeinfo_set_name(struct drm_mode_modeinfo *mode);
+void adf_modeinfo_set_vrefresh(struct drm_mode_modeinfo *mode);
+
+#endif /* _VIDEO_ADF_H */
diff --git a/include/video/adf_client.h b/include/video/adf_client.h
new file mode 100644
index 000000000000..983f2b6a5890
--- /dev/null
+++ b/include/video/adf_client.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _VIDEO_ADF_CLIENT_H_
+#define _VIDEO_ADF_CLIENT_H_
+
+#include <video/adf.h>
+
+int adf_interface_blank(struct adf_interface *intf, u8 state);
+u8 adf_interface_dpms_state(struct adf_interface *intf);
+
+void adf_interface_current_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode);
+size_t adf_interface_modelist(struct adf_interface *intf,
+ struct drm_mode_modeinfo *modelist, size_t n_modes);
+int adf_interface_set_mode(struct adf_interface *intf,
+ struct drm_mode_modeinfo *mode);
+int adf_interface_get_screen_size(struct adf_interface *intf, u16 *width,
+ u16 *height);
+int adf_interface_simple_buffer_alloc(struct adf_interface *intf, u16 w, u16 h,
+ u32 format, struct dma_buf **dma_buf, u32 *offset, u32 *pitch);
+struct sync_fence *adf_interface_simple_post(struct adf_interface *intf,
+ struct adf_buffer *buf);
+
+bool adf_overlay_engine_supports_format(struct adf_overlay_engine *eng,
+ u32 format);
+
+size_t adf_device_attachments(struct adf_device *dev,
+ struct adf_attachment *attachments, size_t n_attachments);
+size_t adf_device_attachments_allowed(struct adf_device *dev,
+ struct adf_attachment *attachments, size_t n_attachments);
+bool adf_device_attached(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+bool adf_device_attach_allowed(struct adf_device *dev,
+ struct adf_overlay_engine *eng, struct adf_interface *intf);
+int adf_device_attach(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+int adf_device_detach(struct adf_device *dev, struct adf_overlay_engine *eng,
+ struct adf_interface *intf);
+
+struct sync_fence *adf_device_post(struct adf_device *dev,
+ struct adf_interface **intfs, size_t n_intfs,
+ struct adf_buffer *bufs, size_t n_bufs, void *custom_data,
+ size_t custom_data_size);
+struct sync_fence *adf_device_post_nocopy(struct adf_device *dev,
+ struct adf_interface **intfs, size_t n_intfs,
+ struct adf_buffer *bufs, size_t n_bufs, void *custom_data,
+ size_t custom_data_size);
+
+#endif /* _VIDEO_ADF_CLIENT_H_ */
diff --git a/include/video/adf_fbdev.h b/include/video/adf_fbdev.h
new file mode 100644
index 000000000000..9c349144b5cd
--- /dev/null
+++ b/include/video/adf_fbdev.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _VIDEO_ADF_FBDEV_H_
+#define _VIDEO_ADF_FBDEV_H_
+
+#include <linux/fb.h>
+#include <video/adf.h>
+
+struct adf_fbdev {
+ struct adf_interface *intf;
+ struct adf_overlay_engine *eng;
+ struct fb_info *info;
+ u32 pseudo_palette[16];
+
+ bool open;
+
+ struct dma_buf *dma_buf;
+ u32 offset;
+ u32 pitch;
+ void *vaddr;
+ u32 format;
+
+ u16 default_xres_virtual;
+ u16 default_yres_virtual;
+ u32 default_format;
+};
+
+void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode,
+ struct fb_videomode *vmode);
+void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode,
+ struct drm_mode_modeinfo *mode);
+
+int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface,
+ struct adf_overlay_engine *eng,
+ u16 xres_virtual, u16 yres_virtual, u32 format,
+ struct fb_ops *fbops, const char *fmt, ...);
+void adf_fbdev_destroy(struct adf_fbdev *fbdev);
+
+int adf_fbdev_open(struct fb_info *info, int user);
+int adf_fbdev_release(struct fb_info *info, int user);
+int adf_fbdev_check_var(struct fb_var_screeninfo *var, struct fb_info *info);
+int adf_fbdev_set_par(struct fb_info *info);
+int adf_fbdev_blank(int blank, struct fb_info *info);
+int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
+int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma);
+
+#endif /* _VIDEO_ADF_FBDEV_H_ */
diff --git a/include/video/adf_format.h b/include/video/adf_format.h
new file mode 100644
index 000000000000..e03182cdcb06
--- /dev/null
+++ b/include/video/adf_format.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _VIDEO_ADF_FORMAT_H
+#define _VIDEO_ADF_FORMAT_H
+
+bool adf_format_is_standard(u32 format);
+bool adf_format_is_rgb(u32 format);
+u8 adf_format_num_planes(u32 format);
+u8 adf_format_bpp(u32 format);
+u8 adf_format_plane_cpp(u32 format, int plane);
+u8 adf_format_horz_chroma_subsampling(u32 format);
+u8 adf_format_vert_chroma_subsampling(u32 format);
+
+#endif /* _VIDEO_ADF_FORMAT_H */
diff --git a/include/video/adf_memblock.h b/include/video/adf_memblock.h
new file mode 100644
index 000000000000..6256e0eebcc9
--- /dev/null
+++ b/include/video/adf_memblock.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _VIDEO_ADF_MEMBLOCK_H_
+#define _VIDEO_ADF_MEMBLOCK_H_
+
+struct dma_buf *adf_memblock_export(phys_addr_t base, size_t size, int flags);
+
+#endif /* _VIDEO_ADF_MEMBLOCK_H_ */
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 81e2a388a0f6..295c1a1d6499 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -765,3 +765,23 @@ void init_cpu_online(const struct cpumask *src)
{
cpumask_copy(to_cpumask(cpu_online_bits), src);
}
+
+static ATOMIC_NOTIFIER_HEAD(idle_notifier);
+
+void idle_notifier_register(struct notifier_block *n)
+{
+ atomic_notifier_chain_register(&idle_notifier, n);
+}
+EXPORT_SYMBOL_GPL(idle_notifier_register);
+
+void idle_notifier_unregister(struct notifier_block *n)
+{
+ atomic_notifier_chain_unregister(&idle_notifier, n);
+}
+EXPORT_SYMBOL_GPL(idle_notifier_unregister);
+
+void idle_notifier_call_chain(unsigned long val)
+{
+ atomic_notifier_call_chain(&idle_notifier, val, NULL);
+}
+EXPORT_SYMBOL_GPL(idle_notifier_call_chain);
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c
index 1adf62b39b96..af0612216811 100644
--- a/kernel/debug/debug_core.c
+++ b/kernel/debug/debug_core.c
@@ -87,6 +87,10 @@ static int kgdb_use_con;
bool dbg_is_early = true;
/* Next cpu to become the master debug core */
int dbg_switch_cpu;
+/* Flag for entering kdb when a panic occurs */
+static bool break_on_panic = true;
+/* Flag for entering kdb when an exception occurs */
+static bool break_on_exception = true;
/* Use kdb or gdbserver mode */
int dbg_kdb_mode = 1;
@@ -101,6 +105,8 @@ early_param("kgdbcon", opt_kgdb_con);
module_param(kgdb_use_con, int, 0644);
module_param(kgdbreboot, int, 0644);
+module_param(break_on_panic, bool, 0644);
+module_param(break_on_exception, bool, 0644);
/*
* Holds information about breakpoints in a kernel. These breakpoints are
@@ -690,6 +696,9 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
if (arch_kgdb_ops.enable_nmi)
arch_kgdb_ops.enable_nmi(0);
+ if (unlikely(signo != SIGTRAP && !break_on_exception))
+ return 1;
+
memset(ks, 0, sizeof(struct kgdb_state));
ks->cpu = raw_smp_processor_id();
ks->ex_vector = evector;
@@ -821,6 +830,9 @@ static int kgdb_panic_event(struct notifier_block *self,
unsigned long val,
void *data)
{
+ if (!break_on_panic)
+ return NOTIFY_DONE;
+
if (dbg_kdb_mode)
kdb_printf("PANIC: %s\n", (char *)data);
kgdb_breakpoint();
diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index 7c70812caea5..13a1e6b5c40e 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -216,7 +216,7 @@ static char *kdb_read(char *buffer, size_t bufsize)
int i;
int diag, dtab_count;
int key;
-
+ static int last_crlf;
diag = kdbgetintenv("DTABCOUNT", &dtab_count);
if (diag)
@@ -237,6 +237,9 @@ poll_again:
return buffer;
if (key != 9)
tab = 0;
+ if (key != 10 && key != 13)
+ last_crlf = 0;
+
switch (key) {
case 8: /* backspace */
if (cp > buffer) {
@@ -254,7 +257,12 @@ poll_again:
*cp = tmp;
}
break;
- case 13: /* enter */
+ case 10: /* new line */
+ case 13: /* carriage return */
+ /* handle \n after \r */
+ if (last_crlf && last_crlf != key)
+ break;
+ last_crlf = key;
*lastchar++ = '\n';
*lastchar++ = '\0';
if (!KDB_STATE(KGDB_TRANS)) {
diff --git a/kernel/fork.c b/kernel/fork.c
index 0cf9cdb6e491..6eaa321dd218 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -201,6 +201,9 @@ struct kmem_cache *vm_area_cachep;
/* SLAB cache for mm_struct structures (tsk->mm) */
static struct kmem_cache *mm_cachep;
+/* Notifier list called when a task struct is freed */
+static ATOMIC_NOTIFIER_HEAD(task_free_notifier);
+
static void account_kernel_stack(struct thread_info *ti, int account)
{
struct zone *zone = page_zone(virt_to_page(ti));
@@ -234,6 +237,18 @@ static inline void put_signal_struct(struct signal_struct *sig)
free_signal_struct(sig);
}
+int task_free_register(struct notifier_block *n)
+{
+ return atomic_notifier_chain_register(&task_free_notifier, n);
+}
+EXPORT_SYMBOL(task_free_register);
+
+int task_free_unregister(struct notifier_block *n)
+{
+ return atomic_notifier_chain_unregister(&task_free_notifier, n);
+}
+EXPORT_SYMBOL(task_free_unregister);
+
void __put_task_struct(struct task_struct *tsk)
{
WARN_ON(!tsk->exit_state);
@@ -246,6 +261,7 @@ void __put_task_struct(struct task_struct *tsk)
delayacct_tsk_free(tsk);
put_signal_struct(tsk->signal);
+ atomic_notifier_call_chain(&task_free_notifier, 0, tsk);
if (!profile_handoff_task(tsk))
free_task(tsk);
}
@@ -730,7 +746,8 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
mm = get_task_mm(task);
if (mm && mm != current->mm &&
- !ptrace_may_access(task, mode)) {
+ !ptrace_may_access(task, mode) &&
+ !capable(CAP_SYS_RESOURCE)) {
mmput(mm);
mm = ERR_PTR(-EACCES);
}
diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c
index abcd6ca86cb7..c72b7a43beb9 100644
--- a/kernel/irq/pm.c
+++ b/kernel/irq/pm.c
@@ -103,14 +103,14 @@ int check_wakeup_irqs(void)
int irq;
for_each_irq_desc(irq, desc) {
- /*
- * Only interrupts which are marked as wakeup source
- * and have not been disabled before the suspend check
- * can abort suspend.
- */
if (irqd_is_wakeup_set(&desc->irq_data)) {
- if (desc->depth == 1 && desc->istate & IRQS_PENDING)
+ if (desc->istate & IRQS_PENDING) {
+ pr_info("Wakeup IRQ %d %s pending, suspend aborted\n",
+ irq,
+ desc->action && desc->action->name ?
+ desc->action->name : "");
return -EBUSY;
+ }
continue;
}
/*
diff --git a/kernel/panic.c b/kernel/panic.c
index d09dc5c32c67..12c9b51e0830 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -27,6 +27,9 @@
#define PANIC_TIMER_STEP 100
#define PANIC_BLINK_SPD 18
+/* Machine specific panic information string */
+char *mach_panic_string;
+
int panic_on_oops = CONFIG_PANIC_ON_OOPS_VALUE;
static unsigned long tainted_mask;
static int pause_on_oops;
@@ -395,6 +398,10 @@ late_initcall(init_oops_id);
void print_oops_end_marker(void)
{
init_oops_id();
+ if (mach_panic_string)
+ printk(KERN_WARNING "Board Information: %s\n",
+ mach_panic_string);
+
pr_warn("---[ end trace %016llx ]---\n", (unsigned long long)oops_id);
}
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index e4e4121fa327..d50a60fd80a7 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -18,6 +18,14 @@ config SUSPEND_FREEZER
Turning OFF this setting is NOT recommended! If in doubt, say Y.
+config HAS_WAKELOCK
+ bool
+ default y
+
+config WAKELOCK
+ bool
+ default y
+
config HIBERNATE_CALLBACKS
bool
@@ -305,3 +313,10 @@ config PM_GENERIC_DOMAINS_RUNTIME
config CPU_PM
bool
depends on SUSPEND || CPU_IDLE
+
+config SUSPEND_TIME
+ bool "Log time spent in suspend"
+ ---help---
+ Prints the time spent in suspend in the kernel log, and
+ keeps statistics on the time spent in suspend in
+ /sys/kernel/debug/suspend_time
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 29472bff11ef..74c713ba61b0 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -11,5 +11,8 @@ obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \
block_io.o
obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o
obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o
+obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
+
+obj-$(CONFIG_SUSPEND) += wakeup_reason.o
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 6dadb25cb0d8..ff75f6c46744 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -26,6 +26,7 @@
#include <linux/suspend.h>
#include <linux/syscore_ops.h>
#include <linux/ftrace.h>
+#include <linux/rtc.h>
#include <trace/events/power.h>
#include <linux/compiler.h>
@@ -443,6 +444,18 @@ static int enter_state(suspend_state_t state)
return error;
}
+static void pm_suspend_marker(char *annotation)
+{
+ struct timespec ts;
+ struct rtc_time tm;
+
+ getnstimeofday(&ts);
+ rtc_time_to_tm(ts.tv_sec, &tm);
+ pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
+ annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+}
+
/**
* pm_suspend - Externally visible function for suspending the system.
* @state: System sleep state to enter.
@@ -457,6 +470,7 @@ int pm_suspend(suspend_state_t state)
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
return -EINVAL;
+ pm_suspend_marker("entry");
error = enter_state(state);
if (error) {
suspend_stats.fail++;
@@ -464,6 +478,7 @@ int pm_suspend(suspend_state_t state)
} else {
suspend_stats.success++;
}
+ pm_suspend_marker("exit");
return error;
}
EXPORT_SYMBOL(pm_suspend);
diff --git a/kernel/power/suspend_time.c b/kernel/power/suspend_time.c
new file mode 100644
index 000000000000..d2a65da9f22c
--- /dev/null
+++ b/kernel/power/suspend_time.c
@@ -0,0 +1,111 @@
+/*
+ * debugfs file to track time spent in suspend
+ *
+ * Copyright (c) 2011, Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <linux/syscore_ops.h>
+#include <linux/time.h>
+
+static struct timespec suspend_time_before;
+static unsigned int time_in_suspend_bins[32];
+
+#ifdef CONFIG_DEBUG_FS
+static int suspend_time_debug_show(struct seq_file *s, void *data)
+{
+ int bin;
+ seq_printf(s, "time (secs) count\n");
+ seq_printf(s, "------------------\n");
+ for (bin = 0; bin < 32; bin++) {
+ if (time_in_suspend_bins[bin] == 0)
+ continue;
+ seq_printf(s, "%4d - %4d %4u\n",
+ bin ? 1 << (bin - 1) : 0, 1 << bin,
+ time_in_suspend_bins[bin]);
+ }
+ return 0;
+}
+
+static int suspend_time_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, suspend_time_debug_show, NULL);
+}
+
+static const struct file_operations suspend_time_debug_fops = {
+ .open = suspend_time_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init suspend_time_debug_init(void)
+{
+ struct dentry *d;
+
+ d = debugfs_create_file("suspend_time", 0755, NULL, NULL,
+ &suspend_time_debug_fops);
+ if (!d) {
+ pr_err("Failed to create suspend_time debug file\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+late_initcall(suspend_time_debug_init);
+#endif
+
+static int suspend_time_syscore_suspend(void)
+{
+ read_persistent_clock(&suspend_time_before);
+
+ return 0;
+}
+
+static void suspend_time_syscore_resume(void)
+{
+ struct timespec after;
+
+ read_persistent_clock(&after);
+
+ after = timespec_sub(after, suspend_time_before);
+
+ time_in_suspend_bins[fls(after.tv_sec)]++;
+
+ pr_info("Suspended for %lu.%03lu seconds\n", after.tv_sec,
+ after.tv_nsec / NSEC_PER_MSEC);
+}
+
+static struct syscore_ops suspend_time_syscore_ops = {
+ .suspend = suspend_time_syscore_suspend,
+ .resume = suspend_time_syscore_resume,
+};
+
+static int suspend_time_syscore_init(void)
+{
+ register_syscore_ops(&suspend_time_syscore_ops);
+
+ return 0;
+}
+
+static void suspend_time_syscore_exit(void)
+{
+ unregister_syscore_ops(&suspend_time_syscore_ops);
+}
+module_init(suspend_time_syscore_init);
+module_exit(suspend_time_syscore_exit);
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
index 019069c84ff6..0936d146b7e6 100644
--- a/kernel/power/wakelock.c
+++ b/kernel/power/wakelock.c
@@ -9,7 +9,6 @@
* manipulate wakelocks on Android.
*/
-#include <linux/capability.h>
#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -191,9 +190,6 @@ int pm_wake_lock(const char *buf)
size_t len;
int ret = 0;
- if (!capable(CAP_BLOCK_SUSPEND))
- return -EPERM;
-
while (*str && !isspace(*str))
str++;
@@ -237,9 +233,6 @@ int pm_wake_unlock(const char *buf)
size_t len;
int ret = 0;
- if (!capable(CAP_BLOCK_SUSPEND))
- return -EPERM;
-
len = strlen(buf);
if (!len)
return -EINVAL;
diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c
new file mode 100644
index 000000000000..e8ec0f9edb17
--- /dev/null
+++ b/kernel/power/wakeup_reason.c
@@ -0,0 +1,140 @@
+/*
+ * kernel/power/wakeup_reason.c
+ *
+ * Logs the reasons which caused the kernel to resume from
+ * the suspend mode.
+ *
+ * Copyright (C) 2014 Google, Inc.
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/wakeup_reason.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/notifier.h>
+#include <linux/suspend.h>
+
+
+#define MAX_WAKEUP_REASON_IRQS 32
+static int irq_list[MAX_WAKEUP_REASON_IRQS];
+static int irqcount;
+static struct kobject *wakeup_reason;
+static spinlock_t resume_reason_lock;
+
+static ssize_t reason_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ int irq_no, buf_offset = 0;
+ struct irq_desc *desc;
+ spin_lock(&resume_reason_lock);
+ for (irq_no = 0; irq_no < irqcount; irq_no++) {
+ desc = irq_to_desc(irq_list[irq_no]);
+ if (desc && desc->action && desc->action->name)
+ buf_offset += sprintf(buf + buf_offset, "%d %s\n",
+ irq_list[irq_no], desc->action->name);
+ else
+ buf_offset += sprintf(buf + buf_offset, "%d\n",
+ irq_list[irq_no]);
+ }
+ spin_unlock(&resume_reason_lock);
+ return buf_offset;
+}
+
+static struct kobj_attribute resume_reason = __ATTR(last_resume_reason, 0444,
+ reason_show, NULL);
+
+static struct attribute *attrs[] = {
+ &resume_reason.attr,
+ NULL,
+};
+static struct attribute_group attr_group = {
+ .attrs = attrs,
+};
+
+/*
+ * logs all the wake up reasons to the kernel
+ * stores the irqs to expose them to the userspace via sysfs
+ */
+void log_wakeup_reason(int irq)
+{
+ struct irq_desc *desc;
+ desc = irq_to_desc(irq);
+ if (desc && desc->action && desc->action->name)
+ printk(KERN_INFO "Resume caused by IRQ %d, %s\n", irq,
+ desc->action->name);
+ else
+ printk(KERN_INFO "Resume caused by IRQ %d\n", irq);
+
+ spin_lock(&resume_reason_lock);
+ if (irqcount == MAX_WAKEUP_REASON_IRQS) {
+ spin_unlock(&resume_reason_lock);
+ printk(KERN_WARNING "Resume caused by more than %d IRQs\n",
+ MAX_WAKEUP_REASON_IRQS);
+ return;
+ }
+
+ irq_list[irqcount++] = irq;
+ spin_unlock(&resume_reason_lock);
+}
+
+/* Detects a suspend and clears all the previous wake up reasons*/
+static int wakeup_reason_pm_event(struct notifier_block *notifier,
+ unsigned long pm_event, void *unused)
+{
+ switch (pm_event) {
+ case PM_SUSPEND_PREPARE:
+ spin_lock(&resume_reason_lock);
+ irqcount = 0;
+ spin_unlock(&resume_reason_lock);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block wakeup_reason_pm_notifier_block = {
+ .notifier_call = wakeup_reason_pm_event,
+};
+
+/* Initializes the sysfs parameter
+ * registers the pm_event notifier
+ */
+int __init wakeup_reason_init(void)
+{
+ int retval;
+ spin_lock_init(&resume_reason_lock);
+ retval = register_pm_notifier(&wakeup_reason_pm_notifier_block);
+ if (retval)
+ printk(KERN_WARNING "[%s] failed to register PM notifier %d\n",
+ __func__, retval);
+
+ wakeup_reason = kobject_create_and_add("wakeup_reasons", kernel_kobj);
+ if (!wakeup_reason) {
+ printk(KERN_WARNING "[%s] failed to create a sysfs kobject\n",
+ __func__);
+ return 1;
+ }
+ retval = sysfs_create_group(wakeup_reason, &attr_group);
+ if (retval) {
+ kobject_put(wakeup_reason);
+ printk(KERN_WARNING "[%s] failed to create a sysfs group %d\n",
+ __func__, retval);
+ }
+ return 0;
+}
+
+late_initcall(wakeup_reason_init);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index e04c455a0e38..36229af94465 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -55,6 +55,10 @@
#include "console_cmdline.h"
#include "braille.h"
+#ifdef CONFIG_EARLY_PRINTK_DIRECT
+extern void printascii(char *);
+#endif
+
int console_printk[4] = {
CONSOLE_LOGLEVEL_DEFAULT, /* console_loglevel */
MESSAGE_LOGLEVEL_DEFAULT, /* default_message_loglevel */
@@ -1716,6 +1720,10 @@ asmlinkage int vprintk_emit(int facility, int level,
}
}
+#ifdef CONFIG_EARLY_PRINTK_DIRECT
+ printascii(text);
+#endif
+
if (level == -1)
level = default_message_loglevel;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index ec1a286684a5..a76a8cfd1298 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7080,6 +7080,14 @@ static inline int preempt_count_equals(int preempt_offset)
return (nested == preempt_offset);
}
+static int __might_sleep_init_called;
+int __init __might_sleep_init(void)
+{
+ __might_sleep_init_called = 1;
+ return 0;
+}
+early_initcall(__might_sleep_init);
+
void __might_sleep(const char *file, int line, int preempt_offset)
{
static unsigned long prev_jiffy; /* ratelimiting */
@@ -7754,6 +7762,24 @@ static void cpu_cgroup_css_offline(struct cgroup_subsys_state *css)
sched_offline_group(tg);
}
+static int cpu_cgroup_allow_attach(struct cgroup_subsys_state *css,
+ struct cgroup_taskset *tset)
+{
+ const struct cred *cred = current_cred(), *tcred;
+ struct task_struct *task;
+
+ cgroup_taskset_for_each(task, tset) {
+ tcred = __task_cred(task);
+
+ if ((current != task) && !capable(CAP_SYS_NICE) &&
+ !uid_eq(cred->euid, tcred->uid) &&
+ !uid_eq(cred->euid, tcred->suid))
+ return -EACCES;
+ }
+
+ return 0;
+}
+
static int cpu_cgroup_can_attach(struct cgroup_subsys_state *css,
struct cgroup_taskset *tset)
{
@@ -8128,6 +8154,7 @@ struct cgroup_subsys cpu_cgrp_subsys = {
.css_offline = cpu_cgroup_css_offline,
.can_attach = cpu_cgroup_can_attach,
.attach = cpu_cgroup_attach,
+ .allow_attach = cpu_cgroup_allow_attach,
.exit = cpu_cgroup_exit,
.legacy_cftypes = cpu_files,
.early_init = 1,
diff --git a/kernel/sys.c b/kernel/sys.c
index ce8129192a26..60a567ca2c56 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -41,6 +41,8 @@
#include <linux/syscore_ops.h>
#include <linux/version.h>
#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/mempolicy.h>
#include <linux/compat.h>
#include <linux/syscalls.h>
@@ -1833,6 +1835,153 @@ static int prctl_get_tid_address(struct task_struct *me, int __user **tid_addr)
}
#endif
+#ifdef CONFIG_MMU
+static int prctl_update_vma_anon_name(struct vm_area_struct *vma,
+ struct vm_area_struct **prev,
+ unsigned long start, unsigned long end,
+ const char __user *name_addr)
+{
+ struct mm_struct * mm = vma->vm_mm;
+ int error = 0;
+ pgoff_t pgoff;
+
+ if (name_addr == vma_get_anon_name(vma)) {
+ *prev = vma;
+ goto out;
+ }
+
+ pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
+ *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma,
+ vma->vm_file, pgoff, vma_policy(vma),
+ name_addr);
+ if (*prev) {
+ vma = *prev;
+ goto success;
+ }
+
+ *prev = vma;
+
+ if (start != vma->vm_start) {
+ error = split_vma(mm, vma, start, 1);
+ if (error)
+ goto out;
+ }
+
+ if (end != vma->vm_end) {
+ error = split_vma(mm, vma, end, 0);
+ if (error)
+ goto out;
+ }
+
+success:
+ if (!vma->vm_file)
+ vma->shared.anon_name = name_addr;
+
+out:
+ if (error == -ENOMEM)
+ error = -EAGAIN;
+ return error;
+}
+
+static int prctl_set_vma_anon_name(unsigned long start, unsigned long end,
+ unsigned long arg)
+{
+ unsigned long tmp;
+ struct vm_area_struct * vma, *prev;
+ int unmapped_error = 0;
+ int error = -EINVAL;
+
+ /*
+ * If the interval [start,end) covers some unmapped address
+ * ranges, just ignore them, but return -ENOMEM at the end.
+ * - this matches the handling in madvise.
+ */
+ vma = find_vma_prev(current->mm, start, &prev);
+ if (vma && start > vma->vm_start)
+ prev = vma;
+
+ for (;;) {
+ /* Still start < end. */
+ error = -ENOMEM;
+ if (!vma)
+ return error;
+
+ /* Here start < (end|vma->vm_end). */
+ if (start < vma->vm_start) {
+ unmapped_error = -ENOMEM;
+ start = vma->vm_start;
+ if (start >= end)
+ return error;
+ }
+
+ /* Here vma->vm_start <= start < (end|vma->vm_end) */
+ tmp = vma->vm_end;
+ if (end < tmp)
+ tmp = end;
+
+ /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */
+ error = prctl_update_vma_anon_name(vma, &prev, start, end,
+ (const char __user *)arg);
+ if (error)
+ return error;
+ start = tmp;
+ if (prev && start < prev->vm_end)
+ start = prev->vm_end;
+ error = unmapped_error;
+ if (start >= end)
+ return error;
+ if (prev)
+ vma = prev->vm_next;
+ else /* madvise_remove dropped mmap_sem */
+ vma = find_vma(current->mm, start);
+ }
+}
+
+static int prctl_set_vma(unsigned long opt, unsigned long start,
+ unsigned long len_in, unsigned long arg)
+{
+ struct mm_struct *mm = current->mm;
+ int error;
+ unsigned long len;
+ unsigned long end;
+
+ if (start & ~PAGE_MASK)
+ return -EINVAL;
+ len = (len_in + ~PAGE_MASK) & PAGE_MASK;
+
+ /* Check to see whether len was rounded up from small -ve to zero */
+ if (len_in && !len)
+ return -EINVAL;
+
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+
+ if (end == start)
+ return 0;
+
+ down_write(&mm->mmap_sem);
+
+ switch (opt) {
+ case PR_SET_VMA_ANON_NAME:
+ error = prctl_set_vma_anon_name(start, end, arg);
+ break;
+ default:
+ error = -EINVAL;
+ }
+
+ up_write(&mm->mmap_sem);
+
+ return error;
+}
+#else /* CONFIG_MMU */
+static int prctl_set_vma(unsigned long opt, unsigned long start,
+ unsigned long len_in, unsigned long arg)
+{
+ return -EINVAL;
+}
+#endif
+
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
unsigned long, arg4, unsigned long, arg5)
{
@@ -1996,6 +2145,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;
return task_no_new_privs(current) ? 1 : 0;
+ case PR_SET_VMA:
+ error = prctl_set_vma(arg2, arg3, arg4, arg5);
+ break;
case PR_GET_THP_DISABLE:
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 75875a741b5e..2c07f5cc486e 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -104,6 +104,8 @@ extern char core_pattern[];
extern unsigned int core_pipe_limit;
#endif
extern int pid_max;
+extern int extra_free_kbytes;
+extern int min_free_order_shift;
extern int pid_max_min, pid_max_max;
extern int percpu_pagelist_fraction;
extern int compat_log;
@@ -1319,6 +1321,21 @@ static struct ctl_table vm_table[] = {
.extra1 = &zero,
},
{
+ .procname = "extra_free_kbytes",
+ .data = &extra_free_kbytes,
+ .maxlen = sizeof(extra_free_kbytes),
+ .mode = 0644,
+ .proc_handler = min_free_kbytes_sysctl_handler,
+ .extra1 = &zero,
+ },
+ {
+ .procname = "min_free_order_shift",
+ .data = &min_free_order_shift,
+ .maxlen = sizeof(min_free_order_shift),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+ {
.procname = "percpu_pagelist_fraction",
.data = &percpu_pagelist_fraction,
.maxlen = sizeof(percpu_pagelist_fraction),
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index a5da09c899dd..37e1fb831d95 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -77,6 +77,9 @@ config EVENT_TRACING
select CONTEXT_SWITCH_TRACER
bool
+config GPU_TRACEPOINTS
+ bool
+
config CONTEXT_SWITCH_TRACER
bool
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 67d6369ddf83..5da3cfb1bb84 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
endif
obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o
obj-$(CONFIG_UPROBE_EVENT) += trace_uprobe.o
+obj-$(CONFIG_GPU_TRACEPOINTS) += gpu-traces.o
obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o
diff --git a/kernel/trace/gpu-traces.c b/kernel/trace/gpu-traces.c
new file mode 100644
index 000000000000..a4b3f00faee3
--- /dev/null
+++ b/kernel/trace/gpu-traces.c
@@ -0,0 +1,23 @@
+/*
+ * GPU tracepoints
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/gpu.h>
+
+EXPORT_TRACEPOINT_SYMBOL(gpu_sched_switch);
+EXPORT_TRACEPOINT_SYMBOL(gpu_job_enqueue);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8a528392b1f4..2cb000150e97 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -812,6 +812,7 @@ static const char *trace_options[] = {
"irq-info",
"markers",
"function-trace",
+ "print-tgid",
NULL
};
@@ -1278,6 +1279,7 @@ void tracing_reset_all_online_cpus(void)
#define SAVED_CMDLINES_DEFAULT 128
#define NO_CMDLINE_MAP UINT_MAX
+static unsigned saved_tgids[SAVED_CMDLINES_DEFAULT];
static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED;
struct saved_cmdlines_buffer {
unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1];
@@ -1515,6 +1517,7 @@ static int trace_save_cmdline(struct task_struct *tsk)
savedcmd->cmdline_idx = idx;
}
+ saved_tgids[idx] = tsk->tgid;
set_cmdline(idx, tsk->comm);
arch_spin_unlock(&trace_cmdline_lock);
@@ -1559,6 +1562,25 @@ void trace_find_cmdline(int pid, char comm[])
preempt_enable();
}
+int trace_find_tgid(int pid)
+{
+ unsigned map;
+ int tgid;
+
+ preempt_disable();
+ arch_spin_lock(&trace_cmdline_lock);
+ map = savedcmd->map_pid_to_cmdline[pid];
+ if (map != NO_CMDLINE_MAP)
+ tgid = saved_tgids[map];
+ else
+ tgid = -1;
+
+ arch_spin_unlock(&trace_cmdline_lock);
+ preempt_enable();
+
+ return tgid;
+}
+
void tracing_record_cmdline(struct task_struct *tsk)
{
if (atomic_read(&trace_record_cmdline_disabled) || !tracing_is_on())
@@ -2536,6 +2558,13 @@ static void print_func_help_header(struct trace_buffer *buf, struct seq_file *m)
seq_puts(m, "# | | | | |\n");
}
+static void print_func_help_header_tgid(struct trace_buffer *buf, struct seq_file *m)
+{
+ print_event_info(buf, m);
+ seq_puts(m, "# TASK-PID TGID CPU# TIMESTAMP FUNCTION\n");
+ seq_puts(m, "# | | | | | |\n");
+}
+
static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file *m)
{
print_event_info(buf, m);
@@ -2548,6 +2577,18 @@ static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file
seq_puts(m, "# | | | |||| | |\n");
}
+static void print_func_help_header_irq_tgid(struct trace_buffer *buf, struct seq_file *m)
+{
+ print_event_info(buf, m);
+ seq_puts(m, "# _-----=> irqs-off\n");
+ seq_puts(m, "# / _----=> need-resched\n");
+ seq_puts(m, "# | / _---=> hardirq/softirq\n");
+ seq_puts(m, "# || / _--=> preempt-depth\n");
+ seq_puts(m, "# ||| / delay\n");
+ seq_puts(m, "# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION\n");
+ seq_puts(m, "# | | | | |||| | |\n");
+}
+
void
print_trace_header(struct seq_file *m, struct trace_iterator *iter)
{
@@ -2848,9 +2889,15 @@ void trace_default_header(struct seq_file *m)
} else {
if (!(trace_flags & TRACE_ITER_VERBOSE)) {
if (trace_flags & TRACE_ITER_IRQ_INFO)
- print_func_help_header_irq(iter->trace_buffer, m);
+ if (trace_flags & TRACE_ITER_TGID)
+ print_func_help_header_irq_tgid(iter->trace_buffer, m);
+ else
+ print_func_help_header_irq(iter->trace_buffer, m);
else
- print_func_help_header(iter->trace_buffer, m);
+ if (trace_flags & TRACE_ITER_TGID)
+ print_func_help_header_tgid(iter->trace_buffer, m);
+ else
+ print_func_help_header(iter->trace_buffer, m);
}
}
}
@@ -3889,6 +3936,50 @@ tracing_saved_cmdlines_size_write(struct file *filp, const char __user *ubuf,
return cnt;
}
+static ssize_t
+tracing_saved_tgids_read(struct file *file, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char *file_buf;
+ char *buf;
+ int len = 0;
+ int pid;
+ int i;
+
+ file_buf = kmalloc(SAVED_CMDLINES_DEFAULT*(16+1+16), GFP_KERNEL);
+ if (!file_buf)
+ return -ENOMEM;
+
+ buf = file_buf;
+
+ for (i = 0; i < SAVED_CMDLINES_DEFAULT; i++) {
+ int tgid;
+ int r;
+
+ pid = savedcmd->map_cmdline_to_pid[i];
+ if (pid == -1 || pid == NO_CMDLINE_MAP)
+ continue;
+
+ tgid = trace_find_tgid(pid);
+ r = sprintf(buf, "%d %d\n", pid, tgid);
+ buf += r;
+ len += r;
+ }
+
+ len = simple_read_from_buffer(ubuf, cnt, ppos,
+ file_buf, len);
+
+ kfree(file_buf);
+
+ return len;
+}
+
+static const struct file_operations tracing_saved_tgids_fops = {
+ .open = tracing_open_generic,
+ .read = tracing_saved_tgids_read,
+ .llseek = generic_file_llseek,
+};
+
static const struct file_operations tracing_saved_cmdlines_size_fops = {
.open = tracing_open_generic,
.read = tracing_saved_cmdlines_size_read,
@@ -6523,6 +6614,9 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer)
trace_create_file("trace_marker", 0220, d_tracer,
tr, &tracing_mark_fops);
+ trace_create_file("saved_tgids", 0444, d_tracer,
+ tr, &tracing_saved_tgids_fops);
+
trace_create_file("trace_clock", 0644, d_tracer, tr,
&trace_clock_fops);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 385391fb1d3b..4cf22fa591d2 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -658,6 +658,7 @@ static inline void __trace_stack(struct trace_array *tr, unsigned long flags,
extern cycle_t ftrace_now(int cpu);
extern void trace_find_cmdline(int pid, char comm[]);
+extern int trace_find_tgid(int pid);
#ifdef CONFIG_DYNAMIC_FTRACE
extern unsigned long ftrace_update_tot_cnt;
@@ -938,6 +939,7 @@ enum trace_iterator_flags {
TRACE_ITER_IRQ_INFO = 0x800000,
TRACE_ITER_MARKERS = 0x1000000,
TRACE_ITER_FUNCTION = 0x2000000,
+ TRACE_ITER_TGID = 0x4000000,
};
/*
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index f0a0c982cde3..4acc812a6e3c 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -65,6 +65,17 @@ struct fgraph_data {
#define TRACE_GRAPH_INDENT 2
+/* Flag options */
+#define TRACE_GRAPH_PRINT_OVERRUN 0x1
+#define TRACE_GRAPH_PRINT_CPU 0x2
+#define TRACE_GRAPH_PRINT_OVERHEAD 0x4
+#define TRACE_GRAPH_PRINT_PROC 0x8
+#define TRACE_GRAPH_PRINT_DURATION 0x10
+#define TRACE_GRAPH_PRINT_ABS_TIME 0x20
+#define TRACE_GRAPH_PRINT_IRQS 0x40
+#define TRACE_GRAPH_PRINT_FLAT 0x80
+
+
static unsigned int max_depth;
static struct tracer_opt trace_opts[] = {
@@ -82,6 +93,8 @@ static struct tracer_opt trace_opts[] = {
{ TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) },
/* Display interrupts */
{ TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) },
+ /* Use standard trace formatting rather than hierarchical */
+ { TRACER_OPT(funcgraph-flat, TRACE_GRAPH_PRINT_FLAT) },
/* Display function name after trailing } */
{ TRACER_OPT(funcgraph-tail, TRACE_GRAPH_PRINT_TAIL) },
{ } /* Empty entry */
@@ -1314,6 +1327,9 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags)
int cpu = iter->cpu;
int ret;
+ if (flags & TRACE_GRAPH_PRINT_FLAT)
+ return TRACE_TYPE_UNHANDLED;
+
if (data && per_cpu_ptr(data->cpu_data, cpu)->ignore) {
per_cpu_ptr(data->cpu_data, cpu)->ignore = 0;
return TRACE_TYPE_HANDLED;
@@ -1371,13 +1387,6 @@ print_graph_function(struct trace_iterator *iter)
return print_graph_function_flags(iter, tracer_flags.val);
}
-static enum print_line_t
-print_graph_function_event(struct trace_iterator *iter, int flags,
- struct trace_event *event)
-{
- return print_graph_function(iter);
-}
-
static void print_lat_header(struct seq_file *s, u32 flags)
{
static const char spaces[] = " " /* 16 spaces */
@@ -1444,6 +1453,11 @@ void print_graph_headers_flags(struct seq_file *s, u32 flags)
{
struct trace_iterator *iter = s->private;
+ if (flags & TRACE_GRAPH_PRINT_FLAT) {
+ trace_default_header(s);
+ return;
+ }
+
if (!(trace_flags & TRACE_ITER_CONTEXT_INFO))
return;
@@ -1515,20 +1529,6 @@ func_graph_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
return 0;
}
-static struct trace_event_functions graph_functions = {
- .trace = print_graph_function_event,
-};
-
-static struct trace_event graph_trace_entry_event = {
- .type = TRACE_GRAPH_ENT,
- .funcs = &graph_functions,
-};
-
-static struct trace_event graph_trace_ret_event = {
- .type = TRACE_GRAPH_RET,
- .funcs = &graph_functions
-};
-
static struct tracer graph_trace __tracer_data = {
.name = "function_graph",
.update_thresh = graph_trace_update_thresh,
@@ -1604,16 +1604,6 @@ static __init int init_graph_trace(void)
{
max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1);
- if (!register_ftrace_event(&graph_trace_entry_event)) {
- pr_warning("Warning: could not register graph trace events\n");
- return 1;
- }
-
- if (!register_ftrace_event(&graph_trace_ret_event)) {
- pr_warning("Warning: could not register graph trace events\n");
- return 1;
- }
-
return register_tracer(&graph_trace);
}
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
index c6977d5a9b12..79da717bc977 100644
--- a/kernel/trace/trace_output.c
+++ b/kernel/trace/trace_output.c
@@ -521,11 +521,25 @@ int trace_print_context(struct trace_iterator *iter)
unsigned long secs, usec_rem;
char comm[TASK_COMM_LEN];
int ret;
+ int tgid;
trace_find_cmdline(entry->pid, comm);
- ret = trace_seq_printf(s, "%16s-%-5d [%03d] ",
- comm, entry->pid, iter->cpu);
+ ret = trace_seq_printf(s, "%16s-%-5d ", comm, entry->pid);
+ if (!ret)
+ return 0;
+
+ if (trace_flags & TRACE_ITER_TGID) {
+ tgid = trace_find_tgid(entry->pid);
+ if (tgid < 0)
+ ret = trace_seq_puts(s, "(-----) ");
+ else
+ ret = trace_seq_printf(s, "(%5d) ", tgid);
+ if (!ret)
+ return 0;
+ }
+
+ ret = trace_seq_printf(s, "[%03d] ", iter->cpu);
if (!ret)
return 0;
@@ -854,6 +868,168 @@ static struct trace_event trace_fn_event = {
.funcs = &trace_fn_funcs,
};
+/* TRACE_GRAPH_ENT */
+static enum print_line_t trace_graph_ent_trace(struct trace_iterator *iter, int flags,
+ struct trace_event *event)
+{
+ struct trace_seq *s = &iter->seq;
+ struct ftrace_graph_ent_entry *field;
+
+ trace_assign_type(field, iter->ent);
+
+ if (!trace_seq_puts(s, "graph_ent: func="))
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ if (!seq_print_ip_sym(s, field->graph_ent.func, flags))
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ if (!trace_seq_puts(s, "\n"))
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ent_raw(struct trace_iterator *iter, int flags,
+ struct trace_event *event)
+{
+ struct ftrace_graph_ent_entry *field;
+
+ trace_assign_type(field, iter->ent);
+
+ if (!trace_seq_printf(&iter->seq, "%lx %d\n",
+ field->graph_ent.func,
+ field->graph_ent.depth))
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ent_hex(struct trace_iterator *iter, int flags,
+ struct trace_event *event)
+{
+ struct ftrace_graph_ent_entry *field;
+ struct trace_seq *s = &iter->seq;
+
+ trace_assign_type(field, iter->ent);
+
+ SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.func);
+ SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.depth);
+
+ return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ent_bin(struct trace_iterator *iter, int flags,
+ struct trace_event *event)
+{
+ struct ftrace_graph_ent_entry *field;
+ struct trace_seq *s = &iter->seq;
+
+ trace_assign_type(field, iter->ent);
+
+ SEQ_PUT_FIELD_RET(s, field->graph_ent.func);
+ SEQ_PUT_FIELD_RET(s, field->graph_ent.depth);
+
+ return TRACE_TYPE_HANDLED;
+}
+
+static struct trace_event_functions trace_graph_ent_funcs = {
+ .trace = trace_graph_ent_trace,
+ .raw = trace_graph_ent_raw,
+ .hex = trace_graph_ent_hex,
+ .binary = trace_graph_ent_bin,
+};
+
+static struct trace_event trace_graph_ent_event = {
+ .type = TRACE_GRAPH_ENT,
+ .funcs = &trace_graph_ent_funcs,
+};
+
+/* TRACE_GRAPH_RET */
+static enum print_line_t trace_graph_ret_trace(struct trace_iterator *iter, int flags,
+ struct trace_event *event)
+{
+ struct trace_seq *s = &iter->seq;
+ struct trace_entry *entry = iter->ent;
+ struct ftrace_graph_ret_entry *field;
+
+ trace_assign_type(field, entry);
+
+ if (!trace_seq_puts(s, "graph_ret: func="))
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ if (!seq_print_ip_sym(s, field->ret.func, flags))
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ if (!trace_seq_puts(s, "\n"))
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ret_raw(struct trace_iterator *iter, int flags,
+ struct trace_event *event)
+{
+ struct ftrace_graph_ret_entry *field;
+
+ trace_assign_type(field, iter->ent);
+
+ if (!trace_seq_printf(&iter->seq, "%lx %lld %lld %ld %d\n",
+ field->ret.func,
+ field->ret.calltime,
+ field->ret.rettime,
+ field->ret.overrun,
+ field->ret.depth));
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ret_hex(struct trace_iterator *iter, int flags,
+ struct trace_event *event)
+{
+ struct ftrace_graph_ret_entry *field;
+ struct trace_seq *s = &iter->seq;
+
+ trace_assign_type(field, iter->ent);
+
+ SEQ_PUT_HEX_FIELD_RET(s, field->ret.func);
+ SEQ_PUT_HEX_FIELD_RET(s, field->ret.calltime);
+ SEQ_PUT_HEX_FIELD_RET(s, field->ret.rettime);
+ SEQ_PUT_HEX_FIELD_RET(s, field->ret.overrun);
+ SEQ_PUT_HEX_FIELD_RET(s, field->ret.depth);
+
+ return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ret_bin(struct trace_iterator *iter, int flags,
+ struct trace_event *event)
+{
+ struct ftrace_graph_ret_entry *field;
+ struct trace_seq *s = &iter->seq;
+
+ trace_assign_type(field, iter->ent);
+
+ SEQ_PUT_FIELD_RET(s, field->ret.func);
+ SEQ_PUT_FIELD_RET(s, field->ret.calltime);
+ SEQ_PUT_FIELD_RET(s, field->ret.rettime);
+ SEQ_PUT_FIELD_RET(s, field->ret.overrun);
+ SEQ_PUT_FIELD_RET(s, field->ret.depth);
+
+ return TRACE_TYPE_HANDLED;
+}
+
+static struct trace_event_functions trace_graph_ret_funcs = {
+ .trace = trace_graph_ret_trace,
+ .raw = trace_graph_ret_raw,
+ .hex = trace_graph_ret_hex,
+ .binary = trace_graph_ret_bin,
+};
+
+static struct trace_event trace_graph_ret_event = {
+ .type = TRACE_GRAPH_RET,
+ .funcs = &trace_graph_ret_funcs,
+};
+
/* TRACE_CTX an TRACE_WAKE */
static enum print_line_t trace_ctxwake_print(struct trace_iterator *iter,
char *delim)
@@ -1244,6 +1420,8 @@ static struct trace_event trace_print_event = {
static struct trace_event *events[] __initdata = {
&trace_fn_event,
+ &trace_graph_ent_event,
+ &trace_graph_ret_event,
&trace_ctx_event,
&trace_wake_event,
&trace_stack_event,
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index a8d6914030fe..3fa6f9dc8950 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -51,6 +51,11 @@ static DEFINE_PER_CPU(unsigned long, soft_lockup_hrtimer_cnt);
static DEFINE_PER_CPU(bool, hard_watchdog_warn);
static DEFINE_PER_CPU(bool, watchdog_nmi_touch);
static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
+#endif
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU
+static cpumask_t __read_mostly watchdog_cpus;
+#endif
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI
static DEFINE_PER_CPU(struct perf_event *, watchdog_ev);
#endif
static unsigned long soft_lockup_nmi_warn;
@@ -198,7 +203,7 @@ void touch_softlockup_watchdog_sync(void)
__raw_get_cpu_var(watchdog_touch_ts) = 0;
}
-#ifdef CONFIG_HARDLOCKUP_DETECTOR
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI
/* watchdog detector functions */
static int is_hardlockup(void)
{
@@ -212,6 +217,76 @@ static int is_hardlockup(void)
}
#endif
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU
+static unsigned int watchdog_next_cpu(unsigned int cpu)
+{
+ cpumask_t cpus = watchdog_cpus;
+ unsigned int next_cpu;
+
+ next_cpu = cpumask_next(cpu, &cpus);
+ if (next_cpu >= nr_cpu_ids)
+ next_cpu = cpumask_first(&cpus);
+
+ if (next_cpu == cpu)
+ return nr_cpu_ids;
+
+ return next_cpu;
+}
+
+static int is_hardlockup_other_cpu(unsigned int cpu)
+{
+ unsigned long hrint = per_cpu(hrtimer_interrupts, cpu);
+
+ if (per_cpu(hrtimer_interrupts_saved, cpu) == hrint)
+ return 1;
+
+ per_cpu(hrtimer_interrupts_saved, cpu) = hrint;
+ return 0;
+}
+
+static void watchdog_check_hardlockup_other_cpu(void)
+{
+ unsigned int next_cpu;
+
+ /*
+ * Test for hardlockups every 3 samples. The sample period is
+ * watchdog_thresh * 2 / 5, so 3 samples gets us back to slightly over
+ * watchdog_thresh (over by 20%).
+ */
+ if (__this_cpu_read(hrtimer_interrupts) % 3 != 0)
+ return;
+
+ /* check for a hardlockup on the next cpu */
+ next_cpu = watchdog_next_cpu(smp_processor_id());
+ if (next_cpu >= nr_cpu_ids)
+ return;
+
+ smp_rmb();
+
+ if (per_cpu(watchdog_nmi_touch, next_cpu) == true) {
+ per_cpu(watchdog_nmi_touch, next_cpu) = false;
+ return;
+ }
+
+ if (is_hardlockup_other_cpu(next_cpu)) {
+ /* only warn once */
+ if (per_cpu(hard_watchdog_warn, next_cpu) == true)
+ return;
+
+ if (hardlockup_panic)
+ panic("Watchdog detected hard LOCKUP on cpu %u", next_cpu);
+ else
+ WARN(1, "Watchdog detected hard LOCKUP on cpu %u", next_cpu);
+
+ per_cpu(hard_watchdog_warn, next_cpu) = true;
+ } else {
+ per_cpu(hard_watchdog_warn, next_cpu) = false;
+ }
+}
+#else
+static inline void watchdog_check_hardlockup_other_cpu(void) { return; }
+#endif
+
static int is_softlockup(unsigned long touch_ts)
{
unsigned long now = get_timestamp();
@@ -223,7 +298,7 @@ static int is_softlockup(unsigned long touch_ts)
return 0;
}
-#ifdef CONFIG_HARDLOCKUP_DETECTOR
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI
static struct perf_event_attr wd_hw_attr = {
.type = PERF_TYPE_HARDWARE,
@@ -273,7 +348,7 @@ static void watchdog_overflow_callback(struct perf_event *event,
__this_cpu_write(hard_watchdog_warn, false);
return;
}
-#endif /* CONFIG_HARDLOCKUP_DETECTOR */
+#endif /* CONFIG_HARDLOCKUP_DETECTOR_NMI */
static void watchdog_interrupt_count(void)
{
@@ -294,6 +369,9 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
/* kick the hardlockup detector */
watchdog_interrupt_count();
+ /* test for hardlockups on the next cpu */
+ watchdog_check_hardlockup_other_cpu();
+
/* kick the softlockup detector */
wake_up_process(__this_cpu_read(softlockup_watchdog));
@@ -441,7 +519,7 @@ static void watchdog(unsigned int cpu)
__touch_watchdog();
}
-#ifdef CONFIG_HARDLOCKUP_DETECTOR
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI
/*
* People like the simple clean cpu node info on boot.
* Reduce the watchdog noise by only printing messages
@@ -517,9 +595,44 @@ static void watchdog_nmi_disable(unsigned int cpu)
return;
}
#else
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU
+static int watchdog_nmi_enable(unsigned int cpu)
+{
+ /*
+ * The new cpu will be marked online before the first hrtimer interrupt
+ * runs on it. If another cpu tests for a hardlockup on the new cpu
+ * before it has run its first hrtimer, it will get a false positive.
+ * Touch the watchdog on the new cpu to delay the first check for at
+ * least 3 sampling periods to guarantee one hrtimer has run on the new
+ * cpu.
+ */
+ per_cpu(watchdog_nmi_touch, cpu) = true;
+ smp_wmb();
+ cpumask_set_cpu(cpu, &watchdog_cpus);
+ return 0;
+}
+
+static void watchdog_nmi_disable(unsigned int cpu)
+{
+ unsigned int next_cpu = watchdog_next_cpu(cpu);
+
+ /*
+ * Offlining this cpu will cause the cpu before this one to start
+ * checking the one after this one. If this cpu just finished checking
+ * the next cpu and updating hrtimer_interrupts_saved, and then the
+ * previous cpu checks it within one sample period, it will trigger a
+ * false positive. Touch the watchdog on the next cpu to prevent it.
+ */
+ if (next_cpu < nr_cpu_ids)
+ per_cpu(watchdog_nmi_touch, next_cpu) = true;
+ smp_wmb();
+ cpumask_clear_cpu(cpu, &watchdog_cpus);
+}
+#else
static int watchdog_nmi_enable(unsigned int cpu) { return 0; }
static void watchdog_nmi_disable(unsigned int cpu) { return; }
-#endif /* CONFIG_HARDLOCKUP_DETECTOR */
+#endif /* CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU */
+#endif /* CONFIG_HARDLOCKUP_DETECTOR_NMI */
static struct smp_hotplug_thread watchdog_threads = {
.store = &softlockup_watchdog,
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index a28590083622..d2562e71f2c4 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -668,15 +668,27 @@ config LOCKUP_DETECTOR
The overhead should be minimal. A periodic hrtimer runs to
generate interrupts and kick the watchdog task every 4 seconds.
An NMI is generated every 10 seconds or so to check for hardlockups.
+ If NMIs are not available on the platform, every 12 seconds the
+ hrtimer interrupt on one cpu will be used to check for hardlockups
+ on the next cpu.
The frequency of hrtimer and NMI events and the soft and hard lockup
thresholds can be controlled through the sysctl watchdog_thresh.
-config HARDLOCKUP_DETECTOR
+config HARDLOCKUP_DETECTOR_NMI
def_bool y
depends on LOCKUP_DETECTOR && !HAVE_NMI_WATCHDOG
depends on PERF_EVENTS && HAVE_PERF_EVENTS_NMI
+config HARDLOCKUP_DETECTOR_OTHER_CPU
+ def_bool y
+ depends on LOCKUP_DETECTOR && SMP
+ depends on !HARDLOCKUP_DETECTOR_NMI && !HAVE_NMI_WATCHDOG
+
+config HARDLOCKUP_DETECTOR
+ def_bool y
+ depends on HARDLOCKUP_DETECTOR_NMI || HARDLOCKUP_DETECTOR_OTHER_CPU
+
config BOOTPARAM_HARDLOCKUP_PANIC
bool "Panic (Reboot) On Hard Lockups"
depends on HARDLOCKUP_DETECTOR
@@ -1038,6 +1050,7 @@ config TRACE_IRQFLAGS
config STACKTRACE
bool "Stack backtrace support"
depends on STACKTRACE_SUPPORT
+ default y
help
This option causes the kernel to create a /proc/pid/stack for
every process, showing its current stack trace.
diff --git a/linaro/configs/android.conf b/linaro/configs/android.conf
new file mode 100644
index 000000000000..f1aefaebcc93
--- /dev/null
+++ b/linaro/configs/android.conf
@@ -0,0 +1,47 @@
+CONFIG_IPV6=y
+# CONFIG_IPV6_SIT is not set
+CONFIG_PANIC_TIMEOUT=0
+CONFIG_HAS_WAKELOCK=y
+CONFIG_WAKELOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_DM_CRYPT=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_ANDROID_PARANOID_NETWORK=y
+CONFIG_NET_ACTIVITY_STATS=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_SWITCH=y
+CONFIG_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_TIMED_OUTPUT=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ANDROID_INTF_ALARM_DEV=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_FUSE_FS=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_SW_SYNC_USER=y
+CONFIG_ION=y
+CONFIG_ION_TEST=y
+CONFIG_ION_DUMMY=y
+CONFIG_AUDIT=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_LSM_MMAP_MIN_ADDR=4096
+CONFIG_SECURITY_SELINUX=y
+CONFIG_EXT4_FS_SECURITY=y
diff --git a/linaro/configs/arndale.conf b/linaro/configs/arndale.conf
new file mode 100644
index 000000000000..b832473d90e3
--- /dev/null
+++ b/linaro/configs/arndale.conf
@@ -0,0 +1,100 @@
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_ARCH_EXYNOS=y
+CONFIG_S3C_LOWLEVEL_UART_PORT=2
+CONFIG_ARCH_EXYNOS5=y
+CONFIG_VMSPLIT_2G=y
+CONFIG_NR_CPUS=2
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_HIGHMEM=y
+# CONFIG_COMPACTION is not set
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init= mem=256M"
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_RUNTIME=y
+CONFIG_CMA=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=128
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_SATA_EXYNOS=y
+CONFIG_AX88796=y
+CONFIG_AX88796_93CX6=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_DM9601=y
+CONFIG_USB_NET_MCS7830=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_SAMSUNG=y
+CONFIG_SERIAL_SAMSUNG_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_S3C2410=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
+CONFIG_EXYNOS_THERMAL=y
+CONFIG_EXYNOS_THERMAL_CORE=y
+CONFIG_MFD_SEC_CORE=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_S5M8767=y
+CONFIG_DRM=y
+CONFIG_DRM_LOAD_EDID_FIRMWARE=y
+CONFIG_DRM_EXYNOS=y
+# CONFIG_DRM_EXYNOS_IOMMU is not set
+CONFIG_DRM_EXYNOS_DMABUF=y
+CONFIG_DRM_EXYNOS_HDMI=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_SAMSUNG=y
+CONFIG_SND_SOC_SMDK_I2S_STUB=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_EXTCON=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_HOST=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_EXYNOS=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_EXYNOS=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_HSIC_USB3503=y
+CONFIG_USB_GADGET=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_IDMAC=y
+CONFIG_MMC_DW_EXYNOS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_S3C=y
+CONFIG_EXYNOS_IOMMU=y
+CONFIG_PHY_EXYNOS5250_SATA=y
+CONFIG_PHY_SAMSUNG_USB2=y
+CONFIG_PHY_EXYNOS4210_USB2=y
+CONFIG_PHY_EXYNOS4X12_USB2=y
+CONFIG_PHY_EXYNOS5250_USB2=y
+CONFIG_EXYNOS_SIMPLE_PHY=y
+CONFIG_PHY_EXYNOS5_USBDRD=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_INFO=y
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+CONFIG_DEBUG_USER=y
+CONFIG_TUN=y
diff --git a/linaro/configs/arndale_octa.conf b/linaro/configs/arndale_octa.conf
new file mode 100644
index 000000000000..2bb86b8aede2
--- /dev/null
+++ b/linaro/configs/arndale_octa.conf
@@ -0,0 +1,89 @@
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_EXYNOS=y
+CONFIG_S3C_LOWLEVEL_UART_PORT=3
+CONFIG_ARCH_EXYNOS5=y
+CONFIG_NR_CPUS=8
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_HIGHMEM=y
+CONFIG_CMA=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC3,115200 init=/linuxrc mem=256M"
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_RUNTIME=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=128
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_AX88796=y
+CONFIG_AX88796_93CX6=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_DM9601=y
+CONFIG_USB_NET_MCS7830=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_SAMSUNG=y
+CONFIG_SERIAL_SAMSUNG_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C_EXYNOS5=y
+CONFIG_I2C_S3C2410=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
+CONFIG_EXYNOS_THERMAL=y
+CONFIG_EXYNOS_THERMAL_CORE=y
+CONFIG_MFD_SEC_CORE=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_S2MPS11=y
+CONFIG_DRM=y
+CONFIG_DRM_LOAD_EDID_FIRMWARE=y
+CONFIG_DRM_EXYNOS=y
+CONFIG_DRM_EXYNOS_IOMMU=y
+CONFIG_DRM_EXYNOS_DMABUF=y
+CONFIG_DRM_EXYNOS_HDMI=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_SAMSUNG=y
+CONFIG_SND_SOC_SMDK_I2S_STUB=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_EXYNOS=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_EXYNOS=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_HOST=y
+CONFIG_USB_HSIC_USB3503=y
+CONFIG_USB_GADGET=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_IDMAC=y
+CONFIG_MMC_DW_EXYNOS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_S3C=y
+CONFIG_EXYNOS_IOMMU=y
+CONFIG_EXYNOS_IOMMU_DEBUG=y
+CONFIG_EXTCON=y
+CONFIG_PHY_SAMSUNG_USB2=y
+CONFIG_PHY_EXYNOS4210_USB2=y
+CONFIG_PHY_EXYNOS4X12_USB2=y
+CONFIG_PHY_EXYNOS5250_USB2=y
+CONFIG_EXYNOS_SIMPLE_PHY=y
+CONFIG_PHY_EXYNOS5_USBDRD=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_USER=y
diff --git a/linaro/configs/beaglebone.conf b/linaro/configs/beaglebone.conf
new file mode 100644
index 000000000000..bb7eefb491a4
--- /dev/null
+++ b/linaro/configs/beaglebone.conf
@@ -0,0 +1,22 @@
+CONFIG_SOC_AM33XX=y
+# CONFIG_THUMB2_KERNEL is not set
+# CONFIG_ARM_APPENDED_DTB is not set
+CONFIG_MFD_TPS65217=y
+CONFIG_REGULATOR_TPS65217=y
+CONFIG_TI_CPSW=y
+CONFIG_SMSC_PHY=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_OTG=y
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_DUAL_ROLE=y
+CONFIG_USB_MUSB_DSPS=y
+CONFIG_USB_MUSB_AM335X_CHILD=y
+CONFIG_USB_TI_CPPI41_DMA=y
+CONFIG_TI_CPPI41=y
+CONFIG_AM335X_PHY_USB=y
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_STORAGE=y
+CONFIG_USB_GADGET_VBUS_DRAW=250
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_MASS_STORAGE=m
diff --git a/linaro/configs/big-LITTLE-IKS.conf b/linaro/configs/big-LITTLE-IKS.conf
new file mode 100644
index 000000000000..eccb00f851e0
--- /dev/null
+++ b/linaro/configs/big-LITTLE-IKS.conf
@@ -0,0 +1,4 @@
+CONFIG_BIG_LITTLE=y
+CONFIG_BL_SWITCHER=y
+CONFIG_ARM_DT_BL_CPUFREQ=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
diff --git a/linaro/configs/bigendian.conf b/linaro/configs/bigendian.conf
new file mode 100644
index 000000000000..6a1020299e85
--- /dev/null
+++ b/linaro/configs/bigendian.conf
@@ -0,0 +1,4 @@
+CONFIG_CPU_BIG_ENDIAN=y
+CONFIG_CPU_ENDIAN_BE8=y
+# CONFIG_VIRTUALIZATION is not set
+# CONFIG_MMC_DW_IDMAC is not set
diff --git a/linaro/configs/capri.conf b/linaro/configs/capri.conf
new file mode 100644
index 000000000000..9eb4bd3a079b
--- /dev/null
+++ b/linaro/configs/capri.conf
@@ -0,0 +1,90 @@
+# CONFIG_SWAP is not set
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_LOG_BUF_SHIFT=19
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_BLK_CGROUP=y
+CONFIG_NAMESPACES=y
+CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM_MOBILE=y
+CONFIG_ARM_THUMBEE=y
+CONFIG_PREEMPT=y
+# CONFIG_COMPACTION is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyS0,115200n8 mem=128M"
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_RUNTIME=y
+CONFIG_PACKET_DIAG=y
+CONFIG_UNIX_DIAG=y
+CONFIG_TCP_MD5SIG=y
+# CONFIG_BLK_DEV is not set
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_INPUT_FF_MEMLESS=y
+CONFIG_INPUT_JOYDEV=y
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_SERIO is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_BCM_KONA_WDT=y
+CONFIG_BCM_KONA_WDT_DEBUG=y
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_S3C_HSOTG=y
+CONFIG_USB_ETH=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_BCM_KONA=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_PWM=y
+CONFIG_GENERIC_PHY=y
+CONFIG_BCM_KONA_USB2_PHY=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_FUSE_FS=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=110
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+# CONFIG_FTRACE is not set
+CONFIG_XZ_DEC=y
+CONFIG_AVERAGE=y
diff --git a/linaro/configs/debug.conf b/linaro/configs/debug.conf
new file mode 100644
index 000000000000..36980566b2d8
--- /dev/null
+++ b/linaro/configs/debug.conf
@@ -0,0 +1 @@
+CONFIG_PROVE_LOCKING=y
diff --git a/linaro/configs/distribution.conf b/linaro/configs/distribution.conf
new file mode 100644
index 000000000000..e7bd73703273
--- /dev/null
+++ b/linaro/configs/distribution.conf
@@ -0,0 +1,62 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_CGROUPS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=32768
+CONFIG_SECCOMP=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_SYN_COOKIES=y
+CONFIG_IPV6=y
+CONFIG_NETLABEL=y
+CONFIG_BRIDGE_NETFILTER=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NETFILTER_NETLINK=m
+CONFIG_NETFILTER_NETLINK_LOG=m
+CONFIG_NETFILTER_XT_CONNMARK=m
+CONFIG_NETFILTER_XT_MARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=m
+CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_NAT_IPV4=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_NAT_IPV6=m
+CONFIG_IP6_NF_TARGET_MASQUERADE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE=m
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=65536
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+# CONFIG_DEVKMEM is not set
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_AUTOFS4_FS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_STRICT_DEVMEM=y
+CONFIG_SECURITY=y
+CONFIG_LSM_MMAP_MIN_ADDR=0
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SMACK=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_DEFAULT_SECURITY_APPARMOR=y
diff --git a/linaro/configs/gcov.conf b/linaro/configs/gcov.conf
new file mode 100644
index 000000000000..2d1209d1cf55
--- /dev/null
+++ b/linaro/configs/gcov.conf
@@ -0,0 +1,3 @@
+CONFIG_GCOV_KERNEL=y
+CONFIG_GCOV_PROFILE_ALL=y
+CONFIG_GCOV_FORMAT_AUTODETECT=y
diff --git a/linaro/configs/hi3xxx.conf b/linaro/configs/hi3xxx.conf
new file mode 100644
index 000000000000..0c46b02c842c
--- /dev/null
+++ b/linaro/configs/hi3xxx.conf
@@ -0,0 +1,18 @@
+CONFIG_ARCH_HI3xxx=y
+CONFIG_SMP=y
+CONFIG_HOTPLUG_CPU=y
+CONFIG_MFD_HI6421_PMIC=y
+CONFIG_REGULATOR_HI6421=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_IDMAC=y
+CONFIG_MMC_DW_PLTFM=y
+CONFIG_MMC_DW_K3=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_K3_DMA=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_MXT224E=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_HI6421_ONKEY=y
+CONFIG_RTC_DRV_HI6421=y
+# CONFIG_THUMB2_KERNEL is not set
diff --git a/linaro/configs/highbank.conf b/linaro/configs/highbank.conf
new file mode 100644
index 000000000000..33b978128777
--- /dev/null
+++ b/linaro/configs/highbank.conf
@@ -0,0 +1,42 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_NO_HZ=y
+CONFIG_HIGHMEM=y
+CONFIG_HIGHPTE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_ARCH_HIGHBANK=y
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_AEABI=y
+CONFIG_CMDLINE="console=ttyAMA0"
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_NET=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_SATA_HIGHBANK=y
+CONFIG_NETDEVICES=y
+CONFIG_NET_CALXEDA_XGMAC=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_IPMI_HANDLER=y
+CONFIG_IPMI_SI=y
+CONFIG_I2C=y
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_SPI=y
+CONFIG_SPI_PL022=y
+CONFIG_GPIO_PL061=y
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_EDAC_HIGHBANK_MC=y
+CONFIG_EDAC_HIGHBANK_L2=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PL031=y
+CONFIG_DMADEVICES=y
+CONFIG_PL330_DMA=y
diff --git a/linaro/configs/hugepage.conf b/linaro/configs/hugepage.conf
new file mode 100644
index 000000000000..85f56540acdd
--- /dev/null
+++ b/linaro/configs/hugepage.conf
@@ -0,0 +1,3 @@
+CONFIG_HUGETLBFS=y
+CONFIG_TRANSPARENT_HUGEPAGE=y
+CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
diff --git a/linaro/configs/ifc6410.conf b/linaro/configs/ifc6410.conf
new file mode 100644
index 000000000000..38c6cc0166e1
--- /dev/null
+++ b/linaro/configs/ifc6410.conf
@@ -0,0 +1,326 @@
+CONFIG_FHANDLE=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_VIRT=y
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM_MOBILE=y
+CONFIG_ARCH_BCM_5301X=y
+CONFIG_ARCH_HIGHBANK=y
+CONFIG_ARCH_HISI=y
+CONFIG_ARCH_HI3xxx=y
+CONFIG_ARCH_KEYSTONE=y
+CONFIG_ARCH_MXC=y
+CONFIG_MACH_IMX51_DT=y
+CONFIG_SOC_IMX53=y
+CONFIG_SOC_IMX6Q=y
+CONFIG_SOC_IMX6SL=y
+CONFIG_SOC_VF610=y
+CONFIG_ARCH_OMAP3=y
+CONFIG_ARCH_OMAP4=y
+CONFIG_SOC_OMAP5=y
+CONFIG_SOC_AM33XX=y
+CONFIG_SOC_AM43XX=y
+CONFIG_SOC_DRA7XX=y
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8X60=y
+CONFIG_ARCH_MSM8960=y
+CONFIG_ARCH_MSM8974=y
+CONFIG_ARCH_ROCKCHIP=y
+CONFIG_ARCH_SOCFPGA=y
+CONFIG_PLAT_SPEAR=y
+CONFIG_ARCH_SPEAR13XX=y
+CONFIG_MACH_SPEAR1310=y
+CONFIG_MACH_SPEAR1340=y
+CONFIG_ARCH_STI=y
+CONFIG_ARCH_EXYNOS=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_ARCH_SIRF=y
+CONFIG_ARCH_TEGRA=y
+CONFIG_ARCH_TEGRA_2x_SOC=y
+CONFIG_ARCH_TEGRA_3x_SOC=y
+CONFIG_ARCH_TEGRA_114_SOC=y
+CONFIG_ARCH_TEGRA_124_SOC=y
+CONFIG_ARCH_U8500=y
+CONFIG_MACH_HREFV60=y
+CONFIG_MACH_SNOWBALL=y
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_ARCH_VEXPRESS_CA9X4=y
+CONFIG_ARCH_WM8850=y
+CONFIG_ARCH_ZYNQ=y
+CONFIG_ARM_THUMBEE=y
+CONFIG_PCI=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_TEGRA=y
+CONFIG_NR_CPUS=8
+CONFIG_HIGHPTE=y
+CONFIG_CMA=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_KEXEC=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_MIP6=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_CAN=y
+CONFIG_CAN_MCP251X=y
+CONFIG_CFG80211=m
+CONFIG_MAC80211=m
+CONFIG_RFKILL=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_RFKILL_GPIO=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=64
+CONFIG_OMAP_OCP2SCP=y
+CONFIG_ICS932S401=y
+CONFIG_APDS9802ALS=y
+CONFIG_ISL29003=y
+CONFIG_EEPROM_AT24=y
+CONFIG_EEPROM_SUNXI_SID=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_AHCI_QCOM=y
+CONFIG_AHCI_SUNXI=y
+CONFIG_SATA_HIGHBANK=y
+CONFIG_SATA_MV=y
+CONFIG_SUN4I_EMAC=y
+CONFIG_ATL1C=y
+CONFIG_MACB=y
+CONFIG_NET_CALXEDA_XGMAC=y
+CONFIG_MVMDIO=y
+CONFIG_KS8851=y
+CONFIG_R8169=y
+CONFIG_SMSC911X=y
+CONFIG_STMMAC_ETH=y
+CONFIG_TI_CPSW=y
+CONFIG_AT803X_PHY=y
+CONFIG_MARVELL_PHY=y
+CONFIG_ICPLUS_PHY=y
+CONFIG_USB_PEGASUS=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_SMSC75XX=y
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_BRCMFMAC=m
+CONFIG_RT2X00=m
+CONFIG_RT2800USB=m
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_TEGRA=y
+CONFIG_KEYBOARD_SPEAR=y
+CONFIG_KEYBOARD_CROS_EC=y
+CONFIG_MOUSE_PS2_ELANTECH=y
+CONFIG_INPUT_MPU3050=y
+CONFIG_SERIO_AMBAKMI=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_SAMSUNG=y
+CONFIG_SERIAL_SAMSUNG_CONSOLE=y
+CONFIG_SERIAL_SIRFSOC=y
+CONFIG_SERIAL_SIRFSOC_CONSOLE=y
+CONFIG_SERIAL_TEGRA=y
+CONFIG_SERIAL_IMX=y
+CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_VT8500=y
+CONFIG_SERIAL_VT8500_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_OMAP=y
+CONFIG_SERIAL_OMAP_CONSOLE=y
+CONFIG_SERIAL_XILINX_PS_UART=y
+CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
+CONFIG_SERIAL_FSL_LPUART=y
+CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
+CONFIG_SERIAL_ST_ASC=y
+CONFIG_SERIAL_ST_ASC_CONSOLE=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MUX_PCA954x=y
+CONFIG_I2C_MUX_PINCTRL=y
+CONFIG_I2C_CADENCE=y
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_I2C_EXYNOS5=y
+CONFIG_I2C_MV64XXX=y
+CONFIG_I2C_SIRF=y
+CONFIG_I2C_TEGRA=y
+CONFIG_SPI=y
+CONFIG_SPI_OMAP24XX=y
+CONFIG_SPI_PL022=y
+CONFIG_SPI_SIRF=y
+CONFIG_SPI_SUN4I=y
+CONFIG_SPI_SUN6I=y
+CONFIG_SPI_TEGRA114=y
+CONFIG_SPI_TEGRA20_SFLASH=y
+CONFIG_SPI_TEGRA20_SLINK=y
+CONFIG_PINCTRL_AS3722=y
+CONFIG_PINCTRL_APQ8064=y
+CONFIG_PINCTRL_PALMAS=y
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_TWL4030=y
+CONFIG_GPIO_PALMAS=y
+CONFIG_GPIO_TPS6586X=y
+CONFIG_GPIO_TPS65910=y
+CONFIG_BATTERY_SBS=y
+CONFIG_CHARGER_TPS65090=y
+CONFIG_POWER_RESET_AS3722=y
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_POWER_RESET_SUN6I=y
+CONFIG_SENSORS_LM90=y
+CONFIG_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_SUNXI_WATCHDOG=y
+CONFIG_MFD_AS3722=y
+CONFIG_MFD_BCM590XX=y
+CONFIG_MFD_CROS_EC=y
+CONFIG_MFD_CROS_EC_SPI=y
+CONFIG_MFD_MAX8907=y
+CONFIG_MFD_SEC_CORE=y
+CONFIG_MFD_PALMAS=y
+CONFIG_MFD_TPS65090=y
+CONFIG_MFD_TPS6586X=y
+CONFIG_MFD_TPS65910=y
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
+CONFIG_REGULATOR_AB8500=y
+CONFIG_REGULATOR_AS3722=y
+CONFIG_REGULATOR_BCM590XX=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_REGULATOR_MAX8907=y
+CONFIG_REGULATOR_PALMAS=y
+CONFIG_REGULATOR_QCOM_RPM=y
+CONFIG_REGULATOR_S2MPS11=y
+CONFIG_REGULATOR_S5M8767=y
+CONFIG_REGULATOR_TPS51632=y
+CONFIG_REGULATOR_TPS62360=y
+CONFIG_REGULATOR_TPS65090=y
+CONFIG_REGULATOR_TPS6586X=y
+CONFIG_REGULATOR_TPS65910=y
+CONFIG_REGULATOR_TWL4030=y
+CONFIG_REGULATOR_VEXPRESS=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_USB_GSPCA=y
+CONFIG_DRM=y
+CONFIG_DRM_TEGRA=y
+CONFIG_DRM_PANEL_SIMPLE=y
+CONFIG_FB_ARMCLCD=y
+CONFIG_FB_WM8505=y
+CONFIG_FB_SIMPLE=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_TEGRA=y
+CONFIG_SND_SOC_TEGRA_RT5640=y
+CONFIG_SND_SOC_TEGRA_WM8753=y
+CONFIG_SND_SOC_TEGRA_WM8903=y
+CONFIG_SND_SOC_TEGRA_TRIMSLICE=y
+CONFIG_SND_SOC_TEGRA_ALC5632=y
+CONFIG_SND_SOC_TEGRA_MAX98090=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
+CONFIG_USB_EHCI_TEGRA=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_ISP1760_HCD=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_HOST=y
+CONFIG_AB8500_USB=y
+CONFIG_SAMSUNG_USB2PHY=y
+CONFIG_SAMSUNG_USB3PHY=y
+CONFIG_USB_GPIO_VBUS=y
+CONFIG_USB_ISP1301=y
+CONFIG_USB_MSM_OTG=y
+CONFIG_USB_MXS_PHY=y
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK_MINORS=16
+CONFIG_MMC_ARMMMCI=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_OF_ARASAN=y
+CONFIG_MMC_SDHCI_ESDHC_IMX=y
+CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_MMC_SDHCI_S3C=y
+CONFIG_MMC_SDHCI_PXAV3=y
+CONFIG_MMC_SDHCI_SPEAR=y
+CONFIG_MMC_SDHCI_S3C_DMA=y
+CONFIG_MMC_SDHCI_BCM_KONA=y
+CONFIG_MMC_OMAP=y
+CONFIG_MMC_OMAP_HS=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_EXYNOS=y
+CONFIG_MMC_SUNXI=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_EDAC_HIGHBANK_MC=y
+CONFIG_EDAC_HIGHBANK_L2=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_AS3722=y
+CONFIG_RTC_DRV_DS1307=y
+CONFIG_RTC_DRV_MAX8907=y
+CONFIG_RTC_DRV_PALMAS=y
+CONFIG_RTC_DRV_TWL4030=y
+CONFIG_RTC_DRV_TPS6586X=y
+CONFIG_RTC_DRV_TPS65910=y
+CONFIG_RTC_DRV_EM3027=y
+CONFIG_RTC_DRV_PL031=y
+CONFIG_RTC_DRV_VT8500=y
+CONFIG_RTC_DRV_SUNXI=y
+CONFIG_RTC_DRV_TEGRA=y
+CONFIG_DMADEVICES=y
+CONFIG_DW_DMAC=y
+CONFIG_TEGRA20_APB_DMA=y
+CONFIG_STE_DMA40=y
+CONFIG_SIRF_DMA=y
+CONFIG_TI_EDMA=y
+CONFIG_PL330_DMA=y
+CONFIG_IMX_SDMA=y
+CONFIG_IMX_DMA=y
+CONFIG_MXS_DMA=y
+CONFIG_DMA_OMAP=y
+CONFIG_QCOM_BAM_DMA=y
+CONFIG_STAGING=y
+CONFIG_SENSORS_ISL29018=y
+CONFIG_SENSORS_ISL29028=y
+CONFIG_MFD_NVEC=y
+CONFIG_KEYBOARD_NVEC=y
+CONFIG_SERIO_NVEC_PS2=y
+CONFIG_NVEC_POWER=y
+CONFIG_QCOM_GSBI=y
+CONFIG_QCOM_RPM=y
+CONFIG_COMMON_CLK_QCOM=y
+CONFIG_MSM_GCC_8660=y
+CONFIG_MSM_MMCC_8960=y
+CONFIG_MSM_MMCC_8974=y
+CONFIG_TEGRA_IOMMU_GART=y
+CONFIG_TEGRA_IOMMU_SMMU=y
+CONFIG_MEMORY=y
+CONFIG_IIO=y
+CONFIG_AK8975=y
+CONFIG_PWM=y
+CONFIG_PWM_TEGRA=y
+CONFIG_PWM_VT8500=y
+CONFIG_OMAP_USB2=y
+CONFIG_PHY_SUN4I_USB=y
+CONFIG_PHY_QCOM_APQ8064_SATA=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_LOCKUP_DETECTOR=y
diff --git a/linaro/configs/imx5.conf b/linaro/configs/imx5.conf
new file mode 100644
index 000000000000..0196c572bb14
--- /dev/null
+++ b/linaro/configs/imx5.conf
@@ -0,0 +1,105 @@
+CONFIG_ARCH_MXC=y
+CONFIG_MXC_PWM=y
+CONFIG_SOC_IMX6Q=y
+CONFIG_HIGHMEM=y
+CONFIG_COMPACTION=y
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.0.101:/shared/nfs ip=dhcp"
+CONFIG_CFG80211=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_RC_PID=y
+CONFIG_MAC80211_RC_DEFAULT_PID=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SMC91X=y
+CONFIG_MARVELL_PHY=y
+CONFIG_DAVICOM_PHY=y
+CONFIG_QSEMI_PHY=y
+CONFIG_LXT_PHY=y
+CONFIG_CICADA_PHY=y
+CONFIG_VITESSE_PHY=y
+CONFIG_SMSC_PHY=y
+CONFIG_BROADCOM_PHY=y
+CONFIG_ICPLUS_PHY=y
+CONFIG_REALTEK_PHY=y
+CONFIG_NATIONAL_PHY=y
+CONFIG_STE10XP=y
+CONFIG_LSI_ET1011C_PHY=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_GPIO=y
+# CONFIG_WLAN is not set
+CONFIG_INPUT_FF_MEMLESS=y
+CONFIG_INPUT_POLLDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_MPR121=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ELANTECH=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_IMX=y
+CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_ALGOBIT=y
+CONFIG_I2C_ALGOPCF=y
+CONFIG_I2C_ALGOPCA=y
+CONFIG_I2C_IMX=y
+CONFIG_SPI=y
+CONFIG_SPI_IMX=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_POWER_SUPPLY_DEBUG=y
+CONFIG_TEST_POWER=y
+CONFIG_MFD_MC13XXX=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_MC13892=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_FB=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x16=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+# CONFIG_SND_DRIVERS is not set
+# CONFIG_SND_ARM is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SND_USB is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_IMX_SOC=y
+CONFIG_HIDRAW=y
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MXC=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_GADGET=y
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_ESDHC_IMX=y
+CONFIG_NEW_LEDS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_INTF_DEV_UIE_EMUL=y
+CONFIG_DMADEVICES=y
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_INFO=y
diff --git a/linaro/configs/kvm-guest.conf b/linaro/configs/kvm-guest.conf
new file mode 100644
index 000000000000..cf174f89d043
--- /dev/null
+++ b/linaro/configs/kvm-guest.conf
@@ -0,0 +1,17 @@
+CONFIG_9P_FS=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_NET_9P=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_BALLOON_COMPACTION=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_VIRTIO_NET=y
+CONFIG_HVC_DRIVER=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_VIRTIO=y
+CONFIG_VIRTIO_BALLOON=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
+CONFIG_VIRTUALIZATION=y
+# CONFIG_THUMB2_KERNEL is not set
diff --git a/linaro/configs/kvm-host.conf b/linaro/configs/kvm-host.conf
new file mode 100644
index 000000000000..d0fb67b35175
--- /dev/null
+++ b/linaro/configs/kvm-host.conf
@@ -0,0 +1,13 @@
+CONFIG_VIRTUALIZATION=y
+CONFIG_ARM_LPAE=y
+CONFIG_ARM_VIRT_EXT=y
+CONFIG_HAVE_KVM_IRQCHIP=y
+CONFIG_KVM_ARM_HOST=y
+CONFIG_KVM_ARM_MAX_VCPUS=4
+CONFIG_KVM_ARM_TIMER=y
+CONFIG_KVM_ARM_VGIC=y
+CONFIG_KVM_MMIO=y
+CONFIG_KVM=y
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BRIDGE=m
+CONFIG_TUN=y
diff --git a/linaro/configs/linaro-base.conf b/linaro/configs/linaro-base.conf
new file mode 100644
index 000000000000..dc1848c04414
--- /dev/null
+++ b/linaro/configs/linaro-base.conf
@@ -0,0 +1,95 @@
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_SCHED_SMT=y
+CONFIG_THUMB2_KERNEL=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_IDLE=y
+CONFIG_BINFMT_MISC=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_NET=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_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_INET_LRO is not set
+CONFIG_NETFILTER=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_CONNECTOR=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_OOPS=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_NAND=y
+CONFIG_NETDEVICES=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
+CONFIG_BTRFS_FS=y
+CONFIG_QUOTA=y
+CONFIG_QFMT_V2=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ECRYPT_FS=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_JFFS2_FS_XATTR=y
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RUBIN=y
+CONFIG_CRAMFS=y
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V2 is not set
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_INFO=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_MICHAEL_MIC=y
+CONFIG_CRC_CCITT=y
+CONFIG_CRC_T10DIF=y
+CONFIG_CRC_ITU_T=y
+CONFIG_CRC7=y
+CONFIG_HW_PERF_EVENTS=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
+CONFIG_PROC_DEVICETREE=y
diff --git a/linaro/configs/linaro-base64.conf b/linaro/configs/linaro-base64.conf
new file mode 100644
index 000000000000..4ce12fd20c03
--- /dev/null
+++ b/linaro/configs/linaro-base64.conf
@@ -0,0 +1,3 @@
+CONFIG_TRANSPARENT_HUGEPAGE=y
+CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
+CONFIG_HUGETLBFS=y
diff --git a/linaro/configs/lt-arndale.conf b/linaro/configs/lt-arndale.conf
new file mode 100644
index 000000000000..c54e0dcd0dcc
--- /dev/null
+++ b/linaro/configs/lt-arndale.conf
@@ -0,0 +1 @@
+CONFIG_EXTRA_FIRMWARE="edid-1920x1080.fw"
diff --git a/linaro/configs/lt-arndale_octa.conf b/linaro/configs/lt-arndale_octa.conf
new file mode 100644
index 000000000000..c54e0dcd0dcc
--- /dev/null
+++ b/linaro/configs/lt-arndale_octa.conf
@@ -0,0 +1 @@
+CONFIG_EXTRA_FIRMWARE="edid-1920x1080.fw"
diff --git a/linaro/configs/multi_v7.conf b/linaro/configs/multi_v7.conf
new file mode 100644
index 000000000000..682be962a196
--- /dev/null
+++ b/linaro/configs/multi_v7.conf
@@ -0,0 +1,151 @@
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_ARCH_MVEBU=y
+CONFIG_MACH_ARMADA_370=y
+CONFIG_MACH_ARMADA_XP=y
+CONFIG_ARCH_BCM=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_ARCH_HIGHBANK=y
+CONFIG_ARCH_KEYSTONE=y
+CONFIG_ARCH_MXC=y
+CONFIG_MACH_IMX51_DT=y
+CONFIG_SOC_IMX53=y
+CONFIG_SOC_IMX6Q=y
+CONFIG_SOC_IMX6SL=y
+CONFIG_SOC_VF610=y
+CONFIG_ARCH_OMAP3=y
+CONFIG_ARCH_OMAP4=y
+CONFIG_SOC_OMAP5=y
+CONFIG_SOC_AM33XX=y
+CONFIG_SOC_AM43XX=y
+CONFIG_ARCH_ROCKCHIP=y
+CONFIG_ARCH_SOCFPGA=y
+CONFIG_PLAT_SPEAR=y
+CONFIG_ARCH_SPEAR13XX=y
+CONFIG_MACH_SPEAR1310=y
+CONFIG_MACH_SPEAR1340=y
+CONFIG_ARCH_STI=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_ARCH_SIRF=y
+CONFIG_ARCH_TEGRA=y
+CONFIG_ARCH_TEGRA_2x_SOC=y
+CONFIG_ARCH_TEGRA_3x_SOC=y
+CONFIG_ARCH_TEGRA_114_SOC=y
+CONFIG_TEGRA_EMC_SCALING_ENABLE=y
+CONFIG_ARCH_U8500=y
+CONFIG_MACH_HREFV60=y
+CONFIG_MACH_SNOWBALL=y
+CONFIG_MACH_UX500_DT=y
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_ARCH_VEXPRESS_CA9X4=y
+CONFIG_ARCH_VIRT=y
+CONFIG_ARCH_WM8850=y
+CONFIG_ARCH_ZYNQ=y
+CONFIG_HIGHPTE=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_OMAP_OCP2SCP=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_SATA_HIGHBANK=y
+CONFIG_SATA_MV=y
+CONFIG_SUN4I_EMAC=y
+CONFIG_NET_CALXEDA_XGMAC=y
+CONFIG_KS8851=y
+CONFIG_SMSC911X=y
+CONFIG_STMMAC_ETH=y
+CONFIG_KEYBOARD_SPEAR=y
+CONFIG_SERIO_AMBAKMI=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_SIRFSOC=y
+CONFIG_SERIAL_SIRFSOC_CONSOLE=y
+CONFIG_SERIAL_TEGRA=y
+CONFIG_SERIAL_IMX=y
+CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_SERIAL_VT8500=y
+CONFIG_SERIAL_VT8500_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_OMAP=y
+CONFIG_SERIAL_OMAP_CONSOLE=y
+CONFIG_SERIAL_XILINX_PS_UART=y
+CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
+CONFIG_SERIAL_FSL_LPUART=y
+CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_I2C_SIRF=y
+CONFIG_I2C_TEGRA=y
+CONFIG_SPI=y
+CONFIG_SPI_OMAP24XX=y
+CONFIG_SPI_PL022=y
+CONFIG_SPI_SIRF=y
+CONFIG_SPI_TEGRA114=y
+CONFIG_SPI_TEGRA20_SLINK=y
+CONFIG_PINCTRL_SINGLE=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_GPIO_TWL4030=y
+CONFIG_REGULATOR_AB8500=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_REGULATOR_TPS51632=y
+CONFIG_REGULATOR_TPS62360=y
+CONFIG_REGULATOR_TWL4030=y
+CONFIG_REGULATOR_VEXPRESS=y
+CONFIG_DRM=y
+CONFIG_TEGRA_HOST1X=y
+CONFIG_DRM_TEGRA=y
+CONFIG_FB_ARMCLCD=y
+CONFIG_FB_WM8505=y
+CONFIG_FB_SIMPLE=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_TEGRA=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_ISP1760_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_HOST=y
+CONFIG_AB8500_USB=y
+CONFIG_OMAP_USB2=y
+CONFIG_OMAP_USB3=y
+CONFIG_SAMSUNG_USB2PHY=y
+CONFIG_SAMSUNG_USB3PHY=y
+CONFIG_USB_GPIO_VBUS=y
+CONFIG_USB_ISP1301=y
+CONFIG_USB_MXS_PHY=y
+CONFIG_MMC=y
+CONFIG_MMC_ARMMMCI=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_ESDHC_IMX=y
+CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_MMC_SDHCI_SPEAR=y
+CONFIG_MMC_OMAP=y
+CONFIG_MMC_OMAP_HS=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_EDAC_HIGHBANK_MC=y
+CONFIG_EDAC_HIGHBANK_L2=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TWL4030=y
+CONFIG_RTC_DRV_PL031=y
+CONFIG_RTC_DRV_VT8500=y
+CONFIG_RTC_DRV_TEGRA=y
+CONFIG_DMADEVICES=y
+CONFIG_DW_DMAC=y
+CONFIG_TEGRA20_APB_DMA=y
+CONFIG_STE_DMA40=y
+CONFIG_SIRF_DMA=y
+CONFIG_TI_EDMA=y
+CONFIG_PL330_DMA=y
+CONFIG_IMX_SDMA=y
+CONFIG_IMX_DMA=y
+CONFIG_MXS_DMA=y
+CONFIG_DMA_OMAP=y
+CONFIG_PWM=y
+CONFIG_PWM_VT8500=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_LOCKUP_DETECTOR=y
diff --git a/linaro/configs/netns.conf b/linaro/configs/netns.conf
new file mode 100644
index 000000000000..7fadd7d67e36
--- /dev/null
+++ b/linaro/configs/netns.conf
@@ -0,0 +1,2 @@
+CONFIG_NAMESPACES=y
+CONFIG_VETH=y
diff --git a/linaro/configs/null.conf b/linaro/configs/null.conf
new file mode 100644
index 000000000000..1f71535af70b
--- /dev/null
+++ b/linaro/configs/null.conf
@@ -0,0 +1 @@
+### null config just for testing multiple config frags
diff --git a/linaro/configs/omap2plus.conf b/linaro/configs/omap2plus.conf
new file mode 100644
index 000000000000..669c5abf6454
--- /dev/null
+++ b/linaro/configs/omap2plus.conf
@@ -0,0 +1,205 @@
+CONFIG_EXPERT=y
+CONFIG_KPROBES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_MULTI_V6=y
+CONFIG_OMAP_RESET_CLOCKS=y
+CONFIG_OMAP_MUX_DEBUG=y
+CONFIG_ARCH_OMAP2=y
+CONFIG_ARCH_OMAP3=y
+CONFIG_ARCH_OMAP4=y
+CONFIG_SOC_OMAP5=y
+CONFIG_SOC_AM33XX=y
+CONFIG_SOC_DRA7XX=y
+CONFIG_ARM_THUMBEE=y
+CONFIG_ARM_ERRATA_411920=y
+CONFIG_NR_CPUS=2
+CONFIG_CMA=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootwait console=ttyO2,115200"
+CONFIG_KEXEC=y
+CONFIG_FPE_NWFPE=y
+CONFIG_PM_DEBUG=y
+CONFIG_IPV6=y
+CONFIG_CAN=m
+CONFIG_CAN_C_CAN=m
+CONFIG_CAN_C_CAN_PLATFORM=m
+CONFIG_BT=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_LL=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_CFG80211=m
+CONFIG_MAC80211=m
+CONFIG_MAC80211_RC_PID=y
+CONFIG_MAC80211_RC_DEFAULT_PID=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DMA_CMA=y
+CONFIG_MTD_NAND_OMAP2=y
+CONFIG_MTD_ONENAND=y
+CONFIG_MTD_ONENAND_VERIFY_WRITE=y
+CONFIG_MTD_ONENAND_OMAP2=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=65536
+CONFIG_SENSORS_TSL2550=m
+CONFIG_BMP085_I2C=m
+CONFIG_SENSORS_LIS3_I2C=m
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_KS8851=y
+CONFIG_KS8851_MLL=y
+CONFIG_SMC91X=y
+CONFIG_SMSC911X=y
+CONFIG_TI_CPSW=y
+CONFIG_AT803X_PHY=y
+CONFIG_SMSC_PHY=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_AN2720=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_SDIO=m
+CONFIG_LIBERTAS_DEBUG=y
+CONFIG_INPUT_JOYDEV=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_MATRIX=m
+CONFIG_KEYBOARD_TWL4030=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_TWL4030_PWRBUTTON=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+CONFIG_SERIAL_OMAP=y
+CONFIG_SERIAL_OMAP_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_SPI=y
+CONFIG_SPI_OMAP24XX=y
+CONFIG_PINCTRL_SINGLE=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_TWL4030=y
+CONFIG_W1=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_SENSORS_LM75=m
+CONFIG_THERMAL=y
+CONFIG_THERMAL_GOV_FAIR_SHARE=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_TI_SOC_THERMAL=y
+CONFIG_OMAP4_THERMAL=y
+CONFIG_OMAP5_THERMAL=y
+CONFIG_DRA752_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_OMAP_WATCHDOG=y
+CONFIG_TWL4030_WATCHDOG=y
+CONFIG_MFD_TPS65217=y
+CONFIG_MFD_TPS65910=y
+CONFIG_TWL6040_CORE=y
+CONFIG_REGULATOR_TPS65023=y
+CONFIG_REGULATOR_TPS6507X=y
+CONFIG_REGULATOR_TPS65217=y
+CONFIG_REGULATOR_TPS65910=y
+CONFIG_REGULATOR_TWL4030=y
+CONFIG_FB=y
+CONFIG_FIRMWARE_EDID=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+CONFIG_OMAP2_DSS=m
+CONFIG_OMAP2_DSS_SDI=y
+CONFIG_OMAP2_DSS_DSI=y
+CONFIG_FB_OMAP2=m
+CONFIG_DISPLAY_ENCODER_TFP410=m
+CONFIG_DISPLAY_ENCODER_TPD12S015=m
+CONFIG_DISPLAY_CONNECTOR_DVI=m
+CONFIG_DISPLAY_CONNECTOR_HDMI=m
+CONFIG_DISPLAY_PANEL_DPI=m
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_LCD_PLATFORM=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_LOGO=y
+CONFIG_SOUND=m
+CONFIG_SND=m
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_VERBOSE_PRINTK=y
+CONFIG_SND_DEBUG=y
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_SOC=m
+CONFIG_SND_OMAP_SOC=m
+CONFIG_SND_OMAP_SOC_OMAP_TWL4030=m
+CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040=m
+CONFIG_SND_OMAP_SOC_OMAP3_PANDORA=m
+CONFIG_USB=y
+CONFIG_USB_DEBUG=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_WDM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_TEST=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_ZERO=m
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_SDIO_UART=y
+CONFIG_MMC_OMAP=y
+CONFIG_MMC_OMAP_HS=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TWL92330=y
+CONFIG_RTC_DRV_TWL4030=y
+CONFIG_RTC_DRV_OMAP=y
+CONFIG_DMADEVICES=y
+CONFIG_TI_EDMA=y
+CONFIG_DMA_OMAP=y
+# CONFIG_EXT3_FS_XATTR is not set
+CONFIG_UBIFS_FS=y
+CONFIG_DEBUG_INFO=y
+CONFIG_PROVE_LOCKING=y
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_SECURITY=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_LIBCRC32C=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
diff --git a/linaro/configs/omap4.conf b/linaro/configs/omap4.conf
new file mode 100644
index 000000000000..50fb9d9cb5b5
--- /dev/null
+++ b/linaro/configs/omap4.conf
@@ -0,0 +1,194 @@
+CONFIG_EXPERT=y
+CONFIG_KPROBES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_OMAP_RESET_CLOCKS=y
+CONFIG_OMAP_MUX_DEBUG=y
+CONFIG_ARCH_OMAP2PLUS=y
+CONFIG_SOC_OMAP5=y
+# CONFIG_ARCH_OMAP2 is not set
+CONFIG_ARCH_VEXPRESS_CA9X4=y
+CONFIG_ARM_THUMBEE=y
+CONFIG_ARM_ERRATA_411920=y
+CONFIG_NR_CPUS=2
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootwait console=ttyO2,115200"
+CONFIG_KEXEC=y
+CONFIG_PM_DEBUG=y
+CONFIG_CAN=m
+CONFIG_CAN_C_CAN=m
+CONFIG_CAN_C_CAN_PLATFORM=m
+CONFIG_BT=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_LL=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_CFG80211=m
+CONFIG_MAC80211=m
+CONFIG_MAC80211_RC_PID=y
+CONFIG_MAC80211_RC_DEFAULT_PID=y
+CONFIG_CMA=y
+CONFIG_MTD_NAND_OMAP2=y
+CONFIG_MTD_ONENAND=y
+CONFIG_MTD_ONENAND_VERIFY_WRITE=y
+CONFIG_MTD_ONENAND_OMAP2=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_SENSORS_TSL2550=m
+CONFIG_SENSORS_LIS3_I2C=m
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_KS8851=y
+CONFIG_KS8851_MLL=y
+CONFIG_SMC91X=y
+CONFIG_SMSC911X=y
+CONFIG_TI_CPSW=y
+CONFIG_SMSC_PHY=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_AN2720=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_SDIO=m
+CONFIG_LIBERTAS_DEBUG=y
+CONFIG_INPUT_JOYDEV=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_MATRIX=m
+CONFIG_KEYBOARD_TWL4030=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=y
+CONFIG_INPUT_TWL4030_PWRBUTTON=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_OMAP=y
+CONFIG_SERIAL_OMAP_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_SPI=y
+CONFIG_SPI_OMAP24XX=y
+CONFIG_PINCTRL_SINGLE=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_TWL4030=y
+CONFIG_W1=y
+CONFIG_SENSORS_LM75=m
+CONFIG_WATCHDOG=y
+CONFIG_OMAP_WATCHDOG=y
+CONFIG_TWL4030_WATCHDOG=y
+CONFIG_MFD_TPS65217=y
+CONFIG_MFD_TPS65910=y
+CONFIG_TWL6040_CORE=y
+CONFIG_REGULATOR_TPS65023=y
+CONFIG_REGULATOR_TPS6507X=y
+CONFIG_REGULATOR_TPS65217=y
+CONFIG_REGULATOR_TPS65910=y
+CONFIG_REGULATOR_TWL4030=y
+CONFIG_FB=y
+CONFIG_FIRMWARE_EDID=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+CONFIG_OMAP2_DSS=m
+CONFIG_OMAP2_DSS_RFBI=y
+CONFIG_OMAP2_DSS_SDI=y
+CONFIG_OMAP2_DSS_DSI=y
+CONFIG_FB_OMAP2=m
+CONFIG_PANEL_GENERIC_DPI=m
+CONFIG_PANEL_TFP410=m
+CONFIG_PANEL_SHARP_LS037V7DW01=m
+CONFIG_PANEL_NEC_NL8048HL11_01B=m
+CONFIG_PANEL_TAAL=m
+CONFIG_PANEL_TPO_TD043MTEA1=m
+CONFIG_PANEL_ACX565AKM=m
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_LCD_PLATFORM=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+CONFIG_SOUND=m
+CONFIG_SND=m
+CONFIG_SND_VERBOSE_PRINTK=y
+CONFIG_SND_DEBUG=y
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_SOC=m
+CONFIG_SND_OMAP_SOC=m
+CONFIG_SND_OMAP_SOC_OMAP_TWL4030=m
+CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040=m
+CONFIG_SND_OMAP_SOC_OMAP3_PANDORA=m
+CONFIG_USB=y
+CONFIG_USB_DEBUG=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_WDM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_TEST=y
+CONFIG_USB_PHY=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_ZERO=m
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_SDIO_UART=y
+CONFIG_MMC_ARMMMCI=y
+CONFIG_MMC_OMAP=y
+CONFIG_MMC_OMAP_HS=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TWL92330=y
+CONFIG_RTC_DRV_TWL4030=y
+CONFIG_RTC_DRV_OMAP=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_OMAP=y
+# CONFIG_EXT3_FS_XATTR is not set
+CONFIG_UBIFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_LIBCRC32C=y
+# CONFIG_CPU_FREQ is not set
diff --git a/linaro/configs/origen.conf b/linaro/configs/origen.conf
new file mode 100644
index 000000000000..9546585f7266
--- /dev/null
+++ b/linaro/configs/origen.conf
@@ -0,0 +1,89 @@
+CONFIG_ARCH_EXYNOS=y
+CONFIG_S3C_LOWLEVEL_UART_PORT=2
+CONFIG_S3C24XX_PWM=y
+CONFIG_MACH_SMDKC210=y
+CONFIG_MACH_ARMLEX4210=y
+CONFIG_MACH_UNIVERSAL_C210=y
+CONFIG_MACH_NURI=y
+CONFIG_MACH_ORIGEN=y
+CONFIG_MACH_SMDK4412=y
+CONFIG_MACH_EXYNOS4_DT=y
+CONFIG_NR_CPUS=2
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_AEABI=y
+CONFIG_CMDLINE="root=/dev/mmcblk0p1 rw rootwait console=ttySAC2,115200"
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_RUNTIME=y
+CONFIG_RFKILL=y
+CONFIG_RFKILL_GPIO=y
+CONFIG_CMA=y
+CONFIG_CMA_SIZE_MBYTES=32
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CFG80211=y
+CONFIG_USB_PEGASUS=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_DM9601=y
+CONFIG_USB_NET_MCS7830=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_SAMSUNG=y
+CONFIG_SERIAL_SAMSUNG_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_S3C2410=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
+CONFIG_SENSORS_EXYNOS4_TMU=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_MFD_MAX8997=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_MAX8997=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_SAMSUNG_S5P_FIMC=y
+CONFIG_VIDEO_S5P_FIMC=y
+CONFIG_VIDEO_SAMSUNG_S5P_TV=y
+CONFIG_VIDEO_SAMSUNG_S5P_HDMI=y
+CONFIG_VIDEO_SAMSUNG_S5P_SDO=y
+CONFIG_VIDEO_SAMSUNG_S5P_MIXER=y
+CONFIG_V4L_MEM2MEM_DRIVERS=y
+CONFIG_VIDEO_SAMSUNG_S5P_G2D=y
+CONFIG_VIDEO_SAMSUNG_S5P_JPEG=y
+CONFIG_VIDEO_SAMSUNG_S5P_MFC=y
+CONFIG_FB=y
+CONFIG_FB_S3C=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_LCD_PLATFORM=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_LOGO=y
+CONFIG_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_S5P=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_EXYNOS=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_S3C=y
+CONFIG_MMC_SDHCI_S3C_DMA=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_S3C=y
+CONFIG_DEBUG_S3C_UART2=y
diff --git a/linaro/configs/panda.conf b/linaro/configs/panda.conf
new file mode 100644
index 000000000000..1771853ceeae
--- /dev/null
+++ b/linaro/configs/panda.conf
@@ -0,0 +1,11 @@
+# CONFIG_ARCH_OMAP2 is not set
+CONFIG_GPIO_PCA953X=y
+CONFIG_REGULATOR_DUMMY=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_LEDS_CLASS=y
+CONFIG_WL_TI=y
+CONFIG_WL12XX=m
+CONFIG_WLCORE=m
+CONFIG_WLCORE_SDIO=m
+CONFIG_WILINK_PLATFORM_DATA=y
diff --git a/linaro/configs/u8500.conf b/linaro/configs/u8500.conf
new file mode 100644
index 000000000000..86e294faa2f6
--- /dev/null
+++ b/linaro/configs/u8500.conf
@@ -0,0 +1,83 @@
+# CONFIG_SWAP is not set
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_ARCH_U8500=y
+CONFIG_MACH_HREFV60=y
+CONFIG_MACH_SNOWBALL=y
+CONFIG_MACH_UX500_DT=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_CMDLINE="root=/dev/ram0 console=ttyAMA2,115200n8"
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_RUNTIME=y
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+CONFIG_IPV6_SIT=m
+CONFIG_PHONET=y
+# CONFIG_WIRELESS is not set
+CONFIG_CAIF=y
+CONFIG_SENSORS_BH1780=y
+CONFIG_SMSC911X=y
+CONFIG_SMSC_PHY=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_NOMADIK=y
+CONFIG_KEYBOARD_STMPE=y
+CONFIG_KEYBOARD_TC3589X=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_BU21013=y
+CONFIG_INPUT_AB8500_PONKEY=y
+# CONFIG_SERIO is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_SPI=y
+CONFIG_SPI_PL022=y
+CONFIG_GPIO_STMPE=y
+CONFIG_GPIO_TC3589X=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
+CONFIG_MFD_STMPE=y
+CONFIG_MFD_TC3589X=y
+CONFIG_AB8500_CORE=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_REGULATOR_AB8500=y
+CONFIG_USB_GADGET=y
+CONFIG_AB8500_USB=y
+CONFIG_MMC=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_ARMMMCI=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_LM3530=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_LP5521=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_AB8500=y
+CONFIG_RTC_DRV_PL031=y
+CONFIG_DMADEVICES=y
+CONFIG_STE_DMA40=y
+CONFIG_STAGING=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
+CONFIG_HSEM_U8500=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_CONFIGFS_FS=m
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_FTRACE is not set
+CONFIG_DEBUG_USER=y
diff --git a/linaro/configs/uprobes.conf b/linaro/configs/uprobes.conf
new file mode 100644
index 000000000000..a9617986284a
--- /dev/null
+++ b/linaro/configs/uprobes.conf
@@ -0,0 +1,7 @@
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_RELAY=y
+CONFIG_KPROBES=y
+CONFIG_UPROBE_EVENT=y
+CONFIG_KPROBES_SANITY_TEST=y
+CONFIG_ARM_KPROBES_TEST=m
diff --git a/linaro/configs/vexpress-tuning.conf b/linaro/configs/vexpress-tuning.conf
new file mode 100644
index 000000000000..adea6cc66ded
--- /dev/null
+++ b/linaro/configs/vexpress-tuning.conf
@@ -0,0 +1 @@
+# CONFIG_PROVE_LOCKING is not set
diff --git a/linaro/configs/vexpress.conf b/linaro/configs/vexpress.conf
new file mode 100644
index 000000000000..f8516e3dec69
--- /dev/null
+++ b/linaro/configs/vexpress.conf
@@ -0,0 +1,65 @@
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_ARCH_VEXPRESS_CA9X4=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+CONFIG_NR_CPUS=8
+CONFIG_HIGHMEM=y
+CONFIG_HIGHPTE=y
+CONFIG_ARM_PSCI=y
+CONFIG_MCPM=y
+CONFIG_ARCH_VEXPRESS_DCSCB=y
+CONFIG_ARCH_VEXPRESS_TC2_PM=y
+CONFIG_ARM_BIG_LITTLE_CPUIDLE=y
+CONFIG_BIG_LITTLE=y
+CONFIG_ARM_BIG_LITTLE_CPUFREQ=y
+CONFIG_ARM_VEXPRESS_SPC_CPUFREQ=y
+CONFIG_PM_OPP=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+CONFIG_CMDLINE="console=ttyAMA0,38400n8 root=/dev/mmcblk0p2 rootwait mmci.fmax=4000000"
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SMSC911X=y
+CONFIG_SMC91X=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_SERIO_AMBAKMI=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_FB=y
+CONFIG_FB_ARMCLCD=y
+CONFIG_FB_ARMHDLCD=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_ARMAACI=y
+CONFIG_USB=y
+CONFIG_USB_ISP1760_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_MMC=y
+CONFIG_MMC_ARMMMCI=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PL031=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_VEXPRESS_CONFIG=y
+CONFIG_SENSORS_VEXPRESS=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_VEXPRESS=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_VIRTIO=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
diff --git a/linaro/configs/vexpress64.conf b/linaro/configs/vexpress64.conf
new file mode 100644
index 000000000000..b416be269621
--- /dev/null
+++ b/linaro/configs/vexpress64.conf
@@ -0,0 +1,59 @@
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=8
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT=y
+CONFIG_CMDLINE="console=ttyAMA0"
+CONFIG_COMPAT=y
+CONFIG_SMC91X=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_SERIO_AMBAKMI=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+# CONFIG_SERIO_I8042 is not set
+CONFIG_FB=y
+CONFIG_FB_ARMCLCD=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_MMC=y
+CONFIG_MMC_ARMMMCI=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PL031=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_VIRTIO=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_SMSC911X=y
+CONFIG_I2C=y
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_USB_HIDDEV=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB=y
+CONFIG_USB_ULPI=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_HCD_SYNOPSYS=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_MHU_MBOX=y
+CONFIG_ARM_SCPI_PROTOCOL=y
+CONFIG_COMMON_CLK_SCPI=y
+CONFIG_ARM_SPCI_CPUFREQ=y
+CONFIG_ARM_BIG_LITTLE_CPUFREQ=y
+CONFIG_ARM_DT_BL_CPUFREQ=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
diff --git a/linaro/configs/xen.conf b/linaro/configs/xen.conf
new file mode 100644
index 000000000000..d24fabbea076
--- /dev/null
+++ b/linaro/configs/xen.conf
@@ -0,0 +1,7 @@
+CONFIG_XEN=y
+CONFIG_XEN_NETDEV_FRONTEND=y
+CONFIG_XEN_NETDEV_BACKEND=y
+CONFIG_XEN_BLKDEV_FRONTEND=y
+CONFIG_XEN_BLKDEV_BACKEND=y
+CONFIG_XENFS=y
+CONFIG_XEN_COMPAT_XENFS=y
diff --git a/mm/madvise.c b/mm/madvise.c
index 0938b30da4ab..dd7d354c5bfa 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -102,7 +102,8 @@ static long madvise_behavior(struct vm_area_struct *vma,
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
*prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma,
- vma->vm_file, pgoff, vma_policy(vma));
+ vma->vm_file, pgoff, vma_policy(vma),
+ vma_get_anon_name(vma));
if (*prev) {
vma = *prev;
goto success;
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 8f5330d74f47..f4ec0db14e2f 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -770,7 +770,7 @@ static int mbind_range(struct mm_struct *mm, unsigned long start,
((vmstart - vma->vm_start) >> PAGE_SHIFT);
prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags,
vma->anon_vma, vma->vm_file, pgoff,
- new_pol);
+ new_pol, vma_get_anon_name(vma));
if (prev) {
vma = prev;
next = vma->vm_next;
diff --git a/mm/mlock.c b/mm/mlock.c
index ce84cb0b83ef..54288223b75c 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -566,7 +566,8 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev,
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
*prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma,
- vma->vm_file, pgoff, vma_policy(vma));
+ vma->vm_file, pgoff, vma_policy(vma),
+ vma_get_anon_name(vma));
if (*prev) {
vma = *prev;
goto success;
diff --git a/mm/mmap.c b/mm/mmap.c
index c1f2ea4a0b99..8c1c5c1f73fe 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -901,7 +901,8 @@ again: remove_next = 1 + (end > next->vm_end);
* per-vma resources, so we don't attempt to merge those.
*/
static inline int is_mergeable_vma(struct vm_area_struct *vma,
- struct file *file, unsigned long vm_flags)
+ struct file *file, unsigned long vm_flags,
+ const char __user *anon_name)
{
/*
* VM_SOFTDIRTY should not prevent from VMA merging, if we
@@ -917,6 +918,8 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma,
return 0;
if (vma->vm_ops && vma->vm_ops->close)
return 0;
+ if (vma_get_anon_name(vma) != anon_name)
+ return 0;
return 1;
}
@@ -947,9 +950,10 @@ static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1,
*/
static int
can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags,
- struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff)
+ struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff,
+ const char __user *anon_name)
{
- if (is_mergeable_vma(vma, file, vm_flags) &&
+ if (is_mergeable_vma(vma, file, vm_flags, anon_name) &&
is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) {
if (vma->vm_pgoff == vm_pgoff)
return 1;
@@ -966,9 +970,10 @@ can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags,
*/
static int
can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags,
- struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff)
+ struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff,
+ const char __user *anon_name)
{
- if (is_mergeable_vma(vma, file, vm_flags) &&
+ if (is_mergeable_vma(vma, file, vm_flags, anon_name) &&
is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) {
pgoff_t vm_pglen;
vm_pglen = vma_pages(vma);
@@ -979,9 +984,9 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags,
}
/*
- * Given a mapping request (addr,end,vm_flags,file,pgoff), figure out
- * whether that can be merged with its predecessor or its successor.
- * Or both (it neatly fills a hole).
+ * Given a mapping request (addr,end,vm_flags,file,pgoff,anon_name),
+ * figure out whether that can be merged with its predecessor or its
+ * successor. Or both (it neatly fills a hole).
*
* In most cases - when called for mmap, brk or mremap - [addr,end) is
* certain not to be mapped by the time vma_merge is called; but when
@@ -1011,7 +1016,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
struct vm_area_struct *prev, unsigned long addr,
unsigned long end, unsigned long vm_flags,
struct anon_vma *anon_vma, struct file *file,
- pgoff_t pgoff, struct mempolicy *policy)
+ pgoff_t pgoff, struct mempolicy *policy,
+ const char __user *anon_name)
{
pgoff_t pglen = (end - addr) >> PAGE_SHIFT;
struct vm_area_struct *area, *next;
@@ -1037,15 +1043,15 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
*/
if (prev && prev->vm_end == addr &&
mpol_equal(vma_policy(prev), policy) &&
- can_vma_merge_after(prev, vm_flags,
- anon_vma, file, pgoff)) {
+ can_vma_merge_after(prev, vm_flags, anon_vma,
+ file, pgoff, anon_name)) {
/*
* OK, it can. Can we now merge in the successor as well?
*/
if (next && end == next->vm_start &&
mpol_equal(policy, vma_policy(next)) &&
- can_vma_merge_before(next, vm_flags,
- anon_vma, file, pgoff+pglen) &&
+ can_vma_merge_before(next, vm_flags, anon_vma,
+ file, pgoff+pglen, anon_name) &&
is_mergeable_anon_vma(prev->anon_vma,
next->anon_vma, NULL)) {
/* cases 1, 6 */
@@ -1065,8 +1071,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
*/
if (next && end == next->vm_start &&
mpol_equal(policy, vma_policy(next)) &&
- can_vma_merge_before(next, vm_flags,
- anon_vma, file, pgoff+pglen)) {
+ can_vma_merge_before(next, vm_flags, anon_vma,
+ file, pgoff+pglen, anon_name)) {
if (prev && addr < prev->vm_end) /* case 4 */
err = vma_adjust(prev, prev->vm_start,
addr, prev->vm_pgoff, NULL);
@@ -1548,7 +1554,8 @@ munmap_back:
/*
* Can we just expand an old mapping?
*/
- vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, NULL);
+ vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff,
+ NULL, NULL);
if (vma)
goto out;
@@ -2670,7 +2677,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
/* Can we just expand an old private anonymous mapping? */
vma = vma_merge(mm, prev, addr, addr + len, flags,
- NULL, NULL, pgoff, NULL);
+ NULL, NULL, pgoff, NULL, NULL);
if (vma)
goto out;
@@ -2829,7 +2836,8 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent))
return NULL; /* should never get here */
new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags,
- vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma));
+ vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma),
+ vma_get_anon_name(vma));
if (new_vma) {
/*
* Source vma may have been merged into new_vma
diff --git a/mm/mprotect.c b/mm/mprotect.c
index c43d557941f8..04e6ed71dc61 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -294,7 +294,8 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
*/
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
*pprev = vma_merge(mm, *pprev, start, end, newflags,
- vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma));
+ vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma),
+ vma_get_anon_name(vma));
if (*pprev) {
vma = *pprev;
goto success;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 18cee0d4c8a2..cf0fa4c514de 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -205,8 +205,21 @@ static char * const zone_names[MAX_NR_ZONES] = {
"Movable",
};
+/*
+ * Try to keep at least this much lowmem free. Do not allow normal
+ * allocations below this point, only high priority ones. Automatically
+ * tuned according to the amount of memory in the system.
+ */
int min_free_kbytes = 1024;
int user_min_free_kbytes = -1;
+int min_free_order_shift = 1;
+
+/*
+ * Extra memory for the system to try freeing. Used to temporarily
+ * free memory, to make space for new workloads. Anyone can allocate
+ * down to the min watermarks controlled by min_free_kbytes above.
+ */
+int extra_free_kbytes = 0;
static unsigned long __meminitdata nr_kernel_pages;
static unsigned long __meminitdata nr_all_pages;
@@ -1738,7 +1751,7 @@ static bool __zone_watermark_ok(struct zone *z, unsigned int order,
free_pages -= z->free_area[o].nr_free << o;
/* Require fewer higher order pages to be free */
- min >>= 1;
+ min >>= min_free_order_shift;
if (free_pages <= min)
return false;
@@ -5658,6 +5671,7 @@ static void setup_per_zone_lowmem_reserve(void)
static void __setup_per_zone_wmarks(void)
{
unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10);
+ unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10);
unsigned long lowmem_pages = 0;
struct zone *zone;
unsigned long flags;
@@ -5669,11 +5683,14 @@ static void __setup_per_zone_wmarks(void)
}
for_each_zone(zone) {
- u64 tmp;
+ u64 min, low;
spin_lock_irqsave(&zone->lock, flags);
- tmp = (u64)pages_min * zone->managed_pages;
- do_div(tmp, lowmem_pages);
+ min = (u64)pages_min * zone->managed_pages;
+ do_div(min, lowmem_pages);
+ low = (u64)pages_low * zone->managed_pages;
+ do_div(low, vm_total_pages);
+
if (is_highmem(zone)) {
/*
* __GFP_HIGH and PF_MEMALLOC allocations usually don't
@@ -5694,11 +5711,13 @@ static void __setup_per_zone_wmarks(void)
* If it's a lowmem zone, reserve a number of pages
* proportionate to the zone's size.
*/
- zone->watermark[WMARK_MIN] = tmp;
+ zone->watermark[WMARK_MIN] = min;
}
- zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> 2);
- zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1);
+ zone->watermark[WMARK_LOW] = min_wmark_pages(zone) +
+ low + (min >> 2);
+ zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) +
+ low + (min >> 1);
__mod_zone_page_state(zone, NR_ALLOC_BATCH,
high_wmark_pages(zone) -
@@ -5823,7 +5842,7 @@ module_init(init_per_zone_wmark_min)
/*
* min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so
* that we can call two helper functions whenever min_free_kbytes
- * changes.
+ * or extra_free_kbytes changes.
*/
int min_free_kbytes_sysctl_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *length, loff_t *ppos)
diff --git a/mm/shmem.c b/mm/shmem.c
index 0e5fb225007c..3f0d3cbfbc95 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -3366,6 +3366,14 @@ struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags
}
EXPORT_SYMBOL_GPL(shmem_file_setup);
+void shmem_set_file(struct vm_area_struct *vma, struct file *file)
+{
+ if (vma->vm_file)
+ fput(vma->vm_file);
+ vma->vm_file = file;
+ vma->vm_ops = &shmem_vm_ops;
+}
+
/**
* shmem_zero_setup - setup a shared anonymous mapping
* @vma: the vma to be mmapped is prepared by do_mmap_pgoff
@@ -3379,10 +3387,7 @@ int shmem_zero_setup(struct vm_area_struct *vma)
if (IS_ERR(file))
return PTR_ERR(file);
- if (vma->vm_file)
- fput(vma->vm_file);
- vma->vm_file = file;
- vma->vm_ops = &shmem_vm_ops;
+ shmem_set_file(vma, file);
return 0;
}
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 2836b5373b2e..e3cccd3be551 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -45,6 +45,7 @@
#include <linux/sysctl.h>
#include <linux/oom.h>
#include <linux/prefetch.h>
+#include <linux/debugfs.h>
#include <linux/printk.h>
#include <asm/tlbflush.h>
@@ -186,6 +187,39 @@ static unsigned long get_lru_size(struct lruvec *lruvec, enum lru_list lru)
return zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru);
}
+struct dentry *debug_file;
+
+static int debug_shrinker_show(struct seq_file *s, void *unused)
+{
+ struct shrinker *shrinker;
+ struct shrink_control sc;
+
+ sc.gfp_mask = -1;
+ sc.nr_to_scan = 0;
+
+ down_read(&shrinker_rwsem);
+ list_for_each_entry(shrinker, &shrinker_list, list) {
+ int num_objs;
+
+ num_objs = shrinker->count_objects(shrinker, &sc);
+ seq_printf(s, "%pf %d\n", shrinker->scan_objects, num_objs);
+ }
+ up_read(&shrinker_rwsem);
+ return 0;
+}
+
+static int debug_shrinker_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_shrinker_show, inode->i_private);
+}
+
+static const struct file_operations debug_shrinker_fops = {
+ .open = debug_shrinker_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
/*
* Add a shrinker callback to be called from the vm.
*/
@@ -215,6 +249,15 @@ int register_shrinker(struct shrinker *shrinker)
}
EXPORT_SYMBOL(register_shrinker);
+static int __init add_shrinker_debug(void)
+{
+ debugfs_create_file("shrinker", 0644, NULL, NULL,
+ &debug_shrinker_fops);
+ return 0;
+}
+
+late_initcall(add_shrinker_debug);
+
/*
* Remove one
*/
diff --git a/net/Kconfig b/net/Kconfig
index 4051fdfa4367..1ebaa97019b2 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -82,6 +82,20 @@ source "net/netlabel/Kconfig"
endif # if INET
+config ANDROID_PARANOID_NETWORK
+ bool "Only allow certain groups to create sockets"
+ default ANDROID
+ help
+ none
+
+config NET_ACTIVITY_STATS
+ bool "Network activity statistics tracking"
+ default ANDROID
+ help
+ Network activity statistics are useful for tracking wireless
+ modem activity on 2G, 3G, 4G wireless networks. Counts number of
+ transmissions and groups them in specified time buckets.
+
config NETWORK_SECMARK
bool "Security Marking"
help
@@ -228,7 +242,7 @@ source "net/mpls/Kconfig"
source "net/hsr/Kconfig"
config RPS
- boolean
+ boolean "RPS"
depends on SMP && SYSFS
default y
diff --git a/net/Makefile b/net/Makefile
index 7ed1970074b0..550c1123047b 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -73,3 +73,4 @@ obj-$(CONFIG_OPENVSWITCH) += openvswitch/
obj-$(CONFIG_VSOCKETS) += vmw_vsock/
obj-$(CONFIG_NET_MPLS_GSO) += mpls/
obj-$(CONFIG_HSR) += hsr/
+obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o
diff --git a/net/activity_stats.c b/net/activity_stats.c
new file mode 100644
index 000000000000..4609ce2043eb
--- /dev/null
+++ b/net/activity_stats.c
@@ -0,0 +1,119 @@
+/* net/activity_stats.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * Author: Mike Chan (mike@android.com)
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/suspend.h>
+#include <net/net_namespace.h>
+
+/*
+ * Track transmission rates in buckets (power of 2).
+ * 1,2,4,8...512 seconds.
+ *
+ * Buckets represent the count of network transmissions at least
+ * N seconds apart, where N is 1 << bucket index.
+ */
+#define BUCKET_MAX 10
+
+/* Track network activity frequency */
+static unsigned long activity_stats[BUCKET_MAX];
+static ktime_t last_transmit;
+static ktime_t suspend_time;
+static DEFINE_SPINLOCK(activity_lock);
+
+void activity_stats_update(void)
+{
+ int i;
+ unsigned long flags;
+ ktime_t now;
+ s64 delta;
+
+ spin_lock_irqsave(&activity_lock, flags);
+ now = ktime_get();
+ delta = ktime_to_ns(ktime_sub(now, last_transmit));
+
+ for (i = BUCKET_MAX - 1; i >= 0; i--) {
+ /*
+ * Check if the time delta between network activity is within the
+ * minimum bucket range.
+ */
+ if (delta < (1000000000ULL << i))
+ continue;
+
+ activity_stats[i]++;
+ last_transmit = now;
+ break;
+ }
+ spin_unlock_irqrestore(&activity_lock, flags);
+}
+
+static int activity_stats_show(struct seq_file *m, void *v)
+{
+ int i;
+ int ret;
+
+ seq_printf(m, "Min Bucket(sec) Count\n");
+
+ for (i = 0; i < BUCKET_MAX; i++) {
+ ret = seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int activity_stats_notifier(struct notifier_block *nb,
+ unsigned long event, void *dummy)
+{
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ suspend_time = ktime_get_real();
+ break;
+
+ case PM_POST_SUSPEND:
+ suspend_time = ktime_sub(ktime_get_real(), suspend_time);
+ last_transmit = ktime_sub(last_transmit, suspend_time);
+ }
+
+ return 0;
+}
+
+static int activity_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, activity_stats_show, PDE_DATA(inode));
+}
+
+static const struct file_operations activity_stats_fops = {
+ .open = activity_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static struct notifier_block activity_stats_notifier_block = {
+ .notifier_call = activity_stats_notifier,
+};
+
+static int __init activity_stats_init(void)
+{
+ proc_create("activity", S_IRUGO,
+ init_net.proc_net_stat, &activity_stats_fops);
+ return register_pm_notifier(&activity_stats_notifier_block);
+}
+
+subsys_initcall(activity_stats_init);
+
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 4dca0299ed96..8f9b42f4e7db 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -104,11 +104,40 @@ void bt_sock_unregister(int proto)
}
EXPORT_SYMBOL(bt_sock_unregister);
+#ifdef CONFIG_PARANOID_NETWORK
+static inline int current_has_bt_admin(void)
+{
+ return !current_euid();
+}
+
+static inline int current_has_bt(void)
+{
+ return current_has_bt_admin();
+}
+# else
+static inline int current_has_bt_admin(void)
+{
+ return 1;
+}
+
+static inline int current_has_bt(void)
+{
+ return 1;
+}
+#endif
+
static int bt_sock_create(struct net *net, struct socket *sock, int proto,
int kern)
{
int err;
+ if (proto == BTPROTO_RFCOMM || proto == BTPROTO_SCO ||
+ proto == BTPROTO_L2CAP) {
+ if (!current_has_bt())
+ return -EPERM;
+ } else if (!current_has_bt_admin())
+ return -EPERM;
+
if (net != &init_net)
return -EAFNOSUPPORT;
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 8ee1cd4053ee..372764f12657 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -15,6 +15,7 @@ obj-y := route.o inetpeer.o protocol.o \
obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o
obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
+obj-$(CONFIG_SYSFS) += sysfs_net_ipv4.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o
obj-$(CONFIG_IP_MROUTE) += ipmr.o
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index d156b3c5f363..7b3c3ab66892 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -119,6 +119,19 @@
#include <linux/mroute.h>
#endif
+#ifdef CONFIG_ANDROID_PARANOID_NETWORK
+#include <linux/android_aid.h>
+
+static inline int current_has_network(void)
+{
+ return in_egroup_p(AID_INET) || capable(CAP_NET_RAW);
+}
+#else
+static inline int current_has_network(void)
+{
+ return 1;
+}
+#endif
/* The inetsw table contains everything that inet_create needs to
* build a new socket.
@@ -257,6 +270,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
int try_loading_module = 0;
int err;
+ if (!current_has_network())
+ return -EACCES;
+
sock->state = SS_UNCONNECTED;
/* Look for the requested type/protocol pair. */
@@ -305,8 +321,7 @@ lookup_protocol:
}
err = -EPERM;
- if (sock->type == SOCK_RAW && !kern &&
- !ns_capable(net->user_ns, CAP_NET_RAW))
+ if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
goto out_rcu_unlock;
sock->ops = answer->ops;
@@ -872,6 +887,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCSIFPFLAGS:
case SIOCGIFPFLAGS:
case SIOCSIFFLAGS:
+ case SIOCKILLADDR:
err = devinet_ioctl(net, cmd, (void __user *)arg);
break;
default:
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 214882e7d6de..629ee7b96bb7 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -59,6 +59,7 @@
#include <net/arp.h>
#include <net/ip.h>
+#include <net/tcp.h>
#include <net/route.h>
#include <net/ip_fib.h>
#include <net/rtnetlink.h>
@@ -934,6 +935,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
case SIOCSIFBRDADDR: /* Set the broadcast address */
case SIOCSIFDSTADDR: /* Set the destination address */
case SIOCSIFNETMASK: /* Set the netmask for the interface */
+ case SIOCKILLADDR: /* Nuke all sockets on this address */
ret = -EPERM;
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto out;
@@ -985,7 +987,8 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
}
ret = -EADDRNOTAVAIL;
- if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
+ if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS
+ && cmd != SIOCKILLADDR)
goto done;
switch (cmd) {
@@ -1112,6 +1115,9 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
inet_insert_ifa(ifa);
}
break;
+ case SIOCKILLADDR: /* Nuke all connections on this address */
+ ret = tcp_nuke_addr(net, (struct sockaddr *) sin);
+ break;
}
done:
rtnl_unlock();
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 79a007c52558..1aa605ab22aa 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -145,6 +145,21 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write,
return ret;
}
+/* Validate changes from /proc interface. */
+static int proc_tcp_default_init_rwnd(struct ctl_table *ctl, int write,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ int old_value = *(int *)ctl->data;
+ int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+ int new_value = *(int *)ctl->data;
+
+ if (write && ret == 0 && (new_value < 3 || new_value > 100))
+ *(int *)ctl->data = old_value;
+
+ return ret;
+}
+
static int proc_tcp_congestion_control(struct ctl_table *ctl, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
@@ -728,6 +743,13 @@ static struct ctl_table ipv4_table[] = {
.extra2 = &one,
},
{
+ .procname = "tcp_default_init_rwnd",
+ .data = &sysctl_tcp_default_init_rwnd,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_tcp_default_init_rwnd
+ },
+ {
.procname = "udp_mem",
.data = &sysctl_udp_mem,
.maxlen = sizeof(sysctl_udp_mem),
diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c
new file mode 100644
index 000000000000..0cbbf10026a6
--- /dev/null
+++ b/net/ipv4/sysfs_net_ipv4.c
@@ -0,0 +1,88 @@
+/*
+ * net/ipv4/sysfs_net_ipv4.c
+ *
+ * sysfs-based networking knobs (so we can, unlike with sysctl, control perms)
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * Robert Love <rlove@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/init.h>
+#include <net/tcp.h>
+
+#define CREATE_IPV4_FILE(_name, _var) \
+static ssize_t _name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return sprintf(buf, "%d\n", _var); \
+} \
+static ssize_t _name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ int val, ret; \
+ ret = sscanf(buf, "%d", &val); \
+ if (ret != 1) \
+ return -EINVAL; \
+ if (val < 0) \
+ return -EINVAL; \
+ _var = val; \
+ return count; \
+} \
+static struct kobj_attribute _name##_attr = \
+ __ATTR(_name, 0644, _name##_show, _name##_store)
+
+CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]);
+CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]);
+CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]);
+
+CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]);
+CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]);
+CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]);
+
+static struct attribute *ipv4_attrs[] = {
+ &tcp_wmem_min_attr.attr,
+ &tcp_wmem_def_attr.attr,
+ &tcp_wmem_max_attr.attr,
+ &tcp_rmem_min_attr.attr,
+ &tcp_rmem_def_attr.attr,
+ &tcp_rmem_max_attr.attr,
+ NULL
+};
+
+static struct attribute_group ipv4_attr_group = {
+ .attrs = ipv4_attrs,
+};
+
+static __init int sysfs_ipv4_init(void)
+{
+ struct kobject *ipv4_kobject;
+ int ret;
+
+ ipv4_kobject = kobject_create_and_add("ipv4", kernel_kobj);
+ if (!ipv4_kobject)
+ return -ENOMEM;
+
+ ret = sysfs_create_group(ipv4_kobject, &ipv4_attr_group);
+ if (ret) {
+ kobject_put(ipv4_kobject);
+ return ret;
+ }
+
+ return 0;
+}
+
+subsys_initcall(sysfs_ipv4_init);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 541f26a67ba2..e957ae5f0803 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -268,12 +268,16 @@
#include <linux/crypto.h>
#include <linux/time.h>
#include <linux/slab.h>
+#include <linux/uid_stat.h>
#include <net/icmp.h>
#include <net/inet_common.h>
#include <net/tcp.h>
#include <net/xfrm.h>
#include <net/ip.h>
+#include <net/ip6_route.h>
+#include <net/ipv6.h>
+#include <net/transp_v6.h>
#include <net/netdma.h>
#include <net/sock.h>
@@ -1299,6 +1303,10 @@ out:
tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
out_nopush:
release_sock(sk);
+
+ if (copied + copied_syn)
+ uid_stat_tcp_snd(from_kuid(&init_user_ns, current_uid()),
+ copied + copied_syn);
return copied + copied_syn;
do_fault:
@@ -1603,6 +1611,8 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
if (copied > 0) {
tcp_recv_skb(sk, seq, &offset);
tcp_cleanup_rbuf(sk, copied);
+ uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()),
+ copied);
}
return copied;
}
@@ -2014,6 +2024,10 @@ skip_copy:
tcp_cleanup_rbuf(sk, copied);
release_sock(sk);
+
+ if (copied > 0)
+ uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()),
+ copied);
return copied;
out:
@@ -2022,6 +2036,9 @@ out:
recv_urg:
err = tcp_recv_urg(sk, msg, len, flags);
+ if (err > 0)
+ uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()),
+ err);
goto out;
recv_sndq:
@@ -3261,3 +3278,105 @@ void __init tcp_init(void)
tcp_tasklet_init();
}
+
+static int tcp_is_local(struct net *net, __be32 addr) {
+ struct rtable *rt;
+ struct flowi4 fl4 = { .daddr = addr };
+ rt = ip_route_output_key(net, &fl4);
+ if (IS_ERR_OR_NULL(rt))
+ return 0;
+ return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK);
+}
+
+#if defined(CONFIG_IPV6)
+static int tcp_is_local6(struct net *net, struct in6_addr *addr) {
+ struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0);
+ return rt6 && rt6->dst.dev && (rt6->dst.dev->flags & IFF_LOOPBACK);
+}
+#endif
+
+/*
+ * tcp_nuke_addr - destroy all sockets on the given local address
+ * if local address is the unspecified address (0.0.0.0 or ::), destroy all
+ * sockets with local addresses that are not configured.
+ */
+int tcp_nuke_addr(struct net *net, struct sockaddr *addr)
+{
+ int family = addr->sa_family;
+ unsigned int bucket;
+
+ struct in_addr *in;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct in6_addr *in6 = NULL;
+#endif
+ if (family == AF_INET) {
+ in = &((struct sockaddr_in *)addr)->sin_addr;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ } else if (family == AF_INET6) {
+ in6 = &((struct sockaddr_in6 *)addr)->sin6_addr;
+#endif
+ } else {
+ return -EAFNOSUPPORT;
+ }
+
+ for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) {
+ struct hlist_nulls_node *node;
+ struct sock *sk;
+ spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket);
+
+restart:
+ spin_lock_bh(lock);
+ sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) {
+ struct inet_sock *inet = inet_sk(sk);
+
+ if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT)
+ continue;
+ if (sock_flag(sk, SOCK_DEAD))
+ continue;
+
+ if (family == AF_INET) {
+ __be32 s4 = inet->inet_rcv_saddr;
+ if (s4 == LOOPBACK4_IPV6)
+ continue;
+
+ if (in->s_addr != s4 &&
+ !(in->s_addr == INADDR_ANY &&
+ !tcp_is_local(net, s4)))
+ continue;
+ }
+
+#if defined(CONFIG_IPV6)
+ if (family == AF_INET6) {
+ struct in6_addr *s6;
+
+ s6 = &sk->sk_v6_rcv_saddr;
+ if (ipv6_addr_type(s6) == IPV6_ADDR_MAPPED)
+ continue;
+
+ if (!ipv6_addr_equal(in6, s6) &&
+ !(ipv6_addr_equal(in6, &in6addr_any) &&
+ !tcp_is_local6(net, s6)))
+ continue;
+ }
+#endif
+
+ sock_hold(sk);
+ spin_unlock_bh(lock);
+
+ local_bh_disable();
+ bh_lock_sock(sk);
+ sk->sk_err = ETIMEDOUT;
+ sk->sk_error_report(sk);
+
+ tcp_done(sk);
+ bh_unlock_sock(sk);
+ local_bh_enable();
+ sock_put(sk);
+
+ goto restart;
+ }
+ spin_unlock_bh(lock);
+ }
+
+ return 0;
+}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index a906e0200ff2..c5c96f68c0fe 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -99,6 +99,7 @@ int sysctl_tcp_thin_dupack __read_mostly;
int sysctl_tcp_moderate_rcvbuf __read_mostly = 1;
int sysctl_tcp_early_retrans __read_mostly = 3;
+int sysctl_tcp_default_init_rwnd __read_mostly = TCP_INIT_CWND * 2;
#define FLAG_DATA 0x01 /* Incoming frame contained data. */
#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 5a7c41fbc6d3..5ef2dfc2dd47 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -197,7 +197,7 @@ u32 tcp_default_init_rwnd(u32 mss)
* (RFC 3517, Section 4, NextSeg() rule (2)). Further place a
* limit when mss is larger than 1460.
*/
- u32 init_rwnd = TCP_INIT_CWND * 2;
+ u32 init_rwnd = sysctl_tcp_default_init_rwnd;
if (mss > 1460)
init_rwnd = max((1460 * init_rwnd) / mss, 2U);
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 2daa3a133e49..60980a256419 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -64,6 +64,20 @@
#include <asm/uaccess.h>
#include <linux/mroute6.h>
+#ifdef CONFIG_ANDROID_PARANOID_NETWORK
+#include <linux/android_aid.h>
+
+static inline int current_has_network(void)
+{
+ return in_egroup_p(AID_INET) || capable(CAP_NET_RAW);
+}
+#else
+static inline int current_has_network(void)
+{
+ return 1;
+}
+#endif
+
MODULE_AUTHOR("Cast of dozens");
MODULE_DESCRIPTION("IPv6 protocol stack for Linux");
MODULE_LICENSE("GPL");
@@ -109,6 +123,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
int try_loading_module = 0;
int err;
+ if (!current_has_network())
+ return -EACCES;
+
/* Look for the requested type/protocol pair. */
lookup_protocol:
err = -ESOCKTNOSUPPORT;
@@ -155,8 +172,7 @@ lookup_protocol:
}
err = -EPERM;
- if (sock->type == SOCK_RAW && !kern &&
- !ns_capable(net->user_ns, CAP_NET_RAW))
+ if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
goto out_rcu_unlock;
sock->ops = answer->ops;
@@ -471,6 +487,21 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
}
EXPORT_SYMBOL(inet6_getname);
+int inet6_killaddr_ioctl(struct net *net, void __user *arg) {
+ struct in6_ifreq ireq;
+ struct sockaddr_in6 sin6;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+ return -EFAULT;
+
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = ireq.ifr6_addr;
+ return tcp_nuke_addr(net, (struct sockaddr *) &sin6);
+}
+
int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
@@ -494,6 +525,8 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return addrconf_del_ifaddr(net, (void __user *) arg);
case SIOCSIFDSTADDR:
return addrconf_set_dstaddr(net, (void __user *) arg);
+ case SIOCKILLADDR:
+ return inet6_killaddr_ioctl(net, (void __user *) arg);
default:
if (!sk->sk_prot->ioctl)
return -ENOIOCTLCMD;
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c
index 8af3eb57f438..958a187a3cc4 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -166,15 +166,15 @@ EXPORT_SYMBOL_GPL(ipv6_find_tlv);
* to explore inner IPv6 header, eg. ICMPv6 error messages.
*
* If target header is found, its offset is set in *offset and return protocol
- * number. Otherwise, return -1.
+ * number. Otherwise, return -ENOENT or -EBADMSG.
*
* If the first fragment doesn't contain the final protocol header or
* NEXTHDR_NONE it is considered invalid.
*
* Note that non-1st fragment is special case that "the protocol number
* of last header" is "next header" field in Fragment header. In this case,
- * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
- * isn't NULL.
+ * *offset is meaningless. If fragoff is not NULL, the fragment offset is
+ * stored in *fragoff; if it is NULL, return -EINVAL.
*
* if flags is not NULL and it's a fragment, then the frag flag
* IP6_FH_F_FRAG will be set. If it's an AH header, the
@@ -253,9 +253,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
if (target < 0 &&
((!ipv6_ext_hdr(hp->nexthdr)) ||
hp->nexthdr == NEXTHDR_NONE)) {
- if (fragoff)
+ if (fragoff) {
*fragoff = _frag_off;
- return hp->nexthdr;
+ return hp->nexthdr;
+ } else {
+ return -EINVAL;
+ }
}
return -ENOENT;
}
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index ad751fe2e82b..e0c6b6172be0 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -1235,6 +1235,8 @@ config NETFILTER_XT_MATCH_OWNER
based on who created the socket: the user or group. It is also
possible to check whether a socket actually exists.
+ Conflicts with '"quota, tag, uid" match'
+
config NETFILTER_XT_MATCH_POLICY
tristate 'IPsec "policy" match support'
depends on XFRM
@@ -1268,6 +1270,22 @@ config NETFILTER_XT_MATCH_PKTTYPE
To compile it as a module, choose M here. If unsure, say N.
+config NETFILTER_XT_MATCH_QTAGUID
+ bool '"quota, tag, owner" match and stats support'
+ depends on NETFILTER_XT_MATCH_SOCKET
+ depends on NETFILTER_XT_MATCH_OWNER=n
+ help
+ This option replaces the `owner' match. In addition to matching
+ on uid, it keeps stats based on a tag assigned to a socket.
+ The full tag is comprised of a UID and an accounting tag.
+ The tags are assignable to sockets from user space (e.g. a download
+ manager can assign the socket to another UID for accounting).
+ Stats and control are done via /proc/net/xt_qtaguid/.
+ It replaces owner as it takes the same arguments, but should
+ really be recognized by the iptables tool.
+
+ If unsure, say `N'.
+
config NETFILTER_XT_MATCH_QUOTA
tristate '"quota" match support'
depends on NETFILTER_ADVANCED
@@ -1278,6 +1296,30 @@ config NETFILTER_XT_MATCH_QUOTA
If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
+config NETFILTER_XT_MATCH_QUOTA2
+ tristate '"quota2" match support'
+ depends on NETFILTER_ADVANCED
+ help
+ This option adds a `quota2' match, which allows to match on a
+ byte counter correctly and not per CPU.
+ It allows naming the quotas.
+ This is based on http://xtables-addons.git.sourceforge.net
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
+
+config NETFILTER_XT_MATCH_QUOTA2_LOG
+ bool '"quota2" Netfilter LOG support'
+ depends on NETFILTER_XT_MATCH_QUOTA2
+ depends on IP_NF_TARGET_ULOG=n # not yes, not module, just no
+ default n
+ help
+ This option allows `quota2' to log ONCE when a quota limit
+ is passed. It logs via NETLINK using the NETLINK_NFLOG family.
+ It logs similarly to how ipt_ULOG would without data.
+
+ If unsure, say `N'.
+
config NETFILTER_XT_MATCH_RATEEST
tristate '"rateest" match support'
depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 8308624a406a..65bd3c3f648e 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -154,7 +154,9 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_CGROUP) += xt_cgroup.o
obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o
obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o
obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o
obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o
obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o
obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o
diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c
index f407ebc13481..df91e26f55f2 100644
--- a/net/netfilter/xt_IDLETIMER.c
+++ b/net/netfilter/xt_IDLETIMER.c
@@ -5,6 +5,7 @@
* After timer expires a kevent will be sent.
*
* Copyright (C) 2004, 2010 Nokia Corporation
+ *
* Written by Timo Teras <ext-timo.teras@nokia.com>
*
* Converted to x_tables and reworked for upstream inclusion
@@ -38,8 +39,10 @@
#include <linux/netfilter/xt_IDLETIMER.h>
#include <linux/kdev_t.h>
#include <linux/kobject.h>
+#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <linux/sysfs.h>
+#include <net/net_namespace.h>
struct idletimer_tg_attr {
struct attribute attr;
@@ -56,6 +59,8 @@ struct idletimer_tg {
struct idletimer_tg_attr attr;
unsigned int refcnt;
+ bool send_nl_msg;
+ bool active;
};
static LIST_HEAD(idletimer_tg_list);
@@ -63,6 +68,32 @@ static DEFINE_MUTEX(list_mutex);
static struct kobject *idletimer_tg_kobj;
+static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer)
+{
+ char iface_msg[NLMSG_MAX_SIZE];
+ char state_msg[NLMSG_MAX_SIZE];
+ char *envp[] = { iface_msg, state_msg, NULL };
+ int res;
+
+ res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s",
+ iface);
+ if (NLMSG_MAX_SIZE <= res) {
+ pr_err("message too long (%d)", res);
+ return;
+ }
+ res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s",
+ timer->active ? "active" : "inactive");
+ if (NLMSG_MAX_SIZE <= res) {
+ pr_err("message too long (%d)", res);
+ return;
+ }
+ pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg);
+ kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp);
+ return;
+
+
+}
+
static
struct idletimer_tg *__idletimer_tg_find_by_label(const char *label)
{
@@ -83,6 +114,7 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr,
{
struct idletimer_tg *timer;
unsigned long expires = 0;
+ unsigned long now = jiffies;
mutex_lock(&list_mutex);
@@ -92,11 +124,15 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr,
mutex_unlock(&list_mutex);
- if (time_after(expires, jiffies))
+ if (time_after(expires, now))
return sprintf(buf, "%u\n",
- jiffies_to_msecs(expires - jiffies) / 1000);
+ jiffies_to_msecs(expires - now) / 1000);
- return sprintf(buf, "0\n");
+ if (timer->send_nl_msg)
+ return sprintf(buf, "0 %d\n",
+ jiffies_to_msecs(now - expires) / 1000);
+ else
+ return sprintf(buf, "0\n");
}
static void idletimer_tg_work(struct work_struct *work)
@@ -105,6 +141,9 @@ static void idletimer_tg_work(struct work_struct *work)
work);
sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name);
+
+ if (timer->send_nl_msg)
+ notify_netlink_uevent(timer->attr.attr.name, timer);
}
static void idletimer_tg_expired(unsigned long data)
@@ -113,6 +152,7 @@ static void idletimer_tg_expired(unsigned long data)
pr_debug("timer %s expired\n", timer->attr.attr.name);
+ timer->active = false;
schedule_work(&timer->work);
}
@@ -145,6 +185,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info)
setup_timer(&info->timer->timer, idletimer_tg_expired,
(unsigned long) info->timer);
info->timer->refcnt = 1;
+ info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true;
+ info->timer->active = true;
mod_timer(&info->timer->timer,
msecs_to_jiffies(info->timeout * 1000) + jiffies);
@@ -168,14 +210,24 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb,
const struct xt_action_param *par)
{
const struct idletimer_tg_info *info = par->targinfo;
+ unsigned long now = jiffies;
pr_debug("resetting timer %s, timeout period %u\n",
info->label, info->timeout);
BUG_ON(!info->timer);
+ info->timer->active = true;
+
+ if (time_before(info->timer->timer.expires, now)) {
+ schedule_work(&info->timer->work);
+ pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n",
+ info->label, info->timer->timer.expires, now);
+ }
+
+ /* TODO: Avoid modifying timers on each packet */
mod_timer(&info->timer->timer,
- msecs_to_jiffies(info->timeout * 1000) + jiffies);
+ msecs_to_jiffies(info->timeout * 1000) + now);
return XT_CONTINUE;
}
@@ -184,8 +236,9 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par)
{
struct idletimer_tg_info *info = par->targinfo;
int ret;
+ unsigned long now = jiffies;
- pr_debug("checkentry targinfo%s\n", info->label);
+ pr_debug("checkentry targinfo %s\n", info->label);
if (info->timeout == 0) {
pr_debug("timeout value is zero\n");
@@ -204,8 +257,16 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par)
info->timer = __idletimer_tg_find_by_label(info->label);
if (info->timer) {
info->timer->refcnt++;
+ info->timer->active = true;
+
+ if (time_before(info->timer->timer.expires, now)) {
+ schedule_work(&info->timer->work);
+ pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n",
+ info->timer->timer.expires, now);
+ }
+
mod_timer(&info->timer->timer,
- msecs_to_jiffies(info->timeout * 1000) + jiffies);
+ msecs_to_jiffies(info->timeout * 1000) + now);
pr_debug("increased refcnt of timer %s to %u\n",
info->label, info->timer->refcnt);
@@ -219,6 +280,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par)
}
mutex_unlock(&list_mutex);
+
return 0;
}
@@ -240,7 +302,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par)
kfree(info->timer);
} else {
pr_debug("decreased refcnt of timer %s to %u\n",
- info->label, info->timer->refcnt);
+ info->label, info->timer->refcnt);
}
mutex_unlock(&list_mutex);
@@ -248,6 +310,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par)
static struct xt_target idletimer_tg __read_mostly = {
.name = "IDLETIMER",
+ .revision = 1,
.family = NFPROTO_UNSPEC,
.target = idletimer_tg_target,
.targetsize = sizeof(struct idletimer_tg_info),
@@ -313,3 +376,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("ipt_IDLETIMER");
MODULE_ALIAS("ip6t_IDLETIMER");
+MODULE_ALIAS("arpt_IDLETIMER");
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
new file mode 100644
index 000000000000..fcd2d8125954
--- /dev/null
+++ b/net/netfilter/xt_qtaguid.c
@@ -0,0 +1,3026 @@
+/*
+ * Kernel iptables module to track stats for packets based on user tags.
+ *
+ * (C) 2011 Google, 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.
+ */
+
+/*
+ * There are run-time debug flags enabled via the debug_mask module param, or
+ * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h.
+ */
+#define DEBUG
+
+#include <linux/file.h>
+#include <linux/inetdevice.h>
+#include <linux/module.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_qtaguid.h>
+#include <linux/ratelimit.h>
+#include <linux/seq_file.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <net/addrconf.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+
+#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#endif
+
+#include <linux/netfilter/xt_socket.h>
+#include "xt_qtaguid_internal.h"
+#include "xt_qtaguid_print.h"
+#include "../../fs/proc/internal.h"
+
+/*
+ * We only use the xt_socket funcs within a similar context to avoid unexpected
+ * return values.
+ */
+#define XT_SOCKET_SUPPORTED_HOOKS \
+ ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN))
+
+
+static const char *module_procdirname = "xt_qtaguid";
+static struct proc_dir_entry *xt_qtaguid_procdir;
+
+static unsigned int proc_iface_perms = S_IRUGO;
+module_param_named(iface_perms, proc_iface_perms, uint, S_IRUGO | S_IWUSR);
+
+static struct proc_dir_entry *xt_qtaguid_stats_file;
+static unsigned int proc_stats_perms = S_IRUGO;
+module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR);
+
+static struct proc_dir_entry *xt_qtaguid_ctrl_file;
+
+/* Everybody can write. But proc_ctrl_write_limited is true by default which
+ * limits what can be controlled. See the can_*() functions.
+ */
+static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO;
+module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR);
+
+/* Limited by default, so the gid of the ctrl and stats proc entries
+ * will limit what can be done. See the can_*() functions.
+ */
+static bool proc_stats_readall_limited = true;
+static bool proc_ctrl_write_limited = true;
+
+module_param_named(stats_readall_limited, proc_stats_readall_limited, bool,
+ S_IRUGO | S_IWUSR);
+module_param_named(ctrl_write_limited, proc_ctrl_write_limited, bool,
+ S_IRUGO | S_IWUSR);
+
+/*
+ * Limit the number of active tags (via socket tags) for a given UID.
+ * Multiple processes could share the UID.
+ */
+static int max_sock_tags = DEFAULT_MAX_SOCK_TAGS;
+module_param(max_sock_tags, int, S_IRUGO | S_IWUSR);
+
+/*
+ * After the kernel has initiallized this module, it is still possible
+ * to make it passive.
+ * Setting passive to Y:
+ * - the iface stats handling will not act on notifications.
+ * - iptables matches will never match.
+ * - ctrl commands silently succeed.
+ * - stats are always empty.
+ * This is mostly usefull when a bug is suspected.
+ */
+static bool module_passive;
+module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR);
+
+/*
+ * Control how qtaguid data is tracked per proc/uid.
+ * Setting tag_tracking_passive to Y:
+ * - don't create proc specific structs to track tags
+ * - don't check that active tag stats exceed some limits.
+ * - don't clean up socket tags on process exits.
+ * This is mostly usefull when a bug is suspected.
+ */
+static bool qtu_proc_handling_passive;
+module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool,
+ S_IRUGO | S_IWUSR);
+
+#define QTU_DEV_NAME "xt_qtaguid"
+
+uint qtaguid_debug_mask = DEFAULT_DEBUG_MASK;
+module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR);
+
+/*---------------------------------------------------------------------------*/
+static const char *iface_stat_procdirname = "iface_stat";
+static struct proc_dir_entry *iface_stat_procdir;
+/*
+ * The iface_stat_all* will go away once userspace gets use to the new fields
+ * that have a format line.
+ */
+static const char *iface_stat_all_procfilename = "iface_stat_all";
+static struct proc_dir_entry *iface_stat_all_procfile;
+static const char *iface_stat_fmt_procfilename = "iface_stat_fmt";
+static struct proc_dir_entry *iface_stat_fmt_procfile;
+
+
+static LIST_HEAD(iface_stat_list);
+static DEFINE_SPINLOCK(iface_stat_list_lock);
+
+static struct rb_root sock_tag_tree = RB_ROOT;
+static DEFINE_SPINLOCK(sock_tag_list_lock);
+
+static struct rb_root tag_counter_set_tree = RB_ROOT;
+static DEFINE_SPINLOCK(tag_counter_set_list_lock);
+
+static struct rb_root uid_tag_data_tree = RB_ROOT;
+static DEFINE_SPINLOCK(uid_tag_data_tree_lock);
+
+static struct rb_root proc_qtu_data_tree = RB_ROOT;
+/* No proc_qtu_data_tree_lock; use uid_tag_data_tree_lock */
+
+static struct qtaguid_event_counts qtu_events;
+/*----------------------------------------------*/
+static bool can_manipulate_uids(void)
+{
+ /* root pwnd */
+ return in_egroup_p(xt_qtaguid_ctrl_file->gid)
+ || unlikely(!from_kuid(&init_user_ns,current_fsuid())) || unlikely(!proc_ctrl_write_limited)
+ || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid));
+}
+
+static bool can_impersonate_uid(kuid_t uid)
+{
+ return uid_eq(uid, current_fsuid()) || can_manipulate_uids();
+}
+
+static bool can_read_other_uid_stats(kuid_t uid)
+{
+ /* root pwnd */
+ return in_egroup_p(xt_qtaguid_stats_file->gid)
+ || unlikely(!from_kuid(&init_user_ns,current_fsuid())) || uid_eq(uid, current_fsuid())
+ || unlikely(!proc_stats_readall_limited)
+ || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid));
+}
+
+static inline void dc_add_byte_packets(struct data_counters *counters, int set,
+ enum ifs_tx_rx direction,
+ enum ifs_proto ifs_proto,
+ int bytes,
+ int packets)
+{
+ counters->bpc[set][direction][ifs_proto].bytes += bytes;
+ counters->bpc[set][direction][ifs_proto].packets += packets;
+}
+
+static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag)
+{
+ struct rb_node *node = root->rb_node;
+
+ while (node) {
+ struct tag_node *data = rb_entry(node, struct tag_node, node);
+ int result;
+ RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): "
+ " node=%p data=%p\n", tag, node, data);
+ result = tag_compare(tag, data->tag);
+ RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): "
+ " data.tag=0x%llx (uid=%u) res=%d\n",
+ tag, data->tag, get_uid_from_tag(data->tag), result);
+ if (result < 0)
+ node = node->rb_left;
+ else if (result > 0)
+ node = node->rb_right;
+ else
+ return data;
+ }
+ return NULL;
+}
+
+static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root)
+{
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct tag_node *this = rb_entry(*new, struct tag_node,
+ node);
+ int result = tag_compare(data->tag, this->tag);
+ RB_DEBUG("qtaguid: %s(): tag=0x%llx"
+ " (uid=%u)\n", __func__,
+ this->tag,
+ get_uid_from_tag(this->tag));
+ parent = *new;
+ if (result < 0)
+ new = &((*new)->rb_left);
+ else if (result > 0)
+ new = &((*new)->rb_right);
+ else
+ BUG();
+ }
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&data->node, parent, new);
+ rb_insert_color(&data->node, root);
+}
+
+static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root)
+{
+ tag_node_tree_insert(&data->tn, root);
+}
+
+static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag)
+{
+ struct tag_node *node = tag_node_tree_search(root, tag);
+ if (!node)
+ return NULL;
+ return rb_entry(&node->node, struct tag_stat, tn.node);
+}
+
+static void tag_counter_set_tree_insert(struct tag_counter_set *data,
+ struct rb_root *root)
+{
+ tag_node_tree_insert(&data->tn, root);
+}
+
+static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root,
+ tag_t tag)
+{
+ struct tag_node *node = tag_node_tree_search(root, tag);
+ if (!node)
+ return NULL;
+ return rb_entry(&node->node, struct tag_counter_set, tn.node);
+
+}
+
+static void tag_ref_tree_insert(struct tag_ref *data, struct rb_root *root)
+{
+ tag_node_tree_insert(&data->tn, root);
+}
+
+static struct tag_ref *tag_ref_tree_search(struct rb_root *root, tag_t tag)
+{
+ struct tag_node *node = tag_node_tree_search(root, tag);
+ if (!node)
+ return NULL;
+ return rb_entry(&node->node, struct tag_ref, tn.node);
+}
+
+static struct sock_tag *sock_tag_tree_search(struct rb_root *root,
+ const struct sock *sk)
+{
+ struct rb_node *node = root->rb_node;
+
+ while (node) {
+ struct sock_tag *data = rb_entry(node, struct sock_tag,
+ sock_node);
+ if (sk < data->sk)
+ node = node->rb_left;
+ else if (sk > data->sk)
+ node = node->rb_right;
+ else
+ return data;
+ }
+ return NULL;
+}
+
+static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root)
+{
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct sock_tag *this = rb_entry(*new, struct sock_tag,
+ sock_node);
+ parent = *new;
+ if (data->sk < this->sk)
+ new = &((*new)->rb_left);
+ else if (data->sk > this->sk)
+ new = &((*new)->rb_right);
+ else
+ BUG();
+ }
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&data->sock_node, parent, new);
+ rb_insert_color(&data->sock_node, root);
+}
+
+static void sock_tag_tree_erase(struct rb_root *st_to_free_tree)
+{
+ struct rb_node *node;
+ struct sock_tag *st_entry;
+
+ node = rb_first(st_to_free_tree);
+ while (node) {
+ st_entry = rb_entry(node, struct sock_tag, sock_node);
+ node = rb_next(node);
+ CT_DEBUG("qtaguid: %s(): "
+ "erase st: sk=%p tag=0x%llx (uid=%u)\n", __func__,
+ st_entry->sk,
+ st_entry->tag,
+ get_uid_from_tag(st_entry->tag));
+ rb_erase(&st_entry->sock_node, st_to_free_tree);
+ sockfd_put(st_entry->socket);
+ kfree(st_entry);
+ }
+}
+
+static struct proc_qtu_data *proc_qtu_data_tree_search(struct rb_root *root,
+ const pid_t pid)
+{
+ struct rb_node *node = root->rb_node;
+
+ while (node) {
+ struct proc_qtu_data *data = rb_entry(node,
+ struct proc_qtu_data,
+ node);
+ if (pid < data->pid)
+ node = node->rb_left;
+ else if (pid > data->pid)
+ node = node->rb_right;
+ else
+ return data;
+ }
+ return NULL;
+}
+
+static void proc_qtu_data_tree_insert(struct proc_qtu_data *data,
+ struct rb_root *root)
+{
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct proc_qtu_data *this = rb_entry(*new,
+ struct proc_qtu_data,
+ node);
+ parent = *new;
+ if (data->pid < this->pid)
+ new = &((*new)->rb_left);
+ else if (data->pid > this->pid)
+ new = &((*new)->rb_right);
+ else
+ BUG();
+ }
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&data->node, parent, new);
+ rb_insert_color(&data->node, root);
+}
+
+static void uid_tag_data_tree_insert(struct uid_tag_data *data,
+ struct rb_root *root)
+{
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct uid_tag_data *this = rb_entry(*new,
+ struct uid_tag_data,
+ node);
+ parent = *new;
+ if (data->uid < this->uid)
+ new = &((*new)->rb_left);
+ else if (data->uid > this->uid)
+ new = &((*new)->rb_right);
+ else
+ BUG();
+ }
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&data->node, parent, new);
+ rb_insert_color(&data->node, root);
+}
+
+static struct uid_tag_data *uid_tag_data_tree_search(struct rb_root *root,
+ uid_t uid)
+{
+ struct rb_node *node = root->rb_node;
+
+ while (node) {
+ struct uid_tag_data *data = rb_entry(node,
+ struct uid_tag_data,
+ node);
+ if (uid < data->uid)
+ node = node->rb_left;
+ else if (uid > data->uid)
+ node = node->rb_right;
+ else
+ return data;
+ }
+ return NULL;
+}
+
+/*
+ * Allocates a new uid_tag_data struct if needed.
+ * Returns a pointer to the found or allocated uid_tag_data.
+ * Returns a PTR_ERR on failures, and lock is not held.
+ * If found is not NULL:
+ * sets *found to true if not allocated.
+ * sets *found to false if allocated.
+ */
+struct uid_tag_data *get_uid_data(uid_t uid, bool *found_res)
+{
+ struct uid_tag_data *utd_entry;
+
+ /* Look for top level uid_tag_data for the UID */
+ utd_entry = uid_tag_data_tree_search(&uid_tag_data_tree, uid);
+ DR_DEBUG("qtaguid: get_uid_data(%u) utd=%p\n", uid, utd_entry);
+
+ if (found_res)
+ *found_res = utd_entry;
+ if (utd_entry)
+ return utd_entry;
+
+ utd_entry = kzalloc(sizeof(*utd_entry), GFP_ATOMIC);
+ if (!utd_entry) {
+ pr_err("qtaguid: get_uid_data(%u): "
+ "tag data alloc failed\n", uid);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ utd_entry->uid = uid;
+ utd_entry->tag_ref_tree = RB_ROOT;
+ uid_tag_data_tree_insert(utd_entry, &uid_tag_data_tree);
+ DR_DEBUG("qtaguid: get_uid_data(%u) new utd=%p\n", uid, utd_entry);
+ return utd_entry;
+}
+
+/* Never returns NULL. Either PTR_ERR or a valid ptr. */
+static struct tag_ref *new_tag_ref(tag_t new_tag,
+ struct uid_tag_data *utd_entry)
+{
+ struct tag_ref *tr_entry;
+ int res;
+
+ if (utd_entry->num_active_tags + 1 > max_sock_tags) {
+ pr_info("qtaguid: new_tag_ref(0x%llx): "
+ "tag ref alloc quota exceeded. max=%d\n",
+ new_tag, max_sock_tags);
+ res = -EMFILE;
+ goto err_res;
+
+ }
+
+ tr_entry = kzalloc(sizeof(*tr_entry), GFP_ATOMIC);
+ if (!tr_entry) {
+ pr_err("qtaguid: new_tag_ref(0x%llx): "
+ "tag ref alloc failed\n",
+ new_tag);
+ res = -ENOMEM;
+ goto err_res;
+ }
+ tr_entry->tn.tag = new_tag;
+ /* tr_entry->num_sock_tags handled by caller */
+ utd_entry->num_active_tags++;
+ tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree);
+ DR_DEBUG("qtaguid: new_tag_ref(0x%llx): "
+ " inserted new tag ref %p\n",
+ new_tag, tr_entry);
+ return tr_entry;
+
+err_res:
+ return ERR_PTR(res);
+}
+
+static struct tag_ref *lookup_tag_ref(tag_t full_tag,
+ struct uid_tag_data **utd_res)
+{
+ struct uid_tag_data *utd_entry;
+ struct tag_ref *tr_entry;
+ bool found_utd;
+ uid_t uid = get_uid_from_tag(full_tag);
+
+ DR_DEBUG("qtaguid: lookup_tag_ref(tag=0x%llx (uid=%u))\n",
+ full_tag, uid);
+
+ utd_entry = get_uid_data(uid, &found_utd);
+ if (IS_ERR_OR_NULL(utd_entry)) {
+ if (utd_res)
+ *utd_res = utd_entry;
+ return NULL;
+ }
+
+ tr_entry = tag_ref_tree_search(&utd_entry->tag_ref_tree, full_tag);
+ if (utd_res)
+ *utd_res = utd_entry;
+ DR_DEBUG("qtaguid: lookup_tag_ref(0x%llx) utd_entry=%p tr_entry=%p\n",
+ full_tag, utd_entry, tr_entry);
+ return tr_entry;
+}
+
+/* Never returns NULL. Either PTR_ERR or a valid ptr. */
+static struct tag_ref *get_tag_ref(tag_t full_tag,
+ struct uid_tag_data **utd_res)
+{
+ struct uid_tag_data *utd_entry;
+ struct tag_ref *tr_entry;
+
+ DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n",
+ full_tag);
+ spin_lock_bh(&uid_tag_data_tree_lock);
+ tr_entry = lookup_tag_ref(full_tag, &utd_entry);
+ BUG_ON(IS_ERR_OR_NULL(utd_entry));
+ if (!tr_entry)
+ tr_entry = new_tag_ref(full_tag, utd_entry);
+
+ spin_unlock_bh(&uid_tag_data_tree_lock);
+ if (utd_res)
+ *utd_res = utd_entry;
+ DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n",
+ full_tag, utd_entry, tr_entry);
+ return tr_entry;
+}
+
+/* Checks and maybe frees the UID Tag Data entry */
+static void put_utd_entry(struct uid_tag_data *utd_entry)
+{
+ /* Are we done with the UID tag data entry? */
+ if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree) &&
+ !utd_entry->num_pqd) {
+ DR_DEBUG("qtaguid: %s(): "
+ "erase utd_entry=%p uid=%u "
+ "by pid=%u tgid=%u uid=%u\n", __func__,
+ utd_entry, utd_entry->uid,
+ current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+ BUG_ON(utd_entry->num_active_tags);
+ rb_erase(&utd_entry->node, &uid_tag_data_tree);
+ kfree(utd_entry);
+ } else {
+ DR_DEBUG("qtaguid: %s(): "
+ "utd_entry=%p still has %d tags %d proc_qtu_data\n",
+ __func__, utd_entry, utd_entry->num_active_tags,
+ utd_entry->num_pqd);
+ BUG_ON(!(utd_entry->num_active_tags ||
+ utd_entry->num_pqd));
+ }
+}
+
+/*
+ * If no sock_tags are using this tag_ref,
+ * decrements refcount of utd_entry, removes tr_entry
+ * from utd_entry->tag_ref_tree and frees.
+ */
+static void free_tag_ref_from_utd_entry(struct tag_ref *tr_entry,
+ struct uid_tag_data *utd_entry)
+{
+ DR_DEBUG("qtaguid: %s(): %p tag=0x%llx (uid=%u)\n", __func__,
+ tr_entry, tr_entry->tn.tag,
+ get_uid_from_tag(tr_entry->tn.tag));
+ if (!tr_entry->num_sock_tags) {
+ BUG_ON(!utd_entry->num_active_tags);
+ utd_entry->num_active_tags--;
+ rb_erase(&tr_entry->tn.node, &utd_entry->tag_ref_tree);
+ DR_DEBUG("qtaguid: %s(): erased %p\n", __func__, tr_entry);
+ kfree(tr_entry);
+ }
+}
+
+static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry)
+{
+ struct rb_node *node;
+ struct tag_ref *tr_entry;
+ tag_t acct_tag;
+
+ DR_DEBUG("qtaguid: %s(tag=0x%llx (uid=%u))\n", __func__,
+ full_tag, get_uid_from_tag(full_tag));
+ acct_tag = get_atag_from_tag(full_tag);
+ node = rb_first(&utd_entry->tag_ref_tree);
+ while (node) {
+ tr_entry = rb_entry(node, struct tag_ref, tn.node);
+ node = rb_next(node);
+ if (!acct_tag || tr_entry->tn.tag == full_tag)
+ free_tag_ref_from_utd_entry(tr_entry, utd_entry);
+ }
+}
+
+static ssize_t read_proc_u64(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ uint64_t *valuep = PDE_DATA(file_inode(file));
+ char tmp[24];
+ size_t tmp_size;
+
+ tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", *valuep);
+ return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size);
+}
+
+static ssize_t read_proc_bool(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ bool *valuep = PDE_DATA(file_inode(file));
+ char tmp[24];
+ size_t tmp_size;
+
+ tmp_size = scnprintf(tmp, sizeof(tmp), "%u\n", *valuep);
+ return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size);
+}
+
+static int get_active_counter_set(tag_t tag)
+{
+ int active_set = 0;
+ struct tag_counter_set *tcs;
+
+ MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)"
+ " (uid=%u)\n",
+ tag, get_uid_from_tag(tag));
+ /* For now we only handle UID tags for active sets */
+ tag = get_utag_from_tag(tag);
+ spin_lock_bh(&tag_counter_set_list_lock);
+ tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag);
+ if (tcs)
+ active_set = tcs->active_set;
+ spin_unlock_bh(&tag_counter_set_list_lock);
+ return active_set;
+}
+
+/*
+ * Find the entry for tracking the specified interface.
+ * Caller must hold iface_stat_list_lock
+ */
+static struct iface_stat *get_iface_entry(const char *ifname)
+{
+ struct iface_stat *iface_entry;
+
+ /* Find the entry for tracking the specified tag within the interface */
+ if (ifname == NULL) {
+ pr_info("qtaguid: iface_stat: get() NULL device name\n");
+ return NULL;
+ }
+
+ /* Iterate over interfaces */
+ list_for_each_entry(iface_entry, &iface_stat_list, list) {
+ if (!strcmp(ifname, iface_entry->ifname))
+ goto done;
+ }
+ iface_entry = NULL;
+done:
+ return iface_entry;
+}
+
+/* This is for fmt2 only */
+static void pp_iface_stat_header(struct seq_file *m)
+{
+ seq_puts(m,
+ "ifname "
+ "total_skb_rx_bytes total_skb_rx_packets "
+ "total_skb_tx_bytes total_skb_tx_packets "
+ "rx_tcp_bytes rx_tcp_packets "
+ "rx_udp_bytes rx_udp_packets "
+ "rx_other_bytes rx_other_packets "
+ "tx_tcp_bytes tx_tcp_packets "
+ "tx_udp_bytes tx_udp_packets "
+ "tx_other_bytes tx_other_packets\n"
+ );
+}
+
+static void pp_iface_stat_line(struct seq_file *m,
+ struct iface_stat *iface_entry)
+{
+ struct data_counters *cnts;
+ int cnt_set = 0; /* We only use one set for the device */
+ cnts = &iface_entry->totals_via_skb;
+ seq_printf(m, "%s %llu %llu %llu %llu %llu %llu %llu %llu "
+ "%llu %llu %llu %llu %llu %llu %llu %llu\n",
+ iface_entry->ifname,
+ dc_sum_bytes(cnts, cnt_set, IFS_RX),
+ dc_sum_packets(cnts, cnt_set, IFS_RX),
+ dc_sum_bytes(cnts, cnt_set, IFS_TX),
+ dc_sum_packets(cnts, cnt_set, IFS_TX),
+ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes,
+ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets,
+ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes,
+ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets,
+ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes,
+ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets,
+ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes,
+ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets,
+ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes,
+ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets,
+ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes,
+ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets);
+}
+
+struct proc_iface_stat_fmt_info {
+ int fmt;
+};
+
+static void *iface_stat_fmt_proc_start(struct seq_file *m, loff_t *pos)
+{
+ struct proc_iface_stat_fmt_info *p = m->private;
+ loff_t n = *pos;
+
+ /*
+ * This lock will prevent iface_stat_update() from changing active,
+ * and in turn prevent an interface from unregistering itself.
+ */
+ spin_lock_bh(&iface_stat_list_lock);
+
+ if (unlikely(module_passive))
+ return NULL;
+
+ if (!n && p->fmt == 2)
+ pp_iface_stat_header(m);
+
+ return seq_list_start(&iface_stat_list, n);
+}
+
+static void *iface_stat_fmt_proc_next(struct seq_file *m, void *p, loff_t *pos)
+{
+ return seq_list_next(p, &iface_stat_list, pos);
+}
+
+static void iface_stat_fmt_proc_stop(struct seq_file *m, void *p)
+{
+ spin_unlock_bh(&iface_stat_list_lock);
+}
+
+static int iface_stat_fmt_proc_show(struct seq_file *m, void *v)
+{
+ struct proc_iface_stat_fmt_info *p = m->private;
+ struct iface_stat *iface_entry;
+ struct rtnl_link_stats64 dev_stats, *stats;
+ struct rtnl_link_stats64 no_dev_stats = {0};
+
+
+ CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n",
+ current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+
+ iface_entry = list_entry(v, struct iface_stat, list);
+
+ if (iface_entry->active) {
+ stats = dev_get_stats(iface_entry->net_dev,
+ &dev_stats);
+ } else {
+ stats = &no_dev_stats;
+ }
+ /*
+ * If the meaning of the data changes, then update the fmtX
+ * string.
+ */
+ if (p->fmt == 1) {
+ seq_printf(m, "%s %d %llu %llu %llu %llu %llu %llu %llu %llu\n",
+ iface_entry->ifname,
+ iface_entry->active,
+ iface_entry->totals_via_dev[IFS_RX].bytes,
+ iface_entry->totals_via_dev[IFS_RX].packets,
+ iface_entry->totals_via_dev[IFS_TX].bytes,
+ iface_entry->totals_via_dev[IFS_TX].packets,
+ stats->rx_bytes, stats->rx_packets,
+ stats->tx_bytes, stats->tx_packets
+ );
+ } else {
+ pp_iface_stat_line(m, iface_entry);
+ }
+ return 0;
+}
+
+static const struct file_operations read_u64_fops = {
+ .read = read_proc_u64,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations read_bool_fops = {
+ .read = read_proc_bool,
+ .llseek = default_llseek,
+};
+
+static void iface_create_proc_worker(struct work_struct *work)
+{
+ struct proc_dir_entry *proc_entry;
+ struct iface_stat_work *isw = container_of(work, struct iface_stat_work,
+ iface_work);
+ struct iface_stat *new_iface = isw->iface_entry;
+
+ /* iface_entries are not deleted, so safe to manipulate. */
+ proc_entry = proc_mkdir(new_iface->ifname, iface_stat_procdir);
+ if (IS_ERR_OR_NULL(proc_entry)) {
+ pr_err("qtaguid: iface_stat: create_proc(): alloc failed.\n");
+ kfree(isw);
+ return;
+ }
+
+ new_iface->proc_ptr = proc_entry;
+
+ proc_create_data("tx_bytes", proc_iface_perms, proc_entry,
+ &read_u64_fops,
+ &new_iface->totals_via_dev[IFS_TX].bytes);
+ proc_create_data("rx_bytes", proc_iface_perms, proc_entry,
+ &read_u64_fops,
+ &new_iface->totals_via_dev[IFS_RX].bytes);
+ proc_create_data("tx_packets", proc_iface_perms, proc_entry,
+ &read_u64_fops,
+ &new_iface->totals_via_dev[IFS_TX].packets);
+ proc_create_data("rx_packets", proc_iface_perms, proc_entry,
+ &read_u64_fops,
+ &new_iface->totals_via_dev[IFS_RX].packets);
+ proc_create_data("active", proc_iface_perms, proc_entry,
+ &read_bool_fops, &new_iface->active);
+
+ IF_DEBUG("qtaguid: iface_stat: create_proc(): done "
+ "entry=%p dev=%s\n", new_iface, new_iface->ifname);
+ kfree(isw);
+}
+
+/*
+ * Will set the entry's active state, and
+ * update the net_dev accordingly also.
+ */
+static void _iface_stat_set_active(struct iface_stat *entry,
+ struct net_device *net_dev,
+ bool activate)
+{
+ if (activate) {
+ entry->net_dev = net_dev;
+ entry->active = true;
+ IF_DEBUG("qtaguid: %s(%s): "
+ "enable tracking. rfcnt=%d\n", __func__,
+ entry->ifname,
+ __this_cpu_read(*net_dev->pcpu_refcnt));
+ } else {
+ entry->active = false;
+ entry->net_dev = NULL;
+ IF_DEBUG("qtaguid: %s(%s): "
+ "disable tracking. rfcnt=%d\n", __func__,
+ entry->ifname,
+ __this_cpu_read(*net_dev->pcpu_refcnt));
+
+ }
+}
+
+/* Caller must hold iface_stat_list_lock */
+static struct iface_stat *iface_alloc(struct net_device *net_dev)
+{
+ struct iface_stat *new_iface;
+ struct iface_stat_work *isw;
+
+ new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC);
+ if (new_iface == NULL) {
+ pr_err("qtaguid: iface_stat: create(%s): "
+ "iface_stat alloc failed\n", net_dev->name);
+ return NULL;
+ }
+ new_iface->ifname = kstrdup(net_dev->name, GFP_ATOMIC);
+ if (new_iface->ifname == NULL) {
+ pr_err("qtaguid: iface_stat: create(%s): "
+ "ifname alloc failed\n", net_dev->name);
+ kfree(new_iface);
+ return NULL;
+ }
+ spin_lock_init(&new_iface->tag_stat_list_lock);
+ new_iface->tag_stat_tree = RB_ROOT;
+ _iface_stat_set_active(new_iface, net_dev, true);
+
+ /*
+ * ipv6 notifier chains are atomic :(
+ * No create_proc_read_entry() for you!
+ */
+ isw = kmalloc(sizeof(*isw), GFP_ATOMIC);
+ if (!isw) {
+ pr_err("qtaguid: iface_stat: create(%s): "
+ "work alloc failed\n", new_iface->ifname);
+ _iface_stat_set_active(new_iface, net_dev, false);
+ kfree(new_iface->ifname);
+ kfree(new_iface);
+ return NULL;
+ }
+ isw->iface_entry = new_iface;
+ INIT_WORK(&isw->iface_work, iface_create_proc_worker);
+ schedule_work(&isw->iface_work);
+ list_add(&new_iface->list, &iface_stat_list);
+ return new_iface;
+}
+
+static void iface_check_stats_reset_and_adjust(struct net_device *net_dev,
+ struct iface_stat *iface)
+{
+ struct rtnl_link_stats64 dev_stats, *stats;
+ bool stats_rewound;
+
+ stats = dev_get_stats(net_dev, &dev_stats);
+ /* No empty packets */
+ stats_rewound =
+ (stats->rx_bytes < iface->last_known[IFS_RX].bytes)
+ || (stats->tx_bytes < iface->last_known[IFS_TX].bytes);
+
+ IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p "
+ "bytes rx/tx=%llu/%llu "
+ "active=%d last_known=%d "
+ "stats_rewound=%d\n", __func__,
+ net_dev ? net_dev->name : "?",
+ iface, net_dev,
+ stats->rx_bytes, stats->tx_bytes,
+ iface->active, iface->last_known_valid, stats_rewound);
+
+ if (iface->active && iface->last_known_valid && stats_rewound) {
+ pr_warn_once("qtaguid: iface_stat: %s(%s): "
+ "iface reset its stats unexpectedly\n", __func__,
+ net_dev->name);
+
+ iface->totals_via_dev[IFS_TX].bytes +=
+ iface->last_known[IFS_TX].bytes;
+ iface->totals_via_dev[IFS_TX].packets +=
+ iface->last_known[IFS_TX].packets;
+ iface->totals_via_dev[IFS_RX].bytes +=
+ iface->last_known[IFS_RX].bytes;
+ iface->totals_via_dev[IFS_RX].packets +=
+ iface->last_known[IFS_RX].packets;
+ iface->last_known_valid = false;
+ IF_DEBUG("qtaguid: %s(%s): iface=%p "
+ "used last known bytes rx/tx=%llu/%llu\n", __func__,
+ iface->ifname, iface, iface->last_known[IFS_RX].bytes,
+ iface->last_known[IFS_TX].bytes);
+ }
+}
+
+/*
+ * Create a new entry for tracking the specified interface.
+ * Do nothing if the entry already exists.
+ * Called when an interface is configured with a valid IP address.
+ */
+static void iface_stat_create(struct net_device *net_dev,
+ struct in_ifaddr *ifa)
+{
+ struct in_device *in_dev = NULL;
+ const char *ifname;
+ struct iface_stat *entry;
+ __be32 ipaddr = 0;
+ struct iface_stat *new_iface;
+
+ IF_DEBUG("qtaguid: iface_stat: create(%s): ifa=%p netdev=%p\n",
+ net_dev ? net_dev->name : "?",
+ ifa, net_dev);
+ if (!net_dev) {
+ pr_err("qtaguid: iface_stat: create(): no net dev\n");
+ return;
+ }
+
+ ifname = net_dev->name;
+ if (!ifa) {
+ in_dev = in_dev_get(net_dev);
+ if (!in_dev) {
+ pr_err("qtaguid: iface_stat: create(%s): no inet dev\n",
+ ifname);
+ return;
+ }
+ IF_DEBUG("qtaguid: iface_stat: create(%s): in_dev=%p\n",
+ ifname, in_dev);
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+ IF_DEBUG("qtaguid: iface_stat: create(%s): "
+ "ifa=%p ifa_label=%s\n",
+ ifname, ifa,
+ ifa->ifa_label ? ifa->ifa_label : "(null)");
+ if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label))
+ break;
+ }
+ }
+
+ if (!ifa) {
+ IF_DEBUG("qtaguid: iface_stat: create(%s): no matching IP\n",
+ ifname);
+ goto done_put;
+ }
+ ipaddr = ifa->ifa_local;
+
+ spin_lock_bh(&iface_stat_list_lock);
+ entry = get_iface_entry(ifname);
+ if (entry != NULL) {
+ IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n",
+ ifname, entry);
+ iface_check_stats_reset_and_adjust(net_dev, entry);
+ _iface_stat_set_active(entry, net_dev, true);
+ IF_DEBUG("qtaguid: %s(%s): "
+ "tracking now %d on ip=%pI4\n", __func__,
+ entry->ifname, true, &ipaddr);
+ goto done_unlock_put;
+ }
+
+ new_iface = iface_alloc(net_dev);
+ IF_DEBUG("qtaguid: iface_stat: create(%s): done "
+ "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr);
+done_unlock_put:
+ spin_unlock_bh(&iface_stat_list_lock);
+done_put:
+ if (in_dev)
+ in_dev_put(in_dev);
+}
+
+static void iface_stat_create_ipv6(struct net_device *net_dev,
+ struct inet6_ifaddr *ifa)
+{
+ struct in_device *in_dev;
+ const char *ifname;
+ struct iface_stat *entry;
+ struct iface_stat *new_iface;
+ int addr_type;
+
+ IF_DEBUG("qtaguid: iface_stat: create6(): ifa=%p netdev=%p->name=%s\n",
+ ifa, net_dev, net_dev ? net_dev->name : "");
+ if (!net_dev) {
+ pr_err("qtaguid: iface_stat: create6(): no net dev!\n");
+ return;
+ }
+ ifname = net_dev->name;
+
+ in_dev = in_dev_get(net_dev);
+ if (!in_dev) {
+ pr_err("qtaguid: iface_stat: create6(%s): no inet dev\n",
+ ifname);
+ return;
+ }
+
+ IF_DEBUG("qtaguid: iface_stat: create6(%s): in_dev=%p\n",
+ ifname, in_dev);
+
+ if (!ifa) {
+ IF_DEBUG("qtaguid: iface_stat: create6(%s): no matching IP\n",
+ ifname);
+ goto done_put;
+ }
+ addr_type = ipv6_addr_type(&ifa->addr);
+
+ spin_lock_bh(&iface_stat_list_lock);
+ entry = get_iface_entry(ifname);
+ if (entry != NULL) {
+ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
+ ifname, entry);
+ iface_check_stats_reset_and_adjust(net_dev, entry);
+ _iface_stat_set_active(entry, net_dev, true);
+ IF_DEBUG("qtaguid: %s(%s): "
+ "tracking now %d on ip=%pI6c\n", __func__,
+ entry->ifname, true, &ifa->addr);
+ goto done_unlock_put;
+ }
+
+ new_iface = iface_alloc(net_dev);
+ IF_DEBUG("qtaguid: iface_stat: create6(%s): done "
+ "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr);
+
+done_unlock_put:
+ spin_unlock_bh(&iface_stat_list_lock);
+done_put:
+ in_dev_put(in_dev);
+}
+
+static struct sock_tag *get_sock_stat_nl(const struct sock *sk)
+{
+ MT_DEBUG("qtaguid: get_sock_stat_nl(sk=%p)\n", sk);
+ return sock_tag_tree_search(&sock_tag_tree, sk);
+}
+
+static struct sock_tag *get_sock_stat(const struct sock *sk)
+{
+ struct sock_tag *sock_tag_entry;
+ MT_DEBUG("qtaguid: get_sock_stat(sk=%p)\n", sk);
+ if (!sk)
+ return NULL;
+ spin_lock_bh(&sock_tag_list_lock);
+ sock_tag_entry = get_sock_stat_nl(sk);
+ spin_unlock_bh(&sock_tag_list_lock);
+ return sock_tag_entry;
+}
+
+static int ipx_proto(const struct sk_buff *skb,
+ struct xt_action_param *par)
+{
+ int thoff = 0, tproto;
+
+ switch (par->family) {
+ case NFPROTO_IPV6:
+ tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
+ if (tproto < 0)
+ MT_DEBUG("%s(): transport header not found in ipv6"
+ " skb=%p\n", __func__, skb);
+ break;
+ case NFPROTO_IPV4:
+ tproto = ip_hdr(skb)->protocol;
+ break;
+ default:
+ tproto = IPPROTO_RAW;
+ }
+ return tproto;
+}
+
+static void
+data_counters_update(struct data_counters *dc, int set,
+ enum ifs_tx_rx direction, int proto, int bytes)
+{
+ switch (proto) {
+ case IPPROTO_TCP:
+ dc_add_byte_packets(dc, set, direction, IFS_TCP, bytes, 1);
+ break;
+ case IPPROTO_UDP:
+ dc_add_byte_packets(dc, set, direction, IFS_UDP, bytes, 1);
+ break;
+ case IPPROTO_IP:
+ default:
+ dc_add_byte_packets(dc, set, direction, IFS_PROTO_OTHER, bytes,
+ 1);
+ break;
+ }
+}
+
+/*
+ * Update stats for the specified interface. Do nothing if the entry
+ * does not exist (when a device was never configured with an IP address).
+ * Called when an device is being unregistered.
+ */
+static void iface_stat_update(struct net_device *net_dev, bool stash_only)
+{
+ struct rtnl_link_stats64 dev_stats, *stats;
+ struct iface_stat *entry;
+
+ stats = dev_get_stats(net_dev, &dev_stats);
+ spin_lock_bh(&iface_stat_list_lock);
+ entry = get_iface_entry(net_dev->name);
+ if (entry == NULL) {
+ IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n",
+ net_dev->name);
+ spin_unlock_bh(&iface_stat_list_lock);
+ return;
+ }
+
+ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
+ net_dev->name, entry);
+ if (!entry->active) {
+ IF_DEBUG("qtaguid: %s(%s): already disabled\n", __func__,
+ net_dev->name);
+ spin_unlock_bh(&iface_stat_list_lock);
+ return;
+ }
+
+ if (stash_only) {
+ entry->last_known[IFS_TX].bytes = stats->tx_bytes;
+ entry->last_known[IFS_TX].packets = stats->tx_packets;
+ entry->last_known[IFS_RX].bytes = stats->rx_bytes;
+ entry->last_known[IFS_RX].packets = stats->rx_packets;
+ entry->last_known_valid = true;
+ IF_DEBUG("qtaguid: %s(%s): "
+ "dev stats stashed rx/tx=%llu/%llu\n", __func__,
+ net_dev->name, stats->rx_bytes, stats->tx_bytes);
+ spin_unlock_bh(&iface_stat_list_lock);
+ return;
+ }
+ entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes;
+ entry->totals_via_dev[IFS_TX].packets += stats->tx_packets;
+ entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes;
+ entry->totals_via_dev[IFS_RX].packets += stats->rx_packets;
+ /* We don't need the last_known[] anymore */
+ entry->last_known_valid = false;
+ _iface_stat_set_active(entry, net_dev, false);
+ IF_DEBUG("qtaguid: %s(%s): "
+ "disable tracking. rx/tx=%llu/%llu\n", __func__,
+ net_dev->name, stats->rx_bytes, stats->tx_bytes);
+ spin_unlock_bh(&iface_stat_list_lock);
+}
+
+/*
+ * Update stats for the specified interface from the skb.
+ * Do nothing if the entry
+ * does not exist (when a device was never configured with an IP address).
+ * Called on each sk.
+ */
+static void iface_stat_update_from_skb(const struct sk_buff *skb,
+ struct xt_action_param *par)
+{
+ struct iface_stat *entry;
+ const struct net_device *el_dev;
+ enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX;
+ int bytes = skb->len;
+ int proto;
+
+ if (!skb->dev) {
+ MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
+ el_dev = par->in ? : par->out;
+ } else {
+ const struct net_device *other_dev;
+ el_dev = skb->dev;
+ other_dev = par->in ? : par->out;
+ if (el_dev != other_dev) {
+ MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
+ "par->(in/out)=%p %s\n",
+ par->hooknum, el_dev, el_dev->name, other_dev,
+ other_dev->name);
+ }
+ }
+
+ if (unlikely(!el_dev)) {
+ pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n",
+ par->hooknum, __func__);
+ BUG();
+ } else if (unlikely(!el_dev->name)) {
+ pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n",
+ par->hooknum, __func__);
+ BUG();
+ } else {
+ proto = ipx_proto(skb, par);
+ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
+ par->hooknum, el_dev->name, el_dev->type,
+ par->family, proto);
+ }
+
+ spin_lock_bh(&iface_stat_list_lock);
+ entry = get_iface_entry(el_dev->name);
+ if (entry == NULL) {
+ IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n",
+ __func__, el_dev->name);
+ spin_unlock_bh(&iface_stat_list_lock);
+ return;
+ }
+
+ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
+ el_dev->name, entry);
+
+ data_counters_update(&entry->totals_via_skb, 0, direction, proto,
+ bytes);
+ spin_unlock_bh(&iface_stat_list_lock);
+}
+
+static void tag_stat_update(struct tag_stat *tag_entry,
+ enum ifs_tx_rx direction, int proto, int bytes)
+{
+ int active_set;
+ active_set = get_active_counter_set(tag_entry->tn.tag);
+ MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%u) set=%d "
+ "dir=%d proto=%d bytes=%d)\n",
+ tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag),
+ active_set, direction, proto, bytes);
+ data_counters_update(&tag_entry->counters, active_set, direction,
+ proto, bytes);
+ if (tag_entry->parent_counters)
+ data_counters_update(tag_entry->parent_counters, active_set,
+ direction, proto, bytes);
+}
+
+/*
+ * Create a new entry for tracking the specified {acct_tag,uid_tag} within
+ * the interface.
+ * iface_entry->tag_stat_list_lock should be held.
+ */
+static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry,
+ tag_t tag)
+{
+ struct tag_stat *new_tag_stat_entry = NULL;
+ IF_DEBUG("qtaguid: iface_stat: %s(): ife=%p tag=0x%llx"
+ " (uid=%u)\n", __func__,
+ iface_entry, tag, get_uid_from_tag(tag));
+ new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC);
+ if (!new_tag_stat_entry) {
+ pr_err("qtaguid: iface_stat: tag stat alloc failed\n");
+ goto done;
+ }
+ new_tag_stat_entry->tn.tag = tag;
+ tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree);
+done:
+ return new_tag_stat_entry;
+}
+
+static void if_tag_stat_update(const char *ifname, uid_t uid,
+ const struct sock *sk, enum ifs_tx_rx direction,
+ int proto, int bytes)
+{
+ struct tag_stat *tag_stat_entry;
+ tag_t tag, acct_tag;
+ tag_t uid_tag;
+ struct data_counters *uid_tag_counters;
+ struct sock_tag *sock_tag_entry;
+ struct iface_stat *iface_entry;
+ struct tag_stat *new_tag_stat = NULL;
+ MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s "
+ "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n",
+ ifname, uid, sk, direction, proto, bytes);
+
+
+ iface_entry = get_iface_entry(ifname);
+ if (!iface_entry) {
+ pr_err_ratelimited("qtaguid: iface_stat: stat_update() "
+ "%s not found\n", ifname);
+ return;
+ }
+ /* It is ok to process data when an iface_entry is inactive */
+
+ MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n",
+ ifname, iface_entry);
+
+ /*
+ * Look for a tagged sock.
+ * It will have an acct_uid.
+ */
+ sock_tag_entry = get_sock_stat(sk);
+ if (sock_tag_entry) {
+ tag = sock_tag_entry->tag;
+ acct_tag = get_atag_from_tag(tag);
+ uid_tag = get_utag_from_tag(tag);
+ } else {
+ acct_tag = make_atag_from_value(0);
+ tag = combine_atag_with_uid(acct_tag, uid);
+ uid_tag = make_tag_from_uid(uid);
+ }
+ MT_DEBUG("qtaguid: iface_stat: stat_update(): "
+ " looking for tag=0x%llx (uid=%u) in ife=%p\n",
+ tag, get_uid_from_tag(tag), iface_entry);
+ /* Loop over tag list under this interface for {acct_tag,uid_tag} */
+ spin_lock_bh(&iface_entry->tag_stat_list_lock);
+
+ tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree,
+ tag);
+ if (tag_stat_entry) {
+ /*
+ * Updating the {acct_tag, uid_tag} entry handles both stats:
+ * {0, uid_tag} will also get updated.
+ */
+ tag_stat_update(tag_stat_entry, direction, proto, bytes);
+ spin_unlock_bh(&iface_entry->tag_stat_list_lock);
+ return;
+ }
+
+ /* Loop over tag list under this interface for {0,uid_tag} */
+ tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree,
+ uid_tag);
+ if (!tag_stat_entry) {
+ /* Here: the base uid_tag did not exist */
+ /*
+ * No parent counters. So
+ * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats.
+ */
+ new_tag_stat = create_if_tag_stat(iface_entry, uid_tag);
+ if (!new_tag_stat)
+ goto unlock;
+ uid_tag_counters = &new_tag_stat->counters;
+ } else {
+ uid_tag_counters = &tag_stat_entry->counters;
+ }
+
+ if (acct_tag) {
+ /* Create the child {acct_tag, uid_tag} and hook up parent. */
+ new_tag_stat = create_if_tag_stat(iface_entry, tag);
+ if (!new_tag_stat)
+ goto unlock;
+ new_tag_stat->parent_counters = uid_tag_counters;
+ } else {
+ /*
+ * For new_tag_stat to be still NULL here would require:
+ * {0, uid_tag} exists
+ * and {acct_tag, uid_tag} doesn't exist
+ * AND acct_tag == 0.
+ * Impossible. This reassures us that new_tag_stat
+ * below will always be assigned.
+ */
+ BUG_ON(!new_tag_stat);
+ }
+ tag_stat_update(new_tag_stat, direction, proto, bytes);
+unlock:
+ spin_unlock_bh(&iface_entry->tag_stat_list_lock);
+}
+
+static int iface_netdev_event_handler(struct notifier_block *nb,
+ unsigned long event, void *ptr) {
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+
+ if (unlikely(module_passive))
+ return NOTIFY_DONE;
+
+ IF_DEBUG("qtaguid: iface_stat: netdev_event(): "
+ "ev=0x%lx/%s netdev=%p->name=%s\n",
+ event, netdev_evt_str(event), dev, dev ? dev->name : "");
+
+ switch (event) {
+ case NETDEV_UP:
+ iface_stat_create(dev, NULL);
+ atomic64_inc(&qtu_events.iface_events);
+ break;
+ case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
+ iface_stat_update(dev, event == NETDEV_DOWN);
+ atomic64_inc(&qtu_events.iface_events);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static int iface_inet6addr_event_handler(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct inet6_ifaddr *ifa = ptr;
+ struct net_device *dev;
+
+ if (unlikely(module_passive))
+ return NOTIFY_DONE;
+
+ IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): "
+ "ev=0x%lx/%s ifa=%p\n",
+ event, netdev_evt_str(event), ifa);
+
+ switch (event) {
+ case NETDEV_UP:
+ BUG_ON(!ifa || !ifa->idev);
+ dev = (struct net_device *)ifa->idev->dev;
+ iface_stat_create_ipv6(dev, ifa);
+ atomic64_inc(&qtu_events.iface_events);
+ break;
+ case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
+ BUG_ON(!ifa || !ifa->idev);
+ dev = (struct net_device *)ifa->idev->dev;
+ iface_stat_update(dev, event == NETDEV_DOWN);
+ atomic64_inc(&qtu_events.iface_events);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static int iface_inetaddr_event_handler(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct in_ifaddr *ifa = ptr;
+ struct net_device *dev;
+
+ if (unlikely(module_passive))
+ return NOTIFY_DONE;
+
+ IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): "
+ "ev=0x%lx/%s ifa=%p\n",
+ event, netdev_evt_str(event), ifa);
+
+ switch (event) {
+ case NETDEV_UP:
+ BUG_ON(!ifa || !ifa->ifa_dev);
+ dev = ifa->ifa_dev->dev;
+ iface_stat_create(dev, ifa);
+ atomic64_inc(&qtu_events.iface_events);
+ break;
+ case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
+ BUG_ON(!ifa || !ifa->ifa_dev);
+ dev = ifa->ifa_dev->dev;
+ iface_stat_update(dev, event == NETDEV_DOWN);
+ atomic64_inc(&qtu_events.iface_events);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block iface_netdev_notifier_blk = {
+ .notifier_call = iface_netdev_event_handler,
+};
+
+static struct notifier_block iface_inetaddr_notifier_blk = {
+ .notifier_call = iface_inetaddr_event_handler,
+};
+
+static struct notifier_block iface_inet6addr_notifier_blk = {
+ .notifier_call = iface_inet6addr_event_handler,
+};
+
+static const struct seq_operations iface_stat_fmt_proc_seq_ops = {
+ .start = iface_stat_fmt_proc_start,
+ .next = iface_stat_fmt_proc_next,
+ .stop = iface_stat_fmt_proc_stop,
+ .show = iface_stat_fmt_proc_show,
+};
+
+static int proc_iface_stat_fmt_open(struct inode *inode, struct file *file)
+{
+ struct proc_iface_stat_fmt_info *s;
+
+ s = __seq_open_private(file, &iface_stat_fmt_proc_seq_ops,
+ sizeof(struct proc_iface_stat_fmt_info));
+ if (!s)
+ return -ENOMEM;
+
+ s->fmt = (uintptr_t)PDE_DATA(inode);
+ return 0;
+}
+
+static const struct file_operations proc_iface_stat_fmt_fops = {
+ .open = proc_iface_stat_fmt_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_private,
+};
+
+static int __init iface_stat_init(struct proc_dir_entry *parent_procdir)
+{
+ int err;
+
+ iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir);
+ if (!iface_stat_procdir) {
+ pr_err("qtaguid: iface_stat: init failed to create proc entry\n");
+ err = -1;
+ goto err;
+ }
+
+ iface_stat_all_procfile = proc_create_data(iface_stat_all_procfilename,
+ proc_iface_perms,
+ parent_procdir,
+ &proc_iface_stat_fmt_fops,
+ (void *)1 /* fmt1 */);
+ if (!iface_stat_all_procfile) {
+ pr_err("qtaguid: iface_stat: init "
+ " failed to create stat_old proc entry\n");
+ err = -1;
+ goto err_zap_entry;
+ }
+
+ iface_stat_fmt_procfile = proc_create_data(iface_stat_fmt_procfilename,
+ proc_iface_perms,
+ parent_procdir,
+ &proc_iface_stat_fmt_fops,
+ (void *)2 /* fmt2 */);
+ if (!iface_stat_fmt_procfile) {
+ pr_err("qtaguid: iface_stat: init "
+ " failed to create stat_all proc entry\n");
+ err = -1;
+ goto err_zap_all_stats_entry;
+ }
+
+
+ err = register_netdevice_notifier(&iface_netdev_notifier_blk);
+ if (err) {
+ pr_err("qtaguid: iface_stat: init "
+ "failed to register dev event handler\n");
+ goto err_zap_all_stats_entries;
+ }
+ err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk);
+ if (err) {
+ pr_err("qtaguid: iface_stat: init "
+ "failed to register ipv4 dev event handler\n");
+ goto err_unreg_nd;
+ }
+
+ err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk);
+ if (err) {
+ pr_err("qtaguid: iface_stat: init "
+ "failed to register ipv6 dev event handler\n");
+ goto err_unreg_ip4_addr;
+ }
+ return 0;
+
+err_unreg_ip4_addr:
+ unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk);
+err_unreg_nd:
+ unregister_netdevice_notifier(&iface_netdev_notifier_blk);
+err_zap_all_stats_entries:
+ remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir);
+err_zap_all_stats_entry:
+ remove_proc_entry(iface_stat_all_procfilename, parent_procdir);
+err_zap_entry:
+ remove_proc_entry(iface_stat_procdirname, parent_procdir);
+err:
+ return err;
+}
+
+static struct sock *qtaguid_find_sk(const struct sk_buff *skb,
+ struct xt_action_param *par)
+{
+ struct sock *sk;
+ unsigned int hook_mask = (1 << par->hooknum);
+
+ MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb,
+ par->hooknum, par->family);
+
+ /*
+ * Let's not abuse the the xt_socket_get*_sk(), or else it will
+ * return garbage SKs.
+ */
+ if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS))
+ return NULL;
+
+ switch (par->family) {
+ case NFPROTO_IPV6:
+ sk = xt_socket_get6_sk(skb, par);
+ break;
+ case NFPROTO_IPV4:
+ sk = xt_socket_get4_sk(skb, par);
+ break;
+ default:
+ return NULL;
+ }
+
+ if (sk) {
+ MT_DEBUG("qtaguid: %p->sk_proto=%u "
+ "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state);
+ /*
+ * When in TCP_TIME_WAIT the sk is not a "struct sock" but
+ * "struct inet_timewait_sock" which is missing fields.
+ */
+ if (sk->sk_state == TCP_TIME_WAIT) {
+ sock_gen_put(sk);
+ sk = NULL;
+ }
+ }
+ return sk;
+}
+
+static void account_for_uid(const struct sk_buff *skb,
+ const struct sock *alternate_sk, uid_t uid,
+ struct xt_action_param *par)
+{
+ const struct net_device *el_dev;
+
+ if (!skb->dev) {
+ MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
+ el_dev = par->in ? : par->out;
+ } else {
+ const struct net_device *other_dev;
+ el_dev = skb->dev;
+ other_dev = par->in ? : par->out;
+ if (el_dev != other_dev) {
+ MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
+ "par->(in/out)=%p %s\n",
+ par->hooknum, el_dev, el_dev->name, other_dev,
+ other_dev->name);
+ }
+ }
+
+ if (unlikely(!el_dev)) {
+ pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum);
+ } else if (unlikely(!el_dev->name)) {
+ pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum);
+ } else {
+ int proto = ipx_proto(skb, par);
+ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
+ par->hooknum, el_dev->name, el_dev->type,
+ par->family, proto);
+
+ if_tag_stat_update(el_dev->name, uid,
+ skb->sk ? skb->sk : alternate_sk,
+ par->in ? IFS_RX : IFS_TX,
+ proto, skb->len);
+ }
+}
+
+static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ const struct xt_qtaguid_match_info *info = par->matchinfo;
+ const struct file *filp;
+ bool got_sock = false;
+ struct sock *sk;
+ kuid_t sock_uid;
+ bool res;
+
+ if (unlikely(module_passive))
+ return (info->match ^ info->invert) == 0;
+
+ MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n",
+ par->hooknum, skb, par->in, par->out, par->family);
+
+ atomic64_inc(&qtu_events.match_calls);
+ if (skb == NULL) {
+ res = (info->match ^ info->invert) == 0;
+ goto ret_res;
+ }
+
+ switch (par->hooknum) {
+ case NF_INET_PRE_ROUTING:
+ case NF_INET_POST_ROUTING:
+ atomic64_inc(&qtu_events.match_calls_prepost);
+ iface_stat_update_from_skb(skb, par);
+ /*
+ * We are done in pre/post. The skb will get processed
+ * further alter.
+ */
+ res = (info->match ^ info->invert);
+ goto ret_res;
+ break;
+ /* default: Fall through and do UID releated work */
+ }
+
+ sk = skb->sk;
+ /*
+ * When in TCP_TIME_WAIT the sk is not a "struct sock" but
+ * "struct inet_timewait_sock" which is missing fields.
+ * So we ignore it.
+ */
+ if (sk && sk->sk_state == TCP_TIME_WAIT)
+ sk = NULL;
+ if (sk == NULL) {
+ /*
+ * A missing sk->sk_socket happens when packets are in-flight
+ * and the matching socket is already closed and gone.
+ */
+ sk = qtaguid_find_sk(skb, par);
+ /*
+ * If we got the socket from the find_sk(), we will need to put
+ * it back, as nf_tproxy_get_sock_v4() got it.
+ */
+ got_sock = sk;
+ if (sk)
+ atomic64_inc(&qtu_events.match_found_sk_in_ct);
+ else
+ atomic64_inc(&qtu_events.match_found_no_sk_in_ct);
+ } else {
+ atomic64_inc(&qtu_events.match_found_sk);
+ }
+ MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n",
+ par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par));
+ if (sk != NULL) {
+ MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n",
+ par->hooknum, sk, sk->sk_socket,
+ sk->sk_socket ? sk->sk_socket->file : (void *)-1LL);
+ filp = sk->sk_socket ? sk->sk_socket->file : NULL;
+ MT_DEBUG("qtaguid[%d]: filp...uid=%u\n",
+ par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1);
+ }
+
+ if (sk == NULL || sk->sk_socket == NULL) {
+ /*
+ * Here, the qtaguid_find_sk() using connection tracking
+ * couldn't find the owner, so for now we just count them
+ * against the system.
+ */
+ /*
+ * TODO: unhack how to force just accounting.
+ * For now we only do iface stats when the uid-owner is not
+ * requested.
+ */
+ if (!(info->match & XT_QTAGUID_UID))
+ account_for_uid(skb, sk, 0, par);
+ MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n",
+ par->hooknum,
+ sk ? sk->sk_socket : NULL);
+ res = (info->match ^ info->invert) == 0;
+ atomic64_inc(&qtu_events.match_no_sk);
+ goto put_sock_ret_res;
+ } else if (info->match & info->invert & XT_QTAGUID_SOCKET) {
+ res = false;
+ goto put_sock_ret_res;
+ }
+ filp = sk->sk_socket->file;
+ if (filp == NULL) {
+ MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum);
+ account_for_uid(skb, sk, 0, par);
+ res = ((info->match ^ info->invert) &
+ (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0;
+ atomic64_inc(&qtu_events.match_no_sk_file);
+ goto put_sock_ret_res;
+ }
+ sock_uid = filp->f_cred->fsuid;
+ /*
+ * TODO: unhack how to force just accounting.
+ * For now we only do iface stats when the uid-owner is not requested
+ */
+ if (!(info->match & XT_QTAGUID_UID))
+ account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par);
+
+ /*
+ * The following two tests fail the match when:
+ * id not in range AND no inverted condition requested
+ * or id in range AND inverted condition requested
+ * Thus (!a && b) || (a && !b) == a ^ b
+ */
+ if (info->match & XT_QTAGUID_UID) {
+ kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min);
+ kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max);
+
+ if (uid_gte(filp->f_cred->fsuid, uid_min) &&
+ uid_lte(filp->f_cred->fsuid, uid_max) ^
+ !(info->invert & XT_QTAGUID_UID)) {
+ MT_DEBUG("qtaguid[%d]: leaving uid not matching\n",
+ par->hooknum);
+ res = false;
+ goto put_sock_ret_res;
+ }
+ }
+ if (info->match & XT_QTAGUID_GID) {
+ kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min);
+ kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max);
+
+ if (gid_gte(filp->f_cred->fsgid, gid_min) &&
+ gid_lte(filp->f_cred->fsgid, gid_max) ^
+ !(info->invert & XT_QTAGUID_GID)) {
+ MT_DEBUG("qtaguid[%d]: leaving gid not matching\n",
+ par->hooknum);
+ res = false;
+ goto put_sock_ret_res;
+ }
+ }
+ MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum);
+ res = true;
+
+put_sock_ret_res:
+ if (got_sock)
+ sock_gen_put(sk);
+ret_res:
+ MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res);
+ return res;
+}
+
+#ifdef DDEBUG
+/* This function is not in xt_qtaguid_print.c because of locks visibility */
+static void prdebug_full_state(int indent_level, const char *fmt, ...)
+{
+ va_list args;
+ char *fmt_buff;
+ char *buff;
+
+ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+ return;
+
+ fmt_buff = kasprintf(GFP_ATOMIC,
+ "qtaguid: %s(): %s {\n", __func__, fmt);
+ BUG_ON(!fmt_buff);
+ va_start(args, fmt);
+ buff = kvasprintf(GFP_ATOMIC,
+ fmt_buff, args);
+ BUG_ON(!buff);
+ pr_debug("%s", buff);
+ kfree(fmt_buff);
+ kfree(buff);
+ va_end(args);
+
+ spin_lock_bh(&sock_tag_list_lock);
+ prdebug_sock_tag_tree(indent_level, &sock_tag_tree);
+ spin_unlock_bh(&sock_tag_list_lock);
+
+ spin_lock_bh(&sock_tag_list_lock);
+ spin_lock_bh(&uid_tag_data_tree_lock);
+ prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree);
+ prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree);
+ spin_unlock_bh(&uid_tag_data_tree_lock);
+ spin_unlock_bh(&sock_tag_list_lock);
+
+ spin_lock_bh(&iface_stat_list_lock);
+ prdebug_iface_stat_list(indent_level, &iface_stat_list);
+ spin_unlock_bh(&iface_stat_list_lock);
+
+ pr_debug("qtaguid: %s(): }\n", __func__);
+}
+#else
+static void prdebug_full_state(int indent_level, const char *fmt, ...) {}
+#endif
+
+struct proc_ctrl_print_info {
+ struct sock *sk; /* socket found by reading to sk_pos */
+ loff_t sk_pos;
+};
+
+static void *qtaguid_ctrl_proc_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct proc_ctrl_print_info *pcpi = m->private;
+ struct sock_tag *sock_tag_entry = v;
+ struct rb_node *node;
+
+ (*pos)++;
+
+ if (!v || v == SEQ_START_TOKEN)
+ return NULL;
+
+ node = rb_next(&sock_tag_entry->sock_node);
+ if (!node) {
+ pcpi->sk = NULL;
+ sock_tag_entry = SEQ_START_TOKEN;
+ } else {
+ sock_tag_entry = rb_entry(node, struct sock_tag, sock_node);
+ pcpi->sk = sock_tag_entry->sk;
+ }
+ pcpi->sk_pos = *pos;
+ return sock_tag_entry;
+}
+
+static void *qtaguid_ctrl_proc_start(struct seq_file *m, loff_t *pos)
+{
+ struct proc_ctrl_print_info *pcpi = m->private;
+ struct sock_tag *sock_tag_entry;
+ struct rb_node *node;
+
+ spin_lock_bh(&sock_tag_list_lock);
+
+ if (unlikely(module_passive))
+ return NULL;
+
+ if (*pos == 0) {
+ pcpi->sk_pos = 0;
+ node = rb_first(&sock_tag_tree);
+ if (!node) {
+ pcpi->sk = NULL;
+ return SEQ_START_TOKEN;
+ }
+ sock_tag_entry = rb_entry(node, struct sock_tag, sock_node);
+ pcpi->sk = sock_tag_entry->sk;
+ } else {
+ sock_tag_entry = (pcpi->sk ? get_sock_stat_nl(pcpi->sk) :
+ NULL) ?: SEQ_START_TOKEN;
+ if (*pos != pcpi->sk_pos) {
+ /* seq_read skipped a next call */
+ *pos = pcpi->sk_pos;
+ return qtaguid_ctrl_proc_next(m, sock_tag_entry, pos);
+ }
+ }
+ return sock_tag_entry;
+}
+
+static void qtaguid_ctrl_proc_stop(struct seq_file *m, void *v)
+{
+ spin_unlock_bh(&sock_tag_list_lock);
+}
+
+/*
+ * Procfs reader to get all active socket tags using style "1)" as described in
+ * fs/proc/generic.c
+ */
+static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
+{
+ struct sock_tag *sock_tag_entry = v;
+ uid_t uid;
+ long f_count;
+
+ CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n",
+ current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+
+ if (sock_tag_entry != SEQ_START_TOKEN) {
+ uid = get_uid_from_tag(sock_tag_entry->tag);
+ CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) "
+ "pid=%u\n",
+ sock_tag_entry->sk,
+ sock_tag_entry->tag,
+ uid,
+ sock_tag_entry->pid
+ );
+ f_count = atomic_long_read(
+ &sock_tag_entry->socket->file->f_count);
+ seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u "
+ "f_count=%lu\n",
+ sock_tag_entry->sk,
+ sock_tag_entry->tag, uid,
+ sock_tag_entry->pid, f_count);
+ } else {
+ seq_printf(m, "events: sockets_tagged=%llu "
+ "sockets_untagged=%llu "
+ "counter_set_changes=%llu "
+ "delete_cmds=%llu "
+ "iface_events=%llu "
+ "match_calls=%llu "
+ "match_calls_prepost=%llu "
+ "match_found_sk=%llu "
+ "match_found_sk_in_ct=%llu "
+ "match_found_no_sk_in_ct=%llu "
+ "match_no_sk=%llu "
+ "match_no_sk_file=%llu\n",
+ atomic64_read(&qtu_events.sockets_tagged),
+ atomic64_read(&qtu_events.sockets_untagged),
+ atomic64_read(&qtu_events.counter_set_changes),
+ atomic64_read(&qtu_events.delete_cmds),
+ atomic64_read(&qtu_events.iface_events),
+ atomic64_read(&qtu_events.match_calls),
+ atomic64_read(&qtu_events.match_calls_prepost),
+ atomic64_read(&qtu_events.match_found_sk),
+ atomic64_read(&qtu_events.match_found_sk_in_ct),
+ atomic64_read(&qtu_events.match_found_no_sk_in_ct),
+ atomic64_read(&qtu_events.match_no_sk),
+ atomic64_read(&qtu_events.match_no_sk_file));
+
+ /* Count the following as part of the last item_index */
+ prdebug_full_state(0, "proc ctrl");
+ }
+
+ return 0;
+}
+
+/*
+ * Delete socket tags, and stat tags associated with a given
+ * accouting tag and uid.
+ */
+static int ctrl_cmd_delete(const char *input)
+{
+ char cmd;
+ int uid_int;
+ kuid_t uid;
+ uid_t entry_uid;
+ tag_t acct_tag;
+ tag_t tag;
+ int res, argc;
+ struct iface_stat *iface_entry;
+ struct rb_node *node;
+ struct sock_tag *st_entry;
+ struct rb_root st_to_free_tree = RB_ROOT;
+ struct tag_stat *ts_entry;
+ struct tag_counter_set *tcs_entry;
+ struct tag_ref *tr_entry;
+ struct uid_tag_data *utd_entry;
+
+ argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid_int);
+ uid = KUIDT_INIT(uid_int);
+ CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c "
+ "user_tag=0x%llx uid=%u\n", input, argc, cmd,
+ acct_tag, uid_int);
+ if (argc < 2) {
+ res = -EINVAL;
+ goto err;
+ }
+ if (!valid_atag(acct_tag)) {
+ pr_info("qtaguid: ctrl_delete(%s): invalid tag\n", input);
+ res = -EINVAL;
+ goto err;
+ }
+ if (argc < 3) {
+ uid = current_fsuid();
+ } else if (!can_impersonate_uid(uid)) {
+ pr_info("qtaguid: ctrl_delete(%s): "
+ "insufficient priv from pid=%u tgid=%u uid=%u\n",
+ input, current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+ res = -EPERM;
+ goto err;
+ }
+
+ tag = combine_atag_with_uid(acct_tag, from_kuid(&init_user_ns,uid));
+ CT_DEBUG("qtaguid: ctrl_delete(%s): "
+ "looking for tag=0x%llx (uid=%u)\n",
+ input, tag, from_kuid(&init_user_ns, uid));
+
+ /* Delete socket tags */
+ spin_lock_bh(&sock_tag_list_lock);
+ node = rb_first(&sock_tag_tree);
+ while (node) {
+ st_entry = rb_entry(node, struct sock_tag, sock_node);
+ entry_uid = get_uid_from_tag(st_entry->tag);
+ node = rb_next(node);
+ if (!uid_eq(make_kuid(&init_user_ns,entry_uid), uid))
+ continue;
+
+ CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n",
+ input, st_entry->tag, entry_uid);
+
+ if (!acct_tag || st_entry->tag == tag) {
+ rb_erase(&st_entry->sock_node, &sock_tag_tree);
+ /* Can't sockfd_put() within spinlock, do it later. */
+ sock_tag_tree_insert(st_entry, &st_to_free_tree);
+ tr_entry = lookup_tag_ref(st_entry->tag, NULL);
+ BUG_ON(tr_entry->num_sock_tags <= 0);
+ tr_entry->num_sock_tags--;
+ /*
+ * TODO: remove if, and start failing.
+ * This is a hack to work around the fact that in some
+ * places we have "if (IS_ERR_OR_NULL(pqd_entry))"
+ * and are trying to work around apps
+ * that didn't open the /dev/xt_qtaguid.
+ */
+ if (st_entry->list.next && st_entry->list.prev)
+ list_del(&st_entry->list);
+ }
+ }
+ spin_unlock_bh(&sock_tag_list_lock);
+
+ sock_tag_tree_erase(&st_to_free_tree);
+
+ /* Delete tag counter-sets */
+ spin_lock_bh(&tag_counter_set_list_lock);
+ /* Counter sets are only on the uid tag, not full tag */
+ tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag);
+ if (tcs_entry) {
+ CT_DEBUG("qtaguid: ctrl_delete(%s): "
+ "erase tcs: tag=0x%llx (uid=%u) set=%d\n",
+ input,
+ tcs_entry->tn.tag,
+ get_uid_from_tag(tcs_entry->tn.tag),
+ tcs_entry->active_set);
+ rb_erase(&tcs_entry->tn.node, &tag_counter_set_tree);
+ kfree(tcs_entry);
+ }
+ spin_unlock_bh(&tag_counter_set_list_lock);
+
+ /*
+ * If acct_tag is 0, then all entries belonging to uid are
+ * erased.
+ */
+ spin_lock_bh(&iface_stat_list_lock);
+ list_for_each_entry(iface_entry, &iface_stat_list, list) {
+ spin_lock_bh(&iface_entry->tag_stat_list_lock);
+ node = rb_first(&iface_entry->tag_stat_tree);
+ while (node) {
+ ts_entry = rb_entry(node, struct tag_stat, tn.node);
+ entry_uid = get_uid_from_tag(ts_entry->tn.tag);
+ node = rb_next(node);
+
+ CT_DEBUG("qtaguid: ctrl_delete(%s): "
+ "ts tag=0x%llx (uid=%u)\n",
+ input, ts_entry->tn.tag, entry_uid);
+
+ if (!uid_eq(make_kuid(&init_user_ns,entry_uid), uid))
+ continue;
+ if (!acct_tag || ts_entry->tn.tag == tag) {
+ CT_DEBUG("qtaguid: ctrl_delete(%s): "
+ "erase ts: %s 0x%llx %u\n",
+ input, iface_entry->ifname,
+ get_atag_from_tag(ts_entry->tn.tag),
+ entry_uid);
+ rb_erase(&ts_entry->tn.node,
+ &iface_entry->tag_stat_tree);
+ kfree(ts_entry);
+ }
+ }
+ spin_unlock_bh(&iface_entry->tag_stat_list_lock);
+ }
+ spin_unlock_bh(&iface_stat_list_lock);
+
+ /* Cleanup the uid_tag_data */
+ spin_lock_bh(&uid_tag_data_tree_lock);
+ node = rb_first(&uid_tag_data_tree);
+ while (node) {
+ utd_entry = rb_entry(node, struct uid_tag_data, node);
+ entry_uid = utd_entry->uid;
+ node = rb_next(node);
+
+ CT_DEBUG("qtaguid: ctrl_delete(%s): "
+ "utd uid=%u\n",
+ input, entry_uid);
+
+ if (!uid_eq(make_kuid(&init_user_ns,entry_uid), uid))
+ continue;
+ /*
+ * Go over the tag_refs, and those that don't have
+ * sock_tags using them are freed.
+ */
+ put_tag_ref_tree(tag, utd_entry);
+ put_utd_entry(utd_entry);
+ }
+ spin_unlock_bh(&uid_tag_data_tree_lock);
+
+ atomic64_inc(&qtu_events.delete_cmds);
+ res = 0;
+
+err:
+ return res;
+}
+
+static int ctrl_cmd_counter_set(const char *input)
+{
+ char cmd;
+ uid_t uid = 0;
+ tag_t tag;
+ int res, argc;
+ struct tag_counter_set *tcs;
+ int counter_set;
+
+ argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid);
+ CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c "
+ "set=%d uid=%u\n", input, argc, cmd,
+ counter_set, uid);
+ if (argc != 3) {
+ res = -EINVAL;
+ goto err;
+ }
+ if (counter_set < 0 || counter_set >= IFS_MAX_COUNTER_SETS) {
+ pr_info("qtaguid: ctrl_counterset(%s): invalid counter_set range\n",
+ input);
+ res = -EINVAL;
+ goto err;
+ }
+ if (!can_manipulate_uids()) {
+ pr_info("qtaguid: ctrl_counterset(%s): "
+ "insufficient priv from pid=%u tgid=%u uid=%u\n",
+ input, current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+ res = -EPERM;
+ goto err;
+ }
+
+ tag = make_tag_from_uid(uid);
+ spin_lock_bh(&tag_counter_set_list_lock);
+ tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag);
+ if (!tcs) {
+ tcs = kzalloc(sizeof(*tcs), GFP_ATOMIC);
+ if (!tcs) {
+ spin_unlock_bh(&tag_counter_set_list_lock);
+ pr_err("qtaguid: ctrl_counterset(%s): "
+ "failed to alloc counter set\n",
+ input);
+ res = -ENOMEM;
+ goto err;
+ }
+ tcs->tn.tag = tag;
+ tag_counter_set_tree_insert(tcs, &tag_counter_set_tree);
+ CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx "
+ "(uid=%u) set=%d\n",
+ input, tag, get_uid_from_tag(tag), counter_set);
+ }
+ tcs->active_set = counter_set;
+ spin_unlock_bh(&tag_counter_set_list_lock);
+ atomic64_inc(&qtu_events.counter_set_changes);
+ res = 0;
+
+err:
+ return res;
+}
+
+static int ctrl_cmd_tag(const char *input)
+{
+ char cmd;
+ int sock_fd = 0;
+ kuid_t uid;
+ unsigned int uid_int;
+ tag_t acct_tag = make_atag_from_value(0);
+ tag_t full_tag;
+ struct socket *el_socket;
+ int res, argc;
+ struct sock_tag *sock_tag_entry;
+ struct tag_ref *tag_ref_entry;
+ struct uid_tag_data *uid_tag_data_entry;
+ struct proc_qtu_data *pqd_entry;
+
+ /* Unassigned args will get defaulted later. */
+ argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid_int);
+ uid = KUIDT_INIT(uid_int);
+ CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d "
+ "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd,
+ acct_tag, uid_int);
+ if (argc < 2) {
+ res = -EINVAL;
+ goto err;
+ }
+ el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */
+ if (!el_socket) {
+ pr_info("qtaguid: ctrl_tag(%s): failed to lookup"
+ " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
+ input, sock_fd, res, current->pid, current->tgid,
+ from_kuid(&init_user_ns,current_fsuid()));
+ goto err;
+ }
+ CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n",
+ input, atomic_long_read(&el_socket->file->f_count),
+ el_socket->sk);
+ if (argc < 3) {
+ acct_tag = make_atag_from_value(0);
+ } else if (!valid_atag(acct_tag)) {
+ pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input);
+ res = -EINVAL;
+ goto err_put;
+ }
+ CT_DEBUG("qtaguid: ctrl_tag(%s): "
+ "pid=%u tgid=%u uid=%u euid=%u fsuid=%u "
+ "ctrl.gid=%u in_group()=%d in_egroup()=%d\n",
+ input, current->pid, current->tgid,
+ from_kuid(&init_user_ns, current_uid()),
+ from_kuid(&init_user_ns, current_euid()),
+ from_kuid(&init_user_ns, current_fsuid()),
+ from_kgid(&init_user_ns,xt_qtaguid_ctrl_file->gid),
+ in_group_p(xt_qtaguid_ctrl_file->gid),
+ in_egroup_p(xt_qtaguid_ctrl_file->gid));
+ if (argc < 4) {
+ uid = current_fsuid();
+ } else if (!can_impersonate_uid(uid)) {
+ pr_info("qtaguid: ctrl_tag(%s): "
+ "insufficient priv from pid=%u tgid=%u uid=%u\n",
+ input, current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+ res = -EPERM;
+ goto err_put;
+ }
+ full_tag = combine_atag_with_uid(acct_tag, from_kuid(&init_user_ns,uid));
+
+ spin_lock_bh(&sock_tag_list_lock);
+ sock_tag_entry = get_sock_stat_nl(el_socket->sk);
+ tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry);
+ if (IS_ERR(tag_ref_entry)) {
+ res = PTR_ERR(tag_ref_entry);
+ spin_unlock_bh(&sock_tag_list_lock);
+ goto err_put;
+ }
+ tag_ref_entry->num_sock_tags++;
+ if (sock_tag_entry) {
+ struct tag_ref *prev_tag_ref_entry;
+
+ CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p "
+ "st@%p ...->f_count=%ld\n",
+ input, el_socket->sk, sock_tag_entry,
+ atomic_long_read(&el_socket->file->f_count));
+ /*
+ * This is a re-tagging, so release the sock_fd that was
+ * locked at the time of the 1st tagging.
+ * There is still the ref from this call's sockfd_lookup() so
+ * it can be done within the spinlock.
+ */
+ sockfd_put(sock_tag_entry->socket);
+ prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag,
+ &uid_tag_data_entry);
+ BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry));
+ BUG_ON(prev_tag_ref_entry->num_sock_tags <= 0);
+ prev_tag_ref_entry->num_sock_tags--;
+ sock_tag_entry->tag = full_tag;
+ } else {
+ CT_DEBUG("qtaguid: ctrl_tag(%s): newtag for sk=%p\n",
+ input, el_socket->sk);
+ sock_tag_entry = kzalloc(sizeof(*sock_tag_entry),
+ GFP_ATOMIC);
+ if (!sock_tag_entry) {
+ pr_err("qtaguid: ctrl_tag(%s): "
+ "socket tag alloc failed\n",
+ input);
+ spin_unlock_bh(&sock_tag_list_lock);
+ res = -ENOMEM;
+ goto err_tag_unref_put;
+ }
+ sock_tag_entry->sk = el_socket->sk;
+ sock_tag_entry->socket = el_socket;
+ sock_tag_entry->pid = current->tgid;
+ sock_tag_entry->tag = combine_atag_with_uid(acct_tag,
+ from_kuid(&init_user_ns,uid));
+ spin_lock_bh(&uid_tag_data_tree_lock);
+ pqd_entry = proc_qtu_data_tree_search(
+ &proc_qtu_data_tree, current->tgid);
+ /*
+ * TODO: remove if, and start failing.
+ * At first, we want to catch user-space code that is not
+ * opening the /dev/xt_qtaguid.
+ */
+ if (IS_ERR_OR_NULL(pqd_entry))
+ pr_warn_once(
+ "qtaguid: %s(): "
+ "User space forgot to open /dev/xt_qtaguid? "
+ "pid=%u tgid=%u uid=%u\n", __func__,
+ current->pid, current->tgid,
+ from_kuid(&init_user_ns,current_fsuid()));
+ else
+ list_add(&sock_tag_entry->list,
+ &pqd_entry->sock_tag_list);
+ spin_unlock_bh(&uid_tag_data_tree_lock);
+
+ sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree);
+ atomic64_inc(&qtu_events.sockets_tagged);
+ }
+ spin_unlock_bh(&sock_tag_list_lock);
+ /* We keep the ref to the socket (file) until it is untagged */
+ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n",
+ input, sock_tag_entry,
+ atomic_long_read(&el_socket->file->f_count));
+ return 0;
+
+err_tag_unref_put:
+ BUG_ON(tag_ref_entry->num_sock_tags <= 0);
+ tag_ref_entry->num_sock_tags--;
+ free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry);
+err_put:
+ CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n",
+ input, atomic_long_read(&el_socket->file->f_count) - 1);
+ /* Release the sock_fd that was grabbed by sockfd_lookup(). */
+ sockfd_put(el_socket);
+ return res;
+
+err:
+ CT_DEBUG("qtaguid: ctrl_tag(%s): done.\n", input);
+ return res;
+}
+
+static int ctrl_cmd_untag(const char *input)
+{
+ char cmd;
+ int sock_fd = 0;
+ struct socket *el_socket;
+ int res, argc;
+ struct sock_tag *sock_tag_entry;
+ struct tag_ref *tag_ref_entry;
+ struct uid_tag_data *utd_entry;
+ struct proc_qtu_data *pqd_entry;
+
+ argc = sscanf(input, "%c %d", &cmd, &sock_fd);
+ CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n",
+ input, argc, cmd, sock_fd);
+ if (argc < 2) {
+ res = -EINVAL;
+ goto err;
+ }
+ el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */
+ if (!el_socket) {
+ pr_info("qtaguid: ctrl_untag(%s): failed to lookup"
+ " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
+ input, sock_fd, res, current->pid, current->tgid,
+ from_kuid(&init_user_ns,current_fsuid()));
+ goto err;
+ }
+ CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n",
+ input, atomic_long_read(&el_socket->file->f_count),
+ el_socket->sk);
+ spin_lock_bh(&sock_tag_list_lock);
+ sock_tag_entry = get_sock_stat_nl(el_socket->sk);
+ if (!sock_tag_entry) {
+ spin_unlock_bh(&sock_tag_list_lock);
+ res = -EINVAL;
+ goto err_put;
+ }
+ /*
+ * The socket already belongs to the current process
+ * so it can do whatever it wants to it.
+ */
+ rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree);
+
+ tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &utd_entry);
+ BUG_ON(!tag_ref_entry);
+ BUG_ON(tag_ref_entry->num_sock_tags <= 0);
+ spin_lock_bh(&uid_tag_data_tree_lock);
+ pqd_entry = proc_qtu_data_tree_search(
+ &proc_qtu_data_tree, current->tgid);
+ /*
+ * TODO: remove if, and start failing.
+ * At first, we want to catch user-space code that is not
+ * opening the /dev/xt_qtaguid.
+ */
+ if (IS_ERR_OR_NULL(pqd_entry))
+ pr_warn_once("qtaguid: %s(): "
+ "User space forgot to open /dev/xt_qtaguid? "
+ "pid=%u tgid=%u uid=%u\n", __func__,
+ current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+ else
+ list_del(&sock_tag_entry->list);
+ spin_unlock_bh(&uid_tag_data_tree_lock);
+ /*
+ * We don't free tag_ref from the utd_entry here,
+ * only during a cmd_delete().
+ */
+ tag_ref_entry->num_sock_tags--;
+ spin_unlock_bh(&sock_tag_list_lock);
+ /*
+ * Release the sock_fd that was grabbed at tag time,
+ * and once more for the sockfd_lookup() here.
+ */
+ sockfd_put(sock_tag_entry->socket);
+ CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n",
+ input, sock_tag_entry,
+ atomic_long_read(&el_socket->file->f_count) - 1);
+ sockfd_put(el_socket);
+
+ kfree(sock_tag_entry);
+ atomic64_inc(&qtu_events.sockets_untagged);
+
+ return 0;
+
+err_put:
+ CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n",
+ input, atomic_long_read(&el_socket->file->f_count) - 1);
+ /* Release the sock_fd that was grabbed by sockfd_lookup(). */
+ sockfd_put(el_socket);
+ return res;
+
+err:
+ CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input);
+ return res;
+}
+
+static ssize_t qtaguid_ctrl_parse(const char *input, size_t count)
+{
+ char cmd;
+ ssize_t res;
+
+ CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n",
+ input, current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+
+ cmd = input[0];
+ /* Collect params for commands */
+ switch (cmd) {
+ case 'd':
+ res = ctrl_cmd_delete(input);
+ break;
+
+ case 's':
+ res = ctrl_cmd_counter_set(input);
+ break;
+
+ case 't':
+ res = ctrl_cmd_tag(input);
+ break;
+
+ case 'u':
+ res = ctrl_cmd_untag(input);
+ break;
+
+ default:
+ res = -EINVAL;
+ goto err;
+ }
+ if (!res)
+ res = count;
+err:
+ CT_DEBUG("qtaguid: ctrl(%s): res=%zd\n", input, res);
+ return res;
+}
+
+#define MAX_QTAGUID_CTRL_INPUT_LEN 255
+static ssize_t qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *offp)
+{
+ char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN];
+
+ if (unlikely(module_passive))
+ return count;
+
+ if (count >= MAX_QTAGUID_CTRL_INPUT_LEN)
+ return -EINVAL;
+
+ if (copy_from_user(input_buf, buffer, count))
+ return -EFAULT;
+
+ input_buf[count] = '\0';
+ return qtaguid_ctrl_parse(input_buf, count);
+}
+
+struct proc_print_info {
+ struct iface_stat *iface_entry;
+ int item_index;
+ tag_t tag; /* tag found by reading to tag_pos */
+ off_t tag_pos;
+ int tag_item_index;
+};
+
+static void pp_stats_header(struct seq_file *m)
+{
+ seq_puts(m,
+ "idx iface acct_tag_hex uid_tag_int cnt_set "
+ "rx_bytes rx_packets "
+ "tx_bytes tx_packets "
+ "rx_tcp_bytes rx_tcp_packets "
+ "rx_udp_bytes rx_udp_packets "
+ "rx_other_bytes rx_other_packets "
+ "tx_tcp_bytes tx_tcp_packets "
+ "tx_udp_bytes tx_udp_packets "
+ "tx_other_bytes tx_other_packets\n");
+}
+
+static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry,
+ int cnt_set)
+{
+ int ret;
+ struct data_counters *cnts;
+ tag_t tag = ts_entry->tn.tag;
+ uid_t stat_uid = get_uid_from_tag(tag);
+ struct proc_print_info *ppi = m->private;
+ /* Detailed tags are not available to everybody */
+ if (get_atag_from_tag(tag) && !can_read_other_uid_stats(
+ make_kuid(&init_user_ns,stat_uid))) {
+ CT_DEBUG("qtaguid: stats line: "
+ "%s 0x%llx %u: insufficient priv "
+ "from pid=%u tgid=%u uid=%u stats.gid=%u\n",
+ ppi->iface_entry->ifname,
+ get_atag_from_tag(tag), stat_uid,
+ current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()),
+ from_kgid(&init_user_ns,xt_qtaguid_stats_file->gid));
+ return 0;
+ }
+ ppi->item_index++;
+ cnts = &ts_entry->counters;
+ ret = seq_printf(m, "%d %s 0x%llx %u %u "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu\n",
+ ppi->item_index,
+ ppi->iface_entry->ifname,
+ get_atag_from_tag(tag),
+ stat_uid,
+ cnt_set,
+ dc_sum_bytes(cnts, cnt_set, IFS_RX),
+ dc_sum_packets(cnts, cnt_set, IFS_RX),
+ dc_sum_bytes(cnts, cnt_set, IFS_TX),
+ dc_sum_packets(cnts, cnt_set, IFS_TX),
+ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes,
+ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets,
+ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes,
+ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets,
+ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes,
+ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets,
+ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes,
+ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets,
+ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes,
+ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets,
+ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes,
+ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets);
+ return ret ?: 1;
+}
+
+static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry)
+{
+ int ret;
+ int counter_set;
+ for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS;
+ counter_set++) {
+ ret = pp_stats_line(m, ts_entry, counter_set);
+ if (ret < 0)
+ return false;
+ }
+ return true;
+}
+
+static int qtaguid_stats_proc_iface_stat_ptr_valid(struct iface_stat *ptr)
+{
+ struct iface_stat *iface_entry;
+
+ if (!ptr)
+ return false;
+
+ list_for_each_entry(iface_entry, &iface_stat_list, list)
+ if (iface_entry == ptr)
+ return true;
+ return false;
+}
+
+static void qtaguid_stats_proc_next_iface_entry(struct proc_print_info *ppi)
+{
+ spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock);
+ list_for_each_entry_continue(ppi->iface_entry, &iface_stat_list, list) {
+ spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock);
+ return;
+ }
+ ppi->iface_entry = NULL;
+}
+
+static void *qtaguid_stats_proc_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct proc_print_info *ppi = m->private;
+ struct tag_stat *ts_entry;
+ struct rb_node *node;
+
+ if (!v) {
+ pr_err("qtaguid: %s(): unexpected v: NULL\n", __func__);
+ return NULL;
+ }
+
+ (*pos)++;
+
+ if (!ppi->iface_entry || unlikely(module_passive))
+ return NULL;
+
+ if (v == SEQ_START_TOKEN)
+ node = rb_first(&ppi->iface_entry->tag_stat_tree);
+ else
+ node = rb_next(&((struct tag_stat *)v)->tn.node);
+
+ while (!node) {
+ qtaguid_stats_proc_next_iface_entry(ppi);
+ if (!ppi->iface_entry)
+ return NULL;
+ node = rb_first(&ppi->iface_entry->tag_stat_tree);
+ }
+
+ ts_entry = rb_entry(node, struct tag_stat, tn.node);
+ ppi->tag = ts_entry->tn.tag;
+ ppi->tag_pos = *pos;
+ ppi->tag_item_index = ppi->item_index;
+ return ts_entry;
+}
+
+static void *qtaguid_stats_proc_start(struct seq_file *m, loff_t *pos)
+{
+ struct proc_print_info *ppi = m->private;
+ struct tag_stat *ts_entry = NULL;
+
+ spin_lock_bh(&iface_stat_list_lock);
+
+ if (*pos == 0) {
+ ppi->item_index = 1;
+ ppi->tag_pos = 0;
+ if (list_empty(&iface_stat_list)) {
+ ppi->iface_entry = NULL;
+ } else {
+ ppi->iface_entry = list_first_entry(&iface_stat_list,
+ struct iface_stat,
+ list);
+ spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock);
+ }
+ return SEQ_START_TOKEN;
+ }
+ if (!qtaguid_stats_proc_iface_stat_ptr_valid(ppi->iface_entry)) {
+ if (ppi->iface_entry) {
+ pr_err("qtaguid: %s(): iface_entry %p not found\n",
+ __func__, ppi->iface_entry);
+ ppi->iface_entry = NULL;
+ }
+ return NULL;
+ }
+
+ spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock);
+
+ if (!ppi->tag_pos) {
+ /* seq_read skipped first next call */
+ ts_entry = SEQ_START_TOKEN;
+ } else {
+ ts_entry = tag_stat_tree_search(
+ &ppi->iface_entry->tag_stat_tree, ppi->tag);
+ if (!ts_entry) {
+ pr_info("qtaguid: %s(): tag_stat.tag 0x%llx not found. Abort.\n",
+ __func__, ppi->tag);
+ return NULL;
+ }
+ }
+
+ if (*pos == ppi->tag_pos) { /* normal resume */
+ ppi->item_index = ppi->tag_item_index;
+ } else {
+ /* seq_read skipped a next call */
+ *pos = ppi->tag_pos;
+ ts_entry = qtaguid_stats_proc_next(m, ts_entry, pos);
+ }
+
+ return ts_entry;
+}
+
+static void qtaguid_stats_proc_stop(struct seq_file *m, void *v)
+{
+ struct proc_print_info *ppi = m->private;
+ if (ppi->iface_entry)
+ spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock);
+ spin_unlock_bh(&iface_stat_list_lock);
+}
+
+/*
+ * Procfs reader to get all tag stats using style "1)" as described in
+ * fs/proc/generic.c
+ * Groups all protocols tx/rx bytes.
+ */
+static int qtaguid_stats_proc_show(struct seq_file *m, void *v)
+{
+ struct tag_stat *ts_entry = v;
+
+ if (v == SEQ_START_TOKEN)
+ pp_stats_header(m);
+ else
+ pp_sets(m, ts_entry);
+
+ return 0;
+}
+
+/*------------------------------------------*/
+static int qtudev_open(struct inode *inode, struct file *file)
+{
+ struct uid_tag_data *utd_entry;
+ struct proc_qtu_data *pqd_entry;
+ struct proc_qtu_data *new_pqd_entry;
+ int res;
+ bool utd_entry_found;
+
+ if (unlikely(qtu_proc_handling_passive))
+ return 0;
+
+ DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n",
+ current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+
+ spin_lock_bh(&uid_tag_data_tree_lock);
+
+ /* Look for existing uid data, or alloc one. */
+ utd_entry = get_uid_data(from_kuid(&init_user_ns,current_fsuid()), &utd_entry_found);
+ if (IS_ERR_OR_NULL(utd_entry)) {
+ res = PTR_ERR(utd_entry);
+ goto err_unlock;
+ }
+
+ /* Look for existing PID based proc_data */
+ pqd_entry = proc_qtu_data_tree_search(&proc_qtu_data_tree,
+ current->tgid);
+ if (pqd_entry) {
+ pr_err("qtaguid: qtudev_open(): %u/%u %u "
+ "%s already opened\n",
+ current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()),
+ QTU_DEV_NAME);
+ res = -EBUSY;
+ goto err_unlock_free_utd;
+ }
+
+ new_pqd_entry = kzalloc(sizeof(*new_pqd_entry), GFP_ATOMIC);
+ if (!new_pqd_entry) {
+ pr_err("qtaguid: qtudev_open(): %u/%u %u: "
+ "proc data alloc failed\n",
+ current->pid, current->tgid, from_kuid(&init_user_ns,current_fsuid()));
+ res = -ENOMEM;
+ goto err_unlock_free_utd;
+ }
+ new_pqd_entry->pid = current->tgid;
+ INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list);
+ new_pqd_entry->parent_tag_data = utd_entry;
+ utd_entry->num_pqd++;
+
+ proc_qtu_data_tree_insert(new_pqd_entry,
+ &proc_qtu_data_tree);
+
+ spin_unlock_bh(&uid_tag_data_tree_lock);
+ DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n",
+ from_kuid(&init_user_ns,current_fsuid()), new_pqd_entry);
+ file->private_data = new_pqd_entry;
+ return 0;
+
+err_unlock_free_utd:
+ if (!utd_entry_found) {
+ rb_erase(&utd_entry->node, &uid_tag_data_tree);
+ kfree(utd_entry);
+ }
+err_unlock:
+ spin_unlock_bh(&uid_tag_data_tree_lock);
+ return res;
+}
+
+static int qtudev_release(struct inode *inode, struct file *file)
+{
+ struct proc_qtu_data *pqd_entry = file->private_data;
+ struct uid_tag_data *utd_entry = pqd_entry->parent_tag_data;
+ struct sock_tag *st_entry;
+ struct rb_root st_to_free_tree = RB_ROOT;
+ struct list_head *entry, *next;
+ struct tag_ref *tr;
+
+ if (unlikely(qtu_proc_handling_passive))
+ return 0;
+
+ /*
+ * Do not trust the current->pid, it might just be a kworker cleaning
+ * up after a dead proc.
+ */
+ DR_DEBUG("qtaguid: qtudev_release(): "
+ "pid=%u tgid=%u uid=%u "
+ "pqd_entry=%p->pid=%u utd_entry=%p->active_tags=%d\n",
+ current->pid, current->tgid, pqd_entry->parent_tag_data->uid,
+ pqd_entry, pqd_entry->pid, utd_entry,
+ utd_entry->num_active_tags);
+
+ spin_lock_bh(&sock_tag_list_lock);
+ spin_lock_bh(&uid_tag_data_tree_lock);
+
+ list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) {
+ st_entry = list_entry(entry, struct sock_tag, list);
+ DR_DEBUG("qtaguid: %s(): "
+ "erase sock_tag=%p->sk=%p pid=%u tgid=%u uid=%u\n",
+ __func__,
+ st_entry, st_entry->sk,
+ current->pid, current->tgid,
+ pqd_entry->parent_tag_data->uid);
+
+ utd_entry = uid_tag_data_tree_search(
+ &uid_tag_data_tree,
+ get_uid_from_tag(st_entry->tag));
+ BUG_ON(IS_ERR_OR_NULL(utd_entry));
+ DR_DEBUG("qtaguid: %s(): "
+ "looking for tag=0x%llx in utd_entry=%p\n", __func__,
+ st_entry->tag, utd_entry);
+ tr = tag_ref_tree_search(&utd_entry->tag_ref_tree,
+ st_entry->tag);
+ BUG_ON(!tr);
+ BUG_ON(tr->num_sock_tags <= 0);
+ tr->num_sock_tags--;
+ free_tag_ref_from_utd_entry(tr, utd_entry);
+
+ rb_erase(&st_entry->sock_node, &sock_tag_tree);
+ list_del(&st_entry->list);
+ /* Can't sockfd_put() within spinlock, do it later. */
+ sock_tag_tree_insert(st_entry, &st_to_free_tree);
+
+ /*
+ * Try to free the utd_entry if no other proc_qtu_data is
+ * using it (num_pqd is 0) and it doesn't have active tags
+ * (num_active_tags is 0).
+ */
+ put_utd_entry(utd_entry);
+ }
+
+ rb_erase(&pqd_entry->node, &proc_qtu_data_tree);
+ BUG_ON(pqd_entry->parent_tag_data->num_pqd < 1);
+ pqd_entry->parent_tag_data->num_pqd--;
+ put_utd_entry(pqd_entry->parent_tag_data);
+ kfree(pqd_entry);
+ file->private_data = NULL;
+
+ spin_unlock_bh(&uid_tag_data_tree_lock);
+ spin_unlock_bh(&sock_tag_list_lock);
+
+
+ sock_tag_tree_erase(&st_to_free_tree);
+
+ prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__,
+ current->pid, current->tgid);
+ return 0;
+}
+
+/*------------------------------------------*/
+static const struct file_operations qtudev_fops = {
+ .owner = THIS_MODULE,
+ .open = qtudev_open,
+ .release = qtudev_release,
+};
+
+static struct miscdevice qtu_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = QTU_DEV_NAME,
+ .fops = &qtudev_fops,
+ /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */
+};
+
+static const struct seq_operations proc_qtaguid_ctrl_seqops = {
+ .start = qtaguid_ctrl_proc_start,
+ .next = qtaguid_ctrl_proc_next,
+ .stop = qtaguid_ctrl_proc_stop,
+ .show = qtaguid_ctrl_proc_show,
+};
+
+static int proc_qtaguid_ctrl_open(struct inode *inode, struct file *file)
+{
+ return seq_open_private(file, &proc_qtaguid_ctrl_seqops,
+ sizeof(struct proc_ctrl_print_info));
+}
+
+static const struct file_operations proc_qtaguid_ctrl_fops = {
+ .open = proc_qtaguid_ctrl_open,
+ .read = seq_read,
+ .write = qtaguid_ctrl_proc_write,
+ .llseek = seq_lseek,
+ .release = seq_release_private,
+};
+
+static const struct seq_operations proc_qtaguid_stats_seqops = {
+ .start = qtaguid_stats_proc_start,
+ .next = qtaguid_stats_proc_next,
+ .stop = qtaguid_stats_proc_stop,
+ .show = qtaguid_stats_proc_show,
+};
+
+static int proc_qtaguid_stats_open(struct inode *inode, struct file *file)
+{
+ return seq_open_private(file, &proc_qtaguid_stats_seqops,
+ sizeof(struct proc_print_info));
+}
+
+static const struct file_operations proc_qtaguid_stats_fops = {
+ .open = proc_qtaguid_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_private,
+};
+
+/*------------------------------------------*/
+static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir)
+{
+ int ret;
+ *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net);
+ if (!*res_procdir) {
+ pr_err("qtaguid: failed to create proc/.../xt_qtaguid\n");
+ ret = -ENOMEM;
+ goto no_dir;
+ }
+
+ xt_qtaguid_ctrl_file = proc_create_data("ctrl", proc_ctrl_perms,
+ *res_procdir,
+ &proc_qtaguid_ctrl_fops,
+ NULL);
+ if (!xt_qtaguid_ctrl_file) {
+ pr_err("qtaguid: failed to create xt_qtaguid/ctrl "
+ " file\n");
+ ret = -ENOMEM;
+ goto no_ctrl_entry;
+ }
+
+ xt_qtaguid_stats_file = proc_create_data("stats", proc_stats_perms,
+ *res_procdir,
+ &proc_qtaguid_stats_fops,
+ NULL);
+ if (!xt_qtaguid_stats_file) {
+ pr_err("qtaguid: failed to create xt_qtaguid/stats "
+ "file\n");
+ ret = -ENOMEM;
+ goto no_stats_entry;
+ }
+ /*
+ * TODO: add support counter hacking
+ * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write;
+ */
+ return 0;
+
+no_stats_entry:
+ remove_proc_entry("ctrl", *res_procdir);
+no_ctrl_entry:
+ remove_proc_entry("xt_qtaguid", NULL);
+no_dir:
+ return ret;
+}
+
+static struct xt_match qtaguid_mt_reg __read_mostly = {
+ /*
+ * This module masquerades as the "owner" module so that iptables
+ * tools can deal with it.
+ */
+ .name = "owner",
+ .revision = 1,
+ .family = NFPROTO_UNSPEC,
+ .match = qtaguid_mt,
+ .matchsize = sizeof(struct xt_qtaguid_match_info),
+ .me = THIS_MODULE,
+};
+
+static int __init qtaguid_mt_init(void)
+{
+ if (qtaguid_proc_register(&xt_qtaguid_procdir)
+ || iface_stat_init(xt_qtaguid_procdir)
+ || xt_register_match(&qtaguid_mt_reg)
+ || misc_register(&qtu_device))
+ return -1;
+ return 0;
+}
+
+/*
+ * TODO: allow unloading of the module.
+ * For now stats are permanent.
+ * Kconfig forces'y/n' and never an 'm'.
+ */
+
+module_init(qtaguid_mt_init);
+MODULE_AUTHOR("jpa <jpa@google.com>");
+MODULE_DESCRIPTION("Xtables: socket owner+tag matching and associated stats");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_owner");
+MODULE_ALIAS("ip6t_owner");
+MODULE_ALIAS("ipt_qtaguid");
+MODULE_ALIAS("ip6t_qtaguid");
diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h
new file mode 100644
index 000000000000..6dc14a9c6889
--- /dev/null
+++ b/net/netfilter/xt_qtaguid_internal.h
@@ -0,0 +1,352 @@
+/*
+ * Kernel iptables module to track stats for packets based on user tags.
+ *
+ * (C) 2011 Google, 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 __XT_QTAGUID_INTERNAL_H__
+#define __XT_QTAGUID_INTERNAL_H__
+
+#include <linux/types.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock_types.h>
+#include <linux/workqueue.h>
+
+/* Iface handling */
+#define IDEBUG_MASK (1<<0)
+/* Iptable Matching. Per packet. */
+#define MDEBUG_MASK (1<<1)
+/* Red-black tree handling. Per packet. */
+#define RDEBUG_MASK (1<<2)
+/* procfs ctrl/stats handling */
+#define CDEBUG_MASK (1<<3)
+/* dev and resource tracking */
+#define DDEBUG_MASK (1<<4)
+
+/* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */
+#define DEFAULT_DEBUG_MASK 0
+
+/*
+ * (Un)Define these *DEBUG to compile out/in the pr_debug calls.
+ * All undef: text size ~ 0x3030; all def: ~ 0x4404.
+ */
+#define IDEBUG
+#define MDEBUG
+#define RDEBUG
+#define CDEBUG
+#define DDEBUG
+
+#define MSK_DEBUG(mask, ...) do { \
+ if (unlikely(qtaguid_debug_mask & (mask))) \
+ pr_debug(__VA_ARGS__); \
+ } while (0)
+#ifdef IDEBUG
+#define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__)
+#else
+#define IF_DEBUG(...) no_printk(__VA_ARGS__)
+#endif
+#ifdef MDEBUG
+#define MT_DEBUG(...) MSK_DEBUG(MDEBUG_MASK, __VA_ARGS__)
+#else
+#define MT_DEBUG(...) no_printk(__VA_ARGS__)
+#endif
+#ifdef RDEBUG
+#define RB_DEBUG(...) MSK_DEBUG(RDEBUG_MASK, __VA_ARGS__)
+#else
+#define RB_DEBUG(...) no_printk(__VA_ARGS__)
+#endif
+#ifdef CDEBUG
+#define CT_DEBUG(...) MSK_DEBUG(CDEBUG_MASK, __VA_ARGS__)
+#else
+#define CT_DEBUG(...) no_printk(__VA_ARGS__)
+#endif
+#ifdef DDEBUG
+#define DR_DEBUG(...) MSK_DEBUG(DDEBUG_MASK, __VA_ARGS__)
+#else
+#define DR_DEBUG(...) no_printk(__VA_ARGS__)
+#endif
+
+extern uint qtaguid_debug_mask;
+
+/*---------------------------------------------------------------------------*/
+/*
+ * Tags:
+ *
+ * They represent what the data usage counters will be tracked against.
+ * By default a tag is just based on the UID.
+ * The UID is used as the base for policing, and can not be ignored.
+ * So a tag will always at least represent a UID (uid_tag).
+ *
+ * A tag can be augmented with an "accounting tag" which is associated
+ * with a UID.
+ * User space can set the acct_tag portion of the tag which is then used
+ * with sockets: all data belonging to that socket will be counted against the
+ * tag. The policing is then based on the tag's uid_tag portion,
+ * and stats are collected for the acct_tag portion separately.
+ *
+ * There could be
+ * a: {acct_tag=1, uid_tag=10003}
+ * b: {acct_tag=2, uid_tag=10003}
+ * c: {acct_tag=3, uid_tag=10003}
+ * d: {acct_tag=0, uid_tag=10003}
+ * a, b, and c represent tags associated with specific sockets.
+ * d is for the totals for that uid, including all untagged traffic.
+ * Typically d is used with policing/quota rules.
+ *
+ * We want tag_t big enough to distinguish uid_t and acct_tag.
+ * It might become a struct if needed.
+ * Nothing should be using it as an int.
+ */
+typedef uint64_t tag_t; /* Only used via accessors */
+
+#define TAG_UID_MASK 0xFFFFFFFFULL
+#define TAG_ACCT_MASK (~0xFFFFFFFFULL)
+
+static inline int tag_compare(tag_t t1, tag_t t2)
+{
+ return t1 < t2 ? -1 : t1 == t2 ? 0 : 1;
+}
+
+static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid)
+{
+ return acct_tag | uid;
+}
+static inline tag_t make_tag_from_uid(uid_t uid)
+{
+ return uid;
+}
+static inline uid_t get_uid_from_tag(tag_t tag)
+{
+ return tag & TAG_UID_MASK;
+}
+static inline tag_t get_utag_from_tag(tag_t tag)
+{
+ return tag & TAG_UID_MASK;
+}
+static inline tag_t get_atag_from_tag(tag_t tag)
+{
+ return tag & TAG_ACCT_MASK;
+}
+
+static inline bool valid_atag(tag_t tag)
+{
+ return !(tag & TAG_UID_MASK);
+}
+static inline tag_t make_atag_from_value(uint32_t value)
+{
+ return (uint64_t)value << 32;
+}
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Maximum number of socket tags that a UID is allowed to have active.
+ * Multiple processes belonging to the same UID contribute towards this limit.
+ * Special UIDs that can impersonate a UID also contribute (e.g. download
+ * manager, ...)
+ */
+#define DEFAULT_MAX_SOCK_TAGS 1024
+
+/*
+ * For now we only track 2 sets of counters.
+ * The default set is 0.
+ * Userspace can activate another set for a given uid being tracked.
+ */
+#define IFS_MAX_COUNTER_SETS 2
+
+enum ifs_tx_rx {
+ IFS_TX,
+ IFS_RX,
+ IFS_MAX_DIRECTIONS
+};
+
+/* For now, TCP, UDP, the rest */
+enum ifs_proto {
+ IFS_TCP,
+ IFS_UDP,
+ IFS_PROTO_OTHER,
+ IFS_MAX_PROTOS
+};
+
+struct byte_packet_counters {
+ uint64_t bytes;
+ uint64_t packets;
+};
+
+struct data_counters {
+ struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS];
+};
+
+static inline uint64_t dc_sum_bytes(struct data_counters *counters,
+ int set,
+ enum ifs_tx_rx direction)
+{
+ return counters->bpc[set][direction][IFS_TCP].bytes
+ + counters->bpc[set][direction][IFS_UDP].bytes
+ + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes;
+}
+
+static inline uint64_t dc_sum_packets(struct data_counters *counters,
+ int set,
+ enum ifs_tx_rx direction)
+{
+ return counters->bpc[set][direction][IFS_TCP].packets
+ + counters->bpc[set][direction][IFS_UDP].packets
+ + counters->bpc[set][direction][IFS_PROTO_OTHER].packets;
+}
+
+
+/* Generic X based nodes used as a base for rb_tree ops */
+struct tag_node {
+ struct rb_node node;
+ tag_t tag;
+};
+
+struct tag_stat {
+ struct tag_node tn;
+ struct data_counters counters;
+ /*
+ * If this tag is acct_tag based, we need to count against the
+ * matching parent uid_tag.
+ */
+ struct data_counters *parent_counters;
+};
+
+struct iface_stat {
+ struct list_head list; /* in iface_stat_list */
+ char *ifname;
+ bool active;
+ /* net_dev is only valid for active iface_stat */
+ struct net_device *net_dev;
+
+ struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS];
+ struct data_counters totals_via_skb;
+ /*
+ * We keep the last_known, because some devices reset their counters
+ * just before NETDEV_UP, while some will reset just before
+ * NETDEV_REGISTER (which is more normal).
+ * So now, if the device didn't do a NETDEV_UNREGISTER and we see
+ * its current dev stats smaller that what was previously known, we
+ * assume an UNREGISTER and just use the last_known.
+ */
+ struct byte_packet_counters last_known[IFS_MAX_DIRECTIONS];
+ /* last_known is usable when last_known_valid is true */
+ bool last_known_valid;
+
+ struct proc_dir_entry *proc_ptr;
+
+ struct rb_root tag_stat_tree;
+ spinlock_t tag_stat_list_lock;
+};
+
+/* This is needed to create proc_dir_entries from atomic context. */
+struct iface_stat_work {
+ struct work_struct iface_work;
+ struct iface_stat *iface_entry;
+};
+
+/*
+ * Track tag that this socket is transferring data for, and not necessarily
+ * the uid that owns the socket.
+ * This is the tag against which tag_stat.counters will be billed.
+ * These structs need to be looked up by sock and pid.
+ */
+struct sock_tag {
+ struct rb_node sock_node;
+ struct sock *sk; /* Only used as a number, never dereferenced */
+ /* The socket is needed for sockfd_put() */
+ struct socket *socket;
+ /* Used to associate with a given pid */
+ struct list_head list; /* in proc_qtu_data.sock_tag_list */
+ pid_t pid;
+
+ tag_t tag;
+};
+
+struct qtaguid_event_counts {
+ /* Various successful events */
+ atomic64_t sockets_tagged;
+ atomic64_t sockets_untagged;
+ atomic64_t counter_set_changes;
+ atomic64_t delete_cmds;
+ atomic64_t iface_events; /* Number of NETDEV_* events handled */
+
+ atomic64_t match_calls; /* Number of times iptables called mt */
+ /* Number of times iptables called mt from pre or post routing hooks */
+ atomic64_t match_calls_prepost;
+ /*
+ * match_found_sk_*: numbers related to the netfilter matching
+ * function finding a sock for the sk_buff.
+ * Total skbs processed is sum(match_found*).
+ */
+ atomic64_t match_found_sk; /* An sk was already in the sk_buff. */
+ /* The connection tracker had or didn't have the sk. */
+ atomic64_t match_found_sk_in_ct;
+ atomic64_t match_found_no_sk_in_ct;
+ /*
+ * No sk could be found. No apparent owner. Could happen with
+ * unsolicited traffic.
+ */
+ atomic64_t match_no_sk;
+ /*
+ * The file ptr in the sk_socket wasn't there.
+ * This might happen for traffic while the socket is being closed.
+ */
+ atomic64_t match_no_sk_file;
+};
+
+/* Track the set active_set for the given tag. */
+struct tag_counter_set {
+ struct tag_node tn;
+ int active_set;
+};
+
+/*----------------------------------------------*/
+/*
+ * The qtu uid data is used to track resources that are created directly or
+ * indirectly by processes (uid tracked).
+ * It is shared by the processes with the same uid.
+ * Some of the resource will be counted to prevent further rogue allocations,
+ * some will need freeing once the owner process (uid) exits.
+ */
+struct uid_tag_data {
+ struct rb_node node;
+ uid_t uid;
+
+ /*
+ * For the uid, how many accounting tags have been set.
+ */
+ int num_active_tags;
+ /* Track the number of proc_qtu_data that reference it */
+ int num_pqd;
+ struct rb_root tag_ref_tree;
+ /* No tag_node_tree_lock; use uid_tag_data_tree_lock */
+};
+
+struct tag_ref {
+ struct tag_node tn;
+
+ /*
+ * This tracks the number of active sockets that have a tag on them
+ * which matches this tag_ref.tn.tag.
+ * A tag ref can live on after the sockets are untagged.
+ * A tag ref can only be removed during a tag delete command.
+ */
+ int num_sock_tags;
+};
+
+struct proc_qtu_data {
+ struct rb_node node;
+ pid_t pid;
+
+ struct uid_tag_data *parent_tag_data;
+
+ /* Tracks the sock_tags that need freeing upon this proc's death */
+ struct list_head sock_tag_list;
+ /* No spinlock_t sock_tag_list_lock; use the global one. */
+};
+
+/*----------------------------------------------*/
+#endif /* ifndef __XT_QTAGUID_INTERNAL_H__ */
diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c
new file mode 100644
index 000000000000..f6a00a3520ed
--- /dev/null
+++ b/net/netfilter/xt_qtaguid_print.c
@@ -0,0 +1,566 @@
+/*
+ * Pretty printing Support for iptables xt_qtaguid module.
+ *
+ * (C) 2011 Google, 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.
+ */
+
+/*
+ * Most of the functions in this file just waste time if DEBUG is not defined.
+ * The matching xt_qtaguid_print.h will static inline empty funcs if the needed
+ * debug flags ore not defined.
+ * Those funcs that fail to allocate memory will panic as there is no need to
+ * hobble allong just pretending to do the requested work.
+ */
+
+#define DEBUG
+
+#include <linux/fs.h>
+#include <linux/gfp.h>
+#include <linux/net.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/spinlock_types.h>
+
+
+#include "xt_qtaguid_internal.h"
+#include "xt_qtaguid_print.h"
+
+#ifdef DDEBUG
+
+static void _bug_on_err_or_null(void *ptr)
+{
+ if (IS_ERR_OR_NULL(ptr)) {
+ pr_err("qtaguid: kmalloc failed\n");
+ BUG();
+ }
+}
+
+char *pp_tag_t(tag_t *tag)
+{
+ char *res;
+
+ if (!tag)
+ res = kasprintf(GFP_ATOMIC, "tag_t@null{}");
+ else
+ res = kasprintf(GFP_ATOMIC,
+ "tag_t@%p{tag=0x%llx, uid=%u}",
+ tag, *tag, get_uid_from_tag(*tag));
+ _bug_on_err_or_null(res);
+ return res;
+}
+
+char *pp_data_counters(struct data_counters *dc, bool showValues)
+{
+ char *res;
+
+ if (!dc)
+ res = kasprintf(GFP_ATOMIC, "data_counters@null{}");
+ else if (showValues)
+ res = kasprintf(
+ GFP_ATOMIC, "data_counters@%p{"
+ "set0{"
+ "rx{"
+ "tcp{b=%llu, p=%llu}, "
+ "udp{b=%llu, p=%llu},"
+ "other{b=%llu, p=%llu}}, "
+ "tx{"
+ "tcp{b=%llu, p=%llu}, "
+ "udp{b=%llu, p=%llu},"
+ "other{b=%llu, p=%llu}}}, "
+ "set1{"
+ "rx{"
+ "tcp{b=%llu, p=%llu}, "
+ "udp{b=%llu, p=%llu},"
+ "other{b=%llu, p=%llu}}, "
+ "tx{"
+ "tcp{b=%llu, p=%llu}, "
+ "udp{b=%llu, p=%llu},"
+ "other{b=%llu, p=%llu}}}}",
+ dc,
+ dc->bpc[0][IFS_RX][IFS_TCP].bytes,
+ dc->bpc[0][IFS_RX][IFS_TCP].packets,
+ dc->bpc[0][IFS_RX][IFS_UDP].bytes,
+ dc->bpc[0][IFS_RX][IFS_UDP].packets,
+ dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].bytes,
+ dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].packets,
+ dc->bpc[0][IFS_TX][IFS_TCP].bytes,
+ dc->bpc[0][IFS_TX][IFS_TCP].packets,
+ dc->bpc[0][IFS_TX][IFS_UDP].bytes,
+ dc->bpc[0][IFS_TX][IFS_UDP].packets,
+ dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].bytes,
+ dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].packets,
+ dc->bpc[1][IFS_RX][IFS_TCP].bytes,
+ dc->bpc[1][IFS_RX][IFS_TCP].packets,
+ dc->bpc[1][IFS_RX][IFS_UDP].bytes,
+ dc->bpc[1][IFS_RX][IFS_UDP].packets,
+ dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].bytes,
+ dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].packets,
+ dc->bpc[1][IFS_TX][IFS_TCP].bytes,
+ dc->bpc[1][IFS_TX][IFS_TCP].packets,
+ dc->bpc[1][IFS_TX][IFS_UDP].bytes,
+ dc->bpc[1][IFS_TX][IFS_UDP].packets,
+ dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes,
+ dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets);
+ else
+ res = kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc);
+ _bug_on_err_or_null(res);
+ return res;
+}
+
+char *pp_tag_node(struct tag_node *tn)
+{
+ char *tag_str;
+ char *res;
+
+ if (!tn) {
+ res = kasprintf(GFP_ATOMIC, "tag_node@null{}");
+ _bug_on_err_or_null(res);
+ return res;
+ }
+ tag_str = pp_tag_t(&tn->tag);
+ res = kasprintf(GFP_ATOMIC,
+ "tag_node@%p{tag=%s}",
+ tn, tag_str);
+ _bug_on_err_or_null(res);
+ kfree(tag_str);
+ return res;
+}
+
+char *pp_tag_ref(struct tag_ref *tr)
+{
+ char *tn_str;
+ char *res;
+
+ if (!tr) {
+ res = kasprintf(GFP_ATOMIC, "tag_ref@null{}");
+ _bug_on_err_or_null(res);
+ return res;
+ }
+ tn_str = pp_tag_node(&tr->tn);
+ res = kasprintf(GFP_ATOMIC,
+ "tag_ref@%p{%s, num_sock_tags=%d}",
+ tr, tn_str, tr->num_sock_tags);
+ _bug_on_err_or_null(res);
+ kfree(tn_str);
+ return res;
+}
+
+char *pp_tag_stat(struct tag_stat *ts)
+{
+ char *tn_str;
+ char *counters_str;
+ char *parent_counters_str;
+ char *res;
+
+ if (!ts) {
+ res = kasprintf(GFP_ATOMIC, "tag_stat@null{}");
+ _bug_on_err_or_null(res);
+ return res;
+ }
+ tn_str = pp_tag_node(&ts->tn);
+ counters_str = pp_data_counters(&ts->counters, true);
+ parent_counters_str = pp_data_counters(ts->parent_counters, false);
+ res = kasprintf(GFP_ATOMIC,
+ "tag_stat@%p{%s, counters=%s, parent_counters=%s}",
+ ts, tn_str, counters_str, parent_counters_str);
+ _bug_on_err_or_null(res);
+ kfree(tn_str);
+ kfree(counters_str);
+ kfree(parent_counters_str);
+ return res;
+}
+
+char *pp_iface_stat(struct iface_stat *is)
+{
+ char *res;
+ if (!is) {
+ res = kasprintf(GFP_ATOMIC, "iface_stat@null{}");
+ } else {
+ struct data_counters *cnts = &is->totals_via_skb;
+ res = kasprintf(GFP_ATOMIC, "iface_stat@%p{"
+ "list=list_head{...}, "
+ "ifname=%s, "
+ "total_dev={rx={bytes=%llu, "
+ "packets=%llu}, "
+ "tx={bytes=%llu, "
+ "packets=%llu}}, "
+ "total_skb={rx={bytes=%llu, "
+ "packets=%llu}, "
+ "tx={bytes=%llu, "
+ "packets=%llu}}, "
+ "last_known_valid=%d, "
+ "last_known={rx={bytes=%llu, "
+ "packets=%llu}, "
+ "tx={bytes=%llu, "
+ "packets=%llu}}, "
+ "active=%d, "
+ "net_dev=%p, "
+ "proc_ptr=%p, "
+ "tag_stat_tree=rb_root{...}}",
+ is,
+ is->ifname,
+ is->totals_via_dev[IFS_RX].bytes,
+ is->totals_via_dev[IFS_RX].packets,
+ is->totals_via_dev[IFS_TX].bytes,
+ is->totals_via_dev[IFS_TX].packets,
+ dc_sum_bytes(cnts, 0, IFS_RX),
+ dc_sum_packets(cnts, 0, IFS_RX),
+ dc_sum_bytes(cnts, 0, IFS_TX),
+ dc_sum_packets(cnts, 0, IFS_TX),
+ is->last_known_valid,
+ is->last_known[IFS_RX].bytes,
+ is->last_known[IFS_RX].packets,
+ is->last_known[IFS_TX].bytes,
+ is->last_known[IFS_TX].packets,
+ is->active,
+ is->net_dev,
+ is->proc_ptr);
+ }
+ _bug_on_err_or_null(res);
+ return res;
+}
+
+char *pp_sock_tag(struct sock_tag *st)
+{
+ char *tag_str;
+ char *res;
+
+ if (!st) {
+ res = kasprintf(GFP_ATOMIC, "sock_tag@null{}");
+ _bug_on_err_or_null(res);
+ return res;
+ }
+ tag_str = pp_tag_t(&st->tag);
+ res = kasprintf(GFP_ATOMIC, "sock_tag@%p{"
+ "sock_node=rb_node{...}, "
+ "sk=%p socket=%p (f_count=%lu), list=list_head{...}, "
+ "pid=%u, tag=%s}",
+ st, st->sk, st->socket, atomic_long_read(
+ &st->socket->file->f_count),
+ st->pid, tag_str);
+ _bug_on_err_or_null(res);
+ kfree(tag_str);
+ return res;
+}
+
+char *pp_uid_tag_data(struct uid_tag_data *utd)
+{
+ char *res;
+
+ if (!utd)
+ res = kasprintf(GFP_ATOMIC, "uid_tag_data@null{}");
+ else
+ res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{"
+ "uid=%u, num_active_acct_tags=%d, "
+ "num_pqd=%d, "
+ "tag_node_tree=rb_root{...}, "
+ "proc_qtu_data_tree=rb_root{...}}",
+ utd, utd->uid,
+ utd->num_active_tags, utd->num_pqd);
+ _bug_on_err_or_null(res);
+ return res;
+}
+
+char *pp_proc_qtu_data(struct proc_qtu_data *pqd)
+{
+ char *parent_tag_data_str;
+ char *res;
+
+ if (!pqd) {
+ res = kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}");
+ _bug_on_err_or_null(res);
+ return res;
+ }
+ parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data);
+ res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{"
+ "node=rb_node{...}, pid=%u, "
+ "parent_tag_data=%s, "
+ "sock_tag_list=list_head{...}}",
+ pqd, pqd->pid, parent_tag_data_str
+ );
+ _bug_on_err_or_null(res);
+ kfree(parent_tag_data_str);
+ return res;
+}
+
+/*------------------------------------------*/
+void prdebug_sock_tag_tree(int indent_level,
+ struct rb_root *sock_tag_tree)
+{
+ struct rb_node *node;
+ struct sock_tag *sock_tag_entry;
+ char *str;
+
+ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+ return;
+
+ if (RB_EMPTY_ROOT(sock_tag_tree)) {
+ str = "sock_tag_tree=rb_root{}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ return;
+ }
+
+ str = "sock_tag_tree=rb_root{";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ indent_level++;
+ for (node = rb_first(sock_tag_tree);
+ node;
+ node = rb_next(node)) {
+ sock_tag_entry = rb_entry(node, struct sock_tag, sock_node);
+ str = pp_sock_tag(sock_tag_entry);
+ pr_debug("%*d: %s,\n", indent_level*2, indent_level, str);
+ kfree(str);
+ }
+ indent_level--;
+ str = "}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+}
+
+void prdebug_sock_tag_list(int indent_level,
+ struct list_head *sock_tag_list)
+{
+ struct sock_tag *sock_tag_entry;
+ char *str;
+
+ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+ return;
+
+ if (list_empty(sock_tag_list)) {
+ str = "sock_tag_list=list_head{}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ return;
+ }
+
+ str = "sock_tag_list=list_head{";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ indent_level++;
+ list_for_each_entry(sock_tag_entry, sock_tag_list, list) {
+ str = pp_sock_tag(sock_tag_entry);
+ pr_debug("%*d: %s,\n", indent_level*2, indent_level, str);
+ kfree(str);
+ }
+ indent_level--;
+ str = "}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+}
+
+void prdebug_proc_qtu_data_tree(int indent_level,
+ struct rb_root *proc_qtu_data_tree)
+{
+ char *str;
+ struct rb_node *node;
+ struct proc_qtu_data *proc_qtu_data_entry;
+
+ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+ return;
+
+ if (RB_EMPTY_ROOT(proc_qtu_data_tree)) {
+ str = "proc_qtu_data_tree=rb_root{}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ return;
+ }
+
+ str = "proc_qtu_data_tree=rb_root{";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ indent_level++;
+ for (node = rb_first(proc_qtu_data_tree);
+ node;
+ node = rb_next(node)) {
+ proc_qtu_data_entry = rb_entry(node,
+ struct proc_qtu_data,
+ node);
+ str = pp_proc_qtu_data(proc_qtu_data_entry);
+ pr_debug("%*d: %s,\n", indent_level*2, indent_level,
+ str);
+ kfree(str);
+ indent_level++;
+ prdebug_sock_tag_list(indent_level,
+ &proc_qtu_data_entry->sock_tag_list);
+ indent_level--;
+
+ }
+ indent_level--;
+ str = "}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+}
+
+void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree)
+{
+ char *str;
+ struct rb_node *node;
+ struct tag_ref *tag_ref_entry;
+
+ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+ return;
+
+ if (RB_EMPTY_ROOT(tag_ref_tree)) {
+ str = "tag_ref_tree{}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ return;
+ }
+
+ str = "tag_ref_tree{";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ indent_level++;
+ for (node = rb_first(tag_ref_tree);
+ node;
+ node = rb_next(node)) {
+ tag_ref_entry = rb_entry(node,
+ struct tag_ref,
+ tn.node);
+ str = pp_tag_ref(tag_ref_entry);
+ pr_debug("%*d: %s,\n", indent_level*2, indent_level,
+ str);
+ kfree(str);
+ }
+ indent_level--;
+ str = "}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+}
+
+void prdebug_uid_tag_data_tree(int indent_level,
+ struct rb_root *uid_tag_data_tree)
+{
+ char *str;
+ struct rb_node *node;
+ struct uid_tag_data *uid_tag_data_entry;
+
+ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+ return;
+
+ if (RB_EMPTY_ROOT(uid_tag_data_tree)) {
+ str = "uid_tag_data_tree=rb_root{}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ return;
+ }
+
+ str = "uid_tag_data_tree=rb_root{";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ indent_level++;
+ for (node = rb_first(uid_tag_data_tree);
+ node;
+ node = rb_next(node)) {
+ uid_tag_data_entry = rb_entry(node, struct uid_tag_data,
+ node);
+ str = pp_uid_tag_data(uid_tag_data_entry);
+ pr_debug("%*d: %s,\n", indent_level*2, indent_level, str);
+ kfree(str);
+ if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) {
+ indent_level++;
+ prdebug_tag_ref_tree(indent_level,
+ &uid_tag_data_entry->tag_ref_tree);
+ indent_level--;
+ }
+ }
+ indent_level--;
+ str = "}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+}
+
+void prdebug_tag_stat_tree(int indent_level,
+ struct rb_root *tag_stat_tree)
+{
+ char *str;
+ struct rb_node *node;
+ struct tag_stat *ts_entry;
+
+ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+ return;
+
+ if (RB_EMPTY_ROOT(tag_stat_tree)) {
+ str = "tag_stat_tree{}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ return;
+ }
+
+ str = "tag_stat_tree{";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ indent_level++;
+ for (node = rb_first(tag_stat_tree);
+ node;
+ node = rb_next(node)) {
+ ts_entry = rb_entry(node, struct tag_stat, tn.node);
+ str = pp_tag_stat(ts_entry);
+ pr_debug("%*d: %s\n", indent_level*2, indent_level,
+ str);
+ kfree(str);
+ }
+ indent_level--;
+ str = "}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+}
+
+void prdebug_iface_stat_list(int indent_level,
+ struct list_head *iface_stat_list)
+{
+ char *str;
+ struct iface_stat *iface_entry;
+
+ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK))
+ return;
+
+ if (list_empty(iface_stat_list)) {
+ str = "iface_stat_list=list_head{}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ return;
+ }
+
+ str = "iface_stat_list=list_head{";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ indent_level++;
+ list_for_each_entry(iface_entry, iface_stat_list, list) {
+ str = pp_iface_stat(iface_entry);
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+ kfree(str);
+
+ spin_lock_bh(&iface_entry->tag_stat_list_lock);
+ if (!RB_EMPTY_ROOT(&iface_entry->tag_stat_tree)) {
+ indent_level++;
+ prdebug_tag_stat_tree(indent_level,
+ &iface_entry->tag_stat_tree);
+ indent_level--;
+ }
+ spin_unlock_bh(&iface_entry->tag_stat_list_lock);
+ }
+ indent_level--;
+ str = "}";
+ pr_debug("%*d: %s\n", indent_level*2, indent_level, str);
+}
+
+#endif /* ifdef DDEBUG */
+/*------------------------------------------*/
+static const char * const netdev_event_strings[] = {
+ "netdev_unknown",
+ "NETDEV_UP",
+ "NETDEV_DOWN",
+ "NETDEV_REBOOT",
+ "NETDEV_CHANGE",
+ "NETDEV_REGISTER",
+ "NETDEV_UNREGISTER",
+ "NETDEV_CHANGEMTU",
+ "NETDEV_CHANGEADDR",
+ "NETDEV_GOING_DOWN",
+ "NETDEV_CHANGENAME",
+ "NETDEV_FEAT_CHANGE",
+ "NETDEV_BONDING_FAILOVER",
+ "NETDEV_PRE_UP",
+ "NETDEV_PRE_TYPE_CHANGE",
+ "NETDEV_POST_TYPE_CHANGE",
+ "NETDEV_POST_INIT",
+ "NETDEV_UNREGISTER_BATCH",
+ "NETDEV_RELEASE",
+ "NETDEV_NOTIFY_PEERS",
+ "NETDEV_JOIN",
+};
+
+const char *netdev_evt_str(int netdev_event)
+{
+ if (netdev_event < 0
+ || netdev_event >= ARRAY_SIZE(netdev_event_strings))
+ return "bad event num";
+ return netdev_event_strings[netdev_event];
+}
diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h
new file mode 100644
index 000000000000..b63871a0be5a
--- /dev/null
+++ b/net/netfilter/xt_qtaguid_print.h
@@ -0,0 +1,120 @@
+/*
+ * Pretty printing Support for iptables xt_qtaguid module.
+ *
+ * (C) 2011 Google, 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 __XT_QTAGUID_PRINT_H__
+#define __XT_QTAGUID_PRINT_H__
+
+#include "xt_qtaguid_internal.h"
+
+#ifdef DDEBUG
+
+char *pp_tag_t(tag_t *tag);
+char *pp_data_counters(struct data_counters *dc, bool showValues);
+char *pp_tag_node(struct tag_node *tn);
+char *pp_tag_ref(struct tag_ref *tr);
+char *pp_tag_stat(struct tag_stat *ts);
+char *pp_iface_stat(struct iface_stat *is);
+char *pp_sock_tag(struct sock_tag *st);
+char *pp_uid_tag_data(struct uid_tag_data *qtd);
+char *pp_proc_qtu_data(struct proc_qtu_data *pqd);
+
+/*------------------------------------------*/
+void prdebug_sock_tag_list(int indent_level,
+ struct list_head *sock_tag_list);
+void prdebug_sock_tag_tree(int indent_level,
+ struct rb_root *sock_tag_tree);
+void prdebug_proc_qtu_data_tree(int indent_level,
+ struct rb_root *proc_qtu_data_tree);
+void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree);
+void prdebug_uid_tag_data_tree(int indent_level,
+ struct rb_root *uid_tag_data_tree);
+void prdebug_tag_stat_tree(int indent_level,
+ struct rb_root *tag_stat_tree);
+void prdebug_iface_stat_list(int indent_level,
+ struct list_head *iface_stat_list);
+
+#else
+
+/*------------------------------------------*/
+static inline char *pp_tag_t(tag_t *tag)
+{
+ return NULL;
+}
+static inline char *pp_data_counters(struct data_counters *dc, bool showValues)
+{
+ return NULL;
+}
+static inline char *pp_tag_node(struct tag_node *tn)
+{
+ return NULL;
+}
+static inline char *pp_tag_ref(struct tag_ref *tr)
+{
+ return NULL;
+}
+static inline char *pp_tag_stat(struct tag_stat *ts)
+{
+ return NULL;
+}
+static inline char *pp_iface_stat(struct iface_stat *is)
+{
+ return NULL;
+}
+static inline char *pp_sock_tag(struct sock_tag *st)
+{
+ return NULL;
+}
+static inline char *pp_uid_tag_data(struct uid_tag_data *qtd)
+{
+ return NULL;
+}
+static inline char *pp_proc_qtu_data(struct proc_qtu_data *pqd)
+{
+ return NULL;
+}
+
+/*------------------------------------------*/
+static inline
+void prdebug_sock_tag_list(int indent_level,
+ struct list_head *sock_tag_list)
+{
+}
+static inline
+void prdebug_sock_tag_tree(int indent_level,
+ struct rb_root *sock_tag_tree)
+{
+}
+static inline
+void prdebug_proc_qtu_data_tree(int indent_level,
+ struct rb_root *proc_qtu_data_tree)
+{
+}
+static inline
+void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree)
+{
+}
+static inline
+void prdebug_uid_tag_data_tree(int indent_level,
+ struct rb_root *uid_tag_data_tree)
+{
+}
+static inline
+void prdebug_tag_stat_tree(int indent_level,
+ struct rb_root *tag_stat_tree)
+{
+}
+static inline
+void prdebug_iface_stat_list(int indent_level,
+ struct list_head *iface_stat_list)
+{
+}
+#endif
+/*------------------------------------------*/
+const char *netdev_evt_str(int netdev_event);
+#endif /* ifndef __XT_QTAGUID_PRINT_H__ */
diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c
new file mode 100644
index 000000000000..00cc65b10298
--- /dev/null
+++ b/net/netfilter/xt_quota2.c
@@ -0,0 +1,391 @@
+/*
+ * xt_quota2 - enhanced xt_quota that can count upwards and in packets
+ * as a minimal accounting match.
+ * by Jan Engelhardt <jengelh@medozas.de>, 2008
+ *
+ * Originally based on xt_quota.c:
+ * netfilter module to enforce network quotas
+ * Sam Johnston <samj@samj.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License; either
+ * version 2 of the License, as published by the Free Software Foundation.
+ */
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#include <net/netlink.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_quota2.h>
+#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
+#include <linux/netfilter_ipv4/ipt_ULOG.h>
+#endif
+
+/**
+ * @lock: lock to protect quota writers from each other
+ */
+struct xt_quota_counter {
+ u_int64_t quota;
+ spinlock_t lock;
+ struct list_head list;
+ atomic_t ref;
+ char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)];
+ struct proc_dir_entry *procfs_entry;
+};
+
+#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
+/* Harald's favorite number +1 :D From ipt_ULOG.C */
+static int qlog_nl_event = 112;
+module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(event_num,
+ "Event number for NETLINK_NFLOG message. 0 disables log."
+ "111 is what ipt_ULOG uses.");
+static struct sock *nflognl;
+#endif
+
+static LIST_HEAD(counter_list);
+static DEFINE_SPINLOCK(counter_list_lock);
+
+static struct proc_dir_entry *proc_xt_quota;
+static unsigned int quota_list_perms = S_IRUGO | S_IWUSR;
+static kuid_t quota_list_uid;
+static kgid_t quota_list_gid;
+static unsigned int quota_list_uid_param;
+static unsigned int quota_list_gid_param;
+module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR);
+module_param_named(uid, quota_list_uid_param, uint, S_IRUGO | S_IWUSR);
+module_param_named(gid, quota_list_gid_param, uint, S_IRUGO | S_IWUSR);
+
+
+#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
+static void quota2_log(unsigned int hooknum,
+ const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const char *prefix)
+{
+ ulog_packet_msg_t *pm;
+ struct sk_buff *log_skb;
+ size_t size;
+ struct nlmsghdr *nlh;
+
+ if (!qlog_nl_event)
+ return;
+
+ size = NLMSG_SPACE(sizeof(*pm));
+ size = max(size, (size_t)NLMSG_GOODSIZE);
+ log_skb = alloc_skb(size, GFP_ATOMIC);
+ if (!log_skb) {
+ pr_err("xt_quota2: cannot alloc skb for logging\n");
+ return;
+ }
+
+ nlh = nlmsg_put(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event,
+ sizeof(*pm), 0);
+ if (!nlh) {
+ pr_err("xt_quota2: nlmsg_put failed\n");
+ kfree_skb(log_skb);
+ return;
+ }
+ pm = nlmsg_data(nlh);
+ if (skb->tstamp.tv64 == 0)
+ __net_timestamp((struct sk_buff *)skb);
+ pm->data_len = 0;
+ pm->hook = hooknum;
+ if (prefix != NULL)
+ strlcpy(pm->prefix, prefix, sizeof(pm->prefix));
+ else
+ *(pm->prefix) = '\0';
+ if (in)
+ strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name));
+ else
+ pm->indev_name[0] = '\0';
+
+ if (out)
+ strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name));
+ else
+ pm->outdev_name[0] = '\0';
+
+ NETLINK_CB(log_skb).dst_group = 1;
+ pr_debug("throwing 1 packets to netlink group 1\n");
+ netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC);
+}
+#else
+static void quota2_log(unsigned int hooknum,
+ const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const char *prefix)
+{
+}
+#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */
+
+static ssize_t quota_proc_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct xt_quota_counter *e = PDE_DATA(file_inode(file));
+ char tmp[24];
+ size_t tmp_size;
+
+ spin_lock_bh(&e->lock);
+ tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", e->quota);
+ spin_unlock_bh(&e->lock);
+ return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size);
+}
+
+static ssize_t quota_proc_write(struct file *file, const char __user *input,
+ size_t size, loff_t *ppos)
+{
+ struct xt_quota_counter *e = PDE_DATA(file_inode(file));
+ char buf[sizeof("18446744073709551616")];
+
+ if (size > sizeof(buf))
+ size = sizeof(buf);
+ if (copy_from_user(buf, input, size) != 0)
+ return -EFAULT;
+ buf[sizeof(buf)-1] = '\0';
+
+ spin_lock_bh(&e->lock);
+ e->quota = simple_strtoull(buf, NULL, 0);
+ spin_unlock_bh(&e->lock);
+ return size;
+}
+
+static const struct file_operations q2_counter_fops = {
+ .read = quota_proc_read,
+ .write = quota_proc_write,
+ .llseek = default_llseek,
+};
+
+static struct xt_quota_counter *
+q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon)
+{
+ struct xt_quota_counter *e;
+ unsigned int size;
+
+ /* Do not need all the procfs things for anonymous counters. */
+ size = anon ? offsetof(typeof(*e), list) : sizeof(*e);
+ e = kmalloc(size, GFP_KERNEL);
+ if (e == NULL)
+ return NULL;
+
+ e->quota = q->quota;
+ spin_lock_init(&e->lock);
+ if (!anon) {
+ INIT_LIST_HEAD(&e->list);
+ atomic_set(&e->ref, 1);
+ strlcpy(e->name, q->name, sizeof(e->name));
+ }
+ return e;
+}
+
+/**
+ * q2_get_counter - get ref to counter or create new
+ * @name: name of counter
+ */
+static struct xt_quota_counter *
+q2_get_counter(const struct xt_quota_mtinfo2 *q)
+{
+ struct proc_dir_entry *p;
+ struct xt_quota_counter *e = NULL;
+ struct xt_quota_counter *new_e;
+
+ if (*q->name == '\0')
+ return q2_new_counter(q, true);
+
+ /* No need to hold a lock while getting a new counter */
+ new_e = q2_new_counter(q, false);
+ if (new_e == NULL)
+ goto out;
+
+ spin_lock_bh(&counter_list_lock);
+ list_for_each_entry(e, &counter_list, list)
+ if (strcmp(e->name, q->name) == 0) {
+ atomic_inc(&e->ref);
+ spin_unlock_bh(&counter_list_lock);
+ kfree(new_e);
+ pr_debug("xt_quota2: old counter name=%s", e->name);
+ return e;
+ }
+ e = new_e;
+ pr_debug("xt_quota2: new_counter name=%s", e->name);
+ list_add_tail(&e->list, &counter_list);
+ /* The entry having a refcount of 1 is not directly destructible.
+ * This func has not yet returned the new entry, thus iptables
+ * has not references for destroying this entry.
+ * For another rule to try to destroy it, it would 1st need for this
+ * func* to be re-invoked, acquire a new ref for the same named quota.
+ * Nobody will access the e->procfs_entry either.
+ * So release the lock. */
+ spin_unlock_bh(&counter_list_lock);
+
+ /* create_proc_entry() is not spin_lock happy */
+ p = e->procfs_entry = proc_create_data(e->name, quota_list_perms,
+ proc_xt_quota, &q2_counter_fops, e);
+
+ if (IS_ERR_OR_NULL(p)) {
+ spin_lock_bh(&counter_list_lock);
+ list_del(&e->list);
+ spin_unlock_bh(&counter_list_lock);
+ goto out;
+ }
+ proc_set_user(p, quota_list_uid, quota_list_gid);
+ return e;
+
+ out:
+ kfree(e);
+ return NULL;
+}
+
+static int quota_mt2_check(const struct xt_mtchk_param *par)
+{
+ struct xt_quota_mtinfo2 *q = par->matchinfo;
+
+ pr_debug("xt_quota2: check() flags=0x%04x", q->flags);
+
+ if (q->flags & ~XT_QUOTA_MASK)
+ return -EINVAL;
+
+ q->name[sizeof(q->name)-1] = '\0';
+ if (*q->name == '.' || strchr(q->name, '/') != NULL) {
+ printk(KERN_ERR "xt_quota.3: illegal name\n");
+ return -EINVAL;
+ }
+
+ q->master = q2_get_counter(q);
+ if (q->master == NULL) {
+ printk(KERN_ERR "xt_quota.3: memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void quota_mt2_destroy(const struct xt_mtdtor_param *par)
+{
+ struct xt_quota_mtinfo2 *q = par->matchinfo;
+ struct xt_quota_counter *e = q->master;
+
+ if (*q->name == '\0') {
+ kfree(e);
+ return;
+ }
+
+ spin_lock_bh(&counter_list_lock);
+ if (!atomic_dec_and_test(&e->ref)) {
+ spin_unlock_bh(&counter_list_lock);
+ return;
+ }
+
+ list_del(&e->list);
+ remove_proc_entry(e->name, proc_xt_quota);
+ spin_unlock_bh(&counter_list_lock);
+ kfree(e);
+}
+
+static bool
+quota_mt2(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ struct xt_quota_mtinfo2 *q = (void *)par->matchinfo;
+ struct xt_quota_counter *e = q->master;
+ bool ret = q->flags & XT_QUOTA_INVERT;
+
+ spin_lock_bh(&e->lock);
+ if (q->flags & XT_QUOTA_GROW) {
+ /*
+ * While no_change is pointless in "grow" mode, we will
+ * implement it here simply to have a consistent behavior.
+ */
+ if (!(q->flags & XT_QUOTA_NO_CHANGE)) {
+ e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len;
+ }
+ ret = true;
+ } else {
+ if (e->quota >= skb->len) {
+ if (!(q->flags & XT_QUOTA_NO_CHANGE))
+ e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len;
+ ret = !ret;
+ } else {
+ /* We are transitioning, log that fact. */
+ if (e->quota) {
+ quota2_log(par->hooknum,
+ skb,
+ par->in,
+ par->out,
+ q->name);
+ }
+ /* we do not allow even small packets from now on */
+ e->quota = 0;
+ }
+ }
+ spin_unlock_bh(&e->lock);
+ return ret;
+}
+
+static struct xt_match quota_mt2_reg[] __read_mostly = {
+ {
+ .name = "quota2",
+ .revision = 3,
+ .family = NFPROTO_IPV4,
+ .checkentry = quota_mt2_check,
+ .match = quota_mt2,
+ .destroy = quota_mt2_destroy,
+ .matchsize = sizeof(struct xt_quota_mtinfo2),
+ .me = THIS_MODULE,
+ },
+ {
+ .name = "quota2",
+ .revision = 3,
+ .family = NFPROTO_IPV6,
+ .checkentry = quota_mt2_check,
+ .match = quota_mt2,
+ .destroy = quota_mt2_destroy,
+ .matchsize = sizeof(struct xt_quota_mtinfo2),
+ .me = THIS_MODULE,
+ },
+};
+
+static int __init quota_mt2_init(void)
+{
+ int ret;
+ pr_debug("xt_quota2: init()");
+
+ quota_list_uid = KUIDT_INIT(quota_list_uid_param);
+ quota_list_gid = KGIDT_INIT(quota_list_gid_param);
+
+
+#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
+ nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL);
+ if (!nflognl)
+ return -ENOMEM;
+#endif
+
+ proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net);
+ if (proc_xt_quota == NULL)
+ return -EACCES;
+
+ ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
+ if (ret < 0)
+ remove_proc_entry("xt_quota", init_net.proc_net);
+ pr_debug("xt_quota2: init() %d", ret);
+ return ret;
+}
+
+static void __exit quota_mt2_exit(void)
+{
+ xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
+ remove_proc_entry("xt_quota", init_net.proc_net);
+}
+
+module_init(quota_mt2_init);
+module_exit(quota_mt2_exit);
+MODULE_DESCRIPTION("Xtables: countdown quota match; up counter");
+MODULE_AUTHOR("Sam Johnston <samj@samj.net>");
+MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_quota2");
+MODULE_ALIAS("ip6t_quota2");
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c
index 1ba67931eb1b..7eb9d7d534d6 100644
--- a/net/netfilter/xt_socket.c
+++ b/net/netfilter/xt_socket.c
@@ -129,9 +129,10 @@ xt_socket_get_sock_v4(struct net *net, const u8 protocol,
return NULL;
}
-static bool
-socket_match(const struct sk_buff *skb, struct xt_action_param *par,
- const struct xt_socket_mtinfo1 *info)
+
+
+struct sock*
+xt_socket_get4_sk(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct iphdr *iph = ip_hdr(skb);
struct udphdr _hdr, *hp = NULL;
@@ -148,7 +149,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
hp = skb_header_pointer(skb, ip_hdrlen(skb),
sizeof(_hdr), &_hdr);
if (hp == NULL)
- return false;
+ return NULL;
protocol = iph->protocol;
saddr = iph->saddr;
@@ -159,9 +160,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
} else if (iph->protocol == IPPROTO_ICMP) {
if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr,
&sport, &dport))
- return false;
+ return NULL;
} else {
- return false;
+ return NULL;
}
#ifdef XT_SOCKET_HAVE_CONNTRACK
@@ -187,6 +188,24 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol,
saddr, daddr, sport, dport,
par->in);
+
+ pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n",
+ protocol, &saddr, ntohs(sport),
+ &daddr, ntohs(dport),
+ &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
+
+ return sk;
+}
+EXPORT_SYMBOL(xt_socket_get4_sk);
+
+static bool
+socket_match(const struct sk_buff *skb, struct xt_action_param *par,
+ const struct xt_socket_mtinfo1 *info)
+{
+ struct sock *sk;
+
+ sk = xt_socket_get4_sk(skb, par);
+
if (sk) {
bool wildcard;
bool transparent = true;
@@ -213,11 +232,6 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
sk = NULL;
}
- pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n",
- protocol, &saddr, ntohs(sport),
- &daddr, ntohs(dport),
- &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
-
return (sk != NULL);
}
@@ -312,8 +326,8 @@ xt_socket_get_sock_v6(struct net *net, const u8 protocol,
return NULL;
}
-static bool
-socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
+struct sock*
+xt_socket_get6_sk(const struct sk_buff *skb, struct xt_action_param *par)
{
struct ipv6hdr *iph = ipv6_hdr(skb);
struct udphdr _hdr, *hp = NULL;
@@ -321,7 +335,6 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
struct in6_addr *daddr = NULL, *saddr = NULL;
__be16 uninitialized_var(dport), uninitialized_var(sport);
int thoff = 0, uninitialized_var(tproto);
- const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
if (tproto < 0) {
@@ -333,7 +346,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
hp = skb_header_pointer(skb, thoff,
sizeof(_hdr), &_hdr);
if (hp == NULL)
- return false;
+ return NULL;
saddr = &iph->saddr;
sport = hp->source;
@@ -343,15 +356,35 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
} else if (tproto == IPPROTO_ICMPV6) {
if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr,
&sport, &dport))
- return false;
+ return NULL;
} else {
- return false;
+ return NULL;
}
if (!sk)
sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto,
saddr, daddr, sport, dport,
par->in);
+
+ pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu "
+ "(orig %pI6:%hu) sock %p\n",
+ tproto, saddr, ntohs(sport),
+ daddr, ntohs(dport),
+ &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
+ return sk;
+}
+EXPORT_SYMBOL(xt_socket_get6_sk);
+
+static bool
+socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ struct sock *sk;
+ const struct xt_socket_mtinfo1 *info;
+
+ info = (struct xt_socket_mtinfo1 *) par->matchinfo;
+
+ sk = xt_socket_get6_sk(skb, par);
+
if (sk) {
bool wildcard;
bool transparent = true;
@@ -378,12 +411,6 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
sk = NULL;
}
- pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu "
- "(orig %pI6:%hu) sock %p\n",
- tproto, saddr, ntohs(sport),
- daddr, ntohs(dport),
- &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
-
return (sk != NULL);
}
#endif
diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig
index 4c10e7e6c9f6..77aa1dfc3f88 100644
--- a/net/rfkill/Kconfig
+++ b/net/rfkill/Kconfig
@@ -10,6 +10,11 @@ menuconfig RFKILL
To compile this driver as a module, choose M here: the
module will be called rfkill.
+config RFKILL_PM
+ bool "Power off on suspend"
+ depends on RFKILL && PM
+ default y
+
# LED trigger support
config RFKILL_LEDS
bool
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index b3b16c070a7f..9a3f14b3540f 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -782,6 +782,7 @@ void rfkill_pause_polling(struct rfkill *rfkill)
}
EXPORT_SYMBOL(rfkill_pause_polling);
+#ifdef CONFIG_RFKILL_PM
void rfkill_resume_polling(struct rfkill *rfkill)
{
BUG_ON(!rfkill);
@@ -817,14 +818,17 @@ static int rfkill_resume(struct device *dev)
return 0;
}
+#endif
static struct class rfkill_class = {
.name = "rfkill",
.dev_release = rfkill_release,
.dev_groups = rfkill_dev_groups,
.dev_uevent = rfkill_dev_uevent,
+#ifdef CONFIG_RFKILL_PM
.suspend = rfkill_suspend,
.resume = rfkill_resume,
+#endif
};
bool rfkill_blocked(struct rfkill *rfkill)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 0798c62e6085..be4671a7a0f9 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -55,7 +55,7 @@
* also linked into the probe response struct.
*/
-#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
+#define IEEE80211_SCAN_RESULT_EXPIRE (3 * HZ)
static void bss_free(struct cfg80211_internal_bss *bss)
{
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 260bf8acfce9..6874d08688a2 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -153,6 +153,7 @@ dtc_cpp_flags = -Wp,-MD,$(depfile).pre.tmp -nostdinc \
-I$(srctree)/arch/$(SRCARCH)/boot/dts \
-I$(srctree)/arch/$(SRCARCH)/boot/dts/include \
-I$(srctree)/drivers/of/testcase-data \
+ -I$(srctree)/include \
-undef -D__DTS__
# Finds the multi-part object the current object will be linked into
@@ -274,6 +275,12 @@ $(obj)/%.dtb: $(src)/%.dts FORCE
dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
+# cat
+# ---------------------------------------------------------------------------
+# Concatentate multiple files together
+quiet_cmd_cat = CAT $@
+cmd_cat = (cat $(filter-out FORCE,$^) > $@) || (rm -f $@; false)
+
# Helper targets for Installing DTBs into the boot directory
quiet_cmd_dtb_install = INSTALL $<
cmd_dtb_install = cp $< $(2)
diff --git a/scripts/package/builddeb b/scripts/package/builddeb
index 35d5a5877d04..0d64196f5b10 100644
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -101,6 +101,9 @@ um)
parisc|mips|powerpc)
installed_image_path="boot/vmlinux-$version"
;;
+arm64)
+ installed_image_path="boot/Image-$version"
+ ;;
*)
installed_image_path="boot/vmlinuz-$version"
esac
@@ -330,6 +333,17 @@ EOF
create_package "$fwpackagename" "$fwdir"
fi
+# Copy device tree files if generated
+stat arch/$ARCH/boot/dts/*.dtb && {
+ mkdir -p "$tmpdir/lib/firmware/$version/device-tree"
+ cp arch/$ARCH/boot/dts/*.dtb "$tmpdir/lib/firmware/$version/device-tree"
+}
+
+# Copy boot wrapper files if built
+if [ "$ARCH" = "arm64" ] ; then
+ stat arch/arm64/boot/*.axf && cp arch/arm64/boot/*.axf "$tmpdir/boot"
+fi
+
cat <<EOF >> debian/control
Package: $libc_headers_packagename
diff --git a/security/capability.c b/security/capability.c
index a74fde6a7468..78273999d097 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -12,6 +12,26 @@
#include <linux/security.h>
+static int cap_binder_set_context_mgr(struct task_struct *mgr)
+{
+ return 0;
+}
+
+static int cap_binder_transaction(struct task_struct *from, struct task_struct *to)
+{
+ return 0;
+}
+
+static int cap_binder_transfer_binder(struct task_struct *from, struct task_struct *to)
+{
+ return 0;
+}
+
+static int cap_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file)
+{
+ return 0;
+}
+
static int cap_syslog(int type)
{
return 0;
@@ -930,6 +950,10 @@ static void cap_audit_rule_free(void *lsmrule)
void __init security_fixup_ops(struct security_operations *ops)
{
+ set_to_cap_if_null(ops, binder_set_context_mgr);
+ set_to_cap_if_null(ops, binder_transaction);
+ set_to_cap_if_null(ops, binder_transfer_binder);
+ set_to_cap_if_null(ops, binder_transfer_file);
set_to_cap_if_null(ops, ptrace_access_check);
set_to_cap_if_null(ops, ptrace_traceme);
set_to_cap_if_null(ops, capget);
diff --git a/security/commoncap.c b/security/commoncap.c
index bab0611afc1e..7ae25198d8ef 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -31,6 +31,10 @@
#include <linux/binfmts.h>
#include <linux/personality.h>
+#ifdef CONFIG_ANDROID_PARANOID_NETWORK
+#include <linux/android_aid.h>
+#endif
+
/*
* If a non-root user executes a setuid-root binary in
* !secure(SECURE_NOROOT) mode, then we raise capabilities.
@@ -78,6 +82,13 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
{
struct user_namespace *ns = targ_ns;
+#ifdef CONFIG_ANDROID_PARANOID_NETWORK
+ if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW))
+ return 0;
+ if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN))
+ return 0;
+#endif
+
/* See if cred has the capability in the target user namespace
* by examining the target user namespace and all of the target
* user namespace's parents.
diff --git a/security/security.c b/security/security.c
index e41b1a8d7644..0a4947334997 100644
--- a/security/security.c
+++ b/security/security.c
@@ -135,6 +135,26 @@ int __init register_security(struct security_operations *ops)
/* Security operations */
+int security_binder_set_context_mgr(struct task_struct *mgr)
+{
+ return security_ops->binder_set_context_mgr(mgr);
+}
+
+int security_binder_transaction(struct task_struct *from, struct task_struct *to)
+{
+ return security_ops->binder_transaction(from, to);
+}
+
+int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to)
+{
+ return security_ops->binder_transfer_binder(from, to);
+}
+
+int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file)
+{
+ return security_ops->binder_transfer_file(from, to, file);
+}
+
int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
{
#ifdef CONFIG_SECURITY_YAMA_STACKED
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index b0e940497e23..35bfac5d8fa8 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1935,6 +1935,67 @@ static inline u32 open_file_to_av(struct file *file)
/* Hook functions begin here. */
+static int selinux_binder_set_context_mgr(struct task_struct *mgr)
+{
+ u32 mysid = current_sid();
+ u32 mgrsid = task_sid(mgr);
+
+ return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL);
+}
+
+static int selinux_binder_transaction(struct task_struct *from, struct task_struct *to)
+{
+ u32 mysid = current_sid();
+ u32 fromsid = task_sid(from);
+ u32 tosid = task_sid(to);
+ int rc;
+
+ if (mysid != fromsid) {
+ rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER, BINDER__IMPERSONATE, NULL);
+ if (rc)
+ return rc;
+ }
+
+ return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, NULL);
+}
+
+static int selinux_binder_transfer_binder(struct task_struct *from, struct task_struct *to)
+{
+ u32 fromsid = task_sid(from);
+ u32 tosid = task_sid(to);
+ return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, NULL);
+}
+
+static int selinux_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file)
+{
+ u32 sid = task_sid(to);
+ struct file_security_struct *fsec = file->f_security;
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode_security_struct *isec = inode->i_security;
+ struct common_audit_data ad;
+ struct selinux_audit_data sad = {0,};
+ int rc;
+
+ ad.type = LSM_AUDIT_DATA_PATH;
+ ad.u.path = file->f_path;
+ ad.selinux_audit_data = &sad;
+
+ if (sid != fsec->sid) {
+ rc = avc_has_perm(sid, fsec->sid,
+ SECCLASS_FD,
+ FD__USE,
+ &ad);
+ if (rc)
+ return rc;
+ }
+
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
+ return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
+ &ad);
+}
+
static int selinux_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
@@ -5775,6 +5836,11 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
static struct security_operations selinux_ops = {
.name = "selinux",
+ .binder_set_context_mgr = selinux_binder_set_context_mgr,
+ .binder_transaction = selinux_binder_transaction,
+ .binder_transfer_binder = selinux_binder_transfer_binder,
+ .binder_transfer_file = selinux_binder_transfer_file,
+
.ptrace_access_check = selinux_ptrace_access_check,
.ptrace_traceme = selinux_ptrace_traceme,
.capget = selinux_capget,
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index be491a74c1ed..bdc9325a4659 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -151,5 +151,6 @@ struct security_class_mapping secclass_map[] = {
{ "kernel_service", { "use_as_override", "create_files_as", NULL } },
{ "tun_socket",
{ COMMON_SOCK_PERMS, "attach_queue", NULL } },
+ { "binder", { "impersonate", "call", "set_context_mgr", "transfer", NULL } },
{ NULL }
};
diff --git a/tools/gator/Makefile b/tools/gator/Makefile
new file mode 100644
index 000000000000..4ab73b0ebedb
--- /dev/null
+++ b/tools/gator/Makefile
@@ -0,0 +1,4 @@
+all:
+ $(MAKE) -C daemon $@
+clean:
+ $(MAKE) -C daemon $@
diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk
new file mode 100644
index 000000000000..44c069cc7e24
--- /dev/null
+++ b/tools/gator/daemon/Android.mk
@@ -0,0 +1,65 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h)
+
+LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -pthread -DETCDIR=\"/etc\" -Ilibsensors
+
+LOCAL_SRC_FILES := \
+ Buffer.cpp \
+ CapturedXML.cpp \
+ Child.cpp \
+ ConfigurationXML.cpp \
+ Driver.cpp \
+ DriverSource.cpp \
+ DynBuf.cpp \
+ EventsXML.cpp \
+ ExternalSource.cpp \
+ FSDriver.cpp \
+ Fifo.cpp \
+ Hwmon.cpp \
+ KMod.cpp \
+ LocalCapture.cpp \
+ Logging.cpp \
+ main.cpp \
+ MaliVideoDriver.cpp \
+ Monitor.cpp \
+ OlySocket.cpp \
+ OlyUtility.cpp \
+ PerfBuffer.cpp \
+ PerfDriver.cpp \
+ PerfGroup.cpp \
+ PerfSource.cpp \
+ Proc.cpp \
+ Sender.cpp \
+ SessionData.cpp \
+ SessionXML.cpp \
+ Source.cpp \
+ StreamlineSetup.cpp \
+ UEvent.cpp \
+ UserSpaceSource.cpp \
+ libsensors/access.c \
+ libsensors/conf-lex.c \
+ libsensors/conf-parse.c \
+ libsensors/data.c \
+ libsensors/error.c \
+ libsensors/general.c \
+ libsensors/init.c \
+ libsensors/sysfs.c \
+ mxml/mxml-attr.c \
+ mxml/mxml-entity.c \
+ mxml/mxml-file.c \
+ mxml/mxml-get.c \
+ mxml/mxml-index.c \
+ mxml/mxml-node.c \
+ mxml/mxml-private.c \
+ mxml/mxml-search.c \
+ mxml/mxml-set.c \
+ mxml/mxml-string.c
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+
+LOCAL_MODULE := gatord
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/tools/gator/daemon/Application.mk b/tools/gator/daemon/Application.mk
new file mode 100644
index 000000000000..631ba54148d1
--- /dev/null
+++ b/tools/gator/daemon/Application.mk
@@ -0,0 +1 @@
+APP_PLATFORM := android-8
diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp
new file mode 100644
index 000000000000..dd19f7f8be76
--- /dev/null
+++ b/tools/gator/daemon/Buffer.cpp
@@ -0,0 +1,391 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "Buffer.h"
+
+#include "Logging.h"
+#include "Sender.h"
+#include "SessionData.h"
+
+#define mask (mSize - 1)
+
+enum {
+ CODE_PEA = 1,
+ CODE_KEYS = 2,
+ CODE_FORMAT = 3,
+ CODE_MAPS = 4,
+ CODE_COMM = 5,
+ CODE_KEYS_OLD = 6,
+};
+
+// Summary Frame Messages
+enum {
+ MESSAGE_SUMMARY = 1,
+ MESSAGE_CORE_NAME = 3,
+};
+
+// From gator_marshaling.c
+#define NEWLINE_CANARY \
+ /* Unix */ \
+ "1\n" \
+ /* Windows */ \
+ "2\r\n" \
+ /* Mac OS */ \
+ "3\r" \
+ /* RISC OS */ \
+ "4\n\r" \
+ /* Add another character so the length isn't 0x0a bytes */ \
+ "5"
+
+Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mCore(core), mBufType(buftype), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mBuf(new char[mSize]), mCommitTime(gSessionData->mLiveRate), mReaderSem(readerSem) {
+ if ((mSize & mask) != 0) {
+ logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2");
+ handleException();
+ }
+ frame();
+}
+
+Buffer::~Buffer() {
+ delete [] mBuf;
+}
+
+void Buffer::write(Sender *const sender) {
+ if (!commitReady()) {
+ return;
+ }
+
+ // determine the size of two halves
+ int length1 = mCommitPos - mReadPos;
+ char *buffer1 = mBuf + mReadPos;
+ int length2 = 0;
+ char *buffer2 = mBuf;
+ if (length1 < 0) {
+ length1 = mSize - mReadPos;
+ length2 = mCommitPos;
+ }
+
+ logg->logMessage("Sending data length1: %i length2: %i", length1, length2);
+
+ // start, middle or end
+ if (length1 > 0) {
+ sender->writeData(buffer1, length1, RESPONSE_APC_DATA);
+ }
+
+ // possible wrap around
+ if (length2 > 0) {
+ sender->writeData(buffer2, length2, RESPONSE_APC_DATA);
+ }
+
+ mReadPos = mCommitPos;
+}
+
+bool Buffer::commitReady() const {
+ return mCommitPos != mReadPos;
+}
+
+int Buffer::bytesAvailable() const {
+ int filled = mWritePos - mReadPos;
+ if (filled < 0) {
+ filled += mSize;
+ }
+
+ int remaining = mSize - filled;
+
+ if (mAvailable) {
+ // Give some extra room; also allows space to insert the overflow error packet
+ remaining -= 200;
+ } else {
+ // Hysteresis, prevents multiple overflow messages
+ remaining -= 2000;
+ }
+
+ return remaining;
+}
+
+bool Buffer::checkSpace(const int bytes) {
+ const int remaining = bytesAvailable();
+
+ if (remaining < bytes) {
+ mAvailable = false;
+ } else {
+ mAvailable = true;
+ }
+
+ return mAvailable;
+}
+
+int Buffer::contiguousSpaceAvailable() const {
+ int remaining = bytesAvailable();
+ int contiguous = mSize - mWritePos;
+ if (remaining < contiguous) {
+ return remaining;
+ } else {
+ return contiguous;
+ }
+}
+
+void Buffer::commit(const uint64_t time) {
+ // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
+ const int typeLength = gSessionData->mLocalCapture ? 0 : 1;
+ int length = mWritePos - mCommitPos;
+ if (length < 0) {
+ length += mSize;
+ }
+ length = length - typeLength - sizeof(int32_t);
+ for (size_t byte = 0; byte < sizeof(int32_t); byte++) {
+ mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF;
+ }
+
+ logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos);
+ mCommitPos = mWritePos;
+
+ if (gSessionData->mLiveRate > 0) {
+ while (time > mCommitTime) {
+ mCommitTime += gSessionData->mLiveRate;
+ }
+ }
+
+ if (!mIsDone) {
+ frame();
+ }
+
+ // send a notification that data is ready
+ sem_post(mReaderSem);
+}
+
+void Buffer::check(const uint64_t time) {
+ int filled = mWritePos - mCommitPos;
+ if (filled < 0) {
+ filled += mSize;
+ }
+ if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) {
+ commit(time);
+ }
+}
+
+void Buffer::packInt(char *const buf, const int size, int &writePos, int32_t x) {
+ int packedBytes = 0;
+ int more = true;
+ while (more) {
+ // low order 7 bits of x
+ char b = x & 0x7f;
+ x >>= 7;
+
+ if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+
+ buf[(writePos + packedBytes) & /*mask*/(size - 1)] = b;
+ packedBytes++;
+ }
+
+ writePos = (writePos + packedBytes) & /*mask*/(size - 1);
+}
+
+void Buffer::packInt(int32_t x) {
+ packInt(mBuf, mSize, mWritePos, x);
+}
+
+void Buffer::packInt64(int64_t x) {
+ int packedBytes = 0;
+ int more = true;
+ while (more) {
+ // low order 7 bits of x
+ char b = x & 0x7f;
+ x >>= 7;
+
+ if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
+ more = false;
+ } else {
+ b |= 0x80;
+ }
+
+ mBuf[(mWritePos + packedBytes) & mask] = b;
+ packedBytes++;
+ }
+
+ mWritePos = (mWritePos + packedBytes) & mask;
+}
+
+void Buffer::writeBytes(const void *const data, size_t count) {
+ size_t i;
+ for (i = 0; i < count; ++i) {
+ mBuf[(mWritePos + i) & mask] = static_cast<const char *>(data)[i];
+ }
+
+ mWritePos = (mWritePos + i) & mask;
+}
+
+void Buffer::writeString(const char *const str) {
+ const int len = strlen(str);
+ packInt(len);
+ writeBytes(str, len);
+}
+
+void Buffer::frame() {
+ if (!gSessionData->mLocalCapture) {
+ packInt(RESPONSE_APC_DATA);
+ }
+ // Reserve space for the length
+ mWritePos += sizeof(int32_t);
+ packInt(mBufType);
+ packInt(mCore);
+}
+
+void Buffer::summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) {
+ packInt(MESSAGE_SUMMARY);
+ writeString(NEWLINE_CANARY);
+ packInt64(timestamp);
+ packInt64(uptime);
+ packInt64(monotonicDelta);
+ writeString("uname");
+ writeString(uname);
+ writeString("");
+ check(1);
+}
+
+void Buffer::coreName(const int core, const int cpuid, const char *const name) {
+ if (checkSpace(3 * MAXSIZE_PACK32 + 0x100)) {
+ packInt(MESSAGE_CORE_NAME);
+ packInt(core);
+ packInt(cpuid);
+ writeString(name);
+ }
+ check(1);
+}
+
+bool Buffer::eventHeader(const uint64_t curr_time) {
+ bool retval = false;
+ if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) {
+ packInt(0); // key of zero indicates a timestamp
+ packInt64(curr_time);
+ retval = true;
+ }
+
+ return retval;
+}
+
+bool Buffer::eventTid(const int tid) {
+ bool retval = false;
+ if (checkSpace(2 * MAXSIZE_PACK32)) {
+ packInt(1); // key of 1 indicates a tid
+ packInt(tid);
+ retval = true;
+ }
+
+ return retval;
+}
+
+void Buffer::event(const int32_t key, const int32_t value) {
+ if (checkSpace(2 * MAXSIZE_PACK32)) {
+ packInt(key);
+ packInt(value);
+ }
+}
+
+void Buffer::event64(const int64_t key, const int64_t value) {
+ if (checkSpace(2 * MAXSIZE_PACK64)) {
+ packInt64(key);
+ packInt64(value);
+ }
+}
+
+void Buffer::pea(const struct perf_event_attr *const pea, int key) {
+ if (checkSpace(2 * MAXSIZE_PACK32 + pea->size)) {
+ packInt(CODE_PEA);
+ writeBytes(pea, pea->size);
+ packInt(key);
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ // Don't know the real perf time so use 1 as it will work for now
+ check(1);
+}
+
+void Buffer::keys(const int count, const __u64 *const ids, const int *const keys) {
+ if (checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) {
+ packInt(CODE_KEYS);
+ packInt(count);
+ for (int i = 0; i < count; ++i) {
+ packInt64(ids[i]);
+ packInt(keys[i]);
+ }
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ check(1);
+}
+
+void Buffer::keysOld(const int keyCount, const int *const keys, const int bytes, const char *const buf) {
+ if (checkSpace((2 + keyCount) * MAXSIZE_PACK32 + bytes)) {
+ packInt(CODE_KEYS_OLD);
+ packInt(keyCount);
+ for (int i = 0; i < keyCount; ++i) {
+ packInt(keys[i]);
+ }
+ writeBytes(buf, bytes);
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ check(1);
+}
+
+void Buffer::format(const int length, const char *const format) {
+ if (checkSpace(MAXSIZE_PACK32 + length + 1)) {
+ packInt(CODE_FORMAT);
+ writeBytes(format, length + 1);
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ check(1);
+}
+
+void Buffer::maps(const int pid, const int tid, const char *const maps) {
+ const int mapsLen = strlen(maps) + 1;
+ if (checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) {
+ packInt(CODE_MAPS);
+ packInt(pid);
+ packInt(tid);
+ writeBytes(maps, mapsLen);
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ check(1);
+}
+
+void Buffer::comm(const int pid, const int tid, const char *const image, const char *const comm) {
+ const int imageLen = strlen(image) + 1;
+ const int commLen = strlen(comm) + 1;
+ if (checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) {
+ packInt(CODE_COMM);
+ packInt(pid);
+ packInt(tid);
+ writeBytes(image, imageLen);
+ writeBytes(comm, commLen);
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ check(1);
+}
+
+void Buffer::setDone() {
+ mIsDone = true;
+ commit(0);
+}
+
+bool Buffer::isDone() const {
+ return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos;
+}
diff --git a/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h
new file mode 100644
index 000000000000..2de1b97ac091
--- /dev/null
+++ b/tools/gator/daemon/Buffer.h
@@ -0,0 +1,102 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 BUFFER_H
+#define BUFFER_H
+
+#include <stdint.h>
+#include <semaphore.h>
+
+#include "k/perf_event.h"
+
+class Sender;
+
+enum {
+ FRAME_SUMMARY = 1,
+ FRAME_BLOCK_COUNTER = 5,
+ FRAME_EXTERNAL = 10,
+ FRAME_PERF_ATTRS = 11,
+ FRAME_PERF = 12,
+};
+
+class Buffer {
+public:
+ static const size_t MAXSIZE_PACK32 = 5;
+ static const size_t MAXSIZE_PACK64 = 10;
+
+ Buffer(int32_t core, int32_t buftype, const int size, sem_t *const readerSem);
+ ~Buffer();
+
+ void write(Sender *sender);
+
+ int bytesAvailable() const;
+ int contiguousSpaceAvailable() const;
+ void commit(const uint64_t time);
+ void check(const uint64_t time);
+
+ void frame();
+
+ // Summary messages
+ void summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname);
+ void coreName(const int core, const int cpuid, const char *const name);
+
+ // Block Counter messages
+ bool eventHeader(uint64_t curr_time);
+ bool eventTid(int tid);
+ void event(int32_t key, int32_t value);
+ void event64(int64_t key, int64_t value);
+
+ // Perf Attrs messages
+ void pea(const struct perf_event_attr *const pea, int key);
+ void keys(const int count, const __u64 *const ids, const int *const keys);
+ void keysOld(const int keyCount, const int *const keys, const int bytes, const char *const buf);
+ void format(const int length, const char *const format);
+ void maps(const int pid, const int tid, const char *const maps);
+ void comm(const int pid, const int tid, const char *const image, const char *const comm);
+
+ void setDone();
+ bool isDone() const;
+
+ // Prefer a new member to using these functions if possible
+ char *getWritePos() { return mBuf + mWritePos; }
+ void advanceWrite(int bytes) { mWritePos = (mWritePos + bytes) & /*mask*/(mSize - 1); }
+ static void packInt(char *const buf, const int size, int &writePos, int32_t x);
+ void packInt(int32_t x);
+ void packInt64(int64_t x);
+ void writeBytes(const void *const data, size_t count);
+ void writeString(const char *const str);
+
+ static void writeLEInt(unsigned char *buf, int v) {
+ buf[0] = (v >> 0) & 0xFF;
+ buf[1] = (v >> 8) & 0xFF;
+ buf[2] = (v >> 16) & 0xFF;
+ buf[3] = (v >> 24) & 0xFF;
+ }
+
+private:
+ bool commitReady() const;
+ bool checkSpace(int bytes);
+
+ const int32_t mCore;
+ const int32_t mBufType;
+ const int mSize;
+ int mReadPos;
+ int mWritePos;
+ int mCommitPos;
+ bool mAvailable;
+ bool mIsDone;
+ char *const mBuf;
+ uint64_t mCommitTime;
+ sem_t *const mReaderSem;
+
+ // Intentionally unimplemented
+ Buffer(const Buffer &);
+ Buffer &operator=(const Buffer &);
+};
+
+#endif // BUFFER_H
diff --git a/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp
new file mode 100644
index 000000000000..4a11415a00c9
--- /dev/null
+++ b/tools/gator/daemon/CapturedXML.cpp
@@ -0,0 +1,144 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "CapturedXML.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+
+#include "SessionData.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+
+CapturedXML::CapturedXML() {
+}
+
+CapturedXML::~CapturedXML() {
+}
+
+mxml_node_t* CapturedXML::getTree(bool includeTime) {
+ mxml_node_t *xml;
+ mxml_node_t *captured;
+ mxml_node_t *target;
+ int x;
+
+ xml = mxmlNewXML("1.0");
+
+ captured = mxmlNewElement(xml, "captured");
+ mxmlElementSetAttr(captured, "version", "1");
+ if (gSessionData->perf.isSetup()) {
+ mxmlElementSetAttr(captured, "type", "Perf");
+ }
+ mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION);
+ if (includeTime) { // Send the following only after the capture is complete
+ if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010)
+ mxmlElementSetAttrf(captured, "created", "%lu", time(NULL)); // Valid until the year 2038
+ }
+ }
+
+ target = mxmlNewElement(captured, "target");
+ mxmlElementSetAttr(target, "name", gSessionData->mCoreName);
+ mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate);
+ mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores);
+ mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mMaxCpuId);
+
+ if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) {
+ mxmlElementSetAttr(target, "supports_live", "yes");
+ }
+
+ if (gSessionData->mLocalCapture) {
+ mxmlElementSetAttr(target, "local_capture", "yes");
+ }
+
+ mxml_node_t *counters = NULL;
+ for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) {
+ const Counter & counter = gSessionData->mCounters[x];
+ if (counter.isEnabled()) {
+ if (counters == NULL) {
+ counters = mxmlNewElement(captured, "counters");
+ }
+ mxml_node_t *const node = mxmlNewElement(counters, "counter");
+ mxmlElementSetAttrf(node, "key", "0x%x", counter.getKey());
+ mxmlElementSetAttr(node, "type", counter.getType());
+ if (counter.getEvent() != -1) {
+ mxmlElementSetAttrf(node, "event", "0x%x", counter.getEvent());
+ }
+ if (counter.getCount() > 0) {
+ mxmlElementSetAttrf(node, "count", "%d", counter.getCount());
+ }
+ if (counter.getCores() > 0) {
+ mxmlElementSetAttrf(node, "cores", "%d", counter.getCores());
+ }
+ }
+ }
+
+ return xml;
+}
+
+char* CapturedXML::getXML(bool includeTime) {
+ char* xml_string;
+ mxml_node_t *xml = getTree(includeTime);
+ xml_string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
+ mxmlDelete(xml);
+ return xml_string;
+}
+
+void CapturedXML::write(char* path) {
+ char file[PATH_MAX];
+
+ // Set full path
+ snprintf(file, PATH_MAX, "%s/captured.xml", path);
+
+ char* xml = getXML(true);
+ if (util->writeToDisk(file, xml) < 0) {
+ logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file);
+ handleException();
+ }
+
+ free(xml);
+}
+
+// whitespace callback utility function used with mini-xml
+const char * mxmlWhitespaceCB(mxml_node_t *node, int loc) {
+ const char *name;
+
+ name = mxmlGetElement(node);
+
+ if (loc == MXML_WS_BEFORE_OPEN) {
+ // Single indentation
+ if (!strcmp(name, "target") || !strcmp(name, "counters"))
+ return("\n ");
+
+ // Double indentation
+ if (!strcmp(name, "counter"))
+ return("\n ");
+
+ // Avoid a carriage return on the first line of the xml file
+ if (!strncmp(name, "?xml", 4))
+ return(NULL);
+
+ // Default - no indentation
+ return("\n");
+ }
+
+ if (loc == MXML_WS_BEFORE_CLOSE) {
+ // No indentation
+ if (!strcmp(name, "captured"))
+ return("\n");
+
+ // Single indentation
+ if (!strcmp(name, "counters"))
+ return("\n ");
+
+ // Default - no carriage return
+ return(NULL);
+ }
+
+ return(NULL);
+}
diff --git a/tools/gator/daemon/CapturedXML.h b/tools/gator/daemon/CapturedXML.h
new file mode 100644
index 000000000000..ed08c44bc3ff
--- /dev/null
+++ b/tools/gator/daemon/CapturedXML.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 __CAPTURED_XML_H__
+#define __CAPTURED_XML_H__
+
+#include "mxml/mxml.h"
+
+class CapturedXML {
+public:
+ CapturedXML();
+ ~CapturedXML();
+ char* getXML(bool includeTime); // the string should be freed by the caller
+ void write(char* path);
+private:
+ mxml_node_t* getTree(bool includeTime);
+};
+
+const char * mxmlWhitespaceCB(mxml_node_t *node, int where);
+
+#endif //__CAPTURED_XML_H__
diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp
new file mode 100644
index 000000000000..1901ecc6a724
--- /dev/null
+++ b/tools/gator/daemon/Child.cpp
@@ -0,0 +1,346 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "Child.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#include "Logging.h"
+#include "CapturedXML.h"
+#include "SessionData.h"
+#include "LocalCapture.h"
+#include "Sender.h"
+#include "OlyUtility.h"
+#include "OlySocket.h"
+#include "StreamlineSetup.h"
+#include "ConfigurationXML.h"
+#include "Driver.h"
+#include "PerfSource.h"
+#include "DriverSource.h"
+#include "ExternalSource.h"
+#include "UserSpaceSource.h"
+
+static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
+static Source *primarySource = NULL;
+static Source *externalSource = NULL;
+static Source *userSpaceSource = NULL;
+static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
+Child* child = NULL; // shared by Child.cpp and main.cpp
+
+extern void cleanUp();
+void handleException() {
+ if (child && child->numExceptions++ > 0) {
+ // it is possible one of the below functions itself can cause an exception, thus allow only one exception
+ logg->logMessage("Received multiple exceptions, terminating the child");
+ exit(1);
+ }
+ fprintf(stderr, "%s", logg->getLastError());
+
+ if (child && child->socket) {
+ if (sender) {
+ // send the error, regardless of the command sent by Streamline
+ sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR);
+
+ // cannot close the socket before Streamline issues the command, so wait for the command before exiting
+ if (gSessionData->mWaitingOnCommand) {
+ char discard;
+ child->socket->receiveNBytes(&discard, 1);
+ }
+
+ // Ensure all data is flushed
+ child->socket->shutdownConnection();
+
+ // this indirectly calls close socket which will ensure the data has been sent
+ delete sender;
+ }
+ }
+
+ if (gSessionData->mLocalCapture)
+ cleanUp();
+
+ exit(1);
+}
+
+// CTRL C Signal Handler for child process
+static void child_handler(int signum) {
+ static bool beenHere = false;
+ if (beenHere == true) {
+ logg->logMessage("Gator is being forced to shut down.");
+ exit(1);
+ }
+ beenHere = true;
+ logg->logMessage("Gator is shutting down.");
+ if (signum == SIGALRM || !primarySource) {
+ exit(1);
+ } else {
+ child->endSession();
+ alarm(5); // Safety net in case endSession does not complete within 5 seconds
+ }
+}
+
+static void *durationThread(void *) {
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0);
+ sem_wait(&startProfile);
+ if (gSessionData->mSessionIsActive) {
+ // Time out after duration seconds
+ // Add a second for host-side filtering
+ sleep(gSessionData->mDuration + 1);
+ if (gSessionData->mSessionIsActive) {
+ logg->logMessage("Duration expired.");
+ child->endSession();
+ }
+ }
+ logg->logMessage("Exit duration thread");
+ return 0;
+}
+
+static void *stopThread(void *) {
+ OlySocket* socket = child->socket;
+
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0);
+ while (gSessionData->mSessionIsActive) {
+ // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected
+ unsigned char header[5];
+ const int result = socket->receiveNBytes((char*)&header, sizeof(header));
+ const char type = header[0];
+ const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24);
+ if (result == -1) {
+ child->endSession();
+ } else if (result > 0) {
+ if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
+ logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
+ } else {
+ // verify a length of zero
+ if (length == 0) {
+ if (type == COMMAND_APC_STOP) {
+ logg->logMessage("Stop command received.");
+ child->endSession();
+ } else {
+ // Ping is used to make sure gator is alive and requires an ACK as the response
+ logg->logMessage("Ping command received.");
+ sender->writeData(NULL, 0, RESPONSE_ACK);
+ }
+ } else {
+ logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length);
+ }
+ }
+ }
+ }
+
+ logg->logMessage("Exit stop thread");
+ return 0;
+}
+
+static void *senderThread(void *) {
+ char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
+
+ sem_post(&senderThreadStarted);
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
+ sem_wait(&haltPipeline);
+
+ while (!primarySource->isDone() ||
+ !externalSource->isDone() ||
+ (userSpaceSource != NULL && !userSpaceSource->isDone())) {
+ sem_wait(&senderSem);
+
+ primarySource->write(sender);
+ externalSource->write(sender);
+ if (userSpaceSource != NULL) {
+ userSpaceSource->write(sender);
+ }
+ }
+
+ // write end-of-capture sequence
+ if (!gSessionData->mLocalCapture) {
+ sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
+ }
+
+ logg->logMessage("Exit sender thread");
+ return 0;
+}
+
+Child::Child() {
+ initialization();
+ gSessionData->mLocalCapture = true;
+}
+
+Child::Child(OlySocket* sock, int conn) {
+ initialization();
+ socket = sock;
+ mNumConnections = conn;
+}
+
+Child::~Child() {
+}
+
+void Child::initialization() {
+ // Set up different handlers for signals
+ gSessionData->mSessionIsActive = true;
+ signal(SIGINT, child_handler);
+ signal(SIGTERM, child_handler);
+ signal(SIGABRT, child_handler);
+ signal(SIGALRM, child_handler);
+ socket = NULL;
+ numExceptions = 0;
+ mNumConnections = 0;
+
+ // Initialize semaphores
+ sem_init(&senderThreadStarted, 0, 0);
+ sem_init(&startProfile, 0, 0);
+ sem_init(&senderSem, 0, 0);
+}
+
+void Child::endSession() {
+ gSessionData->mSessionIsActive = false;
+ primarySource->interrupt();
+ externalSource->interrupt();
+ if (userSpaceSource != NULL) {
+ userSpaceSource->interrupt();
+ }
+ sem_post(&haltPipeline);
+}
+
+void Child::run() {
+ LocalCapture* localCapture = NULL;
+ pthread_t durationThreadID, stopThreadID, senderThreadID;
+
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
+
+ // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
+ mxmlSetWrapMargin(0);
+
+ // Instantiate the Sender - must be done first, after which error messages can be sent
+ sender = new Sender(socket);
+
+ if (mNumConnections > 1) {
+ logg->logError(__FILE__, __LINE__, "Session already in progress");
+ handleException();
+ }
+
+ // Populate gSessionData with the configuration
+ { ConfigurationXML configuration; }
+
+ // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
+ if (!gSessionData->perf.isSetup()) {
+ primarySource = new DriverSource(&senderSem, &startProfile);
+ } else {
+ primarySource = new PerfSource(&senderSem, &startProfile);
+ }
+
+ // Initialize all drivers
+ for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
+ driver->resetCounters();
+ }
+
+ // Set up counters using the associated driver's setup function
+ for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
+ Counter & counter = gSessionData->mCounters[i];
+ if (counter.isEnabled()) {
+ counter.getDriver()->setupCounter(counter);
+ }
+ }
+
+ // Start up and parse session xml
+ if (socket) {
+ // Respond to Streamline requests
+ StreamlineSetup ss(socket);
+ } else {
+ char* xmlString;
+ xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
+ if (xmlString == 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
+ handleException();
+ }
+ gSessionData->parseSessionXML(xmlString);
+ localCapture = new LocalCapture();
+ localCapture->createAPCDirectory(gSessionData->mTargetPath);
+ localCapture->copyImages(gSessionData->mImages);
+ localCapture->write(xmlString);
+ sender->createDataFile(gSessionData->mAPCDir);
+ free(xmlString);
+ }
+
+ // Must be after session XML is parsed
+ if (!primarySource->prepare()) {
+ logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
+ handleException();
+ }
+
+ // Sender thread shall be halted until it is signaled for one shot mode
+ sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
+
+ // Create the duration, stop, and sender threads
+ bool thread_creation_success = true;
+ if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
+ thread_creation_success = false;
+ } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) {
+ thread_creation_success = false;
+ } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)) {
+ thread_creation_success = false;
+ }
+
+ externalSource = new ExternalSource(&senderSem);
+ if (!externalSource->prepare()) {
+ logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
+ handleException();
+ }
+ externalSource->start();
+
+ if (gSessionData->hwmon.countersEnabled() || gSessionData->fsDriver.countersEnabled()) {
+ userSpaceSource = new UserSpaceSource(&senderSem);
+ if (!userSpaceSource->prepare()) {
+ logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
+ handleException();
+ }
+ userSpaceSource->start();
+ }
+
+ if (!thread_creation_success) {
+ logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
+ handleException();
+ }
+
+ // Wait until thread has started
+ sem_wait(&senderThreadStarted);
+
+ // Start profiling
+ primarySource->run();
+
+ if (userSpaceSource != NULL) {
+ userSpaceSource->join();
+ }
+ externalSource->join();
+
+ // Wait for the other threads to exit
+ pthread_join(senderThreadID, NULL);
+
+ // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
+ if (socket) {
+ logg->logMessage("Waiting on stop thread");
+ socket->shutdownConnection();
+ pthread_join(stopThreadID, NULL);
+ }
+
+ // Write the captured xml file
+ if (gSessionData->mLocalCapture) {
+ CapturedXML capturedXML;
+ capturedXML.write(gSessionData->mAPCDir);
+ }
+
+ logg->logMessage("Profiling ended.");
+
+ delete userSpaceSource;
+ delete externalSource;
+ delete primarySource;
+ delete sender;
+ delete localCapture;
+}
diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h
new file mode 100644
index 000000000000..a306a7760819
--- /dev/null
+++ b/tools/gator/daemon/Child.h
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 __CHILD_H__
+#define __CHILD_H__
+
+class OlySocket;
+
+class Child {
+public:
+ Child();
+ Child(OlySocket* sock, int numConnections);
+ ~Child();
+ void run();
+ OlySocket *socket;
+ void endSession();
+ int numExceptions;
+private:
+ int mNumConnections;
+
+ void initialization();
+
+ // Intentionally unimplemented
+ Child(const Child &);
+ Child &operator=(const Child &);
+};
+
+#endif //__CHILD_H__
diff --git a/tools/gator/daemon/Config.h b/tools/gator/daemon/Config.h
new file mode 100644
index 000000000000..6f5e2aae50e1
--- /dev/null
+++ b/tools/gator/daemon/Config.h
@@ -0,0 +1,17 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 CONFIG_H
+#define CONFIG_H
+
+#define ARRAY_LENGTH(A) static_cast<int>(sizeof(A)/sizeof((A)[0]))
+
+#define MAX_PERFORMANCE_COUNTERS 50
+#define NR_CPUS 16
+
+#endif // CONFIG_H
diff --git a/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp
new file mode 100644
index 000000000000..6590dd389196
--- /dev/null
+++ b/tools/gator/daemon/ConfigurationXML.cpp
@@ -0,0 +1,217 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "ConfigurationXML.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <dirent.h>
+
+#include "Driver.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
+
+static const char* ATTR_COUNTER = "counter";
+static const char* ATTR_REVISION = "revision";
+static const char* ATTR_EVENT = "event";
+static const char* ATTR_COUNT = "count";
+static const char* ATTR_CORES = "cores";
+
+ConfigurationXML::ConfigurationXML() {
+ const char * configuration_xml;
+ unsigned int configuration_xml_len;
+ getDefaultConfigurationXml(configuration_xml, configuration_xml_len);
+
+ char path[PATH_MAX];
+
+ getPath(path);
+ mConfigurationXML = util->readFromDisk(path);
+
+ for (int retryCount = 0; retryCount < 2; ++retryCount) {
+ if (mConfigurationXML == NULL) {
+ logg->logMessage("Unable to locate configuration.xml, using default in binary");
+ // null-terminate configuration_xml
+ mConfigurationXML = (char*)malloc(configuration_xml_len + 1);
+ memcpy(mConfigurationXML, (const void*)configuration_xml, configuration_xml_len);
+ mConfigurationXML[configuration_xml_len] = 0;
+ }
+
+ int ret = parse(mConfigurationXML);
+ if (ret == 1) {
+ remove();
+
+ // Free the current configuration and reload
+ free((void*)mConfigurationXML);
+ mConfigurationXML = NULL;
+ continue;
+ }
+
+ break;
+ }
+
+ validate();
+}
+
+ConfigurationXML::~ConfigurationXML() {
+ if (mConfigurationXML) {
+ free((void*)mConfigurationXML);
+ }
+}
+
+int ConfigurationXML::parse(const char* configurationXML) {
+ mxml_node_t *tree, *node;
+ int ret;
+
+ // clear counter overflow
+ gSessionData->mCounterOverflow = 0;
+ gSessionData->mIsEBS = false;
+ mIndex = 0;
+
+ // disable all counters prior to parsing the configuration xml
+ for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
+ gSessionData->mCounters[i].setEnabled(false);
+ }
+
+ tree = mxmlLoadString(NULL, configurationXML, MXML_NO_CALLBACK);
+
+ node = mxmlGetFirstChild(tree);
+ while (node && mxmlGetType(node) != MXML_ELEMENT)
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+
+ ret = configurationsTag(node);
+
+ node = mxmlGetFirstChild(node);
+ while (node) {
+ if (mxmlGetType(node) != MXML_ELEMENT) {
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+ continue;
+ }
+ configurationTag(node);
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+ }
+
+ mxmlDelete(tree);
+
+ return ret;
+}
+
+void ConfigurationXML::validate(void) {
+ for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
+ const Counter & counter = gSessionData->mCounters[i];
+ if (counter.isEnabled()) {
+ if (strcmp(counter.getType(), "") == 0) {
+ logg->logError(__FILE__, __LINE__, "Invalid required attribute in configuration.xml:\n counter=\"%s\"\n event=%d\n", counter.getType(), counter.getEvent());
+ handleException();
+ }
+
+ // iterate through the remaining enabled performance counters
+ for (int j = i + 1; j < MAX_PERFORMANCE_COUNTERS; j++) {
+ const Counter & counter2 = gSessionData->mCounters[j];
+ if (counter2.isEnabled()) {
+ // check if the types are the same
+ if (strcmp(counter.getType(), counter2.getType()) == 0) {
+ logg->logError(__FILE__, __LINE__, "Duplicate performance counter type in configuration.xml: %s", counter.getType());
+ handleException();
+ }
+ }
+ }
+ }
+ }
+}
+
+#define CONFIGURATION_REVISION 3
+int ConfigurationXML::configurationsTag(mxml_node_t *node) {
+ const char* revision_string;
+
+ revision_string = mxmlElementGetAttr(node, ATTR_REVISION);
+ if (!revision_string) {
+ return 1; //revision issue;
+ }
+
+ int revision = strtol(revision_string, NULL, 10);
+ if (revision < CONFIGURATION_REVISION) {
+ return 1; // revision issue
+ }
+
+ // A revision >= CONFIGURATION_REVISION is okay
+ // Greater than can occur when Streamline is newer than gator
+
+ return 0;
+}
+
+void ConfigurationXML::configurationTag(mxml_node_t *node) {
+ // handle all other performance counters
+ if (mIndex >= MAX_PERFORMANCE_COUNTERS) {
+ mIndex++;
+ gSessionData->mCounterOverflow = mIndex;
+ return;
+ }
+
+ // read attributes
+ Counter & counter = gSessionData->mCounters[mIndex];
+ counter.clear();
+ if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER));
+ if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16));
+ if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10));
+ if (mxmlElementGetAttr(node, ATTR_CORES)) counter.setCores(strtol(mxmlElementGetAttr(node, ATTR_CORES), NULL, 10));
+ if (counter.getCount() > 0) {
+ gSessionData->mIsEBS = true;
+ }
+ counter.setEnabled(true);
+
+ // Associate a driver with each counter
+ for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
+ if (driver->claimCounter(counter)) {
+ if (counter.getDriver() != NULL) {
+ logg->logError(__FILE__, __LINE__, "More than one driver has claimed %s:%i", counter.getType(), counter.getEvent());
+ handleException();
+ }
+ counter.setDriver(driver);
+ }
+ }
+
+ // If no driver is associated with the counter, disable it
+ if (counter.getDriver() == NULL) {
+ logg->logMessage("No driver has claimed %s:%i", counter.getType(), counter.getEvent());
+ counter.setEnabled(false);
+ }
+
+ if (counter.isEnabled()) {
+ // update counter index
+ mIndex++;
+ }
+}
+
+void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) {
+#include "defaults_xml.h" // defines and initializes char defaults_xml[] and int defaults_xml_len
+ xml = (const char *)defaults_xml;
+ len = defaults_xml_len;
+}
+
+void ConfigurationXML::getPath(char* path) {
+ if (gSessionData->mConfigurationXMLPath) {
+ strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX);
+ } else {
+ if (util->getApplicationFullPath(path, PATH_MAX) != 0) {
+ logg->logMessage("Unable to determine the full path of gatord, the cwd will be used");
+ }
+ strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1);
+ }
+}
+
+void ConfigurationXML::remove() {
+ char path[PATH_MAX];
+ getPath(path);
+
+ if (::remove(path) != 0) {
+ logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk");
+ handleException();
+ }
+ logg->logMessage("Invalid configuration.xml file detected and removed");
+}
diff --git a/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h
new file mode 100644
index 000000000000..efa415e508b6
--- /dev/null
+++ b/tools/gator/daemon/ConfigurationXML.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 COUNTERS_H
+#define COUNTERS_H
+
+#include "mxml/mxml.h"
+
+class ConfigurationXML {
+public:
+ static void getDefaultConfigurationXml(const char * & xml, unsigned int & len);
+ static void getPath(char* path);
+ static void remove();
+
+ ConfigurationXML();
+ ~ConfigurationXML();
+ const char* getConfigurationXML() {return mConfigurationXML;}
+ void validate(void);
+
+private:
+ char* mConfigurationXML;
+ int mIndex;
+
+ int parse(const char* xmlFile);
+ int configurationsTag(mxml_node_t *node);
+ void configurationTag(mxml_node_t *node);
+
+ // Intentionally unimplemented
+ ConfigurationXML(const ConfigurationXML &);
+ ConfigurationXML &operator=(const ConfigurationXML &);
+};
+
+#endif // COUNTERS_H
diff --git a/tools/gator/daemon/Counter.h b/tools/gator/daemon/Counter.h
new file mode 100644
index 000000000000..5202aa046362
--- /dev/null
+++ b/tools/gator/daemon/Counter.h
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 COUNTER_H
+#define COUNTER_H
+
+#include <string.h>
+
+class Driver;
+
+class Counter {
+public:
+ static const size_t MAX_STRING_LEN = 80;
+ static const size_t MAX_DESCRIPTION_LEN = 400;
+
+ Counter () {
+ clear();
+ }
+
+ void clear () {
+ mType[0] = '\0';
+ mEnabled = false;
+ mEvent = -1;
+ mCount = 0;
+ mCores = -1;
+ mKey = 0;
+ mDriver = NULL;
+ }
+
+ void setType(const char *const type) { strncpy(mType, type, sizeof(mType)); mType[sizeof(mType) - 1] = '\0'; }
+ void setEnabled(const bool enabled) { mEnabled = enabled; }
+ void setEvent(const int event) { mEvent = event; }
+ void setCount(const int count) { mCount = count; }
+ void setCores(const int cores) { mCores = cores; }
+ void setKey(const int key) { mKey = key; }
+ void setDriver(Driver *const driver) { mDriver = driver; }
+
+ const char *getType() const { return mType;}
+ bool isEnabled() const { return mEnabled; }
+ int getEvent() const { return mEvent; }
+ int getCount() const { return mCount; }
+ int getCores() const { return mCores; }
+ int getKey() const { return mKey; }
+ Driver *getDriver() const { return mDriver; }
+
+private:
+ // Intentionally unimplemented
+ Counter(const Counter &);
+ Counter & operator=(const Counter &);
+
+ char mType[MAX_STRING_LEN];
+ bool mEnabled;
+ int mEvent;
+ int mCount;
+ int mCores;
+ int mKey;
+ Driver *mDriver;
+};
+
+#endif // COUNTER_H
diff --git a/tools/gator/daemon/Driver.cpp b/tools/gator/daemon/Driver.cpp
new file mode 100644
index 000000000000..09e040162912
--- /dev/null
+++ b/tools/gator/daemon/Driver.cpp
@@ -0,0 +1,15 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "Driver.h"
+
+Driver *Driver::head = NULL;
+
+Driver::Driver() : next(head) {
+ head = this;
+}
diff --git a/tools/gator/daemon/Driver.h b/tools/gator/daemon/Driver.h
new file mode 100644
index 000000000000..e5ed7b6c1295
--- /dev/null
+++ b/tools/gator/daemon/Driver.h
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 DRIVER_H
+#define DRIVER_H
+
+#include "mxml/mxml.h"
+
+class Counter;
+
+class Driver {
+public:
+ static Driver *getHead() { return head; }
+
+ virtual ~Driver() {}
+
+ // Returns true if this driver can manage the counter
+ virtual bool claimCounter(const Counter &counter) const = 0;
+ // Clears and disables all counters
+ virtual void resetCounters() = 0;
+ // Enables and prepares the counter for capture
+ virtual void setupCounter(Counter &counter) = 0;
+
+ // Emits available counters
+ virtual int writeCounters(mxml_node_t *root) const = 0;
+ // Emits possible dynamically generated events/counters
+ virtual void writeEvents(mxml_node_t *) const {}
+
+ Driver *getNext() const { return next; }
+
+protected:
+ Driver ();
+
+private:
+ static Driver *head;
+ Driver *next;
+
+ // Intentionally unimplemented
+ Driver(const Driver &);
+ Driver &operator=(const Driver &);
+};
+
+#endif // DRIVER_H
diff --git a/tools/gator/daemon/DriverSource.cpp b/tools/gator/daemon/DriverSource.cpp
new file mode 100644
index 000000000000..11d3095ef6d2
--- /dev/null
+++ b/tools/gator/daemon/DriverSource.cpp
@@ -0,0 +1,321 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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.
+ */
+
+#define __STDC_FORMAT_MACROS
+
+#include "DriverSource.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "Buffer.h"
+#include "Child.h"
+#include "DynBuf.h"
+#include "Fifo.h"
+#include "Logging.h"
+#include "Proc.h"
+#include "Sender.h"
+#include "SessionData.h"
+
+extern Child *child;
+
+DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mBuffer(NULL), mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) {
+ int driver_version = 0;
+
+ mBuffer = new Buffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem);
+ if (readIntDriver("/dev/gator/version", &driver_version) == -1) {
+ logg->logError(__FILE__, __LINE__, "Error reading gator driver version");
+ handleException();
+ }
+
+ // Verify the driver version matches the daemon version
+ if (driver_version != PROTOCOL_VERSION) {
+ if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) {
+ // One of the mismatched versions is development version
+ logg->logError(__FILE__, __LINE__,
+ "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n"
+ ">> The following must be synchronized from engineering repository:\n"
+ ">> * gator driver\n"
+ ">> * gator daemon\n"
+ ">> * Streamline", driver_version, PROTOCOL_VERSION);
+ handleException();
+ } else {
+ // Release version mismatch
+ logg->logError(__FILE__, __LINE__,
+ "gator driver version \"%d\" is different than gator daemon version \"%d\".\n"
+ ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION);
+ handleException();
+ }
+ }
+
+ int enable = -1;
+ if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) {
+ logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress.");
+ handleException();
+ }
+
+ readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores);
+ if (gSessionData->mCores == 0) {
+ gSessionData->mCores = 1;
+ }
+
+ if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size");
+ handleException();
+ }
+}
+
+DriverSource::~DriverSource() {
+ delete mFifo;
+
+ // Write zero for safety, as a zero should have already been written
+ writeDriver("/dev/gator/enable", "0");
+
+ // Calls event_buffer_release in the driver
+ if (mBufferFD) {
+ close(mBufferFD);
+ }
+}
+
+bool DriverSource::prepare() {
+ // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
+ logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, mBufferSize);
+ mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem);
+
+ return true;
+}
+
+void DriverSource::bootstrapThread() {
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-bootstrap", 0, 0, 0);
+
+ DynBuf printb;
+ DynBuf b1;
+ DynBuf b2;
+ DynBuf b3;
+
+ if (!readProc(mBuffer, false, &printb, &b1, &b2, &b3)) {
+ logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__);
+ handleException();
+ }
+
+ mBuffer->commit(1);
+ mBuffer->setDone();
+}
+
+void *DriverSource::bootstrapThreadStatic(void *arg) {
+ static_cast<DriverSource *>(arg)->bootstrapThread();
+ return NULL;
+}
+
+void DriverSource::run() {
+ // Get the initial pointer to the collect buffer
+ char *collectBuffer = mFifo->start();
+ int bytesCollected = 0;
+
+ logg->logMessage("********** Profiling started **********");
+
+ // Set the maximum backtrace depth
+ if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) {
+ logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth");
+ handleException();
+ }
+
+ // open the buffer which calls userspace_buffer_open() in the driver
+ mBufferFD = open("/dev/gator/buffer", O_RDONLY);
+ if (mBufferFD < 0) {
+ logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure.");
+ handleException();
+ }
+
+ // set the tick rate of the profiling timer
+ if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to set the driver tick");
+ handleException();
+ }
+
+ // notify the kernel of the response type
+ int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA;
+ if (writeDriver("/dev/gator/response_type", response_type)) {
+ logg->logError(__FILE__, __LINE__, "Unable to write the response type");
+ handleException();
+ }
+
+ // Set the live rate
+ if (writeReadDriver("/dev/gator/live_rate", &gSessionData->mLiveRate)) {
+ logg->logError(__FILE__, __LINE__, "Unable to set the driver live rate");
+ handleException();
+ }
+
+ logg->logMessage("Start the driver");
+
+ // This command makes the driver start profiling by calling gator_op_start() in the driver
+ if (writeDriver("/dev/gator/enable", "1") != 0) {
+ logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure.");
+ handleException();
+ }
+
+ lseek(mBufferFD, 0, SEEK_SET);
+
+ sem_post(mStartProfile);
+
+ pthread_t bootstrapThreadID;
+ if (pthread_create(&bootstrapThreadID, NULL, bootstrapThreadStatic, this) != 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to start the gator_bootstrap thread");
+ handleException();
+ }
+
+ // Collect Data
+ do {
+ // This command will stall until data is received from the driver
+ // Calls event_buffer_read in the driver
+ errno = 0;
+ bytesCollected = read(mBufferFD, collectBuffer, mBufferSize);
+
+ // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data
+ if (bytesCollected == -1 && errno == EINTR) {
+ bytesCollected = read(mBufferFD, collectBuffer, mBufferSize);
+ }
+
+ // return the total bytes written
+ logg->logMessage("Driver read of %d bytes", bytesCollected);
+
+ // In one shot mode, stop collection once all the buffers are filled
+ if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
+ if (bytesCollected == -1 || mFifo->willFill(bytesCollected)) {
+ logg->logMessage("One shot");
+ child->endSession();
+ }
+ }
+ collectBuffer = mFifo->write(bytesCollected);
+ } while (bytesCollected > 0);
+
+ logg->logMessage("Exit collect data loop");
+
+ pthread_join(bootstrapThreadID, NULL);
+}
+
+void DriverSource::interrupt() {
+ // This command should cause the read() function in collect() to return and stop the driver from profiling
+ if (writeDriver("/dev/gator/enable", "0") != 0) {
+ logg->logMessage("Stopping kernel failed");
+ }
+}
+
+bool DriverSource::isDone() {
+ return mLength <= 0 && (mBuffer == NULL || mBuffer->isDone());
+}
+
+void DriverSource::write(Sender *sender) {
+ char *data = mFifo->read(&mLength);
+ if (data != NULL) {
+ sender->writeData(data, mLength, RESPONSE_APC_DATA);
+ mFifo->release();
+ // Assume the summary packet is in the first block received from the driver
+ gSessionData->mSentSummary = true;
+ }
+ if (mBuffer != NULL && !mBuffer->isDone()) {
+ mBuffer->write(sender);
+ if (mBuffer->isDone()) {
+ Buffer *buf = mBuffer;
+ mBuffer = NULL;
+ delete buf;
+ }
+ }
+}
+
+int DriverSource::readIntDriver(const char *fullpath, int *value) {
+ char data[40]; // Sufficiently large to hold any integer
+ const int fd = open(fullpath, O_RDONLY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ const ssize_t bytes = read(fd, data, sizeof(data) - 1);
+ close(fd);
+ if (bytes < 0) {
+ return -1;
+ }
+ data[bytes] = '\0';
+
+ char *endptr;
+ errno = 0;
+ *value = strtol(data, &endptr, 10);
+ if (errno != 0 || *endptr != '\n') {
+ logg->logMessage("Invalid value in file %s", fullpath);
+ return -1;
+ }
+
+ return 0;
+}
+
+int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) {
+ char data[40]; // Sufficiently large to hold any integer
+ const int fd = open(fullpath, O_RDONLY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ const ssize_t bytes = read(fd, data, sizeof(data) - 1);
+ close(fd);
+ if (bytes < 0) {
+ return -1;
+ }
+ data[bytes] = '\0';
+
+ char *endptr;
+ errno = 0;
+ *value = strtoll(data, &endptr, 10);
+ if (errno != 0 || (*endptr != '\n' && *endptr != '\0')) {
+ logg->logMessage("Invalid value in file %s", fullpath);
+ return -1;
+ }
+
+ return 0;
+}
+
+int DriverSource::writeDriver(const char *fullpath, const char *data) {
+ int fd = open(fullpath, O_WRONLY);
+ if (fd < 0) {
+ return -1;
+ }
+ if (::write(fd, data, strlen(data)) < 0) {
+ close(fd);
+ logg->logMessage("Opened but could not write to %s", fullpath);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+int DriverSource::writeDriver(const char *path, int value) {
+ char data[40]; // Sufficiently large to hold any integer
+ snprintf(data, sizeof(data), "%d", value);
+ return writeDriver(path, data);
+}
+
+int DriverSource::writeDriver(const char *path, int64_t value) {
+ char data[40]; // Sufficiently large to hold any integer
+ snprintf(data, sizeof(data), "%" PRIi64, value);
+ return writeDriver(path, data);
+}
+
+int DriverSource::writeReadDriver(const char *path, int *value) {
+ if (writeDriver(path, *value) || readIntDriver(path, value)) {
+ return -1;
+ }
+ return 0;
+}
+
+int DriverSource::writeReadDriver(const char *path, int64_t *value) {
+ if (writeDriver(path, *value) || readInt64Driver(path, value)) {
+ return -1;
+ }
+ return 0;
+}
diff --git a/tools/gator/daemon/DriverSource.h b/tools/gator/daemon/DriverSource.h
new file mode 100644
index 000000000000..ec27b0815bbf
--- /dev/null
+++ b/tools/gator/daemon/DriverSource.h
@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 DRIVERSOURCE_H
+#define DRIVERSOURCE_H
+
+#include <semaphore.h>
+#include <stdint.h>
+
+#include "Source.h"
+
+class Buffer;
+class Fifo;
+
+class DriverSource : public Source {
+public:
+ DriverSource(sem_t *senderSem, sem_t *startProfile);
+ ~DriverSource();
+
+ bool prepare();
+ void run();
+ void interrupt();
+
+ bool isDone();
+ void write(Sender *sender);
+
+ static int readIntDriver(const char *fullpath, int *value);
+ static int readInt64Driver(const char *fullpath, int64_t *value);
+ static int writeDriver(const char *fullpath, const char *data);
+ static int writeDriver(const char *path, int value);
+ static int writeDriver(const char *path, int64_t value);
+ static int writeReadDriver(const char *path, int *value);
+ static int writeReadDriver(const char *path, int64_t *value);
+
+private:
+ static void *bootstrapThreadStatic(void *arg);
+ void bootstrapThread();
+
+ Buffer *mBuffer;
+ Fifo *mFifo;
+ sem_t *const mSenderSem;
+ sem_t *const mStartProfile;
+ int mBufferSize;
+ int mBufferFD;
+ int mLength;
+
+ // Intentionally unimplemented
+ DriverSource(const DriverSource &);
+ DriverSource &operator=(const DriverSource &);
+};
+
+#endif // DRIVERSOURCE_H
diff --git a/tools/gator/daemon/DynBuf.cpp b/tools/gator/daemon/DynBuf.cpp
new file mode 100644
index 000000000000..6f92b336ae19
--- /dev/null
+++ b/tools/gator/daemon/DynBuf.cpp
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "DynBuf.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "Logging.h"
+
+// Pick an aggressive size as buffer is primarily used for disk IO
+#define MIN_BUFFER_FREE (1 << 12)
+
+int DynBuf::resize(const size_t minCapacity) {
+ size_t scaledCapacity = 2 * capacity;
+ if (scaledCapacity < minCapacity) {
+ scaledCapacity = minCapacity;
+ }
+ if (scaledCapacity < 2 * MIN_BUFFER_FREE) {
+ scaledCapacity = 2 * MIN_BUFFER_FREE;
+ }
+ capacity = scaledCapacity;
+
+ buf = static_cast<char *>(realloc(buf, capacity));
+ if (buf == NULL) {
+ return -errno;
+ }
+
+ return 0;
+}
+
+bool DynBuf::read(const char *const path) {
+ int result = false;
+
+ const int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ length = 0;
+
+ for (;;) {
+ const size_t minCapacity = length + MIN_BUFFER_FREE + 1;
+ if (capacity < minCapacity) {
+ if (resize(minCapacity) != 0) {
+ logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ }
+
+ const ssize_t bytes = ::read(fd, buf + length, capacity - length - 1);
+ if (bytes < 0) {
+ logg->logMessage("%s(%s:%i): read failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ } else if (bytes == 0) {
+ break;
+ }
+ length += bytes;
+ }
+
+ buf[length] = '\0';
+ result = true;
+
+ fail:
+ close(fd);
+
+ return result;
+}
+
+int DynBuf::readlink(const char *const path) {
+ ssize_t bytes = MIN_BUFFER_FREE;
+
+ for (;;) {
+ if (static_cast<size_t>(bytes) >= capacity) {
+ const int err = resize(2 * bytes);
+ if (err != 0) {
+ return err;
+ }
+ }
+ bytes = ::readlink(path, buf, capacity);
+ if (bytes < 0) {
+ return -errno;
+ } else if (static_cast<size_t>(bytes) < capacity) {
+ break;
+ }
+ }
+
+ length = bytes;
+ buf[bytes] = '\0';
+
+ return 0;
+}
+
+bool DynBuf::printf(const char *format, ...) {
+ va_list ap;
+
+ if (capacity <= 0) {
+ if (resize(2 * MIN_BUFFER_FREE) != 0) {
+ logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ va_start(ap, format);
+ int bytes = vsnprintf(buf, capacity, format, ap);
+ va_end(ap);
+ if (bytes < 0) {
+ logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (static_cast<size_t>(bytes) > capacity) {
+ if (resize(bytes + 1) != 0) {
+ logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ va_start(ap, format);
+ bytes = vsnprintf(buf, capacity, format, ap);
+ va_end(ap);
+ if (bytes < 0) {
+ logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ length = bytes;
+
+ return true;
+}
diff --git a/tools/gator/daemon/DynBuf.h b/tools/gator/daemon/DynBuf.h
new file mode 100644
index 000000000000..2f4554ab2e49
--- /dev/null
+++ b/tools/gator/daemon/DynBuf.h
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 DYNBUF_H
+#define DYNBUF_H
+
+#include <stdlib.h>
+
+class DynBuf {
+public:
+ DynBuf() : capacity(0), length(0), buf(NULL) {}
+ ~DynBuf() {
+ reset();
+ }
+
+ inline void reset() {
+ capacity = 0;
+ length = 0;
+ if (buf != NULL) {
+ free(buf);
+ buf = NULL;
+ }
+ }
+
+ bool read(const char *const path);
+ // On error instead of printing the error and returning false, this returns -errno
+ int readlink(const char *const path);
+ __attribute__ ((format(printf, 2, 3)))
+ bool printf(const char *format, ...);
+
+ size_t getLength() const { return length; }
+ const char *getBuf() const { return buf; }
+ char *getBuf() { return buf; }
+
+private:
+ int resize(const size_t minCapacity);
+
+ size_t capacity;
+ size_t length;
+ char *buf;
+
+ // Intentionally undefined
+ DynBuf(const DynBuf &);
+ DynBuf &operator=(const DynBuf &);
+};
+
+#endif // DYNBUF_H
diff --git a/tools/gator/daemon/EventsXML.cpp b/tools/gator/daemon/EventsXML.cpp
new file mode 100644
index 000000000000..cf0192ef671f
--- /dev/null
+++ b/tools/gator/daemon/EventsXML.cpp
@@ -0,0 +1,76 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "EventsXML.h"
+
+#include "CapturedXML.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
+
+mxml_node_t *EventsXML::getTree() {
+#include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len
+ char path[PATH_MAX];
+ mxml_node_t *xml;
+ FILE *fl;
+
+ // Avoid unused variable warning
+ (void)events_xml_len;
+
+ // Load the provided or default events xml
+ if (gSessionData->mEventsXMLPath) {
+ strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX);
+ } else {
+ util->getApplicationFullPath(path, PATH_MAX);
+ strncat(path, "events.xml", PATH_MAX - strlen(path) - 1);
+ }
+ fl = fopen(path, "r");
+ if (fl) {
+ xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK);
+ fclose(fl);
+ } else {
+ logg->logMessage("Unable to locate events.xml, using default");
+ xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK);
+ }
+
+ return xml;
+}
+
+char *EventsXML::getXML() {
+ mxml_node_t *xml = getTree();
+
+ // Add dynamic events from the drivers
+ mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND);
+ if (!events) {
+ logg->logMessage("Unable to find <events> node in the events.xml");
+ handleException();
+ }
+ for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
+ driver->writeEvents(events);
+ }
+
+ char *string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
+ mxmlDelete(xml);
+
+ return string;
+}
+
+void EventsXML::write(const char *path) {
+ char file[PATH_MAX];
+
+ // Set full path
+ snprintf(file, PATH_MAX, "%s/events.xml", path);
+
+ char *buf = getXML();
+ if (util->writeToDisk(file, buf) < 0) {
+ logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file);
+ handleException();
+ }
+
+ free(buf);
+}
diff --git a/tools/gator/daemon/EventsXML.h b/tools/gator/daemon/EventsXML.h
new file mode 100644
index 000000000000..ff7a02fd3c78
--- /dev/null
+++ b/tools/gator/daemon/EventsXML.h
@@ -0,0 +1,21 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 EVENTS_XML
+#define EVENTS_XML
+
+#include "mxml/mxml.h"
+
+class EventsXML {
+public:
+ mxml_node_t *getTree();
+ char *getXML();
+ void write(const char* path);
+};
+
+#endif // EVENTS_XML
diff --git a/tools/gator/daemon/ExternalSource.cpp b/tools/gator/daemon/ExternalSource.cpp
new file mode 100644
index 000000000000..b6ec301d0c08
--- /dev/null
+++ b/tools/gator/daemon/ExternalSource.cpp
@@ -0,0 +1,215 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "ExternalSource.h"
+
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "Logging.h"
+#include "OlySocket.h"
+#include "SessionData.h"
+
+static const char MALI_VIDEO[] = "\0mali-video";
+static const char MALI_VIDEO_STARTUP[] = "\0mali-video-startup";
+static const char MALI_VIDEO_V1[] = "MALI_VIDEO 1\n";
+
+static bool setNonblock(const int fd) {
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ logg->logMessage("fcntl getfl failed");
+ return false;
+ }
+
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
+ logg->logMessage("fcntl setfl failed");
+ return false;
+ }
+
+ return true;
+}
+
+ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mInterruptFd(-1), mMveUds(-1) {
+ sem_init(&mBufferSem, 0, 0);
+}
+
+ExternalSource::~ExternalSource() {
+}
+
+void ExternalSource::waitFor(const uint64_t currTime, const int bytes) {
+ while (mBuffer.bytesAvailable() <= bytes) {
+ mBuffer.check(currTime);
+ sem_wait(&mBufferSem);
+ }
+}
+
+void ExternalSource::configureConnection(const int fd, const char *const handshake, size_t size) {
+ if (!setNonblock(fd)) {
+ logg->logError(__FILE__, __LINE__, "Unable to set nonblock on fh");
+ handleException();
+ }
+
+ if (!mMonitor.add(fd)) {
+ logg->logError(__FILE__, __LINE__, "Unable to add fh to monitor");
+ handleException();
+ }
+
+ // Write the handshake to the circular buffer
+ waitFor(1, Buffer::MAXSIZE_PACK32 + 4 + size - 1);
+ mBuffer.packInt(fd);
+ mBuffer.writeLEInt((unsigned char *)mBuffer.getWritePos(), size - 1);
+ mBuffer.advanceWrite(4);
+ mBuffer.writeBytes(handshake, size - 1);
+}
+
+bool ExternalSource::connectMve() {
+ if (!gSessionData->maliVideo.countersEnabled()) {
+ return true;
+ }
+
+ mMveUds = OlySocket::connect(MALI_VIDEO, sizeof(MALI_VIDEO));
+ if (mMveUds < 0) {
+ return false;
+ }
+
+ if (!gSessionData->maliVideo.start(mMveUds)) {
+ return false;
+ }
+
+ configureConnection(mMveUds, MALI_VIDEO_V1, sizeof(MALI_VIDEO_V1));
+
+ return true;
+}
+
+bool ExternalSource::prepare() {
+ if (!mMonitor.init() || !setNonblock(mMveStartupUds.getFd()) || !mMonitor.add(mMveStartupUds.getFd())) {
+ return false;
+ }
+
+ connectMve();
+
+ return true;
+}
+
+void ExternalSource::run() {
+ int pipefd[2];
+
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-external", 0, 0, 0);
+
+ if (pipe(pipefd) != 0) {
+ logg->logError(__FILE__, __LINE__, "pipe failed");
+ handleException();
+ }
+ mInterruptFd = pipefd[1];
+
+ if (!mMonitor.add(pipefd[0])) {
+ logg->logError(__FILE__, __LINE__, "Monitor::add failed");
+ handleException();
+ }
+
+ while (gSessionData->mSessionIsActive) {
+ struct epoll_event events[16];
+ // Clear any pending sem posts
+ while (sem_trywait(&mBufferSem) == 0);
+ int ready = mMonitor.wait(events, ARRAY_LENGTH(events), -1);
+ if (ready < 0) {
+ logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
+ handleException();
+ }
+
+ const uint64_t currTime = getTime();
+
+ for (int i = 0; i < ready; ++i) {
+ const int fd = events[i].data.fd;
+ if (fd == mMveStartupUds.getFd()) {
+ // Mali Video Engine says it's alive
+ int client = mMveStartupUds.acceptConnection();
+ // Don't read from this connection, establish a new connection to Mali-V500
+ close(client);
+ if (!connectMve()) {
+ logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali video connection");
+ handleException();
+ }
+ } else if (fd == pipefd[0]) {
+ // Means interrupt has been called and mSessionIsActive should be reread
+ } else {
+ while (true) {
+ waitFor(currTime, Buffer::MAXSIZE_PACK32 + 4);
+
+ mBuffer.packInt(fd);
+ char *const bytesPos = mBuffer.getWritePos();
+ mBuffer.advanceWrite(4);
+ const int contiguous = mBuffer.contiguousSpaceAvailable();
+ const int bytes = read(fd, mBuffer.getWritePos(), contiguous);
+ if (bytes < 0) {
+ if (errno == EAGAIN) {
+ // Nothing left to read, and Buffer convention dictates that writePos can't go backwards
+ mBuffer.writeLEInt((unsigned char *)bytesPos, 0);
+ break;
+ }
+ // Something else failed, close the socket
+ mBuffer.writeLEInt((unsigned char *)bytesPos, -1);
+ close(fd);
+ break;
+ } else if (bytes == 0) {
+ // The other side is closed
+ mBuffer.writeLEInt((unsigned char *)bytesPos, -1);
+ close(fd);
+ break;
+ }
+
+ mBuffer.writeLEInt((unsigned char *)bytesPos, bytes);
+ mBuffer.advanceWrite(bytes);
+
+ // Short reads also mean nothing is left to read
+ if (bytes < contiguous) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Only call mBufferCheck once per iteration
+ mBuffer.check(currTime);
+ }
+
+ mBuffer.setDone();
+
+ mInterruptFd = -1;
+ close(pipefd[0]);
+ close(pipefd[1]);
+}
+
+void ExternalSource::interrupt() {
+ if (mInterruptFd >= 0) {
+ int8_t c = 0;
+ // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread
+ if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) {
+ logg->logError(__FILE__, __LINE__, "write failed");
+ handleException();
+ }
+ }
+}
+
+bool ExternalSource::isDone() {
+ return mBuffer.isDone();
+}
+
+void ExternalSource::write(Sender *sender) {
+ // Don't send external data until the summary packet is sent so that monotonic delta is available
+ if (!gSessionData->mSentSummary) {
+ return;
+ }
+ if (!mBuffer.isDone()) {
+ mBuffer.write(sender);
+ sem_post(&mBufferSem);
+ }
+}
diff --git a/tools/gator/daemon/ExternalSource.h b/tools/gator/daemon/ExternalSource.h
new file mode 100644
index 000000000000..2e7ed27df255
--- /dev/null
+++ b/tools/gator/daemon/ExternalSource.h
@@ -0,0 +1,49 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 EXTERNALSOURCE_H
+#define EXTERNALSOURCE_H
+
+#include <semaphore.h>
+
+#include "Buffer.h"
+#include "Monitor.h"
+#include "OlySocket.h"
+#include "Source.h"
+
+// Unix domain socket counters from external sources like graphics drivers
+class ExternalSource : public Source {
+public:
+ ExternalSource(sem_t *senderSem);
+ ~ExternalSource();
+
+ bool prepare();
+ void run();
+ void interrupt();
+
+ bool isDone();
+ void write(Sender *sender);
+
+private:
+ void waitFor(const uint64_t currTime, const int bytes);
+ void configureConnection(const int fd, const char *const handshake, size_t size);
+ bool connectMve();
+
+ sem_t mBufferSem;
+ Buffer mBuffer;
+ Monitor mMonitor;
+ OlyServerSocket mMveStartupUds;
+ int mInterruptFd;
+ int mMveUds;
+
+ // Intentionally unimplemented
+ ExternalSource(const ExternalSource &);
+ ExternalSource &operator=(const ExternalSource &);
+};
+
+#endif // EXTERNALSOURCE_H
diff --git a/tools/gator/daemon/FSDriver.cpp b/tools/gator/daemon/FSDriver.cpp
new file mode 100644
index 000000000000..40c8df1af222
--- /dev/null
+++ b/tools/gator/daemon/FSDriver.cpp
@@ -0,0 +1,212 @@
+/**
+ * Copyright (C) ARM Limited 2014. All rights reserved.
+ *
+ * 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 "FSDriver.h"
+
+#include <fcntl.h>
+#include <regex.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "Buffer.h"
+#include "Counter.h"
+#include "DriverSource.h"
+#include "Logging.h"
+#include "SessionData.h"
+
+class FSCounter {
+public:
+ FSCounter(FSCounter *next, char *name, const char *regex);
+ ~FSCounter();
+
+ FSCounter *getNext() const { return next; }
+ int getKey() const { return key; }
+ bool isEnabled() const { return enabled; }
+ void setEnabled(const bool enabled) { this->enabled = enabled; }
+ const char *getName() const { return name; }
+ int64_t read();
+
+private:
+ FSCounter *const next;
+ regex_t reg;
+ char *name;
+ const int key;
+ int enabled : 1,
+ useRegex : 1;
+
+ // Intentionally unimplemented
+ FSCounter(const FSCounter &);
+ FSCounter &operator=(const FSCounter &);
+};
+
+FSCounter::FSCounter(FSCounter *next, char *name, const char *regex) : next(next), name(name), key(getEventKey()), enabled(false), useRegex(regex != NULL) {
+ if (useRegex) {
+ int result = regcomp(&reg, regex, REG_EXTENDED);
+ if (result != 0) {
+ char buf[128];
+ regerror(result, &reg, buf, sizeof(buf));
+ logg->logError(__FILE__, __LINE__, "Invalid regex '%s': %s", regex, buf);
+ handleException();
+ }
+ }
+}
+
+FSCounter::~FSCounter() {
+ free(name);
+ if (useRegex) {
+ regfree(&reg);
+ }
+}
+
+int64_t FSCounter::read() {
+ int64_t value;
+ if (useRegex) {
+ char buf[4096];
+ size_t pos = 0;
+ const int fd = open(name, O_RDONLY);
+ if (fd < 0) {
+ goto fail;
+ }
+ while (pos < sizeof(buf) - 1) {
+ const ssize_t bytes = ::read(fd, buf + pos, sizeof(buf) - pos - 1);
+ if (bytes < 0) {
+ goto fail;
+ } else if (bytes == 0) {
+ break;
+ }
+ pos += bytes;
+ }
+ close(fd);
+ buf[pos] = '\0';
+
+ regmatch_t match[2];
+ int result = regexec(&reg, buf, 2, match, 0);
+ if (result != 0) {
+ regerror(result, &reg, buf, sizeof(buf));
+ logg->logError(__FILE__, __LINE__, "Parsing %s failed: %s", name, buf);
+ handleException();
+ }
+
+ if (match[1].rm_so < 0) {
+ logg->logError(__FILE__, __LINE__, "Parsing %s failed", name);
+ handleException();
+ }
+ char *endptr;
+ errno = 0;
+ value = strtoll(buf + match[1].rm_so, &endptr, 0);
+ if (errno != 0) {
+ logg->logError(__FILE__, __LINE__, "Parsing %s failed: %s", name, strerror(errno));
+ handleException();
+ }
+ } else {
+ if (DriverSource::readInt64Driver(name, &value) != 0) {
+ goto fail;
+ }
+ }
+ return value;
+
+ fail:
+ logg->logError(__FILE__, __LINE__, "Unable to read %s", name);
+ handleException();
+}
+
+FSDriver::FSDriver() : counters(NULL) {
+}
+
+FSDriver::~FSDriver() {
+ while (counters != NULL) {
+ FSCounter * counter = counters;
+ counters = counter->getNext();
+ delete counter;
+ }
+}
+
+void FSDriver::setup(mxml_node_t *const xml) {
+ // fs driver does not currently work with perf
+ if (gSessionData->perf.isSetup()) {
+ return;
+ }
+
+ mxml_node_t *node = xml;
+ while (true) {
+ node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND);
+ if (node == NULL) {
+ break;
+ }
+ const char *counter = mxmlElementGetAttr(node, "counter");
+ if ((counter != NULL) && (counter[0] == '/')) {
+ const char *regex = mxmlElementGetAttr(node, "regex");
+ counters = new FSCounter(counters, strdup(counter), regex);
+ }
+ }
+}
+
+FSCounter *FSDriver::findCounter(const Counter &counter) const {
+ for (FSCounter * fsCounter = counters; fsCounter != NULL; fsCounter = fsCounter->getNext()) {
+ if (strcmp(fsCounter->getName(), counter.getType()) == 0) {
+ return fsCounter;
+ }
+ }
+
+ return NULL;
+}
+
+bool FSDriver::claimCounter(const Counter &counter) const {
+ return findCounter(counter) != NULL;
+}
+
+bool FSDriver::countersEnabled() const {
+ for (FSCounter *counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (counter->isEnabled()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void FSDriver::resetCounters() {
+ for (FSCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ counter->setEnabled(false);
+ }
+}
+
+void FSDriver::setupCounter(Counter &counter) {
+ FSCounter *const fsCounter = findCounter(counter);
+ if (fsCounter == NULL) {
+ counter.setEnabled(false);
+ return;
+ }
+ fsCounter->setEnabled(true);
+ counter.setKey(fsCounter->getKey());
+}
+
+int FSDriver::writeCounters(mxml_node_t *root) const {
+ int count = 0;
+ for (FSCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (access(counter->getName(), R_OK) == 0) {
+ mxml_node_t *node = mxmlNewElement(root, "counter");
+ mxmlElementSetAttr(node, "name", counter->getName());
+ ++count;
+ }
+ }
+
+ return count;
+}
+
+void FSDriver::start() {
+}
+
+void FSDriver::read(Buffer * const buffer) {
+ for (FSCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (!counter->isEnabled()) {
+ continue;
+ }
+ buffer->event(counter->getKey(), counter->read());
+ }
+}
diff --git a/tools/gator/daemon/FSDriver.h b/tools/gator/daemon/FSDriver.h
new file mode 100644
index 000000000000..ef3955362331
--- /dev/null
+++ b/tools/gator/daemon/FSDriver.h
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) ARM Limited 2014. All rights reserved.
+ *
+ * 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 FSDRIVER_H
+#define FSDRIVER_H
+
+#include "Driver.h"
+
+class Buffer;
+class FSCounter;
+
+class FSDriver : public Driver {
+public:
+ FSDriver();
+ ~FSDriver();
+
+ void setup(mxml_node_t *const xml);
+
+ bool claimCounter(const Counter &counter) const;
+ bool countersEnabled() const;
+ void resetCounters();
+ void setupCounter(Counter &counter);
+
+ int writeCounters(mxml_node_t *root) const;
+
+ void start();
+ void read(Buffer * buffer);
+
+private:
+ FSCounter *findCounter(const Counter &counter) const;
+
+ FSCounter *counters;
+
+ // Intentionally unimplemented
+ FSDriver(const FSDriver &);
+ FSDriver &operator=(const FSDriver &);
+};
+
+#endif // FSDRIVER_H
diff --git a/tools/gator/daemon/Fifo.cpp b/tools/gator/daemon/Fifo.cpp
new file mode 100644
index 000000000000..f672e92a6807
--- /dev/null
+++ b/tools/gator/daemon/Fifo.cpp
@@ -0,0 +1,130 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "Fifo.h"
+
+#include <stdlib.h>
+#ifdef WIN32
+#define valloc malloc
+#endif
+
+#include "Logging.h"
+
+// bufferSize is the amount of data to be filled
+// singleBufferSize is the maximum size that may be filled during a single write
+// (bufferSize + singleBufferSize) will be allocated
+Fifo::Fifo(int singleBufferSize, int bufferSize, sem_t* readerSem) {
+ mWrite = mRead = mReadCommit = mRaggedEnd = 0;
+ mWrapThreshold = bufferSize;
+ mSingleBufferSize = singleBufferSize;
+ mReaderSem = readerSem;
+ mBuffer = (char*)valloc(bufferSize + singleBufferSize);
+ mEnd = false;
+
+ if (mBuffer == NULL) {
+ logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize + singleBufferSize);
+ handleException();
+ }
+
+ if (sem_init(&mWaitForSpaceSem, 0, 0)) {
+ logg->logError(__FILE__, __LINE__, "sem_init() failed");
+ handleException();
+ }
+}
+
+Fifo::~Fifo() {
+ free(mBuffer);
+ sem_destroy(&mWaitForSpaceSem);
+}
+
+int Fifo::numBytesFilled() const {
+ return mWrite - mRead + mRaggedEnd;
+}
+
+char* Fifo::start() const {
+ return mBuffer;
+}
+
+bool Fifo::isEmpty() const {
+ return mRead == mWrite && mRaggedEnd == 0;
+}
+
+bool Fifo::isFull() const {
+ return willFill(0);
+}
+
+// Determines if the buffer will fill assuming 'additional' bytes will be added to the buffer
+// 'full' means there is less than singleBufferSize bytes available contiguously; it does not mean there are zero bytes available
+bool Fifo::willFill(int additional) const {
+ if (mWrite > mRead) {
+ if (numBytesFilled() + additional < mWrapThreshold) {
+ return false;
+ }
+ } else {
+ if (numBytesFilled() + additional < mWrapThreshold - mSingleBufferSize) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// This function will stall until contiguous singleBufferSize bytes are available
+char* Fifo::write(int length) {
+ if (length <= 0) {
+ length = 0;
+ mEnd = true;
+ }
+
+ // update the write pointer
+ mWrite += length;
+
+ // handle the wrap-around
+ if (mWrite >= mWrapThreshold) {
+ mRaggedEnd = mWrite;
+ mWrite = 0;
+ }
+
+ // send a notification that data is ready
+ sem_post(mReaderSem);
+
+ // wait for space
+ while (isFull()) {
+ sem_wait(&mWaitForSpaceSem);
+ }
+
+ return &mBuffer[mWrite];
+}
+
+void Fifo::release() {
+ // update the read pointer now that the data has been handled
+ mRead = mReadCommit;
+
+ // handle the wrap-around
+ if (mRead >= mWrapThreshold) {
+ mRaggedEnd = mRead = mReadCommit = 0;
+ }
+
+ // send a notification that data is free (space is available)
+ sem_post(&mWaitForSpaceSem);
+}
+
+// This function will return null if no data is available
+char* Fifo::read(int *const length) {
+ // wait for data
+ if (isEmpty() && !mEnd) {
+ return NULL;
+ }
+
+ // obtain the length
+ do {
+ mReadCommit = mRaggedEnd ? mRaggedEnd : mWrite;
+ *length = mReadCommit - mRead;
+ } while (*length < 0); // plugs race condition without using semaphores
+
+ return &mBuffer[mRead];
+}
diff --git a/tools/gator/daemon/Fifo.h b/tools/gator/daemon/Fifo.h
new file mode 100644
index 000000000000..bdda3f549b50
--- /dev/null
+++ b/tools/gator/daemon/Fifo.h
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 __FIFO_H__
+#define __FIFO_H__
+
+#ifdef WIN32
+#include <windows.h>
+#define sem_t HANDLE
+#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, LONG_MAX, NULL)) == NULL)
+#define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE)
+#define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL)
+#define sem_destroy(sem) CloseHandle(*(sem))
+#else
+#include <semaphore.h>
+#endif
+
+class Fifo {
+public:
+ Fifo(int singleBufferSize, int totalBufferSize, sem_t* readerSem);
+ ~Fifo();
+ int numBytesFilled() const;
+ bool isEmpty() const;
+ bool isFull() const;
+ bool willFill(int additional) const;
+ char* start() const;
+ char* write(int length);
+ void release();
+ char* read(int *const length);
+
+private:
+ int mSingleBufferSize, mWrite, mRead, mReadCommit, mRaggedEnd, mWrapThreshold;
+ sem_t mWaitForSpaceSem;
+ sem_t* mReaderSem;
+ char* mBuffer;
+ bool mEnd;
+
+ // Intentionally unimplemented
+ Fifo(const Fifo &);
+ Fifo &operator=(const Fifo &);
+};
+
+#endif //__FIFO_H__
diff --git a/tools/gator/daemon/Hwmon.cpp b/tools/gator/daemon/Hwmon.cpp
new file mode 100644
index 000000000000..e44424743ef0
--- /dev/null
+++ b/tools/gator/daemon/Hwmon.cpp
@@ -0,0 +1,352 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "Hwmon.h"
+
+#include "libsensors/sensors.h"
+
+#include "Buffer.h"
+#include "Counter.h"
+#include "Logging.h"
+#include "SessionData.h"
+
+class HwmonCounter {
+public:
+ HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature);
+ ~HwmonCounter();
+
+ HwmonCounter *getNext() const { return next; }
+ int getKey() const { return key; }
+ bool isEnabled() const { return enabled; }
+ const char *getName() const { return name; }
+ const char *getLabel() const { return label; }
+ const char *getTitle() const { return title; }
+ bool isDuplicate() const { return duplicate; }
+ const char *getDisplay() const { return display; }
+ const char *getCounterClass() const { return counter_class; }
+ const char *getUnit() const { return unit; }
+ int getModifier() const { return modifier; }
+
+ void setEnabled(const bool enabled) {
+ this->enabled = enabled;
+ // canRead will clear enabled if the counter is not readable
+ canRead();
+ }
+
+ double read();
+ bool canRead();
+
+private:
+ void init(const sensors_chip_name *chip, const sensors_feature *feature);
+
+ HwmonCounter *const next;
+ const int key;
+ int polled : 1,
+ readable : 1,
+ enabled : 1,
+ monotonic: 1,
+ duplicate : 1;
+
+ const sensors_chip_name *chip;
+ const sensors_feature *feature;
+
+ char *name;
+ char *label;
+ const char *title;
+ const char *display;
+ const char *counter_class;
+ const char *unit;
+ int modifier;
+ double previous_value;
+
+ sensors_subfeature_type input;
+
+ // Intentionally unimplemented
+ HwmonCounter(const HwmonCounter &);
+ HwmonCounter &operator=(const HwmonCounter &);
+};
+
+HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(getEventKey()), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) {
+
+ int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1;
+ char *chip_name = new char[len];
+ sensors_snprintf_chip_name(chip_name, len, chip);
+
+ len = snprintf(NULL, 0, "hwmon_%s_%d", chip_name, feature->number) + 1;
+ name = new char[len];
+ snprintf(name, len, "hwmon_%s_%d", chip_name, feature->number);
+
+ delete [] chip_name;
+
+ label = sensors_get_label(chip, feature);
+
+ switch (feature->type) {
+ case SENSORS_FEATURE_IN:
+ title = "Voltage";
+ input = SENSORS_SUBFEATURE_IN_INPUT;
+ display = "maximum";
+ counter_class = "absolute";
+ unit = "V";
+ modifier = 1000;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_FAN:
+ title = "Fan";
+ input = SENSORS_SUBFEATURE_FAN_INPUT;
+ display = "average";
+ counter_class = "absolute";
+ unit = "RPM";
+ modifier = 1;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_TEMP:
+ title = "Temperature";
+ input = SENSORS_SUBFEATURE_TEMP_INPUT;
+ display = "maximum";
+ counter_class = "absolute";
+ unit = "°C";
+ modifier = 1000;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_POWER:
+ title = "Power";
+ input = SENSORS_SUBFEATURE_POWER_INPUT;
+ display = "maximum";
+ counter_class = "absolute";
+ unit = "W";
+ modifier = 1000000;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_ENERGY:
+ title = "Energy";
+ input = SENSORS_SUBFEATURE_ENERGY_INPUT;
+ display = "accumulate";
+ counter_class = "delta";
+ unit = "J";
+ modifier = 1000000;
+ monotonic = true;
+ break;
+ case SENSORS_FEATURE_CURR:
+ title = "Current";
+ input = SENSORS_SUBFEATURE_CURR_INPUT;
+ display = "maximum";
+ counter_class = "absolute";
+ unit = "A";
+ modifier = 1000;
+ monotonic = false;
+ break;
+ case SENSORS_FEATURE_HUMIDITY:
+ title = "Humidity";
+ input = SENSORS_SUBFEATURE_HUMIDITY_INPUT;
+ display = "average";
+ counter_class = "absolute";
+ unit = "%";
+ modifier = 1000;
+ monotonic = false;
+ break;
+ default:
+ logg->logError(__FILE__, __LINE__, "Unsupported hwmon feature %i", feature->type);
+ handleException();
+ }
+
+ for (HwmonCounter * counter = next; counter != NULL; counter = counter->getNext()) {
+ if (strcmp(label, counter->getLabel()) == 0 && strcmp(title, counter->getTitle()) == 0) {
+ duplicate = true;
+ counter->duplicate = true;
+ break;
+ }
+ }
+}
+
+HwmonCounter::~HwmonCounter() {
+ free((void *)label);
+ delete [] name;
+}
+
+double HwmonCounter::read() {
+ double value;
+ double result;
+ const sensors_subfeature *subfeature;
+
+ // Keep in sync with canRead
+ subfeature = sensors_get_subfeature(chip, feature, input);
+ if (!subfeature) {
+ logg->logError(__FILE__, __LINE__, "No input value for hwmon sensor %s", label);
+ handleException();
+ }
+
+ if (sensors_get_value(chip, subfeature->number, &value) != 0) {
+ logg->logError(__FILE__, __LINE__, "Can't get input value for hwmon sensor %s", label);
+ handleException();
+ }
+
+ result = (monotonic ? value - previous_value : value);
+ previous_value = value;
+
+ return result;
+}
+
+bool HwmonCounter::canRead() {
+ if (!polled) {
+ double value;
+ const sensors_subfeature *subfeature;
+ bool result = true;
+
+ subfeature = sensors_get_subfeature(chip, feature, input);
+ if (!subfeature) {
+ result = false;
+ } else {
+ result = sensors_get_value(chip, subfeature->number, &value) == 0;
+ }
+
+ polled = true;
+ readable = result;
+ }
+
+ enabled &= readable;
+
+ return readable;
+}
+
+Hwmon::Hwmon() : counters(NULL) {
+}
+
+Hwmon::~Hwmon() {
+ while (counters != NULL) {
+ HwmonCounter * counter = counters;
+ counters = counter->getNext();
+ delete counter;
+ }
+ sensors_cleanup();
+}
+
+void Hwmon::setup() {
+ // hwmon does not currently work with perf
+ if (gSessionData->perf.isSetup()) {
+ return;
+ }
+
+ int err = sensors_init(NULL);
+ if (err) {
+ logg->logMessage("Failed to initialize libsensors! (%d)", err);
+ return;
+ }
+ sensors_sysfs_no_scaling = 1;
+
+ int chip_nr = 0;
+ const sensors_chip_name *chip;
+ while ((chip = sensors_get_detected_chips(NULL, &chip_nr))) {
+ int feature_nr = 0;
+ const sensors_feature *feature;
+ while ((feature = sensors_get_features(chip, &feature_nr))) {
+ counters = new HwmonCounter(counters, chip, feature);
+ }
+ }
+}
+
+HwmonCounter *Hwmon::findCounter(const Counter &counter) const {
+ for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) {
+ if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) {
+ return hwmonCounter;
+ }
+ }
+
+ return NULL;
+}
+
+bool Hwmon::claimCounter(const Counter &counter) const {
+ return findCounter(counter) != NULL;
+}
+
+bool Hwmon::countersEnabled() const {
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (counter->isEnabled()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Hwmon::resetCounters() {
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ counter->setEnabled(false);
+ }
+}
+
+void Hwmon::setupCounter(Counter &counter) {
+ HwmonCounter *const hwmonCounter = findCounter(counter);
+ if (hwmonCounter == NULL) {
+ counter.setEnabled(false);
+ return;
+ }
+ hwmonCounter->setEnabled(true);
+ counter.setKey(hwmonCounter->getKey());
+}
+
+int Hwmon::writeCounters(mxml_node_t *root) const {
+ int count = 0;
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (!counter->canRead()) {
+ continue;
+ }
+ mxml_node_t *node = mxmlNewElement(root, "counter");
+ mxmlElementSetAttr(node, "name", counter->getName());
+ ++count;
+ }
+
+ return count;
+}
+
+void Hwmon::writeEvents(mxml_node_t *root) const {
+ root = mxmlNewElement(root, "category");
+ mxmlElementSetAttr(root, "name", "hwmon");
+
+ char buf[1024];
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (!counter->canRead()) {
+ continue;
+ }
+ mxml_node_t *node = mxmlNewElement(root, "event");
+ mxmlElementSetAttr(node, "counter", counter->getName());
+ mxmlElementSetAttr(node, "title", counter->getTitle());
+ if (counter->isDuplicate()) {
+ mxmlElementSetAttrf(node, "name", "%s (0x%x)", counter->getLabel(), counter->getKey());
+ } else {
+ mxmlElementSetAttr(node, "name", counter->getLabel());
+ }
+ mxmlElementSetAttr(node, "display", counter->getDisplay());
+ mxmlElementSetAttr(node, "class", counter->getCounterClass());
+ mxmlElementSetAttr(node, "units", counter->getUnit());
+ if (counter->getModifier() != 1) {
+ mxmlElementSetAttrf(node, "modifier", "%d", counter->getModifier());
+ }
+ if (strcmp(counter->getDisplay(), "average") == 0 || strcmp(counter->getDisplay(), "maximum") == 0) {
+ mxmlElementSetAttr(node, "average_selection", "yes");
+ }
+ snprintf(buf, sizeof(buf), "libsensors %s sensor %s (%s)", counter->getTitle(), counter->getLabel(), counter->getName());
+ mxmlElementSetAttr(node, "description", buf);
+ }
+}
+
+void Hwmon::start() {
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (!counter->isEnabled()) {
+ continue;
+ }
+ counter->read();
+ }
+}
+
+void Hwmon::read(Buffer * const buffer) {
+ for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
+ if (!counter->isEnabled()) {
+ continue;
+ }
+ buffer->event(counter->getKey(), counter->read());
+ }
+}
diff --git a/tools/gator/daemon/Hwmon.h b/tools/gator/daemon/Hwmon.h
new file mode 100644
index 000000000000..a22a3609f99f
--- /dev/null
+++ b/tools/gator/daemon/Hwmon.h
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 HWMON_H
+#define HWMON_H
+
+#include "Driver.h"
+
+class Buffer;
+class HwmonCounter;
+
+class Hwmon : public Driver {
+public:
+ Hwmon();
+ ~Hwmon();
+
+ void setup();
+
+ bool claimCounter(const Counter &counter) const;
+ bool countersEnabled() const;
+ void resetCounters();
+ void setupCounter(Counter &counter);
+
+ int writeCounters(mxml_node_t *root) const;
+ void writeEvents(mxml_node_t *root) const;
+
+ void start();
+ void read(Buffer * buffer);
+
+private:
+ HwmonCounter *findCounter(const Counter &counter) const;
+
+ HwmonCounter *counters;
+
+ // Intentionally unimplemented
+ Hwmon(const Hwmon &);
+ Hwmon &operator=(const Hwmon &);
+};
+
+#endif // HWMON_H
diff --git a/tools/gator/daemon/KMod.cpp b/tools/gator/daemon/KMod.cpp
new file mode 100644
index 000000000000..73e123d2f14e
--- /dev/null
+++ b/tools/gator/daemon/KMod.cpp
@@ -0,0 +1,110 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "KMod.h"
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#include "ConfigurationXML.h"
+#include "Counter.h"
+#include "DriverSource.h"
+#include "Logging.h"
+
+// Claim all the counters in /dev/gator/events
+bool KMod::claimCounter(const Counter &counter) const {
+ char text[128];
+ snprintf(text, sizeof(text), "/dev/gator/events/%s", counter.getType());
+ return access(text, F_OK) == 0;
+}
+
+void KMod::resetCounters() {
+ char base[128];
+ char text[128];
+
+ // Initialize all perf counters in the driver, i.e. set enabled to zero
+ struct dirent *ent;
+ DIR* dir = opendir("/dev/gator/events");
+ if (dir) {
+ while ((ent = readdir(dir)) != NULL) {
+ // skip hidden files, current dir, and parent dir
+ if (ent->d_name[0] == '.')
+ continue;
+ snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name);
+ snprintf(text, sizeof(text), "%s/enabled", base);
+ DriverSource::writeDriver(text, 0);
+ snprintf(text, sizeof(text), "%s/count", base);
+ DriverSource::writeDriver(text, 0);
+ }
+ closedir(dir);
+ }
+}
+
+void KMod::setupCounter(Counter &counter) {
+ char base[128];
+ char text[128];
+ snprintf(base, sizeof(base), "/dev/gator/events/%s", counter.getType());
+
+ snprintf(text, sizeof(text), "%s/enabled", base);
+ int enabled = true;
+ if (DriverSource::writeReadDriver(text, &enabled) || !enabled) {
+ counter.setEnabled(false);
+ return;
+ }
+
+ int value = 0;
+ snprintf(text, sizeof(text), "%s/key", base);
+ DriverSource::readIntDriver(text, &value);
+ counter.setKey(value);
+
+ snprintf(text, sizeof(text), "%s/cores", base);
+ if (DriverSource::readIntDriver(text, &value) == 0) {
+ counter.setCores(value);
+ }
+
+ snprintf(text, sizeof(text), "%s/event", base);
+ DriverSource::writeDriver(text, counter.getEvent());
+ snprintf(text, sizeof(text), "%s/count", base);
+ if (access(text, F_OK) == 0) {
+ int count = counter.getCount();
+ if (DriverSource::writeReadDriver(text, &count) && counter.getCount() > 0) {
+ logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount());
+ handleException();
+ }
+ counter.setCount(count);
+ } else if (counter.getCount() > 0) {
+ ConfigurationXML::remove();
+ logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y. The invalid configuration.xml has been removed.\n");
+ handleException();
+ }
+}
+
+int KMod::writeCounters(mxml_node_t *root) const {
+ struct dirent *ent;
+ mxml_node_t *counter;
+
+ // counters.xml is simply a file listing of /dev/gator/events
+ DIR* dir = opendir("/dev/gator/events");
+ if (dir == NULL) {
+ return 0;
+ }
+
+ int count = 0;
+ while ((ent = readdir(dir)) != NULL) {
+ // skip hidden files, current dir, and parent dir
+ if (ent->d_name[0] == '.')
+ continue;
+ counter = mxmlNewElement(root, "counter");
+ mxmlElementSetAttr(counter, "name", ent->d_name);
+ ++count;
+ }
+ closedir(dir);
+
+ return count;
+}
diff --git a/tools/gator/daemon/KMod.h b/tools/gator/daemon/KMod.h
new file mode 100644
index 000000000000..fb7fc8a8f9c6
--- /dev/null
+++ b/tools/gator/daemon/KMod.h
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 KMOD_H
+#define KMOD_H
+
+#include "Driver.h"
+
+// Driver for the gator kernel module
+class KMod : public Driver {
+public:
+ KMod() {}
+ ~KMod() {}
+
+ bool claimCounter(const Counter &counter) const;
+ void resetCounters();
+ void setupCounter(Counter &counter);
+
+ int writeCounters(mxml_node_t *root) const;
+};
+
+#endif // KMOD_H
diff --git a/tools/gator/daemon/LocalCapture.cpp b/tools/gator/daemon/LocalCapture.cpp
new file mode 100644
index 000000000000..d2a4b799d7ac
--- /dev/null
+++ b/tools/gator/daemon/LocalCapture.cpp
@@ -0,0 +1,131 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "LocalCapture.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "SessionData.h"
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "EventsXML.h"
+
+LocalCapture::LocalCapture() {}
+
+LocalCapture::~LocalCapture() {}
+
+void LocalCapture::createAPCDirectory(char* target_path) {
+ gSessionData->mAPCDir = createUniqueDirectory(target_path, ".apc");
+ if ((removeDirAndAllContents(gSessionData->mAPCDir) != 0 || mkdir(gSessionData->mAPCDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)) {
+ logg->logError(__FILE__, __LINE__, "Unable to create directory %s", gSessionData->mAPCDir);
+ handleException();
+ }
+}
+
+void LocalCapture::write(char* string) {
+ char file[PATH_MAX];
+
+ // Set full path
+ snprintf(file, PATH_MAX, "%s/session.xml", gSessionData->mAPCDir);
+
+ // Write the file
+ if (util->writeToDisk(file, string) < 0) {
+ logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file);
+ handleException();
+ }
+
+ // Write events XML
+ EventsXML eventsXML;
+ eventsXML.write(gSessionData->mAPCDir);
+}
+
+char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* ending) {
+ char* output;
+ char path[PATH_MAX];
+
+ // Ensure the path is an absolute path, i.e. starts with a slash
+ if (initialPath == 0 || strlen(initialPath) == 0) {
+ logg->logError(__FILE__, __LINE__, "Missing -o command line option required for a local capture.");
+ handleException();
+ } else if (initialPath[0] != '/') {
+ if (getcwd(path, PATH_MAX) == 0) {
+ logg->logMessage("Unable to retrieve the current working directory");
+ }
+ strncat(path, "/", PATH_MAX - strlen(path) - 1);
+ strncat(path, initialPath, PATH_MAX - strlen(path) - 1);
+ } else {
+ strncpy(path, initialPath, PATH_MAX);
+ path[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string
+ }
+
+ // Add ending if it is not already there
+ if (strcmp(&path[strlen(path) - strlen(ending)], ending) != 0) {
+ strncat(path, ending, PATH_MAX - strlen(path) - 1);
+ }
+
+ output = strdup(path);
+
+ return output;
+}
+
+int LocalCapture::removeDirAndAllContents(char* path) {
+ int error = 0;
+ struct stat mFileInfo;
+ // Does the path exist?
+ if (stat(path, &mFileInfo) == 0) {
+ // Is it a directory?
+ if (mFileInfo.st_mode & S_IFDIR) {
+ DIR * dir = opendir(path);
+ dirent* entry = readdir(dir);
+ while (entry) {
+ if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
+ char* newpath = (char*)malloc(strlen(path) + strlen(entry->d_name) + 2);
+ sprintf(newpath, "%s/%s", path, entry->d_name);
+ error = removeDirAndAllContents(newpath);
+ free(newpath);
+ if (error) {
+ break;
+ }
+ }
+ entry = readdir(dir);
+ }
+ closedir(dir);
+ if (error == 0) {
+ error = rmdir(path);
+ }
+ } else {
+ error = remove(path);
+ }
+ }
+ return error;
+}
+
+void LocalCapture::copyImages(ImageLinkList* ptr) {
+ char dstfilename[PATH_MAX];
+
+ while (ptr) {
+ strncpy(dstfilename, gSessionData->mAPCDir, PATH_MAX);
+ dstfilename[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string
+ if (gSessionData->mAPCDir[strlen(gSessionData->mAPCDir) - 1] != '/') {
+ strncat(dstfilename, "/", PATH_MAX - strlen(dstfilename) - 1);
+ }
+ strncat(dstfilename, util->getFilePart(ptr->path), PATH_MAX - strlen(dstfilename) - 1);
+ if (util->copyFile(ptr->path, dstfilename)) {
+ logg->logMessage("copied file %s to %s", ptr->path, dstfilename);
+ } else {
+ logg->logMessage("copy of file %s to %s failed", ptr->path, dstfilename);
+ }
+
+ ptr = ptr->next;
+ }
+}
diff --git a/tools/gator/daemon/LocalCapture.h b/tools/gator/daemon/LocalCapture.h
new file mode 100644
index 000000000000..b1e7219795cf
--- /dev/null
+++ b/tools/gator/daemon/LocalCapture.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 __LOCAL_CAPTURE_H__
+#define __LOCAL_CAPTURE_H__
+
+struct ImageLinkList;
+
+class LocalCapture {
+public:
+ LocalCapture();
+ ~LocalCapture();
+ void write(char* string);
+ void copyImages(ImageLinkList* ptr);
+ void createAPCDirectory(char* target_path);
+private:
+ char* createUniqueDirectory(const char* path, const char* ending);
+ int removeDirAndAllContents(char* path);
+};
+
+#endif //__LOCAL_CAPTURE_H__
diff --git a/tools/gator/daemon/Logging.cpp b/tools/gator/daemon/Logging.cpp
new file mode 100644
index 000000000000..b8d3178950d6
--- /dev/null
+++ b/tools/gator/daemon/Logging.cpp
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "Logging.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#ifdef WIN32
+#define MUTEX_INIT() mLoggingMutex = CreateMutex(NULL, false, NULL);
+#define MUTEX_LOCK() WaitForSingleObject(mLoggingMutex, 0xFFFFFFFF);
+#define MUTEX_UNLOCK() ReleaseMutex(mLoggingMutex);
+#define snprintf _snprintf
+#else
+#include <pthread.h>
+#define MUTEX_INIT() pthread_mutex_init(&mLoggingMutex, NULL)
+#define MUTEX_LOCK() pthread_mutex_lock(&mLoggingMutex)
+#define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex)
+#endif
+
+// Global thread-safe logging
+Logging* logg = NULL;
+
+Logging::Logging(bool debug) {
+ mDebug = debug;
+ MUTEX_INIT();
+
+ strcpy(mErrBuf, "Unknown Error");
+ strcpy(mLogBuf, "Unknown Message");
+}
+
+Logging::~Logging() {
+}
+
+void Logging::logError(const char* file, int line, const char* fmt, ...) {
+ va_list args;
+
+ MUTEX_LOCK();
+ if (mDebug) {
+ snprintf(mErrBuf, sizeof(mErrBuf), "ERROR[%s:%d]: ", file, line);
+ } else {
+ mErrBuf[0] = 0;
+ }
+
+ va_start(args, fmt);
+ vsnprintf(mErrBuf + strlen(mErrBuf), sizeof(mErrBuf) - 2 - strlen(mErrBuf), fmt, args); // subtract 2 for \n and \0
+ va_end(args);
+
+ if (strlen(mErrBuf) > 0) {
+ strcat(mErrBuf, "\n");
+ }
+ MUTEX_UNLOCK();
+}
+
+void Logging::logMessage(const char* fmt, ...) {
+ if (mDebug) {
+ va_list args;
+
+ MUTEX_LOCK();
+ strcpy(mLogBuf, "INFO: ");
+
+ va_start(args, fmt);
+ vsnprintf(mLogBuf + strlen(mLogBuf), sizeof(mLogBuf) - 2 - strlen(mLogBuf), fmt, args); // subtract 2 for \n and \0
+ va_end(args);
+ strcat(mLogBuf, "\n");
+
+ fprintf(stdout, "%s", mLogBuf);
+ fflush(stdout);
+ MUTEX_UNLOCK();
+ }
+}
diff --git a/tools/gator/daemon/Logging.h b/tools/gator/daemon/Logging.h
new file mode 100644
index 000000000000..4934bb079754
--- /dev/null
+++ b/tools/gator/daemon/Logging.h
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 __LOGGING_H__
+#define __LOGGING_H__
+
+#include <pthread.h>
+
+#define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord"
+
+class Logging {
+public:
+ Logging(bool debug);
+ ~Logging();
+ void logError(const char* file, int line, const char* fmt, ...);
+ void logMessage(const char* fmt, ...);
+ char* getLastError() {return mErrBuf;}
+ char* getLastMessage() {return mLogBuf;}
+
+private:
+ char mErrBuf[4096]; // Arbitrarily large buffer to hold a string
+ char mLogBuf[4096]; // Arbitrarily large buffer to hold a string
+ bool mDebug;
+ pthread_mutex_t mLoggingMutex;
+};
+
+extern Logging* logg;
+
+extern void handleException() __attribute__ ((noreturn));
+
+#endif //__LOGGING_H__
diff --git a/tools/gator/daemon/Makefile b/tools/gator/daemon/Makefile
new file mode 100644
index 000000000000..2ed49fdb688b
--- /dev/null
+++ b/tools/gator/daemon/Makefile
@@ -0,0 +1,25 @@
+#
+# Makefile for ARM Streamline - Gator Daemon
+#
+
+# Uncomment and define CROSS_COMPILE if it is not already defined
+# CROSS_COMPILE=/path/to/cross-compiler/arm-linux-gnueabihf-
+# NOTE: This toolchain uses the hardfloat abi by default. For non-hardfloat
+# targets run 'make SOFTFLOAT=1 SYSROOT=/path/to/sysroot', see
+# README_Streamline.txt for more details
+
+CC = $(CROSS_COMPILE)gcc
+CXX = $(CROSS_COMPILE)g++
+
+# -mthumb-interwork is required for interworking to ARM or Thumb stdlibc
+CPPFLAGS += -mthumb-interwork
+
+ifeq ($(SOFTFLOAT),1)
+ CPPFLAGS += -marm -march=armv4t -mfloat-abi=soft
+ LDFLAGS += -marm -march=armv4t -mfloat-abi=soft
+endif
+ifneq ($(SYSROOT),)
+ LDFLAGS += --sysroot=$(SYSROOT)
+endif
+
+include common.mk
diff --git a/tools/gator/daemon/Makefile_aarch64 b/tools/gator/daemon/Makefile_aarch64
new file mode 100644
index 000000000000..efd1fa002182
--- /dev/null
+++ b/tools/gator/daemon/Makefile_aarch64
@@ -0,0 +1,12 @@
+#
+# Makefile for ARM Streamline - Gator Daemon
+# make -f Makefile_aarch64
+#
+
+# Uncomment and define CROSS_COMPILE if it is not already defined
+# CROSS_COMPILE=/path/to/cross-compiler/aarch64-linux-gnu-
+
+CC = $(CROSS_COMPILE)gcc
+CXX = $(CROSS_COMPILE)g++
+
+include common.mk
diff --git a/tools/gator/daemon/MaliVideoDriver.cpp b/tools/gator/daemon/MaliVideoDriver.cpp
new file mode 100644
index 000000000000..18b413b01a37
--- /dev/null
+++ b/tools/gator/daemon/MaliVideoDriver.cpp
@@ -0,0 +1,253 @@
+/**
+ * Copyright (C) ARM Limited 2014. All rights reserved.
+ *
+ * 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 "MaliVideoDriver.h"
+
+#include <unistd.h>
+
+#include "Buffer.h"
+#include "Counter.h"
+#include "Logging.h"
+#include "SessionData.h"
+
+// From instr/src/mve_instr_comm_protocol.h
+typedef enum mve_instr_configuration_type {
+ MVE_INSTR_RAW = 1 << 0,
+ MVE_INSTR_COUNTERS = 1 << 1,
+ MVE_INSTR_EVENTS = 1 << 2,
+ MVE_INSTR_ACTIVITIES = 1 << 3,
+
+ // Raw always pushed regardless
+ MVE_INSTR_PULL = 1 << 12,
+ // Raw always unpacked regardless
+ MVE_INSTR_PACKED_COMM = 1 << 13,
+ // Don’t send ACKt response
+ MVE_INSTR_NO_AUTO_ACK = 1 << 14,
+} mve_instr_configuration_type_t;
+
+static const char COUNTER[] = "ARM_Mali-V500_cnt";
+static const char EVENT[] = "ARM_Mali-V500_evn";
+static const char ACTIVITY[] = "ARM_Mali-V500_act";
+
+class MaliVideoCounter {
+public:
+ MaliVideoCounter(MaliVideoCounter *next, const char *name, const MaliVideoCounterType type, const int id) : mNext(next), mName(name), mType(type), mId(id), mKey(getEventKey()), mEnabled(false) {
+ }
+
+ ~MaliVideoCounter() {
+ delete mName;
+ }
+
+ MaliVideoCounter *getNext() const { return mNext; }
+ const char *getName() const { return mName; }
+ MaliVideoCounterType getType() const { return mType; }
+ int getId() const { return mId; }
+ int getKey() const { return mKey; }
+ bool isEnabled() const { return mEnabled; }
+ void setEnabled(const bool enabled) { mEnabled = enabled; }
+
+private:
+ MaliVideoCounter *const mNext;
+ const char *const mName;
+ const MaliVideoCounterType mType;
+ // Mali Video id
+ const int mId;
+ // Streamline key
+ const int mKey;
+ bool mEnabled;
+};
+
+MaliVideoDriver::MaliVideoDriver() : mCounters(NULL), mActivityCount(0) {
+}
+
+MaliVideoDriver::~MaliVideoDriver() {
+ while (mCounters != NULL) {
+ MaliVideoCounter *counter = mCounters;
+ mCounters = counter->getNext();
+ delete counter;
+ }
+}
+
+void MaliVideoDriver::setup(mxml_node_t *const xml) {
+ // hwmon does not currently work with perf
+ if (gSessionData->perf.isSetup()) {
+ return;
+ }
+
+ mxml_node_t *node = xml;
+ while (true) {
+ node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND);
+ if (node == NULL) {
+ break;
+ }
+ const char *counter = mxmlElementGetAttr(node, "counter");
+ if (counter == NULL) {
+ // Ignore
+ } else if (strncmp(counter, COUNTER, sizeof(COUNTER) - 1) == 0) {
+ const int i = strtol(counter + sizeof(COUNTER) - 1, NULL, 10);
+ mCounters = new MaliVideoCounter(mCounters, strdup(counter), MVCT_COUNTER, i);
+ } else if (strncmp(counter, EVENT, sizeof(EVENT) - 1) == 0) {
+ const int i = strtol(counter + sizeof(EVENT) - 1, NULL, 10);
+ mCounters = new MaliVideoCounter(mCounters, strdup(counter), MVCT_EVENT, i);
+ } else if (strcmp(counter, ACTIVITY) == 0) {
+ mCounters = new MaliVideoCounter(mCounters, strdup(ACTIVITY), MVCT_ACTIVITY, 0);
+ mActivityCount = 0;
+ while (true) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "activity%i", mActivityCount + 1);
+ if (mxmlElementGetAttr(node, buf) == NULL) {
+ break;
+ }
+ ++mActivityCount;
+ }
+ }
+ }
+}
+
+MaliVideoCounter *MaliVideoDriver::findCounter(const Counter &counter) const {
+ for (MaliVideoCounter *maliVideoCounter = mCounters; maliVideoCounter != NULL; maliVideoCounter = maliVideoCounter->getNext()) {
+ if (strcmp(maliVideoCounter->getName(), counter.getType()) == 0) {
+ return maliVideoCounter;
+ }
+ }
+
+ return NULL;
+}
+
+bool MaliVideoDriver::claimCounter(const Counter &counter) const {
+ return findCounter(counter) != NULL;
+}
+
+bool MaliVideoDriver::countersEnabled() const {
+ for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ if (counter->isEnabled()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void MaliVideoDriver::resetCounters() {
+ for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ counter->setEnabled(false);
+ }
+}
+
+void MaliVideoDriver::setupCounter(Counter &counter) {
+ MaliVideoCounter *const maliVideoCounter = findCounter(counter);
+ if (maliVideoCounter == NULL) {
+ counter.setEnabled(false);
+ return;
+ }
+ maliVideoCounter->setEnabled(true);
+ counter.setKey(maliVideoCounter->getKey());
+}
+
+int MaliVideoDriver::writeCounters(mxml_node_t *root) const {
+ if (access("/dev/mv500", F_OK) != 0) {
+ return 0;
+ }
+
+ int count = 0;
+ for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ mxml_node_t *node = mxmlNewElement(root, "counter");
+ mxmlElementSetAttr(node, "name", counter->getName());
+ ++count;
+ }
+
+ return count;
+}
+
+void MaliVideoDriver::marshalEnable(const MaliVideoCounterType type, char *const buf, const size_t bufsize, int &pos) {
+ // size
+ int numEnabled = 0;
+ for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ if (counter->isEnabled() && (counter->getType() == type)) {
+ ++numEnabled;
+ }
+ }
+ Buffer::packInt(buf, bufsize, pos, numEnabled*sizeof(uint32_t));
+ for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ if (counter->isEnabled() && (counter->getType() == type)) {
+ Buffer::packInt(buf, bufsize, pos, counter->getId());
+ }
+ }
+}
+
+bool MaliVideoDriver::start(const int mveUds) {
+ char buf[256];
+ int pos = 0;
+
+ // code - MVE_INSTR_STARTUP
+ buf[pos++] = 'C';
+ buf[pos++] = 'L';
+ buf[pos++] = 'N';
+ buf[pos++] = 'T';
+ // size
+ Buffer::packInt(buf, sizeof(buf), pos, sizeof(uint32_t));
+ // client_version_number
+ Buffer::packInt(buf, sizeof(buf), pos, 1);
+
+ // code - MVE_INSTR_CONFIGURE
+ buf[pos++] = 'C';
+ buf[pos++] = 'N';
+ buf[pos++] = 'F';
+ buf[pos++] = 'G';
+ // size
+ Buffer::packInt(buf, sizeof(buf), pos, 5*sizeof(uint32_t));
+ // configuration
+ Buffer::packInt(buf, sizeof(buf), pos, MVE_INSTR_COUNTERS | MVE_INSTR_EVENTS | MVE_INSTR_ACTIVITIES | MVE_INSTR_PACKED_COMM);
+ // communication_protocol_version
+ Buffer::packInt(buf, sizeof(buf), pos, 1);
+ // data_protocol_version
+ Buffer::packInt(buf, sizeof(buf), pos, 1);
+ // sample_rate - convert samples/second to ms/sample
+ Buffer::packInt(buf, sizeof(buf), pos, 1000/gSessionData->mSampleRate);
+ // live_rate - convert ns/flush to ms/flush
+ Buffer::packInt(buf, sizeof(buf), pos, gSessionData->mLiveRate/1000000);
+
+ // code - MVE_INSTR_ENABLE_COUNTERS
+ buf[pos++] = 'C';
+ buf[pos++] = 'F';
+ buf[pos++] = 'G';
+ buf[pos++] = 'c';
+ marshalEnable(MVCT_COUNTER, buf, sizeof(buf), pos);
+
+ // code - MVE_INSTR_ENABLE_EVENTS
+ buf[pos++] = 'C';
+ buf[pos++] = 'F';
+ buf[pos++] = 'G';
+ buf[pos++] = 'e';
+ marshalEnable(MVCT_EVENT, buf, sizeof(buf), pos);
+
+ /*
+ // code - MVE_INSTR_ENABLE_ACTIVITIES
+ buf[pos++] = 'C';
+ buf[pos++] = 'F';
+ buf[pos++] = 'G';
+ buf[pos++] = 'a';
+ // size
+ Buffer::packInt(buf, sizeof(buf), pos, mActivityCount*sizeof(uint32_t));
+ for (int i = 0; i < mActivityCount; ++i) {
+ // activity_id
+ Buffer::packInt(buf, sizeof(buf), pos, i);
+ }
+ */
+
+ int written = 0;
+ while (written < pos) {
+ size_t bytes = ::write(mveUds, buf + written, pos - written);
+ if (bytes <= 0) {
+ logg->logMessage("%s(%s:%i): write failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ written += bytes;
+ }
+
+ return true;
+}
diff --git a/tools/gator/daemon/MaliVideoDriver.h b/tools/gator/daemon/MaliVideoDriver.h
new file mode 100644
index 000000000000..00cb80889a74
--- /dev/null
+++ b/tools/gator/daemon/MaliVideoDriver.h
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) ARM Limited 2014. All rights reserved.
+ *
+ * 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 MALIVIDEODRIVER_H
+#define MALIVIDEODRIVER_H
+
+#include "Driver.h"
+
+class MaliVideoCounter;
+
+enum MaliVideoCounterType {
+ MVCT_COUNTER,
+ MVCT_EVENT,
+ MVCT_ACTIVITY,
+};
+
+class MaliVideoDriver : public Driver {
+public:
+ MaliVideoDriver();
+ ~MaliVideoDriver();
+
+ void setup(mxml_node_t *const xml);
+
+ bool claimCounter(const Counter &counter) const;
+ bool countersEnabled() const;
+ void resetCounters();
+ void setupCounter(Counter &counter);
+
+ int writeCounters(mxml_node_t *root) const;
+
+ bool start(const int mveUds);
+
+private:
+ MaliVideoCounter *findCounter(const Counter &counter) const;
+ void marshalEnable(const MaliVideoCounterType type, char *const buf, const size_t bufsize, int &pos);
+
+ MaliVideoCounter *mCounters;
+ int mActivityCount;
+
+ // Intentionally unimplemented
+ MaliVideoDriver(const MaliVideoDriver &);
+ MaliVideoDriver &operator=(const MaliVideoDriver &);
+};
+
+#endif // MALIVIDEODRIVER_H
diff --git a/tools/gator/daemon/Monitor.cpp b/tools/gator/daemon/Monitor.cpp
new file mode 100644
index 000000000000..b34a15f0eb0c
--- /dev/null
+++ b/tools/gator/daemon/Monitor.cpp
@@ -0,0 +1,68 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "Monitor.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "Logging.h"
+
+Monitor::Monitor() : mFd(-1) {
+}
+
+Monitor::~Monitor() {
+ if (mFd >= 0) {
+ ::close(mFd);
+ }
+}
+
+void Monitor::close() {
+ if (mFd >= 0) {
+ ::close(mFd);
+ mFd = -1;
+ }
+}
+
+bool Monitor::init() {
+ mFd = epoll_create(16);
+ if (mFd < 0) {
+ logg->logMessage("%s(%s:%i): epoll_create1 failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ return true;
+}
+
+bool Monitor::add(const int fd) {
+ struct epoll_event event;
+ memset(&event, 0, sizeof(event));
+ event.data.fd = fd;
+ event.events = EPOLLIN;
+ if (epoll_ctl(mFd, EPOLL_CTL_ADD, fd, &event) != 0) {
+ logg->logMessage("%s(%s:%i): epoll_ctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ return true;
+}
+
+int Monitor::wait(struct epoll_event *const events, int maxevents, int timeout) {
+ int result = epoll_wait(mFd, events, maxevents, timeout);
+ if (result < 0) {
+ // Ignore if the call was interrupted as this will happen when SIGINT is received
+ if (errno == EINTR) {
+ result = 0;
+ } else {
+ logg->logMessage("%s(%s:%i): epoll_wait failed", __FUNCTION__, __FILE__, __LINE__);
+ }
+ }
+
+ return result;
+}
diff --git a/tools/gator/daemon/Monitor.h b/tools/gator/daemon/Monitor.h
new file mode 100644
index 000000000000..7194e0e4ca50
--- /dev/null
+++ b/tools/gator/daemon/Monitor.h
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 MONITOR_H
+#define MONITOR_H
+
+#include <sys/epoll.h>
+
+class Monitor {
+public:
+ Monitor();
+ ~Monitor();
+
+ void close();
+ bool init();
+ bool add(const int fd);
+ int wait(struct epoll_event *const events, int maxevents, int timeout);
+
+private:
+
+ int mFd;
+
+ // Intentionally unimplemented
+ Monitor(const Monitor &);
+ Monitor &operator=(const Monitor &);
+};
+
+#endif // MONITOR_H
diff --git a/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp
new file mode 100644
index 000000000000..28774e36e510
--- /dev/null
+++ b/tools/gator/daemon/OlySocket.cpp
@@ -0,0 +1,282 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "OlySocket.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+#include <Winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <netdb.h>
+#endif
+
+#include "Logging.h"
+
+#ifdef WIN32
+#define CLOSE_SOCKET(x) closesocket(x)
+#define SHUTDOWN_RX_TX SD_BOTH
+#define snprintf _snprintf
+#else
+#define CLOSE_SOCKET(x) close(x)
+#define SHUTDOWN_RX_TX SHUT_RDWR
+#endif
+
+OlyServerSocket::OlyServerSocket(int port) {
+#ifdef WIN32
+ WSADATA wsaData;
+ if (WSAStartup(0x0202, &wsaData) != 0) {
+ logg->logError(__FILE__, __LINE__, "Windows socket initialization failed");
+ handleException();
+ }
+#endif
+
+ createServerSocket(port);
+}
+
+OlySocket::OlySocket(int socketID) : mSocketID(socketID) {
+}
+
+#ifndef WIN32
+
+#define MIN(A, B) ({ \
+ const __typeof__(A) __a = A; \
+ const __typeof__(B) __b = B; \
+ __a > __b ? __b : __a; \
+})
+
+OlyServerSocket::OlyServerSocket(const char* path, const size_t pathSize) {
+ // Create socket
+ mFDServer = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (mFDServer < 0) {
+ logg->logError(__FILE__, __LINE__, "Error creating server socket");
+ handleException();
+ }
+
+ // Create sockaddr_in structure, ensuring non-populated fields are zero
+ struct sockaddr_un sockaddr;
+ memset((void*)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sun_family = AF_UNIX;
+ memcpy(sockaddr.sun_path, path, MIN(pathSize, sizeof(sockaddr.sun_path)));
+ sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0';
+
+ // Bind the socket to an address
+ if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
+ logg->logError(__FILE__, __LINE__, "Binding of server socket failed.");
+ handleException();
+ }
+
+ // Listen for connections on this socket
+ if (listen(mFDServer, 1) < 0) {
+ logg->logError(__FILE__, __LINE__, "Listening of server socket failed");
+ handleException();
+ }
+}
+
+int OlySocket::connect(const char* path, const size_t pathSize) {
+ int fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ return -1;
+ }
+
+ // Create sockaddr_in structure, ensuring non-populated fields are zero
+ struct sockaddr_un sockaddr;
+ memset((void*)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sun_family = AF_UNIX;
+ memcpy(sockaddr.sun_path, path, MIN(pathSize, sizeof(sockaddr.sun_path)));
+ sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0';
+
+ if (::connect(fd, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+#endif
+
+OlySocket::~OlySocket() {
+ if (mSocketID > 0) {
+ CLOSE_SOCKET(mSocketID);
+ }
+}
+
+OlyServerSocket::~OlyServerSocket() {
+ if (mFDServer > 0) {
+ CLOSE_SOCKET(mFDServer);
+ }
+}
+
+void OlySocket::shutdownConnection() {
+ // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions
+ shutdown(mSocketID, SHUTDOWN_RX_TX);
+}
+
+void OlySocket::closeSocket() {
+ // Used for closing an accepted socket but keeping the server socket active
+ if (mSocketID > 0) {
+ CLOSE_SOCKET(mSocketID);
+ mSocketID = -1;
+ }
+}
+
+void OlyServerSocket::closeServerSocket() {
+ if (CLOSE_SOCKET(mFDServer) != 0) {
+ logg->logError(__FILE__, __LINE__, "Failed to close server socket.");
+ handleException();
+ }
+ mFDServer = 0;
+}
+
+void OlyServerSocket::createServerSocket(int port) {
+ int family = AF_INET6;
+
+ // Create socket
+ mFDServer = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
+ if (mFDServer < 0) {
+ family = AF_INET;
+ mFDServer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (mFDServer < 0) {
+ logg->logError(__FILE__, __LINE__, "Error creating server socket");
+ handleException();
+ }
+ }
+
+ // Enable address reuse, another solution would be to create the server socket once and only close it when the object exits
+ int on = 1;
+ if (setsockopt(mFDServer, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) {
+ logg->logError(__FILE__, __LINE__, "Setting server socket options failed");
+ handleException();
+ }
+
+ // Create sockaddr_in structure, ensuring non-populated fields are zero
+ struct sockaddr_in6 sockaddr;
+ memset((void*)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sin6_family = family;
+ sockaddr.sin6_port = htons(port);
+ sockaddr.sin6_addr = in6addr_any;
+
+ // Bind the socket to an address
+ if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
+ logg->logError(__FILE__, __LINE__, "Binding of server socket failed.\nIs an instance already running?");
+ handleException();
+ }
+
+ // Listen for connections on this socket
+ if (listen(mFDServer, 1) < 0) {
+ logg->logError(__FILE__, __LINE__, "Listening of server socket failed");
+ handleException();
+ }
+}
+
+// mSocketID is always set to the most recently accepted connection
+// The user of this class should maintain the different socket connections, e.g. by forking the process
+int OlyServerSocket::acceptConnection() {
+ int socketID;
+ if (mFDServer <= 0) {
+ logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket");
+ handleException();
+ }
+
+ // Accept a connection, note that this call blocks until a client connects
+ socketID = accept(mFDServer, NULL, NULL);
+ if (socketID < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket acceptance failed");
+ handleException();
+ }
+ return socketID;
+}
+
+void OlySocket::send(const char* buffer, int size) {
+ if (size <= 0 || buffer == NULL) {
+ return;
+ }
+
+ while (size > 0) {
+ int n = ::send(mSocketID, buffer, size, 0);
+ if (n < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket send error");
+ handleException();
+ }
+ size -= n;
+ buffer += n;
+ }
+}
+
+// Returns the number of bytes received
+int OlySocket::receive(char* buffer, int size) {
+ if (size <= 0 || buffer == NULL) {
+ return 0;
+ }
+
+ int bytes = recv(mSocketID, buffer, size, 0);
+ if (bytes < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket receive error");
+ handleException();
+ } else if (bytes == 0) {
+ logg->logMessage("Socket disconnected");
+ return -1;
+ }
+ return bytes;
+}
+
+// Receive exactly size bytes of data. Note, this function will block until all bytes are received
+int OlySocket::receiveNBytes(char* buffer, int size) {
+ int bytes = 0;
+ while (size > 0 && buffer != NULL) {
+ bytes = recv(mSocketID, buffer, size, 0);
+ if (bytes < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket receive error");
+ handleException();
+ } else if (bytes == 0) {
+ logg->logMessage("Socket disconnected");
+ return -1;
+ }
+ buffer += bytes;
+ size -= bytes;
+ }
+ return bytes;
+}
+
+// Receive data until a carriage return, line feed, or null is encountered, or the buffer fills
+int OlySocket::receiveString(char* buffer, int size) {
+ int bytes_received = 0;
+ bool found = false;
+
+ if (buffer == 0) {
+ return 0;
+ }
+
+ while (!found && bytes_received < size) {
+ // Receive a single character
+ int bytes = recv(mSocketID, &buffer[bytes_received], 1, 0);
+ if (bytes < 0) {
+ logg->logError(__FILE__, __LINE__, "Socket receive error");
+ handleException();
+ } else if (bytes == 0) {
+ logg->logMessage("Socket disconnected");
+ return -1;
+ }
+
+ // Replace carriage returns and line feeds with zero
+ if (buffer[bytes_received] == '\n' || buffer[bytes_received] == '\r' || buffer[bytes_received] == '\0') {
+ buffer[bytes_received] = '\0';
+ found = true;
+ }
+
+ bytes_received++;
+ }
+
+ return bytes_received;
+}
diff --git a/tools/gator/daemon/OlySocket.h b/tools/gator/daemon/OlySocket.h
new file mode 100644
index 000000000000..20c67cc695e1
--- /dev/null
+++ b/tools/gator/daemon/OlySocket.h
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 __OLY_SOCKET_H__
+#define __OLY_SOCKET_H__
+
+#include <stddef.h>
+
+class OlySocket {
+public:
+#ifndef WIN32
+ static int connect(const char* path, const size_t pathSize);
+#endif
+
+ OlySocket(int socketID);
+ ~OlySocket();
+
+ void closeSocket();
+ void shutdownConnection();
+ void send(const char* buffer, int size);
+ int receive(char* buffer, int size);
+ int receiveNBytes(char* buffer, int size);
+ int receiveString(char* buffer, int size);
+
+ bool isValid() const { return mSocketID >= 0; }
+
+private:
+ int mSocketID;
+};
+
+class OlyServerSocket {
+public:
+ OlyServerSocket(int port);
+#ifndef WIN32
+ OlyServerSocket(const char* path, const size_t pathSize);
+#endif
+ ~OlyServerSocket();
+
+ int acceptConnection();
+ void closeServerSocket();
+
+ int getFd() { return mFDServer; }
+
+private:
+ int mFDServer;
+
+ void createServerSocket(int port);
+};
+
+#endif //__OLY_SOCKET_H__
diff --git a/tools/gator/daemon/OlyUtility.cpp b/tools/gator/daemon/OlyUtility.cpp
new file mode 100644
index 000000000000..45340a27d9fa
--- /dev/null
+++ b/tools/gator/daemon/OlyUtility.cpp
@@ -0,0 +1,227 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "OlyUtility.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#if defined(WIN32)
+#include <windows.h>
+#elif defined(__linux__)
+#include <unistd.h>
+#elif defined(DARWIN)
+#include <mach-o/dyld.h>
+#endif
+
+OlyUtility* util = NULL;
+
+bool OlyUtility::stringToBool(const char* string, bool defValue) {
+ char value[32];
+
+ if (string == NULL) {
+ return defValue;
+ }
+
+ strncpy(value, string, sizeof(value));
+ if (value[0] == 0) {
+ return defValue;
+ }
+ value[sizeof(value) - 1] = 0; // strncpy does not guarantee a null-terminated string
+
+ // Convert to lowercase
+ int i = 0;
+ while (value[i]) {
+ value[i] = tolower(value[i]);
+ i++;
+ }
+
+ if (strcmp(value, "true") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "1") == 0 || strcmp(value, "on") == 0) {
+ return true;
+ } else if (strcmp(value, "false") == 0 || strcmp(value, "no") == 0 || strcmp(value, "0") == 0 || strcmp(value, "off") == 0) {
+ return false;
+ } else {
+ return defValue;
+ }
+}
+
+void OlyUtility::stringToLower(char* string) {
+ if (string == NULL) {
+ return;
+ }
+
+ while (*string) {
+ *string = tolower(*string);
+ string++;
+ }
+}
+
+// Modifies fullpath with the path part including the trailing path separator
+int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) {
+ memset(fullpath, 0, sizeOfPath);
+#if defined(WIN32)
+ int length = GetModuleFileName(NULL, fullpath, sizeOfPath);
+#elif defined(__linux__)
+ int length = readlink("/proc/self/exe", fullpath, sizeOfPath);
+#elif defined(DARWIN)
+ uint32_t length_u = (uint32_t)sizeOfPath;
+ int length = sizeOfPath;
+ if (_NSGetExecutablePath(fullpath, &length_u) == 0) {
+ length = strlen(fullpath);
+ }
+#endif
+
+ if (length == sizeOfPath) {
+ return -1;
+ }
+
+ fullpath[length] = 0;
+ getPathPart(fullpath);
+
+ return 0;
+}
+
+char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool appendNull) {
+ // Open the file
+ FILE* pFile = fopen(file, "rb");
+ if (pFile==NULL) {
+ return NULL;
+ }
+
+ // Obtain file size
+ fseek(pFile , 0 , SEEK_END);
+ unsigned int lSize = ftell(pFile);
+ rewind(pFile);
+
+ // Allocate memory to contain the whole file
+ char* buffer = (char*)malloc(lSize + (int)appendNull);
+ if (buffer == NULL) {
+ fclose(pFile);
+ return NULL;
+ }
+
+ // Copy the file into the buffer
+ if (fread(buffer, 1, lSize, pFile) != lSize) {
+ free(buffer);
+ fclose(pFile);
+ return NULL;
+ }
+
+ // Terminate
+ fclose(pFile);
+
+ if (appendNull) {
+ buffer[lSize] = 0;
+ }
+
+ if (size) {
+ *size = lSize;
+ }
+
+ return buffer;
+}
+
+int OlyUtility::writeToDisk(const char* path, const char* data) {
+ // Open the file
+ FILE* pFile = fopen(path, "wb");
+ if (pFile == NULL) {
+ return -1;
+ }
+
+ // Write the data to disk
+ if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) {
+ fclose(pFile);
+ return -1;
+ }
+
+ // Terminate
+ fclose(pFile);
+ return 0;
+}
+
+int OlyUtility::appendToDisk(const char* path, const char* data) {
+ // Open the file
+ FILE* pFile = fopen(path, "a");
+ if (pFile == NULL) {
+ return -1;
+ }
+
+ // Write the data to disk
+ if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) {
+ fclose(pFile);
+ return -1;
+ }
+
+ // Terminate
+ fclose(pFile);
+ return 0;
+}
+
+/**
+ * Copies the srcFile into dstFile in 1kB chunks.
+ * The dstFile will be overwritten if it exists.
+ * 0 is returned on an error; otherwise 1.
+ */
+#define TRANSFER_SIZE 1024
+int OlyUtility::copyFile(const char* srcFile, const char* dstFile) {
+ char buffer[TRANSFER_SIZE];
+ FILE * f_src = fopen(srcFile,"rb");
+ if (!f_src) {
+ return 0;
+ }
+ FILE * f_dst = fopen(dstFile,"wb");
+ if (!f_dst) {
+ fclose(f_src);
+ return 0;
+ }
+ while (!feof(f_src)) {
+ int num_bytes_read = fread(buffer, 1, TRANSFER_SIZE, f_src);
+ if (num_bytes_read < TRANSFER_SIZE && !feof(f_src)) {
+ fclose(f_src);
+ fclose(f_dst);
+ return 0;
+ }
+ int num_bytes_written = fwrite(buffer, 1, num_bytes_read, f_dst);
+ if (num_bytes_written != num_bytes_read) {
+ fclose(f_src);
+ fclose(f_dst);
+ return 0;
+ }
+ }
+ fclose(f_src);
+ fclose(f_dst);
+ return 1;
+}
+
+const char* OlyUtility::getFilePart(const char* path) {
+ const char* last_sep = strrchr(path, PATH_SEPARATOR);
+
+ // in case path is not a full path
+ if (last_sep == NULL) {
+ return path;
+ }
+
+ return last_sep++;
+}
+
+// getPathPart may modify the contents of path
+// returns the path including the trailing path separator
+char* OlyUtility::getPathPart(char* path) {
+ char* last_sep = strrchr(path, PATH_SEPARATOR);
+
+ // in case path is not a full path
+ if (last_sep == NULL) {
+ return 0;
+ }
+ last_sep++;
+ *last_sep = 0;
+
+ return (path);
+}
diff --git a/tools/gator/daemon/OlyUtility.h b/tools/gator/daemon/OlyUtility.h
new file mode 100644
index 000000000000..1d26beb596fa
--- /dev/null
+++ b/tools/gator/daemon/OlyUtility.h
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 OLY_UTILITY_H
+#define OLY_UTILITY_H
+
+#include <stddef.h>
+
+#ifdef WIN32
+#define PATH_SEPARATOR '\\'
+#define CAIMAN_PATH_MAX MAX_PATH
+#define snprintf _snprintf
+#else
+#include <limits.h>
+#define PATH_SEPARATOR '/'
+#define CAIMAN_PATH_MAX PATH_MAX
+#endif
+
+class OlyUtility {
+public:
+ OlyUtility() {};
+ ~OlyUtility() {};
+ bool stringToBool(const char* string, bool defValue);
+ void stringToLower(char* string);
+ int getApplicationFullPath(char* path, int sizeOfPath);
+ char* readFromDisk(const char* file, unsigned int *size = NULL, bool appendNull = true);
+ int writeToDisk(const char* path, const char* file);
+ int appendToDisk(const char* path, const char* file);
+ int copyFile(const char* srcFile, const char* dstFile);
+ const char* getFilePart(const char* path);
+ char* getPathPart(char* path);
+private:
+};
+
+extern OlyUtility* util;
+
+#endif // OLY_UTILITY_H
diff --git a/tools/gator/daemon/PerfBuffer.cpp b/tools/gator/daemon/PerfBuffer.cpp
new file mode 100644
index 000000000000..5fad583f7bd0
--- /dev/null
+++ b/tools/gator/daemon/PerfBuffer.cpp
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "PerfBuffer.h"
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include "Buffer.h"
+#include "Logging.h"
+#include "Sender.h"
+#include "SessionData.h"
+
+PerfBuffer::PerfBuffer() {
+ for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) {
+ mBuf[cpu] = MAP_FAILED;
+ mDiscard[cpu] = false;
+ }
+}
+
+PerfBuffer::~PerfBuffer() {
+ for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) {
+ if (mBuf[cpu] != MAP_FAILED) {
+ munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE);
+ }
+ }
+}
+
+bool PerfBuffer::useFd(const int cpu, const int fd, const int groupFd) {
+ if (fd == groupFd) {
+ if (mBuf[cpu] != MAP_FAILED) {
+ logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu);
+ return false;
+ }
+
+ // The buffer isn't mapped yet
+ mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mBuf[cpu] == MAP_FAILED) {
+ logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ // Check the version
+ struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
+ if (pemp->compat_version != 0) {
+ logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ } else {
+ if (mBuf[cpu] == MAP_FAILED) {
+ logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, groupFd) < 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void PerfBuffer::discard(const int cpu) {
+ if (mBuf[cpu] != MAP_FAILED) {
+ mDiscard[cpu] = true;
+ }
+}
+
+bool PerfBuffer::isEmpty() {
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ if (mBuf[cpu] != MAP_FAILED) {
+ // Take a snapshot of the positions
+ struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
+ const __u64 head = pemp->data_head;
+ const __u64 tail = pemp->data_tail;
+
+ if (head != tail) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool PerfBuffer::send(Sender *const sender) {
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ if (mBuf[cpu] == MAP_FAILED) {
+ continue;
+ }
+
+ // Take a snapshot of the positions
+ struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
+ const __u64 head = pemp->data_head;
+ const __u64 tail = pemp->data_tail;
+
+ if (head > tail) {
+ const uint8_t *const b = static_cast<uint8_t *>(mBuf[cpu]) + gSessionData->mPageSize;
+ const int offset = gSessionData->mLocalCapture ? 1 : 0;
+ unsigned char header[7];
+ header[0] = RESPONSE_APC_DATA;
+ Buffer::writeLEInt(header + 1, head - tail + sizeof(header) - 5);
+ // Should use real packing functions
+ header[5] = FRAME_PERF;
+ header[6] = cpu;
+
+ // Write header
+ sender->writeData(reinterpret_cast<const char *>(&header) + offset, sizeof(header) - offset, RESPONSE_APC_DATA);
+
+ // Write data
+ if ((head & ~BUF_MASK) == (tail & ~BUF_MASK)) {
+ // Not wrapped
+ sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), head - tail, RESPONSE_APC_DATA);
+ } else {
+ // Wrapped
+ sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), BUF_SIZE - (tail & BUF_MASK), RESPONSE_APC_DATA);
+ sender->writeData(reinterpret_cast<const char *>(b), head & BUF_MASK, RESPONSE_APC_DATA);
+ }
+
+ // Update tail with the data read
+ pemp->data_tail = head;
+ }
+
+ if (mDiscard[cpu]) {
+ munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE);
+ mBuf[cpu] = MAP_FAILED;
+ mDiscard[cpu] = false;
+ logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
+ }
+ }
+
+ return true;
+}
diff --git a/tools/gator/daemon/PerfBuffer.h b/tools/gator/daemon/PerfBuffer.h
new file mode 100644
index 000000000000..278a3b9d6db7
--- /dev/null
+++ b/tools/gator/daemon/PerfBuffer.h
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 PERF_BUFFER
+#define PERF_BUFFER
+
+#include "Config.h"
+
+#define BUF_SIZE (gSessionData->mTotalBufferSize * 1024 * 1024)
+#define BUF_MASK (BUF_SIZE - 1)
+
+class Sender;
+
+class PerfBuffer {
+public:
+ PerfBuffer();
+ ~PerfBuffer();
+
+ bool useFd(const int cpu, const int fd, const int groupFd);
+ void discard(const int cpu);
+ bool isEmpty();
+ bool send(Sender *const sender);
+
+private:
+ void *mBuf[NR_CPUS];
+ // After the buffer is flushed it should be unmaped
+ bool mDiscard[NR_CPUS];
+
+ // Intentionally undefined
+ PerfBuffer(const PerfBuffer &);
+ PerfBuffer &operator=(const PerfBuffer &);
+};
+
+#endif // PERF_BUFFER
diff --git a/tools/gator/daemon/PerfDriver.cpp b/tools/gator/daemon/PerfDriver.cpp
new file mode 100644
index 000000000000..ac97a077d266
--- /dev/null
+++ b/tools/gator/daemon/PerfDriver.cpp
@@ -0,0 +1,409 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "PerfDriver.h"
+
+#include <dirent.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "Buffer.h"
+#include "Config.h"
+#include "ConfigurationXML.h"
+#include "Counter.h"
+#include "DriverSource.h"
+#include "DynBuf.h"
+#include "Logging.h"
+#include "PerfGroup.h"
+#include "SessionData.h"
+
+#define PERF_DEVICES "/sys/bus/event_source/devices"
+
+#define TYPE_DERIVED ~0U
+
+// From gator.h
+struct gator_cpu {
+ const int cpuid;
+ // Human readable name
+ const char *const core_name;
+ // gatorfs event and Perf PMU name
+ const char *const pmnc_name;
+ const int pmnc_counters;
+};
+
+// From gator_main.c
+static const struct gator_cpu gator_cpus[] = {
+ { 0xb36, "ARM1136", "ARM_ARM11", 3 },
+ { 0xb56, "ARM1156", "ARM_ARM11", 3 },
+ { 0xb76, "ARM1176", "ARM_ARM11", 3 },
+ { 0xb02, "ARM11MPCore", "ARM_ARM11MPCore", 3 },
+ { 0xc05, "Cortex-A5", "ARMv7_Cortex_A5", 2 },
+ { 0xc07, "Cortex-A7", "ARMv7_Cortex_A7", 4 },
+ { 0xc08, "Cortex-A8", "ARMv7_Cortex_A8", 4 },
+ { 0xc09, "Cortex-A9", "ARMv7_Cortex_A9", 6 },
+ { 0xc0d, "Cortex-A12", "ARMv7_Cortex_A12", 6 },
+ { 0xc0f, "Cortex-A15", "ARMv7_Cortex_A15", 6 },
+ { 0xc0e, "Cortex-A17", "ARMv7_Cortex_A17", 6 },
+ { 0x00f, "Scorpion", "Scorpion", 4 },
+ { 0x02d, "ScorpionMP", "ScorpionMP", 4 },
+ { 0x049, "KraitSIM", "Krait", 4 },
+ { 0x04d, "Krait", "Krait", 4 },
+ { 0x06f, "Krait S4 Pro", "Krait", 4 },
+ { 0xd03, "Cortex-A53", "ARM_Cortex-A53", 6 },
+ { 0xd07, "Cortex-A57", "ARM_Cortex-A57", 6 },
+ { 0xd0f, "AArch64", "ARM_AArch64", 6 },
+};
+
+static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-";
+static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_";
+
+struct uncore_counter {
+ // gatorfs event and Perf PMU name
+ const char *const name;
+ const int count;
+};
+
+static const struct uncore_counter uncore_counters[] = {
+ { "CCI_400", 4 },
+ { "CCI_400-r1", 4 },
+};
+
+class PerfCounter {
+public:
+ PerfCounter(PerfCounter *next, const char *name, uint32_t type, uint64_t config, bool perCpu) : mNext(next), mName(name), mType(type), mCount(0), mKey(getEventKey()), mConfig(config), mEnabled(false), mPerCpu(perCpu) {}
+ ~PerfCounter() {
+ delete [] mName;
+ }
+
+ PerfCounter *getNext() const { return mNext; }
+ const char *getName() const { return mName; }
+ uint32_t getType() const { return mType; }
+ int getCount() const { return mCount; }
+ void setCount(const int count) { mCount = count; }
+ int getKey() const { return mKey; }
+ uint64_t getConfig() const { return mConfig; }
+ void setConfig(const uint64_t config) { mConfig = config; }
+ bool isEnabled() const { return mEnabled; }
+ void setEnabled(const bool enabled) { mEnabled = enabled; }
+ bool isPerCpu() const { return mPerCpu; }
+
+private:
+ PerfCounter *const mNext;
+ const char *const mName;
+ const uint32_t mType;
+ int mCount;
+ const int mKey;
+ uint64_t mConfig;
+ int mEnabled : 1,
+ mPerCpu : 1;
+};
+
+PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false), mLegacySupport(false) {
+}
+
+PerfDriver::~PerfDriver() {
+ while (mCounters != NULL) {
+ PerfCounter *counter = mCounters;
+ mCounters = counter->getNext();
+ delete counter;
+ }
+}
+
+void PerfDriver::addCpuCounters(const char *const counterName, const int type, const int numCounters) {
+ int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1;
+ char *name = new char[len];
+ snprintf(name, len, "%s_ccnt", counterName);
+ mCounters = new PerfCounter(mCounters, name, type, -1, true);
+
+ for (int j = 0; j < numCounters; ++j) {
+ len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1;
+ name = new char[len];
+ snprintf(name, len, "%s_cnt%d", counterName, j);
+ mCounters = new PerfCounter(mCounters, name, type, -1, true);
+ }
+}
+
+void PerfDriver::addUncoreCounters(const char *const counterName, const int type, const int numCounters) {
+ int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1;
+ char *name = new char[len];
+ snprintf(name, len, "%s_ccnt", counterName);
+ mCounters = new PerfCounter(mCounters, name, type, -1, false);
+
+ for (int j = 0; j < numCounters; ++j) {
+ len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1;
+ name = new char[len];
+ snprintf(name, len, "%s_cnt%d", counterName, j);
+ mCounters = new PerfCounter(mCounters, name, type, -1, false);
+ }
+}
+
+// From include/generated/uapi/linux/version.h
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+bool PerfDriver::setup() {
+ // Check the kernel version
+ struct utsname utsname;
+ if (uname(&utsname) != 0) {
+ logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ int release[3] = { 0, 0, 0 };
+ int part = 0;
+ char *ch = utsname.release;
+ while (*ch >= '0' && *ch <= '9' && part < ARRAY_LENGTH(release)) {
+ release[part] = 10*release[part] + *ch - '0';
+
+ ++ch;
+ if (*ch == '.') {
+ ++part;
+ ++ch;
+ }
+ }
+
+ if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 4, 0)) {
+ logg->logMessage("%s(%s:%i): Unsupported kernel version", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ mLegacySupport = KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0);
+
+ if (access(EVENTS_PATH, R_OK) != 0) {
+ logg->logMessage("%s(%s:%i): " EVENTS_PATH " does not exist, is CONFIG_TRACING enabled?", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ // Add supported PMUs
+ bool foundCpu = false;
+ DIR *dir = opendir(PERF_DEVICES);
+ if (dir == NULL) {
+ logg->logMessage("%s(%s:%i): opendif failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ struct dirent *dirent;
+ while ((dirent = readdir(dir)) != NULL) {
+ for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) {
+ // Do the names match exactly?
+ if (strcmp(dirent->d_name, gator_cpus[i].pmnc_name) != 0 &&
+ // Do these names match but have the old vs new prefix?
+ (strncmp(dirent->d_name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) != 0 ||
+ strncmp(gator_cpus[i].pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) != 0 ||
+ strcmp(dirent->d_name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpus[i].pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) != 0)) {
+ continue;
+ }
+
+ int type;
+ char buf[256];
+ snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name);
+ if (DriverSource::readIntDriver(buf, &type) != 0) {
+ continue;
+ }
+
+ foundCpu = true;
+ addCpuCounters(gator_cpus[i].pmnc_name, type, gator_cpus[i].pmnc_counters);
+ }
+
+ for (int i = 0; i < ARRAY_LENGTH(uncore_counters); ++i) {
+ if (strcmp(dirent->d_name, uncore_counters[i].name) != 0) {
+ continue;
+ }
+
+ int type;
+ char buf[256];
+ snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name);
+ if (DriverSource::readIntDriver(buf, &type) != 0) {
+ continue;
+ }
+
+ addUncoreCounters(uncore_counters[i].name, type, uncore_counters[i].count);
+ }
+ }
+ closedir(dir);
+
+ if (!foundCpu) {
+ // If no cpu was found based on pmu names, try by cpuid
+ for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) {
+ if (gSessionData->mMaxCpuId != gator_cpus[i].cpuid) {
+ continue;
+ }
+
+ foundCpu = true;
+ addCpuCounters(gator_cpus[i].pmnc_name, PERF_TYPE_RAW, gator_cpus[i].pmnc_counters);
+ }
+ }
+
+ /*
+ if (!foundCpu) {
+ // If all else fails, use the perf architected counters
+ // 9 because that's how many are in events-Perf-Hardware.xml - assume they can all be enabled at once
+ addCpuCounters("Perf_Hardware", PERF_TYPE_HARDWARE, 9);
+ }
+ */
+
+ // Add supported software counters
+ long long id;
+ DynBuf printb;
+
+ id = getTracepointId("irq/softirq_exit", &printb);
+ if (id >= 0) {
+ mCounters = new PerfCounter(mCounters, "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id, true);
+ }
+
+ id = getTracepointId("irq/irq_handler_exit", &printb);
+ if (id >= 0) {
+ mCounters = new PerfCounter(mCounters, "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id, true);
+ }
+
+ //Linux_block_rq_wr
+ //Linux_block_rq_rd
+ //Linux_net_rx
+ //Linux_net_tx
+
+ id = getTracepointId(SCHED_SWITCH, &printb);
+ if (id >= 0) {
+ mCounters = new PerfCounter(mCounters, "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id, true);
+ }
+
+ //Linux_meminfo_memused
+ //Linux_meminfo_memfree
+ //Linux_meminfo_bufferram
+ //Linux_power_cpu_freq
+ //Linux_power_cpu_idle
+
+ mCounters = new PerfCounter(mCounters, "Linux_cpu_wait_contention", TYPE_DERIVED, -1, false);
+
+ //Linux_cpu_wait_io
+
+ mIsSetup = true;
+ return true;
+}
+
+bool PerfDriver::summary(Buffer *const buffer) {
+ struct utsname utsname;
+ if (uname(&utsname) != 0) {
+ logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ char buf[512];
+ snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine);
+
+ struct timespec ts;
+ if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
+ logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ const int64_t timestamp = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec;
+
+ const int64_t uptime = getTime();
+
+ buffer->summary(timestamp, uptime, 0, buf);
+
+ for (int i = 0; i < gSessionData->mCores; ++i) {
+ // Don't send information on a cpu we know nothing about
+ if (gSessionData->mCpuIds[i] == -1) {
+ continue;
+ }
+
+ int j;
+ for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) {
+ if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) {
+ break;
+ }
+ }
+ if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) {
+ buffer->coreName(i, gSessionData->mCpuIds[i], gator_cpus[j].core_name);
+ } else {
+ if (gSessionData->mCpuIds[i] == -1) {
+ snprintf(buf, sizeof(buf), "Unknown");
+ } else {
+ snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[i]);
+ }
+ buffer->coreName(i, gSessionData->mCpuIds[i], buf);
+ }
+ }
+ buffer->commit(1);
+
+ return true;
+}
+
+PerfCounter *PerfDriver::findCounter(const Counter &counter) const {
+ for (PerfCounter * perfCounter = mCounters; perfCounter != NULL; perfCounter = perfCounter->getNext()) {
+ if (strcmp(perfCounter->getName(), counter.getType()) == 0) {
+ return perfCounter;
+ }
+ }
+
+ return NULL;
+}
+
+bool PerfDriver::claimCounter(const Counter &counter) const {
+ return findCounter(counter) != NULL;
+}
+
+void PerfDriver::resetCounters() {
+ for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ counter->setEnabled(false);
+ }
+}
+
+void PerfDriver::setupCounter(Counter &counter) {
+ PerfCounter *const perfCounter = findCounter(counter);
+ if (perfCounter == NULL) {
+ counter.setEnabled(false);
+ return;
+ }
+
+ // Don't use the config from counters XML if it's not set, ex: software counters
+ if (counter.getEvent() != -1) {
+ perfCounter->setConfig(counter.getEvent());
+ }
+ perfCounter->setCount(counter.getCount());
+ perfCounter->setEnabled(true);
+ counter.setKey(perfCounter->getKey());
+}
+
+int PerfDriver::writeCounters(mxml_node_t *root) const {
+ int count = 0;
+ for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ mxml_node_t *node = mxmlNewElement(root, "counter");
+ mxmlElementSetAttr(node, "name", counter->getName());
+ ++count;
+ }
+
+ return count;
+}
+
+bool PerfDriver::enable(PerfGroup *const group, Buffer *const buffer) const {
+ for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) {
+ if (!group->add(buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), counter->getCount() > 0 ? PERF_SAMPLE_TID | PERF_SAMPLE_IP : 0, counter->isPerCpu() ? PERF_GROUP_PER_CPU : 0)) {
+ logg->logMessage("%s(%s:%i): PerfGroup::add failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+long long PerfDriver::getTracepointId(const char *const name, DynBuf *const printb) {
+ if (!printb->printf(EVENTS_PATH "/%s/id", name)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ return -1;
+ }
+
+ int64_t result;
+ if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) {
+ logg->logMessage("%s(%s:%i): DriverSource::readInt64Driver failed", __FUNCTION__, __FILE__, __LINE__);
+ return -1;
+ }
+
+ return result;
+}
diff --git a/tools/gator/daemon/PerfDriver.h b/tools/gator/daemon/PerfDriver.h
new file mode 100644
index 000000000000..2cae575a7059
--- /dev/null
+++ b/tools/gator/daemon/PerfDriver.h
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 PERFDRIVER_H
+#define PERFDRIVER_H
+
+#include "Driver.h"
+
+// If debugfs is not mounted at /sys/kernel/debug, update DEBUGFS_PATH
+#define DEBUGFS_PATH "/sys/kernel/debug"
+#define EVENTS_PATH DEBUGFS_PATH "/tracing/events"
+
+#define SCHED_SWITCH "sched/sched_switch"
+
+class Buffer;
+class DynBuf;
+class PerfCounter;
+class PerfGroup;
+
+class PerfDriver : public Driver {
+public:
+ PerfDriver();
+ ~PerfDriver();
+
+ bool getLegacySupport() const { return mLegacySupport; }
+
+ bool setup();
+ bool summary(Buffer *const buffer);
+ bool isSetup() const { return mIsSetup; }
+
+ bool claimCounter(const Counter &counter) const;
+ void resetCounters();
+ void setupCounter(Counter &counter);
+
+ int writeCounters(mxml_node_t *root) const;
+
+ bool enable(PerfGroup *const group, Buffer *const buffer) const;
+
+ static long long getTracepointId(const char *const name, DynBuf *const printb);
+
+private:
+ PerfCounter *findCounter(const Counter &counter) const;
+ void addCpuCounters(const char *const counterName, const int type, const int numCounters);
+ void addUncoreCounters(const char *const counterName, const int type, const int numCounters);
+
+ PerfCounter *mCounters;
+ bool mIsSetup;
+ bool mLegacySupport;
+
+ // Intentionally undefined
+ PerfDriver(const PerfDriver &);
+ PerfDriver &operator=(const PerfDriver &);
+};
+
+#endif // PERFDRIVER_H
diff --git a/tools/gator/daemon/PerfGroup.cpp b/tools/gator/daemon/PerfGroup.cpp
new file mode 100644
index 000000000000..2a0239f7c348
--- /dev/null
+++ b/tools/gator/daemon/PerfGroup.cpp
@@ -0,0 +1,226 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "PerfGroup.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "Buffer.h"
+#include "Logging.h"
+#include "Monitor.h"
+#include "PerfBuffer.h"
+#include "SessionData.h"
+
+#define DEFAULT_PEA_ARGS(pea, additionalSampleType) \
+ pea.size = sizeof(pea); \
+ /* Emit time, read_format below, group leader id, and raw tracepoint info */ \
+ pea.sample_type = (gSessionData->perf.getLegacySupport() \
+ ? PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_ID \
+ : PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER ) | additionalSampleType; \
+ /* Emit emit value in group format */ \
+ pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \
+ /* start out disabled */ \
+ pea.disabled = 1; \
+ /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \
+ pea.watermark = 1; \
+ /* Be conservative in flush size as only one buffer set is monitored */ \
+ pea.wakeup_watermark = 3 * BUF_SIZE / 4
+
+static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) {
+ return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) {
+ memset(&mAttrs, 0, sizeof(mAttrs));
+ memset(&mPerCpu, 0, sizeof(mPerCpu));
+ memset(&mKeys, -1, sizeof(mKeys));
+ memset(&mFds, -1, sizeof(mFds));
+}
+
+PerfGroup::~PerfGroup() {
+ for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
+ if (mFds[pos] >= 0) {
+ close(mFds[pos]);
+ }
+ }
+}
+
+bool PerfGroup::add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) {
+ int i;
+ for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ if (mKeys[i] < 0) {
+ break;
+ }
+ }
+
+ if (i >= ARRAY_LENGTH(mKeys)) {
+ logg->logMessage("%s(%s:%i): Too many counters", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ DEFAULT_PEA_ARGS(mAttrs[i], sampleType);
+ mAttrs[i].type = type;
+ mAttrs[i].config = config;
+ mAttrs[i].sample_period = sample;
+ // always be on the CPU but only a group leader can be pinned
+ mAttrs[i].pinned = (i == 0 ? 1 : 0);
+ mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0);
+ mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0);
+ mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0);
+ mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0);
+ mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0);
+ mPerCpu[i] = (flags & PERF_GROUP_PER_CPU);
+
+ mKeys[i] = key;
+
+ buffer->pea(&mAttrs[i], key);
+
+ return true;
+}
+
+bool PerfGroup::prepareCPU(const int cpu) {
+ logg->logMessage("%s(%s:%i): Onlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
+
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ if (mKeys[i] < 0) {
+ continue;
+ }
+
+ if ((cpu != 0) && !mPerCpu[i]) {
+ continue;
+ }
+
+ const int offset = i * gSessionData->mCores;
+ if (mFds[cpu + offset] >= 0) {
+ logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: 0x%llx pinned: %i mmap: %i comm: %i freq: %i task: %i sample_id_all: %i", __FUNCTION__, __FILE__, __LINE__, cpu, (long long)mAttrs[i].type, (long long)mAttrs[i].config, (long long)mAttrs[i].sample_period, (long long)mAttrs[i].sample_type, mAttrs[i].pinned, mAttrs[i].mmap, mAttrs[i].comm, mAttrs[i].freq, mAttrs[i].task, mAttrs[i].sample_id_all);
+ mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT);
+ if (mFds[cpu + offset] < 0) {
+ logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno));
+ continue;
+ }
+
+ if (!mPb->useFd(cpu, mFds[cpu + offset], mFds[cpu])) {
+ logg->logMessage("%s(%s:%i): PerfBuffer::useFd failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int PerfGroup::onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor) {
+ __u64 ids[ARRAY_LENGTH(mKeys)];
+ int coreKeys[ARRAY_LENGTH(mKeys)];
+ int idCount = 0;
+
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ const int fd = mFds[cpu + i * gSessionData->mCores];
+ if (fd < 0) {
+ continue;
+ }
+
+ coreKeys[idCount] = mKeys[i];
+ if (!gSessionData->perf.getLegacySupport() && ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0 &&
+ // Workaround for running 32-bit gatord on 64-bit systems, kernel patch in the works
+ ioctl(fd, (PERF_EVENT_IOC_ID & ~IOCSIZE_MASK) | (8 << _IOC_SIZESHIFT), &ids[idCount]) != 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ ++idCount;
+ }
+
+ if (!monitor->add(mFds[cpu])) {
+ logg->logMessage("%s(%s:%i): Monitor::add failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (!gSessionData->perf.getLegacySupport()) {
+ buffer->keys(idCount, ids, coreKeys);
+ } else {
+ char buf[1024];
+ ssize_t bytes = read(mFds[cpu], buf, sizeof(buf));
+ if (bytes < 0) {
+ logg->logMessage("read failed");
+ return false;
+ }
+ buffer->keysOld(idCount, coreKeys, bytes, buf);
+ }
+
+ if (start) {
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ int offset = i * gSessionData->mCores + cpu;
+ if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE) < 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+ }
+
+ return idCount;
+}
+
+bool PerfGroup::offlineCPU(const int cpu) {
+ logg->logMessage("%s(%s:%i): Offlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
+
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ int offset = i * gSessionData->mCores + cpu;
+ if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE) < 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ // Mark the buffer so that it will be released next time it's read
+ mPb->discard(cpu);
+
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ if (mKeys[i] < 0) {
+ continue;
+ }
+
+ int offset = i * gSessionData->mCores + cpu;
+ if (mFds[offset] >= 0) {
+ close(mFds[offset]);
+ mFds[offset] = -1;
+ }
+ }
+
+ return true;
+}
+
+bool PerfGroup::start() {
+ for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) {
+ if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE) < 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ }
+
+ return true;
+
+ fail:
+ stop();
+
+ return false;
+}
+
+void PerfGroup::stop() {
+ for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
+ if (mFds[pos] >= 0) {
+ ioctl(mFds[pos], PERF_EVENT_IOC_DISABLE);
+ }
+ }
+}
diff --git a/tools/gator/daemon/PerfGroup.h b/tools/gator/daemon/PerfGroup.h
new file mode 100644
index 000000000000..3f1e2bb4d1c8
--- /dev/null
+++ b/tools/gator/daemon/PerfGroup.h
@@ -0,0 +1,57 @@
+ /**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 PERF_GROUP
+#define PERF_GROUP
+
+// Use a snapshot of perf_event.h as it may be more recent than what is on the target and if not newer features won't be supported anyways
+#include "k/perf_event.h"
+
+#include "Config.h"
+
+class Buffer;
+class Monitor;
+class PerfBuffer;
+
+enum PerfGroupFlags {
+ PERF_GROUP_MMAP = 1 << 0,
+ PERF_GROUP_COMM = 1 << 1,
+ PERF_GROUP_FREQ = 1 << 2,
+ PERF_GROUP_TASK = 1 << 3,
+ PERF_GROUP_SAMPLE_ID_ALL = 1 << 4,
+ PERF_GROUP_PER_CPU = 1 << 5,
+};
+
+class PerfGroup {
+public:
+ PerfGroup(PerfBuffer *const pb);
+ ~PerfGroup();
+
+ bool add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags);
+ // Safe to call concurrently
+ bool prepareCPU(const int cpu);
+ // Not safe to call concurrently. Returns the number of events enabled
+ int onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor);
+ bool offlineCPU(int cpu);
+ bool start();
+ void stop();
+
+private:
+ // +1 for the group leader
+ struct perf_event_attr mAttrs[MAX_PERFORMANCE_COUNTERS + 1];
+ bool mPerCpu[MAX_PERFORMANCE_COUNTERS + 1];
+ int mKeys[MAX_PERFORMANCE_COUNTERS + 1];
+ int mFds[NR_CPUS * (MAX_PERFORMANCE_COUNTERS + 1)];
+ PerfBuffer *const mPb;
+
+ // Intentionally undefined
+ PerfGroup(const PerfGroup &);
+ PerfGroup &operator=(const PerfGroup &);
+};
+
+#endif // PERF_GROUP
diff --git a/tools/gator/daemon/PerfSource.cpp b/tools/gator/daemon/PerfSource.cpp
new file mode 100644
index 000000000000..ecfaa66832bd
--- /dev/null
+++ b/tools/gator/daemon/PerfSource.cpp
@@ -0,0 +1,275 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "PerfSource.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "Child.h"
+#include "DynBuf.h"
+#include "Logging.h"
+#include "PerfDriver.h"
+#include "Proc.h"
+#include "SessionData.h"
+
+#define MS_PER_US 1000000
+
+extern Child *child;
+
+static bool sendTracepointFormat(Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) {
+ if (!printb->printf(EVENTS_PATH "/%s/format", name)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ if (!b->read(printb->getBuf())) {
+ logg->logMessage("%s(%s:%i): DynBuf::read failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ buffer->format(b->getLength(), b->getBuf());
+
+ return true;
+}
+
+PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) {
+ long l = sysconf(_SC_PAGE_SIZE);
+ if (l < 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to obtain the page size");
+ handleException();
+ }
+ gSessionData->mPageSize = static_cast<int>(l);
+
+ l = sysconf(_SC_NPROCESSORS_CONF);
+ if (l < 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to obtain the number of cores");
+ handleException();
+ }
+ gSessionData->mCores = static_cast<int>(l);
+}
+
+PerfSource::~PerfSource() {
+}
+
+struct PrepareParallelArgs {
+ PerfGroup *pg;
+ int cpu;
+};
+
+void *prepareParallel(void *arg) {
+ const PrepareParallelArgs *const args = (PrepareParallelArgs *)arg;
+ args->pg->prepareCPU(args->cpu);
+ return NULL;
+}
+
+bool PerfSource::prepare() {
+ DynBuf printb;
+ DynBuf b1;
+ DynBuf b2;
+ DynBuf b3;
+ long long schedSwitchId;
+
+ // Reread cpuinfo since cores may have changed since startup
+ gSessionData->readCpuInfo();
+
+ if (0
+ || !mMonitor.init()
+ || !mUEvent.init()
+ || !mMonitor.add(mUEvent.getFd())
+
+ || (schedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &printb)) < 0
+ || !sendTracepointFormat(&mBuffer, SCHED_SWITCH, &printb, &b1)
+
+ // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID
+ || !mCountersGroup.add(&mBuffer, 100/**/, PERF_TYPE_TRACEPOINT, schedSwitchId, 1, PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL | PERF_GROUP_PER_CPU)
+
+ // Only want TID and IP but not RAW on timer
+ || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(&mBuffer, 99/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, PERF_GROUP_PER_CPU))
+
+ || !gSessionData->perf.enable(&mCountersGroup, &mBuffer)
+ || 0) {
+ logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.4 or later?", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (!gSessionData->perf.summary(&mSummary)) {
+ logg->logMessage("%s(%s:%i): PerfDriver::summary failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ {
+ // Run prepareCPU in parallel as perf_event_open can take more than 1 sec in some cases
+ pthread_t threads[NR_CPUS];
+ PrepareParallelArgs args[NR_CPUS];
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ args[cpu].pg = &mCountersGroup;
+ args[cpu].cpu = cpu;
+ if (pthread_create(&threads[cpu], NULL, prepareParallel, &args[cpu]) != 0) {
+ logg->logMessage("%s(%s:%i): pthread_create failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ if (pthread_join(threads[cpu], NULL) != 0) {
+ logg->logMessage("%s(%s:%i): pthread_join failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+ }
+
+ int numEvents = 0;
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ numEvents += mCountersGroup.onlineCPU(cpu, false, &mBuffer, &mMonitor);
+ }
+ if (numEvents <= 0) {
+ logg->logMessage("%s(%s:%i): PerfGroup::onlineCPU failed on all cores", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ // Start events before reading proc to avoid race conditions
+ if (!mCountersGroup.start()) {
+ logg->logMessage("%s(%s:%i): PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (!readProc(&mBuffer, true, &printb, &b1, &b2, &b3)) {
+ logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ mBuffer.commit(1);
+
+ return true;
+}
+
+static const char CPU_DEVPATH[] = "/devices/system/cpu/cpu";
+
+void PerfSource::run() {
+ int pipefd[2];
+
+ if (pipe(pipefd) != 0) {
+ logg->logError(__FILE__, __LINE__, "pipe failed");
+ handleException();
+ }
+ mInterruptFd = pipefd[1];
+
+ if (!mMonitor.add(pipefd[0])) {
+ logg->logError(__FILE__, __LINE__, "Monitor::add failed");
+ handleException();
+ }
+
+ int timeout = -1;
+ if (gSessionData->mLiveRate > 0) {
+ timeout = gSessionData->mLiveRate/MS_PER_US;
+ }
+
+ sem_post(mStartProfile);
+
+ while (gSessionData->mSessionIsActive) {
+ // +1 for uevents, +1 for pipe
+ struct epoll_event events[NR_CPUS + 2];
+ int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout);
+ if (ready < 0) {
+ logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
+ handleException();
+ }
+
+ for (int i = 0; i < ready; ++i) {
+ if (events[i].data.fd == mUEvent.getFd()) {
+ if (!handleUEvent()) {
+ logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed");
+ handleException();
+ }
+ break;
+ }
+ }
+
+ // send a notification that data is ready
+ sem_post(mSenderSem);
+
+ // In one shot mode, stop collection once all the buffers are filled
+ // Assume timeout == 0 in this case
+ if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
+ logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__);
+ child->endSession();
+ }
+ }
+
+ mCountersGroup.stop();
+ mBuffer.setDone();
+ mIsDone = true;
+
+ // send a notification that data is ready
+ sem_post(mSenderSem);
+
+ mInterruptFd = -1;
+ close(pipefd[0]);
+ close(pipefd[1]);
+}
+
+bool PerfSource::handleUEvent() {
+ UEventResult result;
+ if (!mUEvent.read(&result)) {
+ logg->logMessage("%s(%s:%i): UEvent::Read failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (strcmp(result.mSubsystem, "cpu") == 0) {
+ if (strncmp(result.mDevPath, CPU_DEVPATH, sizeof(CPU_DEVPATH) - 1) != 0) {
+ logg->logMessage("%s(%s:%i): Unexpected cpu DEVPATH format", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ char *endptr;
+ errno = 0;
+ int cpu = strtol(result.mDevPath + sizeof(CPU_DEVPATH) - 1, &endptr, 10);
+ if (errno != 0 || *endptr != '\0') {
+ logg->logMessage("%s(%s:%i): strtol failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ if (strcmp(result.mAction, "online") == 0) {
+ // Only call onlineCPU if prepareCPU succeeded
+ const bool result = mCountersGroup.prepareCPU(cpu) &&
+ mCountersGroup.onlineCPU(cpu, true, &mBuffer, &mMonitor);
+ mBuffer.commit(1);
+ return result;
+ } else if (strcmp(result.mAction, "offline") == 0) {
+ return mCountersGroup.offlineCPU(cpu);
+ }
+ }
+
+ return true;
+}
+
+void PerfSource::interrupt() {
+ if (mInterruptFd >= 0) {
+ int8_t c = 0;
+ // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread
+ if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) {
+ logg->logError(__FILE__, __LINE__, "write failed");
+ handleException();
+ }
+ }
+}
+
+bool PerfSource::isDone () {
+ return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty();
+}
+
+void PerfSource::write (Sender *sender) {
+ if (!mSummary.isDone()) {
+ mSummary.write(sender);
+ gSessionData->mSentSummary = true;
+ }
+ if (!mBuffer.isDone()) {
+ mBuffer.write(sender);
+ }
+ if (!mCountersBuf.send(sender)) {
+ logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed");
+ handleException();
+ }
+}
diff --git a/tools/gator/daemon/PerfSource.h b/tools/gator/daemon/PerfSource.h
new file mode 100644
index 000000000000..3f471c8de414
--- /dev/null
+++ b/tools/gator/daemon/PerfSource.h
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 PERFSOURCE_H
+#define PERFSOURCE_H
+
+#include <semaphore.h>
+
+#include "Buffer.h"
+#include "Monitor.h"
+#include "PerfBuffer.h"
+#include "PerfGroup.h"
+#include "Source.h"
+#include "UEvent.h"
+
+class Sender;
+
+class PerfSource : public Source {
+public:
+ PerfSource(sem_t *senderSem, sem_t *startProfile);
+ ~PerfSource();
+
+ bool prepare();
+ void run();
+ void interrupt();
+
+ bool isDone();
+ void write(Sender *sender);
+
+private:
+ bool handleUEvent();
+
+ Buffer mSummary;
+ Buffer mBuffer;
+ PerfBuffer mCountersBuf;
+ PerfGroup mCountersGroup;
+ Monitor mMonitor;
+ UEvent mUEvent;
+ sem_t *const mSenderSem;
+ sem_t *const mStartProfile;
+ int mInterruptFd;
+ bool mIsDone;
+
+ // Intentionally undefined
+ PerfSource(const PerfSource &);
+ PerfSource &operator=(const PerfSource &);
+};
+
+#endif // PERFSOURCE_H
diff --git a/tools/gator/daemon/Proc.cpp b/tools/gator/daemon/Proc.cpp
new file mode 100644
index 000000000000..9f01770d6609
--- /dev/null
+++ b/tools/gator/daemon/Proc.cpp
@@ -0,0 +1,215 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "Proc.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Buffer.h"
+#include "DynBuf.h"
+#include "Logging.h"
+
+struct ProcStat {
+ // From linux-dev/include/linux/sched.h
+#define TASK_COMM_LEN 16
+ // TASK_COMM_LEN may grow, so be ready for it to get larger
+ char comm[2*TASK_COMM_LEN];
+ long numThreads;
+};
+
+static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf *const b) {
+ if (!b->read(pathname)) {
+ logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__);
+ // This is not a fatal error - the thread just doesn't exist any more
+ return true;
+ }
+
+ char *comm = strchr(b->getBuf(), '(');
+ if (comm == NULL) {
+ logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ ++comm;
+ char *const str = strrchr(comm, ')');
+ if (str == NULL) {
+ logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ *str = '\0';
+ strncpy(ps->comm, comm, sizeof(ps->comm) - 1);
+ ps->comm[sizeof(ps->comm) - 1] = '\0';
+
+ const int count = sscanf(str + 2, " %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %ld", &ps->numThreads);
+ if (count != 1) {
+ logg->logMessage("%s(%s:%i): sscanf failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ return true;
+}
+
+static const char *readProcExe(DynBuf *const printb, const int pid, const int tid, DynBuf *const b) {
+ if (tid == -1 ? !printb->printf("/proc/%i/exe", pid)
+ : !printb->printf("/proc/%i/task/%i/exe", pid, tid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ return NULL;
+ }
+
+ const int err = b->readlink(printb->getBuf());
+ const char *image;
+ if (err == 0) {
+ image = strrchr(b->getBuf(), '/');
+ if (image == NULL) {
+ image = b->getBuf();
+ } else {
+ ++image;
+ }
+ } else if (err == -ENOENT) {
+ // readlink /proc/[pid]/exe returns ENOENT for kernel threads
+ image = "\0";
+ } else {
+ logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__);
+ return NULL;
+ }
+
+ // Android apps are run by app_process but the cmdline is changed to reference the actual app name
+ if (strcmp(image, "app_process") != 0) {
+ return image;
+ }
+
+ if (tid == -1 ? !printb->printf("/proc/%i/cmdline", pid)
+ : !printb->printf("/proc/%i/task/%i/cmdline", pid, tid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ return NULL;
+ }
+
+ if (!b->read(printb->getBuf())) {
+ logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__);
+ return NULL;
+ }
+
+ return b->getBuf();
+}
+
+static bool readProcTask(Buffer *const buffer, const int pid, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) {
+ bool result = false;
+
+ if (!b1->printf("/proc/%i/task", pid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ return result;
+ }
+ DIR *task = opendir(b1->getBuf());
+ if (task == NULL) {
+ logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
+ return result;
+ }
+
+ struct dirent *dirent;
+ while ((dirent = readdir(task)) != NULL) {
+ char *endptr;
+ const int tid = strtol(dirent->d_name, &endptr, 10);
+ if (*endptr != '\0') {
+ // Ignore task items that are not integers like ., etc...
+ continue;
+ }
+
+ if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ ProcStat ps;
+ if (!readProcStat(&ps, printb->getBuf(), b1)) {
+ logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+
+ const char *const image = readProcExe(printb, pid, tid, b2);
+ if (image == NULL) {
+ logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+
+ buffer->comm(pid, tid, image, ps.comm);
+ }
+
+ result = true;
+
+ fail:
+ closedir(task);
+
+ return result;
+}
+
+bool readProc(Buffer *const buffer, bool sendMaps, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) {
+ bool result = false;
+
+ DIR *proc = opendir("/proc");
+ if (proc == NULL) {
+ logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
+ return result;
+ }
+
+ struct dirent *dirent;
+ while ((dirent = readdir(proc)) != NULL) {
+ char *endptr;
+ const int pid = strtol(dirent->d_name, &endptr, 10);
+ if (*endptr != '\0') {
+ // Ignore proc items that are not integers like ., cpuinfo, etc...
+ continue;
+ }
+
+ if (!printb->printf("/proc/%i/stat", pid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ ProcStat ps;
+ if (!readProcStat(&ps, printb->getBuf(), b1)) {
+ logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+
+ if (sendMaps) {
+ if (!printb->printf("/proc/%i/maps", pid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ if (!b2->read(printb->getBuf())) {
+ logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__);
+ // This is not a fatal error - the process just doesn't exist any more
+ continue;
+ }
+
+ buffer->maps(pid, pid, b2->getBuf());
+ }
+ if (ps.numThreads <= 1) {
+ const char *const image = readProcExe(printb, pid, -1, b1);
+ if (image == NULL) {
+ logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+
+ buffer->comm(pid, pid, image, ps.comm);
+ } else {
+ if (!readProcTask(buffer, pid, printb, b1, b3)) {
+ logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ }
+ }
+
+ result = true;
+
+ fail:
+ closedir(proc);
+
+ return result;
+}
diff --git a/tools/gator/daemon/Proc.h b/tools/gator/daemon/Proc.h
new file mode 100644
index 000000000000..31c2eecb7aeb
--- /dev/null
+++ b/tools/gator/daemon/Proc.h
@@ -0,0 +1,17 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 PROC_H
+#define PROC_H
+
+class Buffer;
+class DynBuf;
+
+bool readProc(Buffer *const buffer, bool sendMaps, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3);
+
+#endif // PROC_H
diff --git a/tools/gator/daemon/Sender.cpp b/tools/gator/daemon/Sender.cpp
new file mode 100644
index 000000000000..3a981a6427be
--- /dev/null
+++ b/tools/gator/daemon/Sender.cpp
@@ -0,0 +1,134 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "Sender.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "Buffer.h"
+#include "Logging.h"
+#include "OlySocket.h"
+#include "SessionData.h"
+
+Sender::Sender(OlySocket* socket) {
+ mDataFile = NULL;
+ mDataSocket = NULL;
+
+ // Set up the socket connection
+ if (socket) {
+ char streamline[64] = {0};
+ mDataSocket = socket;
+
+ // Receive magic sequence - can wait forever
+ // Streamline will send data prior to the magic sequence for legacy support, which should be ignored for v4+
+ while (strcmp("STREAMLINE", streamline) != 0) {
+ if (mDataSocket->receiveString(streamline, sizeof(streamline)) == -1) {
+ logg->logError(__FILE__, __LINE__, "Socket disconnected");
+ handleException();
+ }
+ }
+
+ // Send magic sequence - must be done first, after which error messages can be sent
+ char magic[32];
+ snprintf(magic, 32, "GATOR %i\n", PROTOCOL_VERSION);
+ mDataSocket->send(magic, strlen(magic));
+
+ gSessionData->mWaitingOnCommand = true;
+ logg->logMessage("Completed magic sequence");
+ }
+
+ pthread_mutex_init(&mSendMutex, NULL);
+}
+
+Sender::~Sender() {
+ // Just close it as the client socket is on the stack
+ if (mDataSocket != NULL) {
+ mDataSocket->closeSocket();
+ mDataSocket = NULL;
+ }
+ if (mDataFile != NULL) {
+ fclose(mDataFile);
+ }
+}
+
+void Sender::createDataFile(char* apcDir) {
+ if (apcDir == NULL) {
+ return;
+ }
+
+ mDataFileName = (char*)malloc(strlen(apcDir) + 12);
+ sprintf(mDataFileName, "%s/0000000000", apcDir);
+ mDataFile = fopen(mDataFileName, "wb");
+ if (!mDataFile) {
+ logg->logError(__FILE__, __LINE__, "Failed to open binary file: %s", mDataFileName);
+ handleException();
+ }
+}
+
+template<typename T>
+inline T min(const T a, const T b) {
+ return (a < b ? a : b);
+}
+
+void Sender::writeData(const char* data, int length, int type) {
+ if (length < 0 || (data == NULL && length > 0)) {
+ return;
+ }
+
+ // Multiple threads call writeData()
+ pthread_mutex_lock(&mSendMutex);
+
+ // Send data over the socket connection
+ if (mDataSocket) {
+ // Start alarm
+ const int alarmDuration = 8;
+ alarm(alarmDuration);
+
+ // Send data over the socket, sending the type and size first
+ logg->logMessage("Sending data with length %d", length);
+ if (type != RESPONSE_APC_DATA) {
+ // type and length already added by the Collector for apc data
+ unsigned char header[5];
+ header[0] = type;
+ Buffer::writeLEInt(header + 1, length);
+ mDataSocket->send((char*)&header, sizeof(header));
+ }
+
+ // 100Kbits/sec * alarmDuration sec / 8 bits/byte
+ const int chunkSize = 100*1000 * alarmDuration / 8;
+ int pos = 0;
+ while (true) {
+ mDataSocket->send((const char*)data + pos, min(length - pos, chunkSize));
+ pos += chunkSize;
+ if (pos >= length) {
+ break;
+ }
+
+ // Reset the alarm
+ alarm(alarmDuration);
+ logg->logMessage("Resetting the alarm");
+ }
+
+ // Stop alarm
+ alarm(0);
+ }
+
+ // Write data to disk as long as it is not meta data
+ if (mDataFile && type == RESPONSE_APC_DATA) {
+ logg->logMessage("Writing data with length %d", length);
+ // Send data to the data file
+ if (fwrite(data, 1, length, mDataFile) != (unsigned int)length) {
+ logg->logError(__FILE__, __LINE__, "Failed writing binary file %s", mDataFileName);
+ handleException();
+ }
+ }
+
+ pthread_mutex_unlock(&mSendMutex);
+}
diff --git a/tools/gator/daemon/Sender.h b/tools/gator/daemon/Sender.h
new file mode 100644
index 000000000000..33b6cc3c5d8d
--- /dev/null
+++ b/tools/gator/daemon/Sender.h
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 __SENDER_H__
+#define __SENDER_H__
+
+#include <stdio.h>
+#include <pthread.h>
+
+class OlySocket;
+
+enum {
+ RESPONSE_XML = 1,
+ RESPONSE_APC_DATA = 3,
+ RESPONSE_ACK = 4,
+ RESPONSE_NAK = 5,
+ RESPONSE_ERROR = 0xFF
+};
+
+class Sender {
+public:
+ Sender(OlySocket* socket);
+ ~Sender();
+ void writeData(const char* data, int length, int type);
+ void createDataFile(char* apcDir);
+private:
+ OlySocket* mDataSocket;
+ FILE* mDataFile;
+ char* mDataFileName;
+ pthread_mutex_t mSendMutex;
+
+ // Intentionally unimplemented
+ Sender(const Sender &);
+ Sender &operator=(const Sender &);
+};
+
+#endif //__SENDER_H__
diff --git a/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp
new file mode 100644
index 000000000000..14d995fc39fa
--- /dev/null
+++ b/tools/gator/daemon/SessionData.cpp
@@ -0,0 +1,194 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "SessionData.h"
+
+#include <string.h>
+#include <sys/mman.h>
+
+#include "SessionXML.h"
+#include "Logging.h"
+
+SessionData* gSessionData = NULL;
+
+SessionData::SessionData() {
+ initialize();
+}
+
+SessionData::~SessionData() {
+}
+
+void SessionData::initialize() {
+ mWaitingOnCommand = false;
+ mSessionIsActive = false;
+ mLocalCapture = false;
+ mOneShot = false;
+ mSentSummary = false;
+ const size_t cpuIdSize = sizeof(int)*NR_CPUS;
+ // Share mCpuIds across all instances of gatord
+ mCpuIds = (int *)mmap(NULL, cpuIdSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (mCpuIds == MAP_FAILED) {
+ logg->logError(__FILE__, __LINE__, "Unable to mmap shared memory for cpuids");
+ handleException();
+ }
+ memset(mCpuIds, -1, cpuIdSize);
+ readCpuInfo();
+ mConfigurationXMLPath = NULL;
+ mSessionXMLPath = NULL;
+ mEventsXMLPath = NULL;
+ mTargetPath = NULL;
+ mAPCDir = NULL;
+ mSampleRate = 0;
+ mLiveRate = 0;
+ mDuration = 0;
+ mBacktraceDepth = 0;
+ mTotalBufferSize = 0;
+ // sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module
+ mCores = 1;
+ mPageSize = 0;
+}
+
+void SessionData::parseSessionXML(char* xmlString) {
+ SessionXML session(xmlString);
+ session.parse();
+
+ // Set session data values - use prime numbers just below the desired value to reduce the chance of events firing at the same time
+ if (strcmp(session.parameters.sample_rate, "high") == 0) {
+ mSampleRate = 9973; // 10000
+ } else if (strcmp(session.parameters.sample_rate, "normal") == 0) {
+ mSampleRate = 997; // 1000
+ } else if (strcmp(session.parameters.sample_rate, "low") == 0) {
+ mSampleRate = 97; // 100
+ } else if (strcmp(session.parameters.sample_rate, "none") == 0) {
+ mSampleRate = 0;
+ } else {
+ logg->logError(__FILE__, __LINE__, "Invalid sample rate (%s) in session xml.", session.parameters.sample_rate);
+ handleException();
+ }
+ mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0;
+ mDuration = session.parameters.duration;
+
+ // Determine buffer size (in MB) based on buffer mode
+ mOneShot = true;
+ if (strcmp(session.parameters.buffer_mode, "streaming") == 0) {
+ mOneShot = false;
+ mTotalBufferSize = 1;
+ } else if (strcmp(session.parameters.buffer_mode, "small") == 0) {
+ mTotalBufferSize = 1;
+ } else if (strcmp(session.parameters.buffer_mode, "normal") == 0) {
+ mTotalBufferSize = 4;
+ } else if (strcmp(session.parameters.buffer_mode, "large") == 0) {
+ mTotalBufferSize = 16;
+ } else {
+ logg->logError(__FILE__, __LINE__, "Invalid value for buffer mode in session xml.");
+ handleException();
+ }
+
+ mImages = session.parameters.images;
+ // Convert milli- to nanoseconds
+ mLiveRate = session.parameters.live_rate * (int64_t)1000000;
+ if (mLiveRate > 0 && mLocalCapture) {
+ logg->logMessage("Local capture is not compatable with live, disabling live");
+ mLiveRate = 0;
+ }
+}
+
+void SessionData::readCpuInfo() {
+ char temp[256]; // arbitrarily large amount
+ strcpy(mCoreName, "unknown");
+ mMaxCpuId = -1;
+
+ FILE* f = fopen("/proc/cpuinfo", "r");
+ if (f == NULL) {
+ logg->logMessage("Error opening /proc/cpuinfo\n"
+ "The core name in the captured xml file will be 'unknown'.");
+ return;
+ }
+
+ bool foundCoreName = false;
+ int processor = -1;
+ while (fgets(temp, sizeof(temp), f)) {
+ const size_t len = strlen(temp);
+
+ if (len == 1) {
+ // New section, clear the processor. Streamline will not know the cpus if the pre Linux 3.8 format of cpuinfo is encountered but also that no incorrect information will be transmitted.
+ processor = -1;
+ continue;
+ }
+
+ if (len > 0) {
+ temp[len - 1] = '\0'; // Replace the line feed with a null
+ }
+
+ const bool foundHardware = strstr(temp, "Hardware") != 0;
+ const bool foundCPUPart = strstr(temp, "CPU part") != 0;
+ const bool foundProcessor = strstr(temp, "processor") != 0;
+ if (foundHardware || foundCPUPart || foundProcessor) {
+ char* position = strchr(temp, ':');
+ if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) {
+ logg->logMessage("Unknown format of /proc/cpuinfo\n"
+ "The core name in the captured xml file will be 'unknown'.");
+ return;
+ }
+ position += 2;
+
+ if (foundHardware) {
+ strncpy(mCoreName, position, sizeof(mCoreName));
+ mCoreName[sizeof(mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string
+ foundCoreName = true;
+ }
+
+ if (foundCPUPart) {
+ const int cpuId = strtol(position, NULL, 0);
+ // If this does not have the full topology in /proc/cpuinfo, mCpuIds[0] may not have the 1 CPU part emitted - this guarantees it's in mMaxCpuId
+ if (cpuId > mMaxCpuId) {
+ mMaxCpuId = cpuId;
+ }
+ if (processor >= NR_CPUS) {
+ logg->logMessage("Too many processors, please increase NR_CPUS");
+ } else if (processor >= 0) {
+ mCpuIds[processor] = cpuId;
+ }
+ }
+
+ if (foundProcessor) {
+ processor = strtol(position, NULL, 0);
+ }
+ }
+ }
+
+ if (!foundCoreName) {
+ logg->logMessage("Could not determine core name from /proc/cpuinfo\n"
+ "The core name in the captured xml file will be 'unknown'.");
+ }
+ fclose(f);
+}
+
+uint64_t getTime() {
+ struct timespec ts;
+#ifndef CLOCK_MONOTONIC_RAW
+ // Android doesn't have this defined but it was added in Linux 2.6.28
+#define CLOCK_MONOTONIC_RAW 4
+#endif
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) {
+ logg->logError(__FILE__, __LINE__, "Failed to get uptime");
+ handleException();
+ }
+ return (NS_PER_S*ts.tv_sec + ts.tv_nsec);
+}
+
+int getEventKey() {
+ // key 0 is reserved as a timestamp
+ // key 1 is reserved as the marker for thread specific counters
+ // Odd keys are assigned by the driver, even keys by the daemon
+ static int key = 2;
+
+ const int ret = key;
+ key += 2;
+ return ret;
+}
diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h
new file mode 100644
index 000000000000..835082d86c4b
--- /dev/null
+++ b/tools/gator/daemon/SessionData.h
@@ -0,0 +1,86 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 SESSION_DATA_H
+#define SESSION_DATA_H
+
+#include <stdint.h>
+
+#include "Config.h"
+#include "Counter.h"
+#include "FSDriver.h"
+#include "Hwmon.h"
+#include "MaliVideoDriver.h"
+#include "PerfDriver.h"
+
+#define PROTOCOL_VERSION 19
+#define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions
+
+#define NS_PER_S ((uint64_t)1000000000)
+
+struct ImageLinkList {
+ char* path;
+ struct ImageLinkList *next;
+};
+
+class SessionData {
+public:
+ static const size_t MAX_STRING_LEN = 80;
+
+ SessionData();
+ ~SessionData();
+ void initialize();
+ void parseSessionXML(char* xmlString);
+ void readCpuInfo();
+
+ Hwmon hwmon;
+ FSDriver fsDriver;
+ PerfDriver perf;
+ MaliVideoDriver maliVideo;
+
+ char mCoreName[MAX_STRING_LEN];
+ struct ImageLinkList *mImages;
+ char* mConfigurationXMLPath;
+ char* mSessionXMLPath;
+ char* mEventsXMLPath;
+ char* mTargetPath;
+ char* mAPCDir;
+
+ bool mWaitingOnCommand;
+ bool mSessionIsActive;
+ bool mLocalCapture;
+ bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled
+ bool mIsEBS;
+ bool mSentSummary;
+
+ int mBacktraceDepth;
+ int mTotalBufferSize; // number of MB to use for the entire collection buffer
+ int mSampleRate;
+ int64_t mLiveRate;
+ int mDuration;
+ int mCores;
+ int mPageSize;
+ int *mCpuIds;
+ int mMaxCpuId;
+
+ // PMU Counters
+ int mCounterOverflow;
+ Counter mCounters[MAX_PERFORMANCE_COUNTERS];
+
+private:
+ // Intentionally unimplemented
+ SessionData(const SessionData &);
+ SessionData &operator=(const SessionData &);
+};
+
+extern SessionData* gSessionData;
+
+uint64_t getTime();
+int getEventKey();
+
+#endif // SESSION_DATA_H
diff --git a/tools/gator/daemon/SessionXML.cpp b/tools/gator/daemon/SessionXML.cpp
new file mode 100644
index 000000000000..8cdc9409ca21
--- /dev/null
+++ b/tools/gator/daemon/SessionXML.cpp
@@ -0,0 +1,111 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "SessionXML.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
+
+static const char* TAG_SESSION = "session";
+static const char* TAG_IMAGE = "image";
+
+static const char* ATTR_VERSION = "version";
+static const char* ATTR_CALL_STACK_UNWINDING = "call_stack_unwinding";
+static const char* ATTR_BUFFER_MODE = "buffer_mode";
+static const char* ATTR_SAMPLE_RATE = "sample_rate";
+static const char* ATTR_DURATION = "duration";
+static const char* ATTR_PATH = "path";
+static const char* ATTR_LIVE_RATE = "live_rate";
+
+SessionXML::SessionXML(const char *str) {
+ parameters.buffer_mode[0] = 0;
+ parameters.sample_rate[0] = 0;
+ parameters.duration = 0;
+ parameters.call_stack_unwinding = false;
+ parameters.live_rate = 0;
+ parameters.images = NULL;
+ mPath = 0;
+ mSessionXML = (const char *)str;
+ logg->logMessage(mSessionXML);
+}
+
+SessionXML::~SessionXML() {
+ if (mPath != 0) {
+ free((char *)mSessionXML);
+ }
+}
+
+void SessionXML::parse() {
+ mxml_node_t *tree;
+ mxml_node_t *node;
+
+ tree = mxmlLoadString(NULL, mSessionXML, MXML_NO_CALLBACK);
+ node = mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND);
+
+ if (node) {
+ sessionTag(tree, node);
+ mxmlDelete(tree);
+ return;
+ }
+
+ logg->logError(__FILE__, __LINE__, "No session tag found in the session.xml file");
+ handleException();
+}
+
+void SessionXML::sessionTag(mxml_node_t *tree, mxml_node_t *node) {
+ int version = 0;
+ if (mxmlElementGetAttr(node, ATTR_VERSION)) version = strtol(mxmlElementGetAttr(node, ATTR_VERSION), NULL, 10);
+ if (version != 1) {
+ logg->logError(__FILE__, __LINE__, "Invalid session.xml version: %d", version);
+ handleException();
+ }
+
+ // copy to pre-allocated strings
+ if (mxmlElementGetAttr(node, ATTR_BUFFER_MODE)) {
+ strncpy(parameters.buffer_mode, mxmlElementGetAttr(node, ATTR_BUFFER_MODE), sizeof(parameters.buffer_mode));
+ parameters.buffer_mode[sizeof(parameters.buffer_mode) - 1] = 0; // strncpy does not guarantee a null-terminated string
+ }
+ if (mxmlElementGetAttr(node, ATTR_SAMPLE_RATE)) {
+ strncpy(parameters.sample_rate, mxmlElementGetAttr(node, ATTR_SAMPLE_RATE), sizeof(parameters.sample_rate));
+ parameters.sample_rate[sizeof(parameters.sample_rate) - 1] = 0; // strncpy does not guarantee a null-terminated string
+ }
+
+ // integers/bools
+ parameters.call_stack_unwinding = util->stringToBool(mxmlElementGetAttr(node, ATTR_CALL_STACK_UNWINDING), false);
+ if (mxmlElementGetAttr(node, ATTR_DURATION)) parameters.duration = strtol(mxmlElementGetAttr(node, ATTR_DURATION), NULL, 10);
+ if (mxmlElementGetAttr(node, ATTR_LIVE_RATE)) parameters.live_rate = strtol(mxmlElementGetAttr(node, ATTR_LIVE_RATE), NULL, 10);
+
+ // parse subtags
+ node = mxmlGetFirstChild(node);
+ while (node) {
+ if (mxmlGetType(node) != MXML_ELEMENT) {
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+ continue;
+ }
+ if (strcmp(TAG_IMAGE, mxmlGetElement(node)) == 0) {
+ sessionImage(node);
+ }
+ node = mxmlWalkNext(node, tree, MXML_NO_DESCEND);
+ }
+}
+
+void SessionXML::sessionImage(mxml_node_t *node) {
+ int length = strlen(mxmlElementGetAttr(node, ATTR_PATH));
+ struct ImageLinkList *image;
+
+ image = (struct ImageLinkList *)malloc(sizeof(struct ImageLinkList));
+ image->path = (char*)malloc(length + 1);
+ image->path = strdup(mxmlElementGetAttr(node, ATTR_PATH));
+ image->next = parameters.images;
+ parameters.images = image;
+}
diff --git a/tools/gator/daemon/SessionXML.h b/tools/gator/daemon/SessionXML.h
new file mode 100644
index 000000000000..e146094a4d17
--- /dev/null
+++ b/tools/gator/daemon/SessionXML.h
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 SESSION_XML_H
+#define SESSION_XML_H
+
+#include "mxml/mxml.h"
+
+struct ImageLinkList;
+
+struct ConfigParameters {
+ char buffer_mode[64]; // buffer mode, "streaming", "low", "normal", "high" defines oneshot and buffer size
+ char sample_rate[64]; // capture mode, "high", "normal", or "low"
+ int duration; // length of profile in seconds
+ bool call_stack_unwinding; // whether stack unwinding is performed
+ int live_rate;
+ struct ImageLinkList *images; // linked list of image strings
+};
+
+class SessionXML {
+public:
+ SessionXML(const char *str);
+ ~SessionXML();
+ void parse();
+ ConfigParameters parameters;
+private:
+ const char *mSessionXML;
+ const char *mPath;
+ void sessionTag(mxml_node_t *tree, mxml_node_t *node);
+ void sessionImage(mxml_node_t *node);
+
+ // Intentionally unimplemented
+ SessionXML(const SessionXML &);
+ SessionXML &operator=(const SessionXML &);
+};
+
+#endif // SESSION_XML_H
diff --git a/tools/gator/daemon/Source.cpp b/tools/gator/daemon/Source.cpp
new file mode 100644
index 000000000000..60cf704e599b
--- /dev/null
+++ b/tools/gator/daemon/Source.cpp
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "Source.h"
+
+#include "Logging.h"
+
+Source::Source() : mThreadID() {
+}
+
+Source::~Source() {
+}
+
+void Source::start() {
+ if (pthread_create(&mThreadID, NULL, runStatic, this)) {
+ logg->logError(__FILE__, __LINE__, "Failed to create source thread");
+ handleException();
+ }
+}
+
+void Source::join() {
+ pthread_join(mThreadID, NULL);
+}
+
+void *Source::runStatic(void *arg) {
+ static_cast<Source *>(arg)->run();
+ return NULL;
+}
diff --git a/tools/gator/daemon/Source.h b/tools/gator/daemon/Source.h
new file mode 100644
index 000000000000..56ac3d6e94f3
--- /dev/null
+++ b/tools/gator/daemon/Source.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 SOURCE_H
+#define SOURCE_H
+
+#include <pthread.h>
+
+class Sender;
+
+class Source {
+public:
+ Source();
+ virtual ~Source();
+
+ virtual bool prepare() = 0;
+ void start();
+ virtual void run() = 0;
+ virtual void interrupt() = 0;
+ void join();
+
+ virtual bool isDone() = 0;
+ virtual void write(Sender *sender) = 0;
+
+private:
+ static void *runStatic(void *arg);
+
+ pthread_t mThreadID;
+
+ // Intentionally undefined
+ Source(const Source &);
+ Source &operator=(const Source &);
+};
+
+#endif // SOURCE_H
diff --git a/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp
new file mode 100644
index 000000000000..2b61eaeb290d
--- /dev/null
+++ b/tools/gator/daemon/StreamlineSetup.cpp
@@ -0,0 +1,272 @@
+/**
+ * Copyright (C) ARM Limited 2011-2014. All rights reserved.
+ *
+ * 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 "StreamlineSetup.h"
+
+#include "Buffer.h"
+#include "CapturedXML.h"
+#include "ConfigurationXML.h"
+#include "Driver.h"
+#include "EventsXML.h"
+#include "Logging.h"
+#include "OlySocket.h"
+#include "OlyUtility.h"
+#include "Sender.h"
+#include "SessionData.h"
+
+static const char* TAG_SESSION = "session";
+static const char* TAG_REQUEST = "request";
+static const char* TAG_CONFIGURATIONS = "configurations";
+
+static const char* ATTR_TYPE = "type";
+static const char* VALUE_EVENTS = "events";
+static const char* VALUE_CONFIGURATION = "configuration";
+static const char* VALUE_COUNTERS = "counters";
+static const char* VALUE_CAPTURED = "captured";
+static const char* VALUE_DEFAULTS = "defaults";
+
+StreamlineSetup::StreamlineSetup(OlySocket* s) {
+ bool ready = false;
+ char* data = NULL;
+ int type;
+
+ mSocket = s;
+
+ // Receive commands from Streamline (master)
+ while (!ready) {
+ // receive command over socket
+ gSessionData->mWaitingOnCommand = true;
+ data = readCommand(&type);
+
+ // parse and handle data
+ switch (type) {
+ case COMMAND_REQUEST_XML:
+ handleRequest(data);
+ break;
+ case COMMAND_DELIVER_XML:
+ handleDeliver(data);
+ break;
+ case COMMAND_APC_START:
+ logg->logMessage("Received apc start request");
+ ready = true;
+ break;
+ case COMMAND_APC_STOP:
+ logg->logMessage("Received apc stop request before apc start request");
+ exit(0);
+ break;
+ case COMMAND_DISCONNECT:
+ logg->logMessage("Received disconnect command");
+ exit(0);
+ break;
+ case COMMAND_PING:
+ logg->logMessage("Received ping command");
+ sendData(NULL, 0, RESPONSE_ACK);
+ break;
+ default:
+ logg->logError(__FILE__, __LINE__, "Target error: Unknown command type, %d", type);
+ handleException();
+ }
+
+ free(data);
+ }
+
+ if (gSessionData->mCounterOverflow > 0) {
+ logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow);
+ handleException();
+ }
+}
+
+StreamlineSetup::~StreamlineSetup() {
+}
+
+char* StreamlineSetup::readCommand(int* command) {
+ unsigned char header[5];
+ char* data;
+ int response;
+
+ // receive type and length
+ response = mSocket->receiveNBytes((char*)&header, sizeof(header));
+
+ // After receiving a single byte, we are no longer waiting on a command
+ gSessionData->mWaitingOnCommand = false;
+
+ if (response < 0) {
+ logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
+ handleException();
+ }
+
+ const char type = header[0];
+ const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24);
+
+ // add artificial limit
+ if ((length < 0) || length > 1024 * 1024) {
+ logg->logError(__FILE__, __LINE__, "Target error: Invalid length received, %d", length);
+ handleException();
+ }
+
+ // allocate memory to contain the xml file, size of zero returns a zero size object
+ data = (char*)calloc(length + 1, 1);
+ if (data == NULL) {
+ logg->logError(__FILE__, __LINE__, "Unable to allocate memory for xml");
+ handleException();
+ }
+
+ // receive data
+ response = mSocket->receiveNBytes(data, length);
+ if (response < 0) {
+ logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
+ handleException();
+ }
+
+ // null terminate the data for string parsing
+ if (length > 0) {
+ data[length] = 0;
+ }
+
+ *command = type;
+ return data;
+}
+
+void StreamlineSetup::handleRequest(char* xml) {
+ mxml_node_t *tree, *node;
+ const char * attr = NULL;
+
+ tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK);
+ node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_TYPE, NULL, MXML_DESCEND_FIRST);
+ if (node) {
+ attr = mxmlElementGetAttr(node, ATTR_TYPE);
+ }
+ if (attr && strcmp(attr, VALUE_EVENTS) == 0) {
+ sendEvents();
+ logg->logMessage("Sent events xml response");
+ } else if (attr && strcmp(attr, VALUE_CONFIGURATION) == 0) {
+ sendConfiguration();
+ logg->logMessage("Sent configuration xml response");
+ } else if (attr && strcmp(attr, VALUE_COUNTERS) == 0) {
+ sendCounters();
+ logg->logMessage("Sent counters xml response");
+ } else if (attr && strcmp(attr, VALUE_CAPTURED) == 0) {
+ CapturedXML capturedXML;
+ char* capturedText = capturedXML.getXML(false);
+ sendData(capturedText, strlen(capturedText), RESPONSE_XML);
+ free(capturedText);
+ logg->logMessage("Sent captured xml response");
+ } else if (attr && strcmp(attr, VALUE_DEFAULTS) == 0) {
+ sendDefaults();
+ logg->logMessage("Sent default configuration xml response");
+ } else {
+ char error[] = "Unknown request";
+ sendData(error, strlen(error), RESPONSE_NAK);
+ logg->logMessage("Received unknown request:\n%s", xml);
+ }
+
+ mxmlDelete(tree);
+}
+
+void StreamlineSetup::handleDeliver(char* xml) {
+ mxml_node_t *tree;
+
+ // Determine xml type
+ tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK);
+ if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) {
+ // Session XML
+ gSessionData->parseSessionXML(xml);
+ sendData(NULL, 0, RESPONSE_ACK);
+ logg->logMessage("Received session xml");
+ } else if (mxmlFindElement(tree, tree, TAG_CONFIGURATIONS, NULL, NULL, MXML_DESCEND_FIRST)) {
+ // Configuration XML
+ writeConfiguration(xml);
+ sendData(NULL, 0, RESPONSE_ACK);
+ logg->logMessage("Received configuration xml");
+ } else {
+ // Unknown XML
+ logg->logMessage("Received unknown XML delivery type");
+ sendData(NULL, 0, RESPONSE_NAK);
+ }
+
+ mxmlDelete(tree);
+}
+
+void StreamlineSetup::sendData(const char* data, uint32_t length, char type) {
+ unsigned char header[5];
+ header[0] = type;
+ Buffer::writeLEInt(header + 1, length);
+ mSocket->send((char*)&header, sizeof(header));
+ mSocket->send((const char*)data, length);
+}
+
+void StreamlineSetup::sendEvents() {
+ EventsXML eventsXML;
+ char* string = eventsXML.getXML();
+ sendString(string, RESPONSE_XML);
+ free(string);
+}
+
+void StreamlineSetup::sendConfiguration() {
+ ConfigurationXML xml;
+
+ const char* string = xml.getConfigurationXML();
+ sendData(string, strlen(string), RESPONSE_XML);
+}
+
+void StreamlineSetup::sendDefaults() {
+ // Send the config built into the binary
+ const char* xml;
+ unsigned int size;
+ ConfigurationXML::getDefaultConfigurationXml(xml, size);
+
+ // Artificial size restriction
+ if (size > 1024*1024) {
+ logg->logError(__FILE__, __LINE__, "Corrupt default configuration file");
+ handleException();
+ }
+
+ sendData(xml, size, RESPONSE_XML);
+}
+
+void StreamlineSetup::sendCounters() {
+ mxml_node_t *xml;
+ mxml_node_t *counters;
+
+ xml = mxmlNewXML("1.0");
+ counters = mxmlNewElement(xml, "counters");
+ int count = 0;
+ for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
+ count += driver->writeCounters(counters);
+ }
+
+ if (count == 0) {
+ logg->logError(__FILE__, __LINE__, "No counters found, this could be because /dev/gator/events can not be read or because perf is not working correctly");
+ handleException();
+ }
+
+ char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
+ sendString(string, RESPONSE_XML);
+
+ free(string);
+ mxmlDelete(xml);
+}
+
+void StreamlineSetup::writeConfiguration(char* xml) {
+ char path[PATH_MAX];
+
+ ConfigurationXML::getPath(path);
+
+ if (util->writeToDisk(path, xml) < 0) {
+ logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path);
+ handleException();
+ }
+
+ // Re-populate gSessionData with the configuration, as it has now changed
+ { ConfigurationXML configuration; }
+
+ if (gSessionData->mCounterOverflow > 0) {
+ logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow);
+ handleException();
+ }
+}
diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h
new file mode 100644
index 000000000000..b380f46b98f0
--- /dev/null
+++ b/tools/gator/daemon/StreamlineSetup.h
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 __STREAMLINE_SETUP_H__
+#define __STREAMLINE_SETUP_H__
+
+#include <stdint.h>
+#include <string.h>
+
+class OlySocket;
+
+// Commands from Streamline
+enum {
+ COMMAND_REQUEST_XML = 0,
+ COMMAND_DELIVER_XML = 1,
+ COMMAND_APC_START = 2,
+ COMMAND_APC_STOP = 3,
+ COMMAND_DISCONNECT = 4,
+ COMMAND_PING = 5
+};
+
+class StreamlineSetup {
+public:
+ StreamlineSetup(OlySocket *socket);
+ ~StreamlineSetup();
+private:
+ OlySocket* mSocket;
+
+ char* readCommand(int*);
+ void handleRequest(char* xml);
+ void handleDeliver(char* xml);
+ void sendData(const char* data, uint32_t length, char type);
+ void sendString(const char* string, int type) {sendData(string, strlen(string), type);}
+ void sendEvents();
+ void sendConfiguration();
+ void sendDefaults();
+ void sendCounters();
+ void writeConfiguration(char* xml);
+
+ // Intentionally unimplemented
+ StreamlineSetup(const StreamlineSetup &);
+ StreamlineSetup &operator=(const StreamlineSetup &);
+};
+
+#endif //__STREAMLINE_SETUP_H__
diff --git a/tools/gator/daemon/UEvent.cpp b/tools/gator/daemon/UEvent.cpp
new file mode 100644
index 000000000000..54d45751e3c9
--- /dev/null
+++ b/tools/gator/daemon/UEvent.cpp
@@ -0,0 +1,76 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 "UEvent.h"
+
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <linux/netlink.h>
+
+#include "Logging.h"
+
+static const char EMPTY[] = "";
+static const char ACTION[] = "ACTION=";
+static const char DEVPATH[] = "DEVPATH=";
+static const char SUBSYSTEM[] = "SUBSYSTEM=";
+
+UEvent::UEvent() : mFd(-1) {
+}
+
+UEvent::~UEvent() {
+ if (mFd >= 0) {
+ close(mFd);
+ }
+}
+
+bool UEvent::init() {
+ mFd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+ if (mFd < 0) {
+ logg->logMessage("%s(%s:%i): socket failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ struct sockaddr_nl sockaddr;
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.nl_family = AF_NETLINK;
+ sockaddr.nl_groups = 1; // bitmask: (1 << 0) == kernel events, (1 << 1) == udev events
+ sockaddr.nl_pid = 0;
+ if (bind(mFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) {
+ logg->logMessage("%s(%s:%i): bind failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ return true;
+}
+
+bool UEvent::read(UEventResult *const result) {
+ ssize_t bytes = recv(mFd, result->mBuf, sizeof(result->mBuf), 0);
+ if (bytes <= 0) {
+ logg->logMessage("%s(%s:%i): recv failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ result->mAction = EMPTY;
+ result->mDevPath = EMPTY;
+ result->mSubsystem = EMPTY;
+
+ for (int pos = 0; pos < bytes; pos += strlen(result->mBuf + pos) + 1) {
+ char *const str = result->mBuf + pos;
+ if (strncmp(str, ACTION, sizeof(ACTION) - 1) == 0) {
+ result->mAction = str + sizeof(ACTION) - 1;
+ } else if (strncmp(str, DEVPATH, sizeof(DEVPATH) - 1) == 0) {
+ result->mDevPath = str + sizeof(DEVPATH) - 1;
+ } else if (strncmp(str, SUBSYSTEM, sizeof(SUBSYSTEM) - 1) == 0) {
+ result->mSubsystem = str + sizeof(SUBSYSTEM) - 1;
+ }
+ }
+
+ return true;
+}
diff --git a/tools/gator/daemon/UEvent.h b/tools/gator/daemon/UEvent.h
new file mode 100644
index 000000000000..2f7ef2c93f5d
--- /dev/null
+++ b/tools/gator/daemon/UEvent.h
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * 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 UEVENT_H
+#define UEVENT_H
+
+struct UEventResult {
+ const char *mAction;
+ const char *mDevPath;
+ const char *mSubsystem;
+ char mBuf[1<<13];
+};
+
+class UEvent {
+public:
+ UEvent();
+ ~UEvent();
+
+ bool init();
+ bool read(UEventResult *const result);
+ int getFd() const { return mFd; }
+
+private:
+ int mFd;
+
+ // Intentionally undefined
+ UEvent(const UEvent &);
+ UEvent &operator=(const UEvent &);
+};
+
+#endif // UEVENT_H
diff --git a/tools/gator/daemon/UserSpaceSource.cpp b/tools/gator/daemon/UserSpaceSource.cpp
new file mode 100644
index 000000000000..8c328e0e0fb5
--- /dev/null
+++ b/tools/gator/daemon/UserSpaceSource.cpp
@@ -0,0 +1,89 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 "UserSpaceSource.h"
+
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "Child.h"
+#include "DriverSource.h"
+#include "Logging.h"
+#include "SessionData.h"
+
+#define NS_PER_US 1000
+
+extern Child *child;
+
+UserSpaceSource::UserSpaceSource(sem_t *senderSem) : mBuffer(0, FRAME_BLOCK_COUNTER, gSessionData->mTotalBufferSize*1024*1024, senderSem) {
+}
+
+UserSpaceSource::~UserSpaceSource() {
+}
+
+bool UserSpaceSource::prepare() {
+ return true;
+}
+
+void UserSpaceSource::run() {
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0);
+
+ gSessionData->hwmon.start();
+ gSessionData->fsDriver.start();
+
+ int64_t monotonic_started = 0;
+ while (monotonic_started <= 0) {
+ usleep(10);
+
+ if (DriverSource::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) {
+ logg->logError(__FILE__, __LINE__, "Error reading gator driver start time");
+ handleException();
+ }
+ }
+
+ uint64_t next_time = 0;
+ while (gSessionData->mSessionIsActive) {
+ const uint64_t curr_time = getTime() - monotonic_started;
+ // Sample ten times a second ignoring gSessionData->mSampleRate
+ next_time += NS_PER_S/10;//gSessionData->mSampleRate;
+ if (next_time < curr_time) {
+ logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time);
+ next_time = curr_time;
+ }
+
+ if (mBuffer.eventHeader(curr_time)) {
+ gSessionData->hwmon.read(&mBuffer);
+ gSessionData->fsDriver.read(&mBuffer);
+ // Only check after writing all counters so that time and corresponding counters appear in the same frame
+ mBuffer.check(curr_time);
+ }
+
+ if (mBuffer.bytesAvailable() <= 0) {
+ logg->logMessage("One shot (counters)");
+ child->endSession();
+ }
+
+ usleep((next_time - curr_time)/NS_PER_US);
+ }
+
+ mBuffer.setDone();
+}
+
+void UserSpaceSource::interrupt() {
+ // Do nothing
+}
+
+bool UserSpaceSource::isDone() {
+ return mBuffer.isDone();
+}
+
+void UserSpaceSource::write(Sender *sender) {
+ if (!mBuffer.isDone()) {
+ mBuffer.write(sender);
+ }
+}
diff --git a/tools/gator/daemon/UserSpaceSource.h b/tools/gator/daemon/UserSpaceSource.h
new file mode 100644
index 000000000000..9b3666016dc5
--- /dev/null
+++ b/tools/gator/daemon/UserSpaceSource.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 USERSPACESOURCE_H
+#define USERSPACESOURCE_H
+
+#include <semaphore.h>
+
+#include "Buffer.h"
+#include "Source.h"
+
+// User space counters
+class UserSpaceSource : public Source {
+public:
+ UserSpaceSource(sem_t *senderSem);
+ ~UserSpaceSource();
+
+ bool prepare();
+ void run();
+ void interrupt();
+
+ bool isDone();
+ void write(Sender *sender);
+
+private:
+ Buffer mBuffer;
+
+ // Intentionally unimplemented
+ UserSpaceSource(const UserSpaceSource &);
+ UserSpaceSource &operator=(const UserSpaceSource &);
+};
+
+#endif // USERSPACESOURCE_H
diff --git a/tools/gator/daemon/c++.cpp b/tools/gator/daemon/c++.cpp
new file mode 100644
index 000000000000..6041e5e96469
--- /dev/null
+++ b/tools/gator/daemon/c++.cpp
@@ -0,0 +1,40 @@
+/**
+ * Minimal set of C++ functions so that libstdc++ is not required
+ *
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+void operator delete(void *ptr) {
+ if (ptr != NULL) {
+ free(ptr);
+ }
+}
+
+void operator delete[](void *ptr) {
+ operator delete(ptr);
+}
+
+void *operator new(size_t size) {
+ void *ptr = malloc(size == 0 ? 1 : size);
+ if (ptr == NULL) {
+ abort();
+ }
+ return ptr;
+}
+
+void *operator new[](size_t size) {
+ return operator new(size);
+}
+
+extern "C"
+void __cxa_pure_virtual() {
+ printf("pure virtual method called\n");
+ abort();
+}
diff --git a/tools/gator/daemon/common.mk b/tools/gator/daemon/common.mk
new file mode 100644
index 000000000000..769a92e51a35
--- /dev/null
+++ b/tools/gator/daemon/common.mk
@@ -0,0 +1,52 @@
+# -g produces debugging information
+# -O3 maximum optimization
+# -O0 no optimization, used for debugging
+# -Wall enables most warnings
+# -Werror treats warnings as errors
+# -std=c++0x is the planned new c++ standard
+# -std=c++98 is the 1998 c++ standard
+CPPFLAGS += -O3 -Wall -fno-exceptions -pthread -MMD -DETCDIR=\"/etc\" -Ilibsensors
+CXXFLAGS += -fno-rtti -Wextra # -Weffc++
+ifeq ($(WERROR),1)
+ CPPFLAGS += -Werror
+endif
+# -s strips the binary of debug info
+LDFLAGS += -s
+LDLIBS += -lrt -lm -pthread
+TARGET = gatord
+C_SRC = $(wildcard mxml/*.c) $(wildcard libsensors/*.c)
+CXX_SRC = $(wildcard *.cpp)
+
+all: $(TARGET)
+
+events.xml: events_header.xml $(wildcard events-*.xml) events_footer.xml
+ cat $^ > $@
+
+include $(wildcard *.d)
+include $(wildcard mxml/*.d)
+
+EventsXML.cpp: events_xml.h
+ConfigurationXML.cpp: defaults_xml.h
+
+# Don't regenerate conf-lex.c or conf-parse.c
+libsensors/conf-lex.c: ;
+libsensors/conf-parse.c: ;
+
+%_xml.h: %.xml escape
+ ./escape $< > $@
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
+
+%.o: %.cpp
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
+
+$(TARGET): $(CXX_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o)
+ $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
+
+# Intentionally ignore CC as a native binary is required
+escape: escape.c
+ gcc $^ -o $@
+
+clean:
+ rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h defaults_xml.h
diff --git a/tools/gator/daemon/defaults.xml b/tools/gator/daemon/defaults.xml
new file mode 100644
index 000000000000..39a0f656f7e6
--- /dev/null
+++ b/tools/gator/daemon/defaults.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configurations revision="3">
+ <configuration counter="ARM_ARM11_ccnt" event="0xff"/>
+ <configuration counter="ARM_ARM11_cnt0" event="0x7"/>
+ <configuration counter="ARM_ARM11_cnt1" event="0xb"/>
+ <configuration counter="ARM_ARM11MPCore_ccnt" event="0xff"/>
+ <configuration counter="ARM_ARM11MPCore_cnt0" event="0x08"/>
+ <configuration counter="ARM_ARM11MPCore_cnt1" event="0x0b"/>
+ <configuration counter="ARMv7_Cortex_A5_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A5_cnt0" event="0x8"/>
+ <configuration counter="ARMv7_Cortex_A5_cnt1" event="0x1"/>
+ <configuration counter="ARMv7_Cortex_A7_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A7_cnt0" event="0x08"/>
+ <configuration counter="ARMv7_Cortex_A7_cnt1" event="0x10"/>
+ <configuration counter="ARMv7_Cortex_A7_cnt2" event="0x16"/>
+ <configuration counter="ARMv7_Cortex_A8_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A8_cnt0" event="0x8"/>
+ <configuration counter="ARMv7_Cortex_A8_cnt1" event="0x44"/>
+ <configuration counter="ARMv7_Cortex_A8_cnt2" event="0x43"/>
+ <configuration counter="ARMv7_Cortex_A8_cnt3" event="0x10"/>
+ <configuration counter="ARMv7_Cortex_A9_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt0" event="0x68"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt1" event="0x06"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt2" event="0x07"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt3" event="0x03"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt4" event="0x04"/>
+ <configuration counter="ARMv7_Cortex_A12_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A12_cnt0" event="0x08"/>
+ <configuration counter="ARMv7_Cortex_A12_cnt1" event="0x16"/>
+ <configuration counter="ARMv7_Cortex_A12_cnt2" event="0x10"/>
+ <configuration counter="ARMv7_Cortex_A12_cnt3" event="0x19"/>
+ <configuration counter="ARMv7_Cortex_A15_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A15_cnt0" event="0x8"/>
+ <configuration counter="ARMv7_Cortex_A15_cnt1" event="0x16"/>
+ <configuration counter="ARMv7_Cortex_A15_cnt2" event="0x10"/>
+ <configuration counter="ARMv7_Cortex_A15_cnt3" event="0x19"/>
+ <configuration counter="ARM_Cortex-A53_ccnt" event="0x11"/>
+ <configuration counter="ARM_Cortex-A53_cnt0" event="0x8"/>
+ <configuration counter="ARM_Cortex-A53_cnt1" event="0x16"/>
+ <configuration counter="ARM_Cortex-A53_cnt2" event="0x10"/>
+ <configuration counter="ARM_Cortex-A53_cnt3" event="0x19"/>
+ <configuration counter="ARM_Cortex-A57_ccnt" event="0x11"/>
+ <configuration counter="ARM_Cortex-A57_cnt0" event="0x8"/>
+ <configuration counter="ARM_Cortex-A57_cnt1" event="0x16"/>
+ <configuration counter="ARM_Cortex-A57_cnt2" event="0x10"/>
+ <configuration counter="ARM_Cortex-A57_cnt3" event="0x19"/>
+ <configuration counter="Scorpion_ccnt" event="0xff"/>
+ <configuration counter="Scorpion_cnt0" event="0x08"/>
+ <configuration counter="Scorpion_cnt1" event="0x10"/>
+ <configuration counter="ScorpionMP_ccnt" event="0xff"/>
+ <configuration counter="ScorpionMP_cnt0" event="0x08"/>
+ <configuration counter="ScorpionMP_cnt1" event="0x10"/>
+ <configuration counter="Krait_ccnt" event="0xff"/>
+ <configuration counter="Krait_cnt0" event="0x08"/>
+ <configuration counter="Krait_cnt1" event="0x10"/>
+ <configuration counter="Linux_block_rq_wr"/>
+ <configuration counter="Linux_block_rq_rd"/>
+ <configuration counter="Linux_meminfo_memused"/>
+ <configuration counter="Linux_meminfo_memfree"/>
+ <configuration counter="Linux_power_cpu_freq"/>
+ <configuration counter="ARM_Mali-4xx_fragment"/>
+ <configuration counter="ARM_Mali-4xx_vertex"/>
+ <configuration counter="ARM_Mali-T6xx_fragment" cores="1"/>
+ <configuration counter="ARM_Mali-T6xx_vertex" cores="1"/>
+ <configuration counter="ARM_Mali-T6xx_opencl" cores="1"/>
+ <configuration counter="L2C-310_cnt0" event="0x1"/>
+</configurations>
diff --git a/tools/gator/daemon/escape.c b/tools/gator/daemon/escape.c
new file mode 100644
index 000000000000..2b0863aaf425
--- /dev/null
+++ b/tools/gator/daemon/escape.c
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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.
+ */
+
+/*
+ * The Makefile in the daemon folder builds and executes 'escape'
+ * 'escape' creates configuration_xml.h from configuration.xml and events_xml.h from events-*.xml
+ * these genereated xml files are then #included and built as part of the gatord binary
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+static void print_escaped_path(char *path) {
+ if (isdigit(*path)) {
+ printf("__");
+ }
+ for (; *path != '\0'; ++path) {
+ printf("%c", isalnum(*path) ? *path : '_');
+ }
+}
+
+int main(int argc, char *argv[]) {
+ int i;
+ char *path;
+ FILE *in = NULL;
+ int ch;
+ unsigned int len = 0;
+
+ for (i = 1; i < argc && argv[i][0] == '-'; ++i) ;
+ if (i == argc) {
+ fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ path = argv[i];
+
+ errno = 0;
+ if ((in = fopen(path, "r")) == NULL) {
+ fprintf(stderr, "Unable to open '%s': %s\n", path, strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ printf("static const unsigned char ");
+ print_escaped_path(path);
+ printf("[] = {");
+ for (;;) {
+ ch = fgetc(in);
+ if (len != 0) {
+ printf(",");
+ }
+ if (len % 12 == 0) {
+ printf("\n ");
+ }
+ // Write out a null character after the contents of the file but do not increment len
+ printf(" 0x%.2x", (ch == EOF ? 0 : ch));
+ if (ch == EOF) {
+ break;
+ }
+ ++len;
+ }
+ printf("\n};\nstatic const unsigned int ");
+ print_escaped_path(path);
+ printf("_len = %i;\n", len);
+
+ fclose(in);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tools/gator/daemon/events-ARM11.xml b/tools/gator/daemon/events-ARM11.xml
new file mode 100644
index 000000000000..57e323546314
--- /dev/null
+++ b/tools/gator/daemon/events-ARM11.xml
@@ -0,0 +1,39 @@
+ <counter_set name="ARM_ARM11_cnt" count="3"/>
+ <category name="ARM11" counter_set="ARM_ARM11_cnt" per_cpu="yes">
+ <event counter="ARM_ARM11_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Cache" name="Inst miss" description="Instruction cache miss to a cacheable location, which requires a fetch from external memory"/>
+ <event event="0x01" title="Pipeline" name="Instruction stall" description="Stall because instruction buffer cannot deliver an instruction"/>
+ <event event="0x02" title="Pipeline" name="Data stall" description="Stall because of a data dependency"/>
+ <event event="0x03" title="Cache" name="Inst micro TLB miss" description="Instruction MicroTLB miss (unused on ARM1156)"/>
+ <event event="0x04" title="Cache" name="Data micro TLB miss" description="Data MicroTLB miss (unused on ARM1156)"/>
+ <event event="0x05" title="Branch" name="Instruction executed" description="Branch instruction executed, branch might or might not have changed program flow"/>
+ <event event="0x06" title="Branch" name="Mispredicted" description="Branch mis-predicted"/>
+ <event event="0x07" title="Instruction" name="Executed" description="Instructions executed"/>
+ <event event="0x09" title="Cache" name="Data access" description="Data cache access, not including Cache operations"/>
+ <event event="0x0a" title="Cache" name="Data all access" description="Data cache access, not including Cache Operations regardless of whether or not the location is cacheable"/>
+ <event event="0x0b" title="Cache" name="Data miss" description="Data cache miss, not including Cache Operations"/>
+ <event event="0x0c" title="Cache" name="Write-back" description="Data cache write-back"/>
+ <event event="0x0d" title="Program Counter" name="SW change" description="Software changed the PC"/>
+ <event event="0x0f" title="Cache " name="TLB miss" description="Main TLB miss (unused on ARM1156)"/>
+ <event event="0x10" title="External" name="Access" description="Explicit external data or peripheral access"/>
+ <event event="0x11" title="Cache" name="Data miss" description="Stall because of Load Store Unit request queue being full"/>
+ <event event="0x12" title="Write Buffer" name="Drains" description="The number of times the Write Buffer was drained because of a Data Synchronization Barrier command or Strongly Ordered operation"/>
+ <event event="0x13" title="Disable Interrupts" name="FIQ" description="The number of cycles which FIQ interrupts are disabled (ARM1156 only)"/>
+ <event event="0x14" title="Disable Interrupts" name="IRQ" description="The number of cycles which IRQ interrupts are disabled (ARM1156 only)"/>
+ <event event="0x20" title="ETM" name="ETMEXTOUT[0]" description="ETMEXTOUT[0] signal was asserted for a cycle"/>
+ <event event="0x21" title="ETM" name="ETMEXTOUT[1]" description="ETMEXTOUT[1] signal was asserted for a cycle"/>
+ <event event="0x22" title="ETM" name="ETMEXTOUT[0,1]" description="ETMEXTOUT[0] or ETMEXTOUT[1] was asserted"/>
+ <event event="0x23" title="Procedure" name="Calls" description="Procedure call instruction executed"/>
+ <event event="0x24" title="Procedure" name="Returns" description="Procedure return instruction executed"/>
+ <event event="0x25" title="Procedure" name="Return and predicted" description="Procedure return instruction executed and return address predicted"/>
+ <event event="0x26" title="Procedure" name="Return and mispredicted" description="Procedure return instruction executed and return address predicted incorrectly"/>
+ <event event="0x30" title="Cache" name="Inst tag or parity error" description="Instruction cache Tag or Valid RAM parity error (ARM1156 only)"/>
+ <event event="0x31" title="Cache" name="Inst parity error" description="Instruction cache RAM parity error (ARM1156 only)"/>
+ <event event="0x32" title="Cache" name="Data tag or parity error" description="Data cache Tag or Valid RAM parity error (ARM1156 only)"/>
+ <event event="0x33" title="Cache" name="Data parity error" description="Data cache RAM parity error (ARM1156 only)"/>
+ <event event="0x34" title="ITCM" name="Error" description="ITCM error (ARM1156 only)"/>
+ <event event="0x35" title="DTCM" name="Error" description="DTCM error (ARM1156 only)"/>
+ <event event="0x36" title="Procedure" name="Return address pop" description="Procedure return address popped off the return stack (ARM1156 only)"/>
+ <event event="0x37" title="Procedure" name="Return address misprediction" description="Procedure return address popped off the return stack has been incorrectly predicted by the PFU (ARM1156 only)"/>
+ <event event="0x38" title="Cache" name="Data dirty parity error" description="Data cache Dirty RAM parity error (ARM1156 only)"/>
+ </category>
diff --git a/tools/gator/daemon/events-ARM11MPCore.xml b/tools/gator/daemon/events-ARM11MPCore.xml
new file mode 100644
index 000000000000..2d5c5e199e66
--- /dev/null
+++ b/tools/gator/daemon/events-ARM11MPCore.xml
@@ -0,0 +1,26 @@
+ <counter_set name="ARM_ARM11MPCore_cnt" count="3"/>
+ <category name="ARM11MPCore" counter_set="ARM_ARM11MPCore_cnt" per_cpu="yes">
+ <event counter="ARM_ARM11MPCore_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Cache" name="Inst miss" description="Instruction cache miss to a cacheable location, which requires a fetch from external memory"/>
+ <event event="0x01" title="Pipeline" name="Instruction stall" description="Stall because instruction buffer cannot deliver an instruction"/>
+ <event event="0x02" title="Pipeline" name="Data stall" description="Stall because of a data dependency"/>
+ <event event="0x03" title="Cache" name="Inst micro TLB miss" description="Instruction MicroTLB miss (unused on ARM1156)"/>
+ <event event="0x04" title="Cache" name="Data micro TLB miss" description="Data MicroTLB miss (unused on ARM1156)"/>
+ <event event="0x05" title="Branch" name="Instruction executed" description="Branch instructions executed, branch might or might not have changed program flow"/>
+ <event event="0x06" title="Branch" name="Not predicted" description="Branch not predicted"/>
+ <event event="0x07" title="Branch" name="Mispredicted" description="Branch mispredicted"/>
+ <event event="0x08" title="Core" name="Instructions" description="Instructions executed"/>
+ <event event="0x09" title="Core" name="Folded Instructions" description="Folded instructions executed"/>
+ <event event="0x0a" title="Cache" name="Data read access" description="Data cache read access, not including cache operations"/>
+ <event event="0x0b" title="Cache" name="Data read miss" description="Data cache miss, not including Cache Operations"/>
+ <event event="0x0c" title="Cache" name="Data write access" description="Data cache write access"/>
+ <event event="0x0d" title="Cache" name="Data write miss" description="Data cache write miss"/>
+ <event event="0x0e" title="Cache" name="Data line eviction" description="Data cache line eviction, not including cache operations"/>
+ <event event="0x0f" title="Branch" name="PC change w/o mode change" description="Software changed the PC and there is not a mode change"/>
+ <event event="0x10" title="Cache " name="TLB miss" description="Main TLB miss"/>
+ <event event="0x11" title="External" name="External Memory request" description="External memory request (cache refill, noncachable, write-back)"/>
+ <event event="0x12" title="Cache" name="Stall" description="Stall because of Load Store Unit request queue being full"/>
+ <event event="0x13" title="Write Buffer" name="Drains" description="The number of times the Write Buffer was drained because of LSU ordering constraints or CP15 operations (Data Synchronization Barrier command) or Strongly Ordered operation"/>
+ <event event="0x14" title="Write Buffer" name="Write Merges" description="Buffered write merged in a store buffer slot"/>
+ <event event="0xFF" title="Core" name="Cycle counter" description="An increment each cycle"/>
+ </category>
diff --git a/tools/gator/daemon/events-CCI-400.xml b/tools/gator/daemon/events-CCI-400.xml
new file mode 100644
index 000000000000..20002efd1543
--- /dev/null
+++ b/tools/gator/daemon/events-CCI-400.xml
@@ -0,0 +1,98 @@
+ <counter_set name="CCI_400_cnt" count="4"/>
+ <category name="CCI-400" counter_set="CCI_400_cnt" per_cpu="no" supports_event_based_sampling="yes">
+ <event counter="CCI_400_ccnt" event="0xff" title="CCI-400 Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" description="The number of core clock cycles"/>
+ <option_set name="Slave">
+ <option event_delta="0x00" name="S0" description="Slave interface 0"/>
+ <option event_delta="0x20" name="S1" description="Slave interface 1"/>
+ <option event_delta="0x40" name="S2" description="Slave interface 2"/>
+ <option event_delta="0x60" name="S3" description="Slave interface 3"/>
+ <option event_delta="0x80" name="S4" description="Slave interface 4"/>
+ </option_set>
+ <event event="0x00" option_set="Slave" title="CCI-400" name="Read: any" description="Read request handshake: any"/>
+ <event event="0x01" option_set="Slave" title="CCI-400" name="Read: transaction" description="Read request handshake: device transaction"/>
+ <event event="0x02" option_set="Slave" title="CCI-400" name="Read: normal" description="Read request handshake: normal, non-shareable or system-shareable, but not barrier or cache maintenance operation"/>
+ <event event="0x03" option_set="Slave" title="CCI-400" name="Read: shareable" description="Read request handshake: inner- or outer-shareable, but not barrier, DVM message or cache maintenance operation"/>
+ <event event="0x04" option_set="Slave" title="CCI-400" name="Read: cache" description="Read request handshake: cache maintenance operation, CleanInvalid, CleanShared, MakeInvalid"/>
+ <event event="0x05" option_set="Slave" title="CCI-400" name="Read: memory barrier" description="Read request handshake: memory barrier"/>
+ <event event="0x06" option_set="Slave" title="CCI-400" name="Read: sync barrier" description="Read request handshake: synchronization barrier"/>
+ <event event="0x07" option_set="Slave" title="CCI-400" name="Read: DVM message, no sync" description="Read request handshake: DVM message, not synchronization"/>
+ <event event="0x08" option_set="Slave" title="CCI-400" name="Read: DVM message, sync" description="Read request handshake: DVM message, synchronization"/>
+ <event event="0x09" option_set="Slave" title="CCI-400" name="Read: stall" description="Read request stall cycle because the transaction tracker is full. Increase SIx_R_MAX to avoid this stall"/>
+ <event event="0x0a" option_set="Slave" title="CCI-400" name="Read data last handshake" description="Read data last handshake: data returned from the snoop instead of from downstream"/>
+ <event event="0x0b" option_set="Slave" title="CCI-400" name="Read data stall cycle" description="Read data stall cycle: RVALIDS is HIGH, RREADYS is LOW"/>
+ <event event="0x0c" option_set="Slave" title="CCI-400" name="Write: any" description="Write request handshake: any"/>
+ <event event="0x0d" option_set="Slave" title="CCI-400" name="Write: transaction" description="Write request handshake: device transaction"/>
+ <event event="0x0e" option_set="Slave" title="CCI-400" name="Write: normal" description="Write request handshake: normal, non-shareable, or system-shareable, but not barrier"/>
+ <event event="0x0f" option_set="Slave" title="CCI-400" name="Write: shareable" description="Write request handshake: inner- or outer-shareable, WriteBack or WriteClean"/>
+ <event event="0x10" option_set="Slave" title="CCI-400" name="Write: WriteUnique" description="Write request handshake: WriteUnique"/>
+ <event event="0x11" option_set="Slave" title="CCI-400" name="Write: WriteLineUnique" description="Write request handshake: WriteLineUnique"/>
+ <event event="0x12" option_set="Slave" title="CCI-400" name="Write: Evict" description="Write request handshake: Evict"/>
+ <event event="0x13" option_set="Slave" title="CCI-400" name="Write stall: tracker full" description="Write request stall cycle because the transaction tracker is full. Increase SIx_W_MAX to avoid this stall"/>
+ <option_set name="Master">
+ <option event_delta="0xa0" name="M0" description="Master interface 0"/>
+ <option event_delta="0xc0" name="M1" description="Master interface 1"/>
+ <option event_delta="0xe0" name="M2" description="Master interface 2"/>
+ </option_set>
+ <event event="0x14" option_set="Master" title="CCI-400" name="Retry fetch" description="RETRY of speculative fetch transaction"/>
+ <event event="0x15" option_set="Master" title="CCI-400" name="Read stall: address hazard" description="Read request stall cycle because of an address hazard"/>
+ <event event="0x16" option_set="Master" title="CCI-400" name="Read stall: ID hazard" description="Read request stall cycle because of an ID hazard"/>
+ <event event="0x17" option_set="Master" title="CCI-400" name="Read stall: tracker full" description="Read request stall cycle because the transaction tracker is full. Increase MIx_R_MAX to avoid this stall. See the CoreLink CCI-400 Cache Coherent Interconnect Integration Manual"/>
+ <event event="0x18" option_set="Master" title="CCI-400" name="Read stall: barrier hazard" description="Read request stall cycle because of a barrier hazard"/>
+ <event event="0x19" option_set="Master" title="CCI-400" name="Write stall: barrier hazard" description="Write request stall cycle because of a barrier hazard"/>
+ <event event="0x1a" option_set="Master" title="CCI-400" name="Write stall: tracker full" description="Write request stall cycle because the transaction tracker is full. Increase MIx_W_MAX to avoid this stall. See the CoreLink CCI-400 Cache Coherent Interconnect Integration Manual"/>
+ </category>
+ <counter_set name="CCI_400-r1_cnt" count="4"/>
+ <category name="CCI-400" counter_set="CCI_400-r1_cnt" per_cpu="no" supports_event_based_sampling="yes">
+ <event counter="CCI_400-r1_ccnt" event="0xff" title="CCI-400 Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" description="The number of core clock cycles"/>
+ <option_set name="Slave">
+ <option event_delta="0x00" name="S0" description="Slave interface 0"/>
+ <option event_delta="0x20" name="S1" description="Slave interface 1"/>
+ <option event_delta="0x40" name="S2" description="Slave interface 2"/>
+ <option event_delta="0x60" name="S3" description="Slave interface 3"/>
+ <option event_delta="0x80" name="S4" description="Slave interface 4"/>
+ </option_set>
+ <event event="0x00" option_set="Slave" title="CCI-400" name="Read: any" description="Read request handshake: any"/>
+ <event event="0x01" option_set="Slave" title="CCI-400" name="Read: transaction" description="Read request handshake: device transaction"/>
+ <event event="0x02" option_set="Slave" title="CCI-400" name="Read: normal" description="Read request handshake: normal, non-shareable or system-shareable, but not barrier or cache maintenance operation"/>
+ <event event="0x03" option_set="Slave" title="CCI-400" name="Read: shareable" description="Read request handshake: inner- or outer-shareable, but not barrier, DVM message or cache maintenance operation"/>
+ <event event="0x04" option_set="Slave" title="CCI-400" name="Read: cache" description="Read request handshake: cache maintenance operation"/>
+ <event event="0x05" option_set="Slave" title="CCI-400" name="Read: memory barrier" description="Read request handshake: memory barrier"/>
+ <event event="0x06" option_set="Slave" title="CCI-400" name="Read: sync barrier" description="Read request handshake: synchronization barrier"/>
+ <event event="0x07" option_set="Slave" title="CCI-400" name="Read: DVM message, no sync" description="Read request handshake: DVM message, not synchronization"/>
+ <event event="0x08" option_set="Slave" title="CCI-400" name="Read: DVM message, sync" description="Read request handshake: DVM message, synchronization"/>
+ <event event="0x09" option_set="Slave" title="CCI-400" name="Read: stall" description="Read request stall cycle because the transaction tracker is full. Increase SIx_R_MAX to avoid this stall"/>
+ <event event="0x0a" option_set="Slave" title="CCI-400" name="Read data last handshake" description="Read data last handshake: data returned from the snoop instead of from downstream"/>
+ <event event="0x0b" option_set="Slave" title="CCI-400" name="Read data stall cycle" description="Read data stall cycle: RVALIDS is HIGH, RREADYS is LOW"/>
+ <event event="0x0c" option_set="Slave" title="CCI-400" name="Write: any" description="Write request handshake: any"/>
+ <event event="0x0d" option_set="Slave" title="CCI-400" name="Write: transaction" description="Write request handshake: device transaction"/>
+ <event event="0x0e" option_set="Slave" title="CCI-400" name="Write: normal" description="Write request handshake: normal, non-shareable, or system-shareable, but not barrier"/>
+ <event event="0x0f" option_set="Slave" title="CCI-400" name="Write: shareable" description="Write request handshake: inner- or outer-shareable, WriteBack or WriteClean"/>
+ <event event="0x10" option_set="Slave" title="CCI-400" name="Write: WriteUnique" description="Write request handshake: WriteUnique"/>
+ <event event="0x11" option_set="Slave" title="CCI-400" name="Write: WriteLineUnique" description="Write request handshake: WriteLineUnique"/>
+ <event event="0x12" option_set="Slave" title="CCI-400" name="Write: Evict" description="Write request handshake: Evict"/>
+ <event event="0x13" option_set="Slave" title="CCI-400" name="Write stall: tracker full" description="Write request stall cycle because the transaction tracker is full. Increase SIx_W_MAX to avoid this stall"/>
+ <event event="0x14" option_set="Slave" title="CCI-400" name="Read stall: slave hazard" description="Read request stall cycle because of a slave interface ID hazard"/>
+ <option_set name="Master">
+ <option event_delta="0xa0" name="M0" description="Master interface 0"/>
+ <option event_delta="0xc0" name="M1" description="Master interface 1"/>
+ <option event_delta="0xe0" name="M2" description="Master interface 2"/>
+ </option_set>
+ <event event="0x00" option_set="Master" title="CCI-400" name="Retry fetch" description="RETRY of speculative fetch transaction"/>
+ <event event="0x01" option_set="Master" title="CCI-400" name="Read stall: address hazard" description="Stall cycle because of an address hazard. A read or write invalidation is stalled because of an outstanding transaction to an overlapping address"/>
+ <event event="0x02" option_set="Master" title="CCI-400" name="Read stall: ID hazard" description="Read request stall cycle because of a master interface ID hazard"/>
+ <event event="0x03" option_set="Master" title="CCI-400" name="Read stall: tracker full" description="A read request with a QoS value in the high priority group is stalled for a cycle because the read transaction queue is full. Increase MIx_R_MAX to avoid this stall"/>
+ <event event="0x04" option_set="Master" title="CCI-400" name="Read stall: barrier hazard" description="Read request stall cycle because of a barrier hazard"/>
+ <event event="0x05" option_set="Master" title="CCI-400" name="Write stall: barrier hazard" description="Write request stall cycle because of a barrier hazard"/>
+ <event event="0x06" option_set="Master" title="CCI-400" name="Write stall: tracker full" description="A write request is stalled for a cycle because the write transaction tracker is full. Increase MIx_W_MAX to avoid this stall"/>
+ <event event="0x07" option_set="Master" title="CCI-400" name="Read Stall: Low Priority" description="A read request with a QoS value in the low priority group is stalled for a cycle because there are no slots available in the read queue for the low priority group"/>
+ <event event="0x08" option_set="Master" title="CCI-400" name="Read Stall: Medium Priority" description="A read request with a QoS value in the medium priority group is stalled for a cycle because there are no slots available in the read queue for the medium priority group"/>
+ <event event="0x09" option_set="Master" title="CCI-400" name="Read Stall: VN0" description="A read request is stalled for a cycle while it was waiting for a QVN token on VN0"/>
+ <event event="0x0a" option_set="Master" title="CCI-400" name="Read Stall: VN1" description="A read request is stalled for a cycle while it was waiting for a QVN token on VN1"/>
+ <event event="0x0b" option_set="Master" title="CCI-400" name="Read Stall: VN2" description="A read request is stalled for a cycle while it was waiting for a QVN token on VN2"/>
+ <event event="0x0c" option_set="Master" title="CCI-400" name="Read Stall: VN3" description="A read request is stalled for a cycle while it was waiting for a QVN token on VN3"/>
+ <event event="0x0d" option_set="Master" title="CCI-400" name="Write Stall: VN0" description="A write request is stalled for a cycle while it was waiting for a QVN token on VN0"/>
+ <event event="0x0e" option_set="Master" title="CCI-400" name="Write Stall: VN1" description="A write request is stalled for a cycle while it was waiting for a QVN token on VN1"/>
+ <event event="0x0f" option_set="Master" title="CCI-400" name="Write Stall: VN2" description="A write request is stalled for a cycle while it was waiting for a QVN token on VN2"/>
+ <event event="0x10" option_set="Master" title="CCI-400" name="Write Stall: VN" description="A write request is stalled for a cycle while it was waiting for a QVN token on VN"/>
+ <event event="0x11" option_set="Master" title="CCI-400" name="WriteUnique or WriteLineUnique Stall" description="A WriteUnique or WriteLineUnique request is stalled for a cycle because of an address hazard"/>
+ </category>
diff --git a/tools/gator/daemon/events-CCN-504.xml b/tools/gator/daemon/events-CCN-504.xml
new file mode 100644
index 000000000000..6ef3e6483717
--- /dev/null
+++ b/tools/gator/daemon/events-CCN-504.xml
@@ -0,0 +1,113 @@
+ <counter_set name="CCN-504_cnt" count="4"/>
+ <category name="CCN-504" counter_set="CCN-504_cnt">
+ <event counter="CCN-504_ccnt" title="CCN-504 Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" description="The number of core clock cycles"/>
+ <option_set name="XP_Region">
+ <option event_delta="0x400000" name="XP 0" description="Crosspoint 0"/>
+ <option event_delta="0x410000" name="XP 1" description="Crosspoint 1"/>
+ <option event_delta="0x420000" name="XP 2" description="Crosspoint 2"/>
+ <option event_delta="0x430000" name="XP 3" description="Crosspoint 3"/>
+ <option event_delta="0x440000" name="XP 4" description="Crosspoint 4"/>
+ <option event_delta="0x450000" name="XP 5" description="Crosspoint 5"/>
+ <option event_delta="0x460000" name="XP 6" description="Crosspoint 6"/>
+ <option event_delta="0x470000" name="XP 7" description="Crosspoint 7"/>
+ <option event_delta="0x480000" name="XP 8" description="Crosspoint 8"/>
+ <option event_delta="0x490000" name="XP 9" description="Crosspoint 9"/>
+ <option event_delta="0x4A0000" name="XP 10" description="Crosspoint 10"/>
+ </option_set>
+ <event event="0x0801" option_set="XP_Region" title="CCN-504" name="Bus 0: REQ: H-bit" description="Bus 0: REQ: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0802" option_set="XP_Region" title="CCN-504" name="Bus 0: REQ: S-bit" description="Bus 0: REQ: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0803" option_set="XP_Region" title="CCN-504" name="Bus 0: REQ: P-Cnt" description="Bus 0: REQ: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0804" option_set="XP_Region" title="CCN-504" name="Bus 0: REQ: TknV" description="Bus 0: REQ: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0809" option_set="XP_Region" title="CCN-504" name="Bus 1: REQ: H-bit" description="Bus 1: REQ: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x080A" option_set="XP_Region" title="CCN-504" name="Bus 1: REQ: S-bit" description="Bus 1: REQ: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x080B" option_set="XP_Region" title="CCN-504" name="Bus 1: REQ: P-Cnt" description="Bus 1: REQ: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x080C" option_set="XP_Region" title="CCN-504" name="Bus 1: REQ: TknV" description="Bus 1: REQ: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0811" option_set="XP_Region" title="CCN-504" name="Bus 0: RSP: H-bit" description="Bus 0: RSP: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0812" option_set="XP_Region" title="CCN-504" name="Bus 0: RSP: S-bit" description="Bus 0: RSP: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0813" option_set="XP_Region" title="CCN-504" name="Bus 0: RSP: P-Cnt" description="Bus 0: RSP: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0814" option_set="XP_Region" title="CCN-504" name="Bus 0: RSP: TknV" description="Bus 0: RSP: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0819" option_set="XP_Region" title="CCN-504" name="Bus 1: RSP: H-bit" description="Bus 1: RSP: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x081A" option_set="XP_Region" title="CCN-504" name="Bus 1: RSP: S-bit" description="Bus 1: RSP: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x081B" option_set="XP_Region" title="CCN-504" name="Bus 1: RSP: P-Cnt" description="Bus 1: RSP: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x081C" option_set="XP_Region" title="CCN-504" name="Bus 1: RSP: TknV" description="Bus 1: RSP: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0821" option_set="XP_Region" title="CCN-504" name="Bus 0: SNP: H-bit" description="Bus 0: SNP: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0822" option_set="XP_Region" title="CCN-504" name="Bus 0: SNP: S-bit" description="Bus 0: SNP: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0823" option_set="XP_Region" title="CCN-504" name="Bus 0: SNP: P-Cnt" description="Bus 0: SNP: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0824" option_set="XP_Region" title="CCN-504" name="Bus 0: SNP: TknV" description="Bus 0: SNP: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0829" option_set="XP_Region" title="CCN-504" name="Bus 1: SNP: H-bit" description="Bus 1: SNP: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x082A" option_set="XP_Region" title="CCN-504" name="Bus 1: SNP: S-bit" description="Bus 1: SNP: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x082B" option_set="XP_Region" title="CCN-504" name="Bus 1: SNP: P-Cnt" description="Bus 1: SNP: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x082C" option_set="XP_Region" title="CCN-504" name="Bus 1: SNP: TknV" description="Bus 1: SNP: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0831" option_set="XP_Region" title="CCN-504" name="Bus 0: DAT: H-bit" description="Bus 0: DAT: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0832" option_set="XP_Region" title="CCN-504" name="Bus 0: DAT: S-bit" description="Bus 0: DAT: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0833" option_set="XP_Region" title="CCN-504" name="Bus 0: DAT: P-Cnt" description="Bus 0: DAT: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0834" option_set="XP_Region" title="CCN-504" name="Bus 0: DAT: TknV" description="Bus 0: DAT: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0839" option_set="XP_Region" title="CCN-504" name="Bus 1: DAT: H-bit" description="Bus 1: DAT: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x083A" option_set="XP_Region" title="CCN-504" name="Bus 1: DAT: S-bit" description="Bus 1: DAT: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x083B" option_set="XP_Region" title="CCN-504" name="Bus 1: DAT: P-Cnt" description="Bus 1: DAT: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x083C" option_set="XP_Region" title="CCN-504" name="Bus 1: DAT: TknV" description="Bus 1: DAT: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0871" option_set="XP_Region" title="CCN-504" name="Bus 0: DATB: H-bit" description="Bus 0: DATB: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x0872" option_set="XP_Region" title="CCN-504" name="Bus 0: DATB: S-bit" description="Bus 0: DATB: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x0873" option_set="XP_Region" title="CCN-504" name="Bus 0: DATB: P-Cnt" description="Bus 0: DATB: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x0874" option_set="XP_Region" title="CCN-504" name="Bus 0: DATB: TknV" description="Bus 0: DATB: No TknV, signaled when this XP transmits a valid packet."/>
+ <event event="0x0879" option_set="XP_Region" title="CCN-504" name="Bus 1: DATB: H-bit" description="Bus 1: DATB: Set H-bit, signaled when this XP sets the H-bit."/>
+ <event event="0x087A" option_set="XP_Region" title="CCN-504" name="Bus 1: DATB: S-bit" description="Bus 1: DATB: Set S-bit, signaled when this XP sets the S-bit."/>
+ <event event="0x087B" option_set="XP_Region" title="CCN-504" name="Bus 1: DATB: P-Cnt" description="Bus 1: DATB: Set P-Cnt, signaled when this XP sets the P-Cnt. This is not applicable for the SNP VC."/>
+ <event event="0x087C" option_set="XP_Region" title="CCN-504" name="Bus 1: DATB: TknV" description="Bus 1: DATB: No TknV, signaled when this XP transmits a valid packet."/>
+ <option_set name="HN-F_Region">
+ <option event_delta="0x200000" name="HN-F 3" description="Fully-coherent Home Node 3"/>
+ <option event_delta="0x210000" name="HN-F 5" description="Fully-coherent Home Node 5"/>
+ <option event_delta="0x220000" name="HN-F 7" description="Fully-coherent Home Node 7"/>
+ <option event_delta="0x230000" name="HN-F 8" description="Fully-coherent Home Node 8"/>
+ <option event_delta="0x240000" name="HN-F 13" description="Fully-coherent Home Node 13"/>
+ <option event_delta="0x250000" name="HN-F 15" description="Fully-coherent Home Node 15"/>
+ <option event_delta="0x260000" name="HN-F 17" description="Fully-coherent Home Node 17"/>
+ <option event_delta="0x270000" name="HN-F 18" description="Fully-coherent Home Node 18"/>
+ </option_set>
+ <event event="0x0401" option_set="HN-F_Region" title="CCN-504" name="Cache Miss" description="Counts the total cache misses. This is the first time lookup result, and is high priority."/>
+ <event event="0x0402" option_set="HN-F_Region" title="CCN-504" name="L3 SF Cache Access" description="Counts the number of cache accesses. This is the first time access, and is high priority."/>
+ <event event="0x0403" option_set="HN-F_Region" title="CCN-504" name="Cache Fill" description="Counts the total allocations in the HN L3 cache, and all cache line allocations to the L3 cache."/>
+ <event event="0x0404" option_set="HN-F_Region" title="CCN-504" name="POCQ Retry" description="Counts the number of requests that have been retried."/>
+ <event event="0x0405" option_set="HN-F_Region" title="CCN-504" name="POCQ Reqs Recvd" description="Counts the number of requests received by HN."/>
+ <event event="0x0406" option_set="HN-F_Region" title="CCN-504" name="SF Hit" description="Counts the number of snoop filter hits."/>
+ <event event="0x0407" option_set="HN-F_Region" title="CCN-504" name="SF Evictions" description="Counts the number of snoop filter evictions. Cache invalidations are initiated."/>
+ <event event="0x0408" option_set="HN-F_Region" title="CCN-504" name="Snoops Sent" description="Counts the number of snoops sent. Does not differentiate between broadcast or directed snoops."/>
+ <event event="0x0409" option_set="HN-F_Region" title="CCN-504" name="Snoops Broadcast" description="Counts the number of snoop broadcasts sent."/>
+ <event event="0x040A" option_set="HN-F_Region" title="CCN-504" name="L3 Eviction" description="Counts the number of L3 evictions."/>
+ <event event="0x040B" option_set="HN-F_Region" title="CCN-504" name="L3 Fill Invalid Way" description="Counts the number of L3 fills to an invalid way."/>
+ <event event="0x040C" option_set="HN-F_Region" title="CCN-504" name="MC Retries" description="Counts the number of transactions retried by the memory controller."/>
+ <event event="0x040D" option_set="HN-F_Region" title="CCN-504" name="MC Reqs" description="Counts the number of requests to the memory controller."/>
+ <event event="0x040E" option_set="HN-F_Region" title="CCN-504" name="QOS HH Retry" description="Counts the number of times a highest-priority QoS class was retried at the HN-F."/>
+ <option_set name="RN-I_Region">
+ <option event_delta="0x800000" name="RN-I 0" description="I/O-coherent Requesting Node 0"/>
+ <option event_delta="0x820000" name="RN-I 2" description="I/O-coherent Requesting Node 2"/>
+ <option event_delta="0x860000" name="RN-I 6" description="I/O-coherent Requesting Node 6"/>
+ <option event_delta="0x8C0000" name="RN-I 12" description="I/O-coherent Requesting Node 12"/>
+ <option event_delta="0x900000" name="RN-I 16" description="I/O-coherent Requesting Node 16"/>
+ <option event_delta="0x940000" name="RN-I 20" description="I/O-coherent Requesting Node 20"/>
+ </option_set>
+ <event event="0x1601" option_set="RN-I_Region" title="CCN-504" name="S0 RDataBeats" description="S0 RDataBeats."/>
+ <event event="0x1602" option_set="RN-I_Region" title="CCN-504" name="S1 RDataBeats" description="S1 RDataBeats."/>
+ <event event="0x1603" option_set="RN-I_Region" title="CCN-504" name="S2 RDataBeats" description="S2 RDataBeats."/>
+ <event event="0x1604" option_set="RN-I_Region" title="CCN-504" name="RXDAT Flits received" description="RXDAT Flits received."/>
+ <event event="0x1605" option_set="RN-I_Region" title="CCN-504" name="TXDAT Flits sent" description="TXDAT Flits sent."/>
+ <event event="0x1606" option_set="RN-I_Region" title="CCN-504" name="Total TXREQ Flits sent" description="Total TXREQ Flits sent."/>
+ <event event="0x1607" option_set="RN-I_Region" title="CCN-504" name="Retried TXREQ Flits sent" description="Retried TXREQ Flits sent."/>
+ <event event="0x1608" option_set="RN-I_Region" title="CCN-504" name="RRT full" description="RRT full."/>
+ <event event="0x1609" option_set="RN-I_Region" title="CCN-504" name="WRT full" description="WRT full."/>
+ <event event="0x160A" option_set="RN-I_Region" title="CCN-504" name="Replayed TXREQ Flits" description="Replayed TXREQ Flits."/>
+ <option_set name="SBAS_Region">
+ <option event_delta="0x810000" name="SBAS 1" description="ACE master to CHI protocol bridge 1"/>
+ <option event_delta="0x890000" name="SBAS 9" description="ACE master to CHI protocol bridge 9"/>
+ <option event_delta="0x8B0000" name="SBAS 11" description="ACE master to CHI protocol bridge 11"/>
+ <option event_delta="0x930000" name="SBAS 19" description="ACE master to CHI protocol bridge 19"/>
+ </option_set>
+ <event event="0x1001" option_set="SBAS_Region" title="CCN-504" name="S0 RDataBeats" description="S0 RDataBeats."/>
+ <event event="0x1004" option_set="SBAS_Region" title="CCN-504" name="RXDAT Flits received" description="RXDAT Flits received."/>
+ <event event="0x1005" option_set="SBAS_Region" title="CCN-504" name="TXDAT Flits sent" description="TXDAT Flits sent."/>
+ <event event="0x1006" option_set="SBAS_Region" title="CCN-504" name="Total TXREQ Flits sent" description="Total TXREQ Flits sent."/>
+ <event event="0x1007" option_set="SBAS_Region" title="CCN-504" name="Retried TXREQ Flits sent" description="Retried TXREQ Flits sent."/>
+ <event event="0x1008" option_set="SBAS_Region" title="CCN-504" name="RRT full" description="RRT full."/>
+ <event event="0x1009" option_set="SBAS_Region" title="CCN-504" name="WRT full" description="WRT full."/>
+ <event event="0x100A" option_set="SBAS_Region" title="CCN-504" name="Replayed TXREQ Flits" description="Replayed TXREQ Flits."/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A12.xml b/tools/gator/daemon/events-Cortex-A12.xml
new file mode 100644
index 000000000000..9c04354ad137
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A12.xml
@@ -0,0 +1,86 @@
+ <counter_set name="ARMv7_Cortex_A12_cnt" count="6"/>
+ <category name="Cortex-A12" counter_set="ARMv7_Cortex_A12_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A12_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="L1 inst access" description="Instruction cache access"/>
+ <event event="0x15" title="Cache" name="L1 data write" description="Level 1 data cache Write-Back"/>
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <event event="0x19" title="Bus" name="Access" description="Bus - Access"/>
+ <event event="0x1b" title="Instruction" name="Speculative" description="Instruction speculatively executed"/>
+ <event event="0x1c" title="Memory" name="Translation table" description="Write to translation table base architecturally executed"/>
+ <event event="0x1d" title="Bus" name="Cycle" description="Bus - Cycle"/>
+ <event event="0x40" title="Cache" name="L1 data read" description="Level 1 data cache access - Read"/>
+ <event event="0x41" title="Cache" name="L1 data access write" description="Level 1 data cache access - Write"/>
+ <event event="0x50" title="Cache" name="L2 data read" description="Level 2 data cache access - Read"/>
+ <event event="0x51" title="Cache" name="L2 data access write" description="Level 2 data cache access - Write"/>
+ <event event="0x56" title="Cache" name="L2 data victim" description="Level 2 data cache Write-Back - Victim"/>
+ <event event="0x57" title="Cache" name="L2 data clean" description="Level 2 data cache Write-Back - Cleaning and coherency"/>
+ <event event="0x58" title="Cache" name="L2 data invalidate" description="Level 2 data cache invalidate"/>
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <event event="0x62" title="Bus" name="Access shared" description="Bus access - Normal"/>
+ <event event="0x63" title="Bus" name="Access not shared" description="Bus access - Not normal"/>
+ <event event="0x64" title="Bus" name="Access normal" description="Bus access - Normal"/>
+ <event event="0x65" title="Bus" name="Peripheral" description="Bus access - Peripheral"/>
+ <event event="0x66" title="Memory" name="Read" description="Data memory access - Read"/>
+ <event event="0x67" title="Memory" name="Write" description="Data memory access - Write"/>
+ <event event="0x68" title="Memory" name="Unaligned Read" description="Unaligned access - Read"/>
+ <event event="0x69" title="Memory" name="Unaligned Write" description="Unaligned access - Write"/>
+ <event event="0x6a" title="Memory" name="Unaligned" description="Unaligned access"/>
+ <event event="0x6c" title="Intrinsic" name="LDREX" description="Exclusive instruction speculatively executed - LDREX"/>
+ <event event="0x6e" title="Intrinsic" name="STREX fail" description="Exclusive instruction speculatively executed - STREX fail"/>
+ <event event="0x6f" title="Intrinsic" name="STREX" description="Exclusive instruction speculatively executed - STREX"/>
+ <event event="0x70" title="Instruction" name="Load" description="Instruction speculatively executed - Load"/>
+ <event event="0x71" title="Instruction" name="Store" description="Instruction speculatively executed - Store"/>
+ <event event="0x72" title="Instruction" name="Load/Store" description="Instruction speculatively executed - Load or store"/>
+ <event event="0x73" title="Instruction" name="Integer" description="Instruction speculatively executed - Integer data processing"/>
+ <event event="0x74" title="Instruction" name="Advanced SIMD" description="Instruction speculatively executed - Advanced SIMD"/>
+ <event event="0x75" title="Instruction" name="VFP" description="Instruction speculatively executed - VFP"/>
+ <event event="0x76" title="Instruction" name="Software change" description="Instruction speculatively executed - Software change of the PC"/>
+ <event event="0x78" title="Instruction" name="Immediate branch" description="Branch speculatively executed - Immediate branch"/>
+ <event event="0x79" title="Instruction" name="Procedure return" description="Branch speculatively executed - Procedure return"/>
+ <event event="0x7a" title="Instruction" name="Indirect branch" description="Branch speculatively executed - Indirect branch"/>
+ <event event="0x7c" title="Instruction" name="ISB" description="Barrier speculatively executed - ISB"/>
+ <event event="0x7d" title="Instruction" name="DSB" description="Barrier speculatively executed - DSB"/>
+ <event event="0x7e" title="Instruction" name="DMB" description="Barrier speculatively executed - DMB"/>
+ <event event="0x81" title="Exception" name="Undefined" description="Exception taken, other synchronous"/>
+ <event event="0x8a" title="Exception" name="Hypervisor call" description="Exception taken, Hypervisor Call"/>
+ <event event="0xc0" title="Instruction" name="Stalled Linefill" description="Instruction side stalled due to a Linefill"/>
+ <event event="0xc1" title="Instruction" name="Stalled Page Table Walk" description="Instruction Side stalled due to a Page Table Walk"/>
+ <event event="0xc2" title="Cache" name="4 Ways Read" description="Number of set of 4 ways read in the instruction cache - Tag RAM"/>
+ <event event="0xc3" title="Cache" name="Ways Read" description="Number of ways read in the instruction cache - Data RAM"/>
+ <event event="0xc4" title="Cache" name="BATC Read" description="Number of ways read in the instruction BTAC RAM"/>
+ <event event="0xca" title="Memory" name="Snoop" description="Data snooped from other processor. This event counts memory-read operations that read data from another processor within the local Cortex-A12 cluster, rather than accessing the L2 cache or issuing an external read. It increments on each transaction, rather than on each beat of data"/>
+ <event event="0xd3" title="Slots" name="Load-Store Unit" description="Duration during which all slots in the Load-Store Unit are busy"/>
+ <event event="0xd8" title="Slots" name="Load-Store Issue Queue" description="Duration during which all slots in the Load-Store Issue queue are busy"/>
+ <event event="0xd9" title="Slots" name="Data Processing Issue Queue" description="Duration during which all slots in the Data Processing issue queue are busy"/>
+ <event event="0xda" title="Slots" name="Data Engine Issue Queue" description="Duration during which all slots in the Data Engine issue queue are busy"/>
+ <event event="0xdb" title="NEON" name="Flush" description="Number of NEON instruction which fail their condition code and lead to a flush of the DE pipe"/>
+ <event event="0xdc" title="Hypervisor" name="Traps" description="Number of Trap to hypervisor"/>
+ <event event="0xde" title="PTM" name="EXTOUT 0" description="PTM EXTOUT 0"/>
+ <event event="0xdf" title="PTM" name="EXTOUT 1" description="PTM EXTOUT 1"/>
+ <event event="0xe0" title="MMU" name="Table Walk" description="Duration during which the MMU handle a Page table walk"/>
+ <event event="0xe1" title="MMU" name="Stage1 Table Walk" description="Duration during which the MMU handle a Stage1 Page table walk"/>
+ <event event="0xe2" title="MMU" name="Stage2 Table Walk" description="Duration during which the MMU handle a Stage2 Page table walk"/>
+ <event event="0xe3" title="MMU" name="LSU Table Walk" description="Duration during which the MMU handle a Page table walk requested by the Load Store Unit"/>
+ <event event="0xe4" title="MMU" name="Instruction Table Walk" description="Duration during which the MMU handle a Page table walk requested by the Instruction side"/>
+ <event event="0xe5" title="MMU" name="Preload Table Walk" description="Duration during which the MMU handle a Page table walk requested by a Preload instruction or Prefetch request"/>
+ <event event="0xe6" title="MMU" name="cp15 Table Walk" description="Duration during which the MMU handle a Page table walk requested by a cp15 operation (maintenance by MVA and VA-to-PA operation)"/>
+ <event event="0xe7" title="Cache" name="L1 PLD TLB refill" description="Level 1 PLD TLB refill"/>
+ <event event="0xe8" title="Cache" name="L1 CP15 TLB refill" description="Level 1 CP15 TLB refill"/>
+ <event event="0xe9" title="Cache" name="L1 TLB flush" description="Level 1 TLB flush"/>
+ <event event="0xea" title="Cache" name="L2 TLB access" description="Level 2 TLB access"/>
+ <event event="0xeb" title="Cache" name="L2 TLB miss" description="Level 2 TLB miss"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A15.xml b/tools/gator/daemon/events-Cortex-A15.xml
new file mode 100644
index 000000000000..f50e55d66195
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A15.xml
@@ -0,0 +1,68 @@
+ <counter_set name="ARMv7_Cortex_A15_cnt" count="6"/>
+ <category name="Cortex-A15" counter_set="ARMv7_Cortex_A15_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A15_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="L1 inst access" description="Instruction cache access"/>
+ <event event="0x15" title="Cache" name="L1 data write" description="Level 1 data cache Write-Back"/>
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <event event="0x19" title="Bus" name="Access" description="Bus - Access"/>
+ <event event="0x1a" title="Memory" name="Error" description="Local memory error"/>
+ <event event="0x1b" title="Instruction" name="Speculative" description="Instruction speculatively executed"/>
+ <event event="0x1c" title="Memory" name="Translation table" description="Write to translation table base architecturally executed"/>
+ <event event="0x1d" title="Bus" name="Cycle" description="Bus - Cycle"/>
+ <event event="0x40" title="Cache" name="L1 data read" description="Level 1 data cache access - Read"/>
+ <event event="0x41" title="Cache" name="L1 data access write" description="Level 1 data cache access - Write"/>
+ <event event="0x42" title="Cache" name="L1 data refill read" description="Level 1 data cache refill - Read"/>
+ <event event="0x43" title="Cache" name="L1 data refill write" description="Level 1 data cache refill - Write"/>
+ <event event="0x46" title="Cache" name="L1 data victim" description="Level 1 data cache Write-Back - Victim"/>
+ <event event="0x47" title="Cache" name="L1 data clean" description="Level 1 data cache Write-Back - Cleaning and coherency"/>
+ <event event="0x48" title="Cache" name="L1 data invalidate" description="Level 1 data cache invalidate"/>
+ <event event="0x4c" title="TLB" name="L1 data refill read" description="Level 1 data TLB refill - Read"/>
+ <event event="0x4d" title="TLB" name="L1 data refill write" description="Level 1 data TLB refill - Write"/>
+ <event event="0x50" title="Cache" name="L2 data read" description="Level 2 data cache access - Read"/>
+ <event event="0x51" title="Cache" name="L2 data access write" description="Level 2 data cache access - Write"/>
+ <event event="0x52" title="Cache" name="L2 data refill read" description="Level 2 data cache refill - Read"/>
+ <event event="0x53" title="Cache" name="L2 data refill write" description="Level 2 data cache refill - Write"/>
+ <event event="0x56" title="Cache" name="L2 data victim" description="Level 2 data cache Write-Back - Victim"/>
+ <event event="0x57" title="Cache" name="L2 data clean" description="Level 2 data cache Write-Back - Cleaning and coherency"/>
+ <event event="0x58" title="Cache" name="L2 data invalidate" description="Level 2 data cache invalidate"/>
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <event event="0x61" title="Bus" name="Write" description="Bus access - Write"/>
+ <event event="0x64" title="Bus" name="Access normal" description="Bus access - Normal"/>
+ <event event="0x65" title="Bus" name="Peripheral" description="Bus access - Peripheral"/>
+ <event event="0x66" title="Memory" name="Read" description="Data memory access - Read"/>
+ <event event="0x67" title="Memory" name="Write" description="Data memory access - Write"/>
+ <event event="0x68" title="Memory" name="Unaligned Read" description="Unaligned access - Read"/>
+ <event event="0x69" title="Memory" name="Unaligned Write" description="Unaligned access - Write"/>
+ <event event="0x6a" title="Memory" name="Unaligned" description="Unaligned access"/>
+ <event event="0x6c" title="Intrinsic" name="LDREX" description="Exclusive instruction speculatively executed - LDREX"/>
+ <event event="0x6d" title="Intrinsic" name="STREX pass" description="Exclusive instruction speculatively executed - STREX pass"/>
+ <event event="0x6e" title="Intrinsic" name="STREX fail" description="Exclusive instruction speculatively executed - STREX fail"/>
+ <event event="0x70" title="Instruction" name="Load" description="Instruction speculatively executed - Load"/>
+ <event event="0x71" title="Instruction" name="Store" description="Instruction speculatively executed - Store"/>
+ <event event="0x72" title="Instruction" name="Load/Store" description="Instruction speculatively executed - Load or store"/>
+ <event event="0x73" title="Instruction" name="Integer" description="Instruction speculatively executed - Integer data processing"/>
+ <event event="0x74" title="Instruction" name="Advanced SIMD" description="Instruction speculatively executed - Advanced SIMD"/>
+ <event event="0x75" title="Instruction" name="VFP" description="Instruction speculatively executed - VFP"/>
+ <event event="0x76" title="Instruction" name="Software change" description="Instruction speculatively executed - Software change of the PC"/>
+ <event event="0x78" title="Instruction" name="Immediate branch" description="Branch speculatively executed - Immediate branch"/>
+ <event event="0x79" title="Instruction" name="Procedure return" description="Branch speculatively executed - Procedure return"/>
+ <event event="0x7a" title="Instruction" name="Indirect branch" description="Branch speculatively executed - Indirect branch"/>
+ <event event="0x7c" title="Instruction" name="ISB" description="Barrier speculatively executed - ISB"/>
+ <event event="0x7d" title="Instruction" name="DSB" description="Barrier speculatively executed - DSB"/>
+ <event event="0x7e" title="Instruction" name="DMB" description="Barrier speculatively executed - DMB"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A5.xml b/tools/gator/daemon/events-Cortex-A5.xml
new file mode 100644
index 000000000000..d67581d77c08
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A5.xml
@@ -0,0 +1,36 @@
+ <counter_set name="ARMv7_Cortex_A5_cnt" count="2"/>
+ <category name="Cortex-A5" counter_set="ARMv7_Cortex_A5_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A5_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Branch" name="PC change" description="Software change of the Program Counter, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Procedure" name="Return" description="Procedure return, other than exception return, architecturally executed"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="Instruction access" description="Instruction cache access"/>
+ <event event="0x15" title="Cache" name="Data eviction" description="Data cache eviction"/>
+ <event event="0x86" title="Interrupts" name="IRQ" description="IRQ exception taken"/>
+ <event event="0x87" title="Interrupts" name="FIQ" description="FIQ exception taken"/>
+ <event event="0xC0" title="Memory" name="External request" description="External memory request"/>
+ <event event="0xC1" title="Memory" name="Non-cacheable ext req" description="Non-cacheable external memory request"/>
+ <event event="0xC2" title="Cache" name="Linefill" description="Linefill because of prefetch"/>
+ <event event="0xC3" title="Cache" name="Linefill dropped" description="Prefetch linefill dropped"/>
+ <event event="0xC4" title="Cache" name="Allocate mode enter" description="Entering read allocate mode"/>
+ <event event="0xC5" title="Cache" name="Allocate mode" description="Read allocate mode"/>
+ <event event="0xC7" title="ETM" name="ETM Ext Out[0]" description="ETM - ETM Ext Out[0]"/>
+ <event event="0xC8" title="ETM" name="ETM Ext Out[1]" description="ETM - ETM Ext Out[1]"/>
+ <event event="0xC9" title="Instruction" name="Pipeline stall" description="Data Write operation that stalls the pipeline because the store buffer is full"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A53.xml b/tools/gator/daemon/events-Cortex-A53.xml
new file mode 100644
index 000000000000..5ba17907d5ab
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A53.xml
@@ -0,0 +1,87 @@
+ <counter_set name="ARM_Cortex-A53_cnt" count="6"/>
+ <category name="Cortex-A53" counter_set="ARM_Cortex-A53_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARM_Cortex-A53_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="L1 inst access" description="Level 1 instruction cache access"/>
+ <event event="0x15" title="Cache" name="L1 data write" description="Level 1 data cache Write-Back"/>
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <event event="0x19" title="Bus" name="Access" description="Bus access"/>
+ <event event="0x1A" title="Memory" name="Error" description="Local memory error"/>
+ <event event="0x1B" title="Instruction" name="Speculative" description="Operation speculatively executed"/>
+ <event event="0x1C" title="Memory" name="Translation table" description="Instruction architecturally executed (condition check pass) - Write to translation table base"/>
+ <event event="0x1D" title="Bus" name="Cycle" description="Bus cycle"/>
+ <event event="0x1E" title="Counter chain" name="Odd Performance" description="Odd performance counter chain mode"/>
+ <event event="0x40" title="Cache" name="L1 data read" description="Level 1 data cache access - Read"/>
+ <event event="0x41" title="Cache" name="L1 data access write" description="Level 1 data cache access - Write"/>
+ <event event="0x42" title="Cache" name="L1 data refill read" description="Level 1 data cache refill - Read"/>
+ <event event="0x43" title="Cache" name="L1 data refill write" description="Level 1 data cache refill - Write"/>
+ <event event="0x46" title="Cache" name="L1 data victim" description="Level 1 data cache Write-back - Victim"/>
+ <event event="0x47" title="Cache" name="L1 data clean" description="Level 1 data cache Write-back - Cleaning and coherency"/>
+ <event event="0x48" title="Cache" name="L1 data invalidate" description="Level 1 data cache invalidate"/>
+ <event event="0x4C" title="Cache" name="L1 data refill read" description="Level 1 data TLB refill - Read"/>
+ <event event="0x4D" title="Cache" name="L1 data refill write" description="Level 1 data TLB refill - Write"/>
+ <event event="0x50" title="Cache" name="L2 data read" description="Level 2 data cache access - Read"/>
+ <event event="0x51" title="Cache" name="L2 data access write" description="Level 2 data cache access - Write"/>
+ <event event="0x52" title="Cache" name="L2 data refill read" description="Level 2 data cache refill - Read"/>
+ <event event="0x53" title="Cache" name="L2 data refill write" description="Level 2 data cache refill - Write"/>
+ <event event="0x56" title="Cache" name="L2 data victim" description="Level 2 data cache Write-back - Victim"/>
+ <event event="0x57" title="Cache" name="L2 data clean" description="Level 2 data cache Write-back - Cleaning and coherency"/>
+ <event event="0x58" title="Cache" name="L2 data invalidate" description="Level 2 data cache invalidate"/>
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <event event="0x61" title="Bus" name="Write" description="Bus access - Write"/>
+ <event event="0x62" title="Bus" name="Access shared" description="Bus access - Normal"/>
+ <event event="0x63" title="Bus" name="Access not shared" description="Bus access - Not normal"/>
+ <event event="0x64" title="Bus" name="Access normal" description="Bus access - Normal"/>
+ <event event="0x65" title="Bus" name="Peripheral" description="Bus access - Peripheral"/>
+ <event event="0x66" title="Memory" name="Read" description="Data memory access - Read"/>
+ <event event="0x67" title="Memory" name="Write" description="Data memory access - Write"/>
+ <event event="0x68" title="Memory" name="Unaligned Read" description="Unaligned access - Read"/>
+ <event event="0x69" title="Memory" name="Unaligned Write" description="Unaligned access - Write"/>
+ <event event="0x6A" title="Memory" name="Unaligned" description="Unaligned access"/>
+ <event event="0x6C" title="Intrinsic" name="LDREX" description="Exclusive operation speculatively executed - LDREX"/>
+ <event event="0x6D" title="Intrinsic" name="STREX pass" description="Exclusive instruction speculatively executed - STREX pass"/>
+ <event event="0x6E" title="Intrinsic" name="STREX fail" description="Exclusive operation speculatively executed - STREX fail"/>
+ <event event="0x70" title="Instruction" name="Load" description="Operation speculatively executed - Load"/>
+ <event event="0x71" title="Instruction" name="Store" description="Operation speculatively executed - Store"/>
+ <event event="0x72" title="Instruction" name="Load/Store" description="Operation speculatively executed - Load or store"/>
+ <event event="0x73" title="Instruction" name="Integer" description="Operation speculatively executed - Integer data processing"/>
+ <event event="0x74" title="Instruction" name="Advanced SIMD" description="Operation speculatively executed - Advanced SIMD"/>
+ <event event="0x75" title="Instruction" name="VFP" description="Operation speculatively executed - VFP"/>
+ <event event="0x76" title="Instruction" name="Software change" description="Operation speculatively executed - Software change of the PC"/>
+ <event event="0x77" title="Instruction" name="Crypto" description="Operation speculatively executed, crypto data processing"/>
+ <event event="0x78" title="Instruction" name="Immediate branch" description="Branch speculatively executed - Immediate branch"/>
+ <event event="0x79" title="Instruction" name="Procedure return" description="Branch speculatively executed - Procedure return"/>
+ <event event="0x7A" title="Instruction" name="Indirect branch" description="Branch speculatively executed - Indirect branch"/>
+ <event event="0x7C" title="Instruction" name="ISB" description="Barrier speculatively executed - ISB"/>
+ <event event="0x7D" title="Instruction" name="DSB" description="Barrier speculatively executed - DSB"/>
+ <event event="0x7E" title="Instruction" name="DMB" description="Barrier speculatively executed - DMB"/>
+ <event event="0x81" title="Exception" name="Undefined" description="Exception taken, other synchronous"/>
+ <event event="0x82" title="Exception" name="Supervisor" description="Exception taken, Supervisor Call"/>
+ <event event="0x83" title="Exception" name="Instruction abort" description="Exception taken, Instruction Abort"/>
+ <event event="0x84" title="Exception" name="Data abort" description="Exception taken, Data Abort or SError"/>
+ <event event="0x86" title="Interrupts" name="IRQ" description="Exception taken, IRQ"/>
+ <event event="0x87" title="Interrupts" name="FIQ" description="Exception taken, FIQ"/>
+ <event event="0x88" title="Exception" name="Secure monitor call" description="Exception taken, Secure Monitor Call"/>
+ <event event="0x8A" title="Exception" name="Hypervisor call" description="Exception taken, Hypervisor Call"/>
+ <event event="0x8B" title="Exception" name="Instruction abort non-local" description="Exception taken, Instruction Abort not taken locally"/>
+ <event event="0x8C" title="Exception" name="Data abort non-local" description="Exception taken, Data Abort or SError not taken locally"/>
+ <event event="0x8D" title="Exception" name="Other non-local" description="Exception taken - Other traps not taken locally"/>
+ <event event="0x8E" title="Exception" name="IRQ non-local" description="Exception taken, IRQ not taken locally"/>
+ <event event="0x8F" title="Exception" name="FIQ non-local" description="Exception taken, FIQ not taken locally"/>
+ <event event="0x90" title="Release Consistency" name="Load" description="Release consistency instruction speculatively executed - Load Acquire"/>
+ <event event="0x91" title="Release Consistency" name="Store" description="Release consistency instruction speculatively executed - Store Release"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A57.xml b/tools/gator/daemon/events-Cortex-A57.xml
new file mode 100644
index 000000000000..fbe96c2d4eb2
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A57.xml
@@ -0,0 +1,87 @@
+ <counter_set name="ARM_Cortex-A57_cnt" count="6"/>
+ <category name="Cortex-A57" counter_set="ARM_Cortex-A57_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARM_Cortex-A57_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="L1 inst access" description="Level 1 instruction cache access"/>
+ <event event="0x15" title="Cache" name="L1 data write" description="Level 1 data cache Write-Back"/>
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <event event="0x19" title="Bus" name="Access" description="Bus access"/>
+ <event event="0x1A" title="Memory" name="Error" description="Local memory error"/>
+ <event event="0x1B" title="Instruction" name="Speculative" description="Operation speculatively executed"/>
+ <event event="0x1C" title="Memory" name="Translation table" description="Instruction architecturally executed (condition check pass) - Write to translation table base"/>
+ <event event="0x1D" title="Bus" name="Cycle" description="Bus cycle"/>
+ <event event="0x1E" title="Counter chain" name="Odd Performance" description="Odd performance counter chain mode"/>
+ <event event="0x40" title="Cache" name="L1 data read" description="Level 1 data cache access - Read"/>
+ <event event="0x41" title="Cache" name="L1 data access write" description="Level 1 data cache access - Write"/>
+ <event event="0x42" title="Cache" name="L1 data refill read" description="Level 1 data cache refill - Read"/>
+ <event event="0x43" title="Cache" name="L1 data refill write" description="Level 1 data cache refill - Write"/>
+ <event event="0x46" title="Cache" name="L1 data victim" description="Level 1 data cache Write-back - Victim"/>
+ <event event="0x47" title="Cache" name="L1 data clean" description="Level 1 data cache Write-back - Cleaning and coherency"/>
+ <event event="0x48" title="Cache" name="L1 data invalidate" description="Level 1 data cache invalidate"/>
+ <event event="0x4C" title="Cache" name="L1 data refill read" description="Level 1 data TLB refill - Read"/>
+ <event event="0x4D" title="Cache" name="L1 data refill write" description="Level 1 data TLB refill - Write"/>
+ <event event="0x50" title="Cache" name="L2 data read" description="Level 2 data cache access - Read"/>
+ <event event="0x51" title="Cache" name="L2 data access write" description="Level 2 data cache access - Write"/>
+ <event event="0x52" title="Cache" name="L2 data refill read" description="Level 2 data cache refill - Read"/>
+ <event event="0x53" title="Cache" name="L2 data refill write" description="Level 2 data cache refill - Write"/>
+ <event event="0x56" title="Cache" name="L2 data victim" description="Level 2 data cache Write-back - Victim"/>
+ <event event="0x57" title="Cache" name="L2 data clean" description="Level 2 data cache Write-back - Cleaning and coherency"/>
+ <event event="0x58" title="Cache" name="L2 data invalidate" description="Level 2 data cache invalidate"/>
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <event event="0x61" title="Bus" name="Write" description="Bus access - Write"/>
+ <event event="0x62" title="Bus" name="Access shared" description="Bus access - Normal"/>
+ <event event="0x63" title="Bus" name="Access not shared" description="Bus access - Not normal"/>
+ <event event="0x64" title="Bus" name="Access normal" description="Bus access - Normal"/>
+ <event event="0x65" title="Bus" name="Peripheral" description="Bus access - Peripheral"/>
+ <event event="0x66" title="Memory" name="Read" description="Data memory access - Read"/>
+ <event event="0x67" title="Memory" name="Write" description="Data memory access - Write"/>
+ <event event="0x68" title="Memory" name="Unaligned Read" description="Unaligned access - Read"/>
+ <event event="0x69" title="Memory" name="Unaligned Write" description="Unaligned access - Write"/>
+ <event event="0x6A" title="Memory" name="Unaligned" description="Unaligned access"/>
+ <event event="0x6C" title="Intrinsic" name="LDREX" description="Exclusive operation speculatively executed - LDREX"/>
+ <event event="0x6D" title="Intrinsic" name="STREX pass" description="Exclusive instruction speculatively executed - STREX pass"/>
+ <event event="0x6E" title="Intrinsic" name="STREX fail" description="Exclusive operation speculatively executed - STREX fail"/>
+ <event event="0x70" title="Instruction" name="Load" description="Operation speculatively executed - Load"/>
+ <event event="0x71" title="Instruction" name="Store" description="Operation speculatively executed - Store"/>
+ <event event="0x72" title="Instruction" name="Load/Store" description="Operation speculatively executed - Load or store"/>
+ <event event="0x73" title="Instruction" name="Integer" description="Operation speculatively executed - Integer data processing"/>
+ <event event="0x74" title="Instruction" name="Advanced SIMD" description="Operation speculatively executed - Advanced SIMD"/>
+ <event event="0x75" title="Instruction" name="VFP" description="Operation speculatively executed - VFP"/>
+ <event event="0x76" title="Instruction" name="Software change" description="Operation speculatively executed - Software change of the PC"/>
+ <event event="0x77" title="Instruction" name="Crypto" description="Operation speculatively executed, crypto data processing"/>
+ <event event="0x78" title="Instruction" name="Immediate branch" description="Branch speculatively executed - Immediate branch"/>
+ <event event="0x79" title="Instruction" name="Procedure return" description="Branch speculatively executed - Procedure return"/>
+ <event event="0x7A" title="Instruction" name="Indirect branch" description="Branch speculatively executed - Indirect branch"/>
+ <event event="0x7C" title="Instruction" name="ISB" description="Barrier speculatively executed - ISB"/>
+ <event event="0x7D" title="Instruction" name="DSB" description="Barrier speculatively executed - DSB"/>
+ <event event="0x7E" title="Instruction" name="DMB" description="Barrier speculatively executed - DMB"/>
+ <event event="0x81" title="Exception" name="Undefined" description="Exception taken, other synchronous"/>
+ <event event="0x82" title="Exception" name="Supervisor" description="Exception taken, Supervisor Call"/>
+ <event event="0x83" title="Exception" name="Instruction abort" description="Exception taken, Instruction Abort"/>
+ <event event="0x84" title="Exception" name="Data abort" description="Exception taken, Data Abort or SError"/>
+ <event event="0x86" title="Interrupts" name="IRQ" description="Exception taken, IRQ"/>
+ <event event="0x87" title="Interrupts" name="FIQ" description="Exception taken, FIQ"/>
+ <event event="0x88" title="Exception" name="Secure monitor call" description="Exception taken, Secure Monitor Call"/>
+ <event event="0x8A" title="Exception" name="Hypervisor call" description="Exception taken, Hypervisor Call"/>
+ <event event="0x8B" title="Exception" name="Instruction abort non-local" description="Exception taken, Instruction Abort not taken locally"/>
+ <event event="0x8C" title="Exception" name="Data abort non-local" description="Exception taken, Data Abort or SError not taken locally"/>
+ <event event="0x8D" title="Exception" name="Other non-local" description="Exception taken - Other traps not taken locally"/>
+ <event event="0x8E" title="Exception" name="IRQ non-local" description="Exception taken, IRQ not taken locally"/>
+ <event event="0x8F" title="Exception" name="FIQ non-local" description="Exception taken, FIQ not taken locally"/>
+ <event event="0x90" title="Release Consistency" name="Load" description="Release consistency instruction speculatively executed - Load Acquire"/>
+ <event event="0x91" title="Release Consistency" name="Store" description="Release consistency instruction speculatively executed - Store Release"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A7.xml b/tools/gator/daemon/events-Cortex-A7.xml
new file mode 100644
index 000000000000..6e078b3cffa3
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A7.xml
@@ -0,0 +1,43 @@
+ <counter_set name="ARMv7_Cortex_A7_cnt" count="4"/>
+ <category name="Cortex-A7" counter_set="ARMv7_Cortex_A7_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A7_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Memory" name="Data Read" description="Data read architecturally executed"/>
+ <event event="0x07" title="Memory" name="Data Write" description="Data write architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Branch" name="PC change" description="Software change of the Program Counter, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x13" title="Memory" name="Memory access" description="Data memory access"/>
+ <event event="0x14" title="Cache" name="L1 inst access" description="Instruction cache access"/>
+ <event event="0x15" title="Cache" name="L1 data eviction" description="Level 1 data cache eviction"/>
+ <event event="0x16" title="Cache" name="L2 data access" description="Level 2 data cache access"/>
+ <event event="0x17" title="Cache" name="L2 data refill" description="Level 2 data cache refill"/>
+ <event event="0x18" title="Cache" name="L2 data write" description="Level 2 data cache Write-Back"/>
+ <event event="0x19" title="Bus" name="Access" description="Bus - Access"/>
+ <event event="0x1d" title="Bus" name="Cycle" description="Bus - Cycle"/>
+ <event event="0x60" title="Bus" name="Read" description="Bus access - Read"/>
+ <event event="0x61" title="Bus" name="Write" description="Bus access - Write"/>
+ <event event="0x86" title="Exception" name="IRQ" description="IRQ exception taken"/>
+ <event event="0x87" title="Exception" name="FIQ" description="FIQ exception taken"/>
+ <event event="0xC0" title="Memory" name="External request" description="External memory request"/>
+ <event event="0xC1" title="Memory" name="Non-cacheable ext req" description="Non-cacheable external memory request"/>
+ <event event="0xC2" title="Cache" name="Linefill" description="Linefill because of prefetch"/>
+ <event event="0xC3" title="Cache" name="Linefill dropped" description="Prefetch linefill dropped"/>
+ <event event="0xC4" title="Cache" name="Allocate mode enter" description="Entering read allocate mode"/>
+ <event event="0xC5" title="Cache" name="Allocate mode" description="Read allocate mode"/>
+ <event event="0xC7" title="ETM" name="ETM Ext Out[0]" description="ETM - ETM Ext Out[0]"/>
+ <event event="0xC8" title="ETM" name="ETM Ext Out[1]" description="ETM - ETM Ext Out[1]"/>
+ <event event="0xC9" title="Instruction" name="Pipeline stall" description="Data Write operation that stalls the pipeline because the store buffer is full"/>
+ <event event="0xCA" title="Memory" name="Snoop" description="Data snooped from other processor. This event counts memory-read operations that read data from another processor within the local cluster, rather than accessing the L2 cache or issuing an external read."/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A8.xml b/tools/gator/daemon/events-Cortex-A8.xml
new file mode 100644
index 000000000000..a69e25ab2c34
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A8.xml
@@ -0,0 +1,52 @@
+ <counter_set name="ARMv7_Cortex_A8_cnt" count="4"/>
+ <category name="Cortex-A8" counter_set="ARMv7_Cortex_A8_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A8_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Branch" name="PC change" description="Software change of the Program Counter, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Procedure" name="Return" description="Procedure return, other than exception return, architecturally executed"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x40" title="Cache" name="Write buffer full" description="Any write buffer full cycle"/>
+ <event event="0x41" title="Cache" name="L2 store" description="Any store that is merged in the L2 memory system"/>
+ <event event="0x42" title="Cache" name="Bufferable transaction" description="Any bufferable store transaction from load/store to L2 cache, excluding eviction or cast out data"/>
+ <event event="0x43" title="Cache" name="L2 access" description="Any accesses to the L2 cache"/>
+ <event event="0x44" title="Cache" name="L2 miss" description="Any cacheable miss in the L2 cache"/>
+ <event event="0x45" title="AXI" name="Read" description="The number of AXI read data transfers"/>
+ <event event="0x46" title="AXI" name="Write" description="The number of AXI write data transfers"/>
+ <event event="0x47" title="Memory" name="Replay event" description="Any replay event in the memory system"/>
+ <event event="0x48" title="Memory" name="Unaligned access replay" description="Any unaligned memory access that results in a replay"/>
+ <event event="0x49" title="Cache" name="L1 data hash miss" description="Any L1 data memory access that misses in the cache as a result of the hashing algorithm"/>
+ <event event="0x4a" title="Cache" name="L1 inst hash miss" description="Any L1 instruction memory access that misses in the cache as a result of the hashing algorithm"/>
+ <event event="0x4b" title="Cache" name="L1 page coloring" description="Any L1 data memory access in which a page coloring alias occurs"/>
+ <event event="0x4c" title="NEON" name="L1 cache hit" description="Any NEON access that hits in the L1 data cache"/>
+ <event event="0x4d" title="NEON" name="L1 cache access" description="Any NEON cacheable data accesses for L1 data cache"/>
+ <event event="0x4e" title="NEON" name="L2 cache access" description="Any L2 cache accesses as a result of a NEON memory access"/>
+ <event event="0x4f" title="NEON" name="L2 cache hit" description="Any NEON hit in the L2 cache"/>
+ <event event="0x50" title="Cache" name="L1 inst access" description="Any L1 instruction cache access, excluding CP15 cache accesses"/>
+ <event event="0x51" title="Branch" name="Return stack misprediction" description="Any return stack misprediction because of incorrect target address for a taken return stack pop"/>
+ <event event="0x52" title="Branch" name="Direction misprediction" description="Branch direction misprediction"/>
+ <event event="0x53" title="Branch" name="Taken prediction" description="Any predictable branch that is predicted to be taken"/>
+ <event event="0x54" title="Branch" name="Executed and taken prediction" description="Any predictable branch that is executed and taken"/>
+ <event event="0x55" title="Core" name="Operations issued" description="Number of operations issued, where an operation is either: an instruction or one operation in a sequence of operations that make up a multi-cycle instruction"/>
+ <event event="0x56" title="Core" name="No issue cycles" description="Increment for every cycle that no instructions are available for issue"/>
+ <event event="0x57" title="Core" name="Issue cycles" description="For every cycle, this event counts the number of instructions issued in that cycle. Multi-cycle instructions are only counted once"/>
+ <event event="0x58" title="NEON" name="MRC data wait" description="Number of cycles the processor stalls waiting on MRC data from NEON"/>
+ <event event="0x59" title="NEON" name="Full queue" description="Number of cycles that the processor stalls as a result of a full NEON instruction queue or NEON load queue"/>
+ <event event="0x5a" title="NEON" name="Idle" description="Number of cycles that NEON and integer processors are both not idle"/>
+ <event event="0x70" title="External" name="PMUEXTIN[0]" description="Counts any event from external input source PMUEXTIN[0]"/>
+ <event event="0x71" title="External" name="PMUEXTIN[1]" description="Counts any event from external input source PMUEXTIN[1]"/>
+ <event event="0x72" title="External" name="PMUEXTIN[0,1]" description="Counts any event from both external input sources PMUEXTIN[0] and PMUEXTIN[1]"/>
+ </category>
diff --git a/tools/gator/daemon/events-Cortex-A9.xml b/tools/gator/daemon/events-Cortex-A9.xml
new file mode 100644
index 000000000000..3e7f8289062e
--- /dev/null
+++ b/tools/gator/daemon/events-Cortex-A9.xml
@@ -0,0 +1,65 @@
+ <counter_set name="ARMv7_Cortex_A9_cnt" count="6"/>
+ <category name="Cortex-A9" counter_set="ARMv7_Cortex_A9_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A9_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Branch" name="PC change" description="Software change of the Program Counter, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x40" title="Java" name="Bytecode execute" description="Counts the number of Java bytecodes being decoded, including speculative ones"/>
+ <event event="0x41" title="Java" name="SW bytecode execute" description="Counts the number of software java bytecodes being decoded, including speculative ones"/>
+ <event event="0x42" title="Jazelle" name="Backward branch execute" description="Counts the number of Jazelle taken branches being executed"/>
+ <event event="0x50" title="Cache" name="Coherency miss" description="Counts the number of coherent linefill requests performed by the Cortex-A9 processor which also miss in all the other Cortex-A9 processors, meaning that the request is sent to the external memory"/>
+ <event event="0x51" title="Cache" name="Coherency hit" description="Counts the number of coherent linefill requests performed by the Cortex-A9 processor which hit in another Cortex-A9 processor, meaning that the linefill data is fetched directly from the relevant Cortex-A9 cache"/>
+ <event event="0x60" title="Cache" name="Inst dependent stall" description="Counts the number of cycles where the processor is ready to accept new instructions, but does not receive any because of the instruction side not being able to provide any and the instruction cache is currently performing at least one linefill"/>
+ <event event="0x61" title="Cache" name="Data dependent stall" description="Counts the number of cycles where the core has some instructions that it cannot issue to any pipeline, and the Load Store unit has at least one pending linefill request, and no pending TLB requests"/>
+ <event event="0x62" title="Cache" name="TLB stall" description="Counts the number of cycles where the processor is stalled waiting for the completion of translation table walks from the main TLB"/>
+ <event event="0x63" title="Intrinsic" name="STREX pass" description="Counts the number of STREX instructions architecturally executed and passed"/>
+ <event event="0x64" title="Intrinsic" name="STREX fail" description="Counts the number of STREX instructions architecturally executed and failed"/>
+ <event event="0x65" title="Cache" name="Data eviction" description="Counts the number of eviction requests because of a linefill in the data cache"/>
+ <event event="0x66" title="Pipeline" name="Issue stage no dispatch" description="Counts the number of cycles where the issue stage does not dispatch any instruction because it is empty or cannot dispatch any instructions"/>
+ <event event="0x67" title="Pipeline" name="Issue stage empty" description="Counts the number of cycles where the issue stage is empty"/>
+ <event event="0x68" title="Instruction" name="Executed" description="Counts the number of instructions going through the Register Renaming stage. This number is an approximate number of the total number of instructions speculatively executed, and even more approximate of the total number of instructions architecturally executed"/>
+ <event event="0x69" title="Cache" name="Data linefills" description="Counts the number of linefills performed on the external AXI bus"/>
+ <event event="0x6A" title="Cache" name="Prefetch linefills" description="Counts the number of data linefills caused by prefetcher requests"/>
+ <event event="0x6B" title="Cache" name="Prefetch hits" description="Counts the number of cache hits in a line that belongs to a stream followed by the prefetcher"/>
+ <event event="0x6E" title="Core" name="Functions" description="Counts the number of procedure returns whose condition codes do not fail, excluding all returns from exception"/>
+ <event event="0x70" title="Instruction" name="Main execution unit" description="Counts the number of instructions being executed in the main execution pipeline of the processor, the multiply pipeline and arithmetic logic unit pipeline"/>
+ <event event="0x71" title="Instruction" name="Second execution unit" description="Counts the number of instructions being executed in the processor second execution pipeline (ALU)"/>
+ <event event="0x72" title="Instruction" name="Load/Store" description="Counts the number of instructions being executed in the Load/Store unit"/>
+ <event event="0x73" title="Instruction" name="Floating point" description="Counts the number of Floating-point instructions going through the Register Rename stage"/>
+ <event event="0x74" title="Instruction" name="NEON" description="Counts the number of NEON instructions going through the Register Rename stage"/>
+ <event event="0x80" title="Stalls" name="PLD" description="Counts the number of cycles where the processor is stalled because PLD slots are all full"/>
+ <event event="0x81" title="Stalls" name="Memory write" description="Counts the number of cycles when the processor is stalled and the data side is stalled too because it is full and executing writes to the external memory"/>
+ <event event="0x82" title="Stalls" name="Inst main TLB miss" description="Counts the number of stall cycles because of main TLB misses on requests issued by the instruction side"/>
+ <event event="0x83" title="Stalls" name="Data main TLB miss" description="Counts the number of stall cycles because of main TLB misses on requests issued by the data side"/>
+ <event event="0x84" title="Stalls" name="Inst micro TLB miss" description="Counts the number of stall cycles because of micro TLB misses on the instruction side"/>
+ <event event="0x85" title="Stalls" name="Data micro TLB miss" description="Counts the number of stall cycles because of micro TLB misses on the data side"/>
+ <event event="0x86" title="Stalls" name="DMB" description="Counts the number of stall cycles because of the execution of a DMB memory barrier"/>
+ <event event="0x8A" title="Clock" name="Integer core" description="Counts the number of cycles during which the integer core clock is enabled"/>
+ <event event="0x8B" title="Clock" name="Data engine" description="Counts the number of cycles during which the Data Engine clock is enabled"/>
+ <event event="0x8C" title="Clock" name="NEON" description="Counts the number of cycles when the NEON SIMD clock is enabled"/>
+ <event event="0x8D" title="Memory" name="TLB inst allocations" description="Counts the number of TLB allocations because of Instruction requests"/>
+ <event event="0x8E" title="Memory" name="TLB data allocations" description="Counts the number of TLB allocations because of Data requests"/>
+ <event event="0x90" title="Instruction" name="ISB" description="Counts the number of ISB instructions architecturally executed"/>
+ <event event="0x91" title="Instruction" name="DSB" description="Counts the number of DSB instructions architecturally executed"/>
+ <event event="0x92" title="Instruction" name="DMB" description="Counts the number of DMB instructions speculatively executed"/>
+ <event event="0x93" title="External" name="Interrupts" description="Counts the number of external interrupts executed by the processor"/>
+ <event event="0xA0" title="PLE" name="Cache line rq completed" description="Counts the number of PLE cache line requests completed"/>
+ <event event="0xA1" title="PLE" name="Cache line rq skipped" description="Counts the number of PLE cache line requests skipped"/>
+ <event event="0xA2" title="PLE" name="FIFO flush" description="Counts the number of PLE FIFO flush requests"/>
+ <event event="0xA3" title="PLE" name="Request completed" description="Counts the number of PLE FIFO flush completed"/>
+ <event event="0xA4" title="PLE" name="FIFO overflow" description="Counts the number of PLE FIFO flush overflowed"/>
+ <event event="0xA5" title="PLE" name="Request programmed" description="Counts the number of PLE FIFO flush program requests"/>
+ </category>
diff --git a/tools/gator/daemon/events-Filesystem.xml b/tools/gator/daemon/events-Filesystem.xml
new file mode 100644
index 000000000000..5feeb9014a63
--- /dev/null
+++ b/tools/gator/daemon/events-Filesystem.xml
@@ -0,0 +1,11 @@
+ <category name="Filesystem">
+ <!-- counter attributes must be unique -->
+ <!-- regex item in () is the value shown -->
+ <!-- these counters are not compatible with userspace gator, i.e. gator.ko must be loaded -->
+ <!--
+ <event counter="/sys/devices/system/cpu/cpu1/online" title="online" name="cpu 1" class="absolute" description="If cpu 1 is online"/>
+ <event counter="/proc/self/loginuid" title="loginuid" name="loginuid" class="absolute" description="loginuid"/>
+ <event counter="/proc/self/stat" title="stat" name="rss" class="absolute" regex="-?[0-9]+ \(.*\) . -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ -?[0-9]+ (-?[0-9]+)" units="pages" description="resident set size"/>
+ <event counter="/proc/stat" title="proc-stat" name="processes" class="absolute" regex="processes ([0-9]+)" description="Number of processes and threads created"/>
+ -->
+ </category>
diff --git a/tools/gator/daemon/events-Krait-architected.xml b/tools/gator/daemon/events-Krait-architected.xml
new file mode 100644
index 000000000000..b8d3bcb48de7
--- /dev/null
+++ b/tools/gator/daemon/events-Krait-architected.xml
@@ -0,0 +1,22 @@
+ <counter_set name="Krait_cnt" count="4"/>
+ <category name="Krait" counter_set="Krait_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="Krait_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Program Counter" name="SW change" description="Software change of PC, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Branch" name="Procedure Return" description="Procedure return architecturally executed (not by exceptions)"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ </category>
diff --git a/tools/gator/daemon/events-L2C-310.xml b/tools/gator/daemon/events-L2C-310.xml
new file mode 100644
index 000000000000..923fb90334d0
--- /dev/null
+++ b/tools/gator/daemon/events-L2C-310.xml
@@ -0,0 +1,18 @@
+ <counter_set name="L2C-310_cnt" count="2"/>
+ <category name="L2C-310" counter_set="L2C-310_cnt" per_cpu="no">
+ <event event="0x1" title="L2 Cache" name="CastOUT" description="Eviction, CastOUT, of a line from the L2 cache"/>
+ <event event="0x2" title="L2 Cache" name="Data Read Hit" description="Data read hit in the L2 cache"/>
+ <event event="0x3" title="L2 Cache" name="Data Read Request" description="Data read lookup to the L2 cache. Subsequently results in a hit or miss"/>
+ <event event="0x4" title="L2 Cache" name="Data Write Hit" description="Data write hit in the L2 cache"/>
+ <event event="0x5" title="L2 Cache" name="Data Write Request" description="Data write lookup to the L2 cache. Subsequently results in a hit or miss"/>
+ <event event="0x6" title="L2 Cache" name="Data Write-Through Request" description="Data write lookup to the L2 cache with Write-Through attribute. Subsequently results in a hit or miss"/>
+ <event event="0x7" title="L2 Cache" name="Instruction Read Hit" description="Instruction read hit in the L2 cache"/>
+ <event event="0x8" title="L2 Cache" name="Instruction Read Request" description="Instruction read lookup to the L2 cache. Subsequently results in a hit or miss"/>
+ <event event="0x9" title="L2 Cache" name="Write Allocate Miss" description="Allocation into the L2 cache caused by a write, with Write-Allocate attribute, miss"/>
+ <event event="0xa" title="L2 Cache" name="Internal Prefetch Allocate" description="Allocation of a prefetch generated by L2C-310 into the L2 cache"/>
+ <event event="0xb" title="L2 Cache" name="Prefitch Hit" description="Prefetch hint hits in the L2 cache"/>
+ <event event="0xc" title="L2 Cache" name="Prefitch Allocate" description="Prefetch hint allocated into the L2 cache"/>
+ <event event="0xd" title="L2 Cache" name="Speculative Read Received" description="Speculative read received"/>
+ <event event="0xe" title="L2 Cache" name="Speculative Read Confirmed" description="Speculative read confirmed"/>
+ <event event="0xf" title="L2 Cache" name="Prefetch Hint Received" description="Prefetch hint received"/>
+ </category>
diff --git a/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml
new file mode 100644
index 000000000000..c306dd62208e
--- /dev/null
+++ b/tools/gator/daemon/events-Linux.xml
@@ -0,0 +1,16 @@
+ <category name="Linux">
+ <event counter="Linux_irq_softirq" title="Interrupts" name="SoftIRQ" per_cpu="yes" description="Linux SoftIRQ taken"/>
+ <event counter="Linux_irq_irq" title="Interrupts" name="IRQ" per_cpu="yes" description="Linux IRQ taken"/>
+ <event counter="Linux_block_rq_wr" title="Disk IO" name="Write" units="B" description="Disk IO Bytes Written"/>
+ <event counter="Linux_block_rq_rd" title="Disk IO" name="Read" units="B" description="Disk IO Bytes Read"/>
+ <event counter="Linux_net_rx" title="Network" name="Receive" units="B" description="Receive network traffic, including effect from Streamline"/>
+ <event counter="Linux_net_tx" title="Network" name="Transmit" units="B" description="Transmit network traffic, including effect from Streamline"/>
+ <event counter="Linux_sched_switch" title="Scheduler" name="Switch" per_cpu="yes" description="Context switch events"/>
+ <event counter="Linux_meminfo_memused" title="Memory" name="Used" class="absolute" units="B" proc="yes" description="Total used memory size. Note: a process' used memory includes shared memory that may be counted more than once (equivalent to RES from top). Kernel threads are not filterable."/>
+ <event counter="Linux_meminfo_memfree" title="Memory" name="Free" class="absolute" display="minimum" units="B" description="Available memory size"/>
+ <event counter="Linux_meminfo_bufferram" title="Memory" name="Buffer" class="absolute" units="B" description="Memory used by OS disk buffers"/>
+ <event counter="Linux_power_cpu_freq" title="Clock" name="Frequency" per_cpu="yes" class="absolute" units="Hz" series_composition="overlay" average_cores="yes" description="Frequency setting of the CPU"/>
+ <event counter="Linux_power_cpu_idle" title="Idle" name="State" per_cpu="yes" class="absolute" description="CPU Idle State + 1, set the Sample Rate to None to prevent the hrtimer from interrupting the system"/>
+ <event counter="Linux_cpu_wait_contention" title="CPU Contention" name="Wait" per_cpu="no" class="activity" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" color="0x003c96fb" description="Thread waiting on contended resource"/>
+ <event counter="Linux_cpu_wait_io" title="CPU I/O" name="Wait" per_cpu="no" class="activity" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" color="0x00b30000" description="Thread waiting on I/O resource"/>
+ </category>
diff --git a/tools/gator/daemon/events-Mali-4xx.xml b/tools/gator/daemon/events-Mali-4xx.xml
new file mode 100644
index 000000000000..0a95dfeb6485
--- /dev/null
+++ b/tools/gator/daemon/events-Mali-4xx.xml
@@ -0,0 +1,245 @@
+ <counter_set name="ARM_Mali-4xx_VP_0_cnt" count="2"/>
+ <counter_set name="ARM_Mali-4xx_SW_cnt" count="0"/>
+ <category name="Mali Vertex Processor" counter_set="ARM_Mali-4xx_VP_0_cnt" per_cpu="no">
+ <event event="0x01" title="Mali-4xx VP" name="Active cycles" description="Number of cycles per frame the MaliGP2 was active."/>
+ <event event="0x02" title="Mali-4xx VP" name="Active cycles, vertex shader" description="Number of cycles per frame the vertex shader unit was active."/>
+ <event event="0x03" title="Mali-4xx VP" name="Active cycles, vertex storer" description="Number of cycles per frame the vertex storer unit was active."/>
+ <event event="0x04" title="Mali-4xx VP" name="Active cycles, vertex loader" description="Number of cycles per frame the vertex loader unit was active."/>
+ <event event="0x05" title="Mali-4xx VP" name="Cycles vertex loader waiting for vertex shader" description="Number of cycles per frame the vertex loader was idle while waiting on the vertex shader."/>
+ <event event="0x06" title="Mali-4xx VP" name="Words read, system bus" description="Total number of 64 bit words read by the GP2 from the system bus per frame."/>
+ <event event="0x07" title="Mali-4xx VP" name="Words written, system bus" description="Total number of 64 bit words written by the GP2 to the system bus per frame."/>
+ <event event="0x08" title="Mali-4xx VP" name="Read bursts, system bus" description="Number of read bursts by the GP2 from the system bus per frame."/>
+ <event event="0x09" title="Mali-4xx VP" name="Write bursts, system bus" description="Number of write bursts from the MaliGP2 to the system bus per frame."/>
+ <event event="0x0a" title="Mali-4xx VP" name="Vertices processed" description="Number of vertices processed by the MaliGP2 per frame."/>
+ <event event="0x0b" title="Mali-4xx VP" name="Vertices fetched" description="Number of vertices fetched by the MaliGP2 per frame."/>
+ <event event="0x0c" title="Mali-4xx VP" name="Primitives fetched" description="Number of graphics primitives fetched by the MaliGP2 per frame."/>
+ <event event="0x0e" title="Mali-4xx VP" name="Primitives culled" description="Number of graphics primitives discarded per frame, because they were seen from the back or were offscreen."/>
+ <event event="0x0f" title="Mali-4xx VP" name="Commands written to tiles" description="Number of commands (8 Bytes, mainly primitives) written by GP2 to the PP input data structure per frame."/>
+ <event event="0x10" title="Mali-4xx VP" name="Memory blocks allocated" description="Number of overflow data blocks needed for outputting the PP input data structure per frame ."/>
+ <event event="0x13" title="Mali-4xx VP" name="Vertex loader cache misses" description="Number of cache misses for the vertex shader's vertex input unit per frame."/>
+ <event event="0x16" title="Mali-4xx VP" name="Active cycles, vertex shader command processor" description="Number of cycles per frame the GP2 vertex shader command processor was active. This includes time waiting for semaphores."/>
+ <event event="0x17" title="Mali-4xx VP" name="Active cycles, PLBU command processor" description="Number of cycles per frame the MaliGP2 PLBU command processor was active. This includes time waiting for semaphores."/>
+ <event event="0x18" title="Mali-4xx VP" name="Active Cycles, PLBU list writer" description="Number of cycles per frame the MaliGP2 PLBU output unit was active. This includes time spent waiting on the bus."/>
+ <event event="0x19" title="Mali-4xx VP" name="Active cycles, PLBU geometry processing" description="Number of cycles per frame the MaliGP2 PLBU was active, excepting final data output. In other words: active cycles through the prepare list commands. This includes time spent waiting on the bus."/>
+ <event event="0x1b" title="Mali-4xx VP" name="Active cycles, PLBU primitive assembly" description="Number of active cycles per frame spent by the MaliGP2 PLBU doing primitive assembly. This does not include scissoring or final output. This includes time spent waiting on the bus."/>
+ <event event="0x1c" title="Mali-4xx VP" name="Active cycles, PLBU vertex fetcher" description="Number of active cycles per frame spent by the MaliGP2 PLBU fetching vertex data. This includes time spent waiting on the bus."/>
+ <event event="0x1e" title="Mali-4xx VP" name="Active cycles, Bounding-box and command generator" description="Number of active cycles per frame spent by the MaliGP2 PLBU setting up bounding boxes and commands (mainly graphics primitives). This includes time spent waiting on the bus."/>
+ <event event="0x20" title="Mali-4xx VP" name="Active cycles, Scissor tile iterator" description="Number of active cycles per frame spent by the MaliGP2 PLBU iterating over tiles to perform scissoring. This includes time spent waiting on the bus."/>
+ <event event="0x21" title="Mali-4xx VP" name="Active cycles, PLBU tile iterator" description="Number of active cycles per frame spent by the MaliGP2 PLBU iterating over the tiles in the bounding box generating commands (mainly graphics primitives). This includes time spent waiting on the bus."/>
+ </category>
+ <category name="Mali Fragment Processor" per_cpu="no">
+ <counter_set name="ARM_Mali-4xx_FP_0_cnt" title="Mali-4xx FP0" description="Mali GPU Fragment Processor 0" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_1_cnt" title="Mali-4xx FP1" description="Mali GPU Fragment Processor 1" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_2_cnt" title="Mali-4xx FP2" description="Mali GPU Fragment Processor 2" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_3_cnt" title="Mali-4xx FP3" description="Mali GPU Fragment Processor 3" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_4_cnt" title="Mali-4xx FP4" description="Mali GPU Fragment Processor 4" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_5_cnt" title="Mali-4xx FP5" description="Mali GPU Fragment Processor 5" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_6_cnt" title="Mali-4xx FP6" description="Mali GPU Fragment Processor 6" count="2"/>
+ <counter_set name="ARM_Mali-4xx_FP_7_cnt" title="Mali-4xx FP7" description="Mali GPU Fragment Processor 7" count="2"/>
+ <event event="0x00" title="Mali-4xx FP" name="Active clock cycles" description="Active clock cycles, between polygon start and IRQ."/>
+ <event event="0x02" title="Mali-4xx FP" name="Total bus reads" description="Total number of 64-bit words read from the bus."/>
+ <event event="0x03" title="Mali-4xx FP" name="Total bus writes" description="Total number of 64-bit words written to the bus."/>
+ <event event="0x04" title="Mali-4xx FP" name="Bus read request cycles" description="Number of cycles during which the bus read request signal was HIGH."/>
+ <event event="0x05" title="Mali-4xx FP" name="Bus write request cycles" description="Number of cycles during which the bus write request signal was HIGH."/>
+ <event event="0x06" title="Mali-4xx FP" name="Bus read transactions count" description="Number of read requests accepted by the bus."/>
+ <event event="0x07" title="Mali-4xx FP" name="Bus write transactions" description="Number of write requests accepted by the bus."/>
+ <event event="0x09" title="Mali-4xx FP" name="Tile writeback writes" description="64-bit words written to the bus by the writeback unit."/>
+ <event event="0x0a" title="Mali-4xx FP" name="Store unit writes" description="64-bit words written to the bus by the store unit."/>
+ <event event="0x0d" title="Mali-4xx FP" name="Texture cache uncompressed reads" description="Number of 64-bit words read from the bus into the uncompressed textures cache."/>
+ <event event="0x0e" title="Mali-4xx FP" name="Polygon list reads" description="Number of 64-bit words read from the bus by the polygon list reader."/>
+ <event event="0x0f" title="Mali-4xx FP" name="RSW reads" description="Number of 64-bit words read from the bus into the Render State Word register."/>
+ <event event="0x10" title="Mali-4xx FP" name="Vertex cache reads" description="Number of 64-bit words read from the bus into the vertex cache."/>
+ <event event="0x11" title="Mali-4xx FP" name="Uniform remapping reads" description="Number of 64-bit words read from the bus when reading from the uniform remapping table."/>
+ <event event="0x12" title="Mali-4xx FP" name="Program cache reads" description="Number of 64-bit words read from the bus into the fragment shader program cache."/>
+ <event event="0x13" title="Mali-4xx FP" name="Varying reads" description="Number of 64-bit words containing varyings generated by the vertex processing read from the bus."/>
+ <event event="0x14" title="Mali-4xx FP" name="Texture descriptors reads" description="Number of 64-bit words containing texture descriptors read from the bus."/>
+ <event event="0x15" title="Mali-4xx FP" name="Texture descriptor remapping reads" description="Number of 64-bit words read from the bus when reading from the texture descriptor remapping table."/>
+ <event event="0x17" title="Mali-4xx FP" name="Load unit reads" description="Number of 64-bit words read from the bus by the LOAD sub-instruction."/>
+ <event event="0x18" title="Mali-4xx FP" name="Polygon count" description="Number of triangles read from the polygon list."/>
+ <event event="0x19" title="Mali-4xx FP" name="Pixel rectangle count" description="Number of pixel rectangles read from the polygon list."/>
+ <event event="0x1a" title="Mali-4xx FP" name="Lines count" description="Number of lines read from the polygon list."/>
+ <event event="0x1b" title="Mali-4xx FP" name="Points count" description="Number of points read from the polygon list."/>
+ <event event="0x1c" title="Mali-4xx FP" name="Stall cycles PolygonListReader" description="Number of clock cycles the Polygon List Reader waited for output being collected."/>
+ <event event="0x1d" title="Mali-4xx FP" name="Stall cycles triangle setup" description="Number of clock cycles the TSC waited for input."/>
+ <event event="0x1e" title="Mali-4xx FP" name="Quad rasterized count" description="Number of 2x?2 quads output from rasterizer."/>
+ <event event="0x1f" title="Mali-4xx FP" name="Fragment rasterized count" description="Number of fragment rasterized. Fragments/(Quads*4) gives average actual fragments per quad."/>
+ <event event="0x20" title="Mali-4xx FP" name="Fragment rejected fragment-kill count" description="Number of fragments exiting the fragment shader as killed."/>
+ <event event="0x21" title="Mali-4xx FP" name="Fragment rejected fwd-fragment-kill count" description="Number of fragments killed by forward fragment kill."/>
+ <event event="0x22" title="Mali-4xx FP" name="Fragment passed z/stencil count" description="Number of fragments passing Z and stencil test."/>
+ <event event="0x23" title="Mali-4xx FP" name="Patches rejected early z/stencil count" description="Number of patches rejected by EarlyZ. A patch can be 8x8, 4x4 or 2x2 pixels."/>
+ <event event="0x24" title="Mali-4xx FP" name="Patches evaluated" description="Number of patches evaluated for EarlyZ rejection."/>
+ <event event="0x25" title="Mali-4xx FP" name="Instruction completed count" description="Number of fragment shader instruction words completed. It is a function of pixels processed and the length of the shader programs."/>
+ <event event="0x26" title="Mali-4xx FP" name="Instruction failed rendezvous count" description="Number of fragment shader instructions not completed because of failed Rendezvous."/>
+ <event event="0x27" title="Mali-4xx FP" name="Instruction failed varying-miss count" description="Number of fragment shader instructions not completed because of failed varying operation."/>
+ <event event="0x28" title="Mali-4xx FP" name="Instruction failed texture-miss count" description="Number of fragment shader instructions not completed because of failed texture operation."/>
+ <event event="0x29" title="Mali-4xx FP" name="Instruction failed load-miss count" description="Number of fragment shader instructions not completed because of failed load operation."/>
+ <event event="0x2a" title="Mali-4xx FP" name="Instruction failed tile read-miss count" description="Number of fragment shader instructions not completed because of failed read from the tilebuffer."/>
+ <event event="0x2b" title="Mali-4xx FP" name="Instruction failed store-miss count" description="Number of fragment shader instructions not completed because of failed store operation."/>
+ <event event="0x2c" title="Mali-4xx FP" name="Rendezvous breakage count" description="Number of Rendezvous breakages reported."/>
+ <event event="0x2d" title="Mali-4xx FP" name="Pipeline bubbles cycle count" description="Number of unused cycles in the fragment shader while rendering is active."/>
+ <event event="0x2e" title="Mali-4xx FP" name="Texture mapper multipass count" description="Number of texture operations looped because of more texture passes needed."/>
+ <event event="0x2f" title="Mali-4xx FP" name="Texture mapper cycle count" description="Number of texture operation cycles."/>
+ <event event="0x30" title="Mali-4xx FP" name="Vertex cache hit count" description="Number of times a requested vertex was found in the cache (Number of vertex cache hits)."/>
+ <event event="0x31" title="Mali-4xx FP" name="Vertex cache miss count" description="Number of times a requested vertex was not found in the cache (Number of vertex cache misses)."/>
+ <event event="0x32" title="Mali-4xx FP" name="Varying cache hit count" description="Number of times a requested varying was found in the cache (Number of varying cache hits)."/>
+ <event event="0x33" title="Mali-4xx FP" name="Varying cache miss count" description="Number of times a requested varying was not found in the cache (Number of varying cache misses)."/>
+ <event event="0x34" title="Mali-4xx FP" name="Varying cache conflict miss count" description="Number of times a requested varying was not in the cache and its value, retrieved from memory, must overwrite an older cache entry. This happens when an access pattern cannot be serviced by the cache."/>
+ <event event="0x35" title="Mali-4xx FP" name="Texture cache hit count" description="Number of times a requested texel was found in the texture cache (Number of texture cache hits)."/>
+ <event event="0x36" title="Mali-4xx FP" name="Texture cache miss count" description="Number of times a requested texel was not found in the texture cache (Number of texture cache misses)."/>
+ <event event="0x37" title="Mali-4xx FP" name="Texture cache conflict miss count" description="Number of times a requested texel was not in the cache and its value, retrieved from memory, must overwrite an older cache entry. This happens when an access pattern cannot be serviced by the cache."/>
+ <event event="0x38" title="Mali-4xx FP" name="Compressed texture cache hit count" description="Number of times a requested item was found in the cache."/>
+ <event event="0x39" title="Mali-4xx FP" name="Compressed texture cache miss count" description="Number of times a requested item was not found in the cache."/>
+ <event event="0x3a" title="Mali-4xx FP" name="Load/Store cache hit count" description="Number of hits in the load/store cache."/>
+ <event event="0x3b" title="Mali-4xx FP" name="Load/Store cache miss count" description="Number of misses in the load/store cache."/>
+ <event event="0x3c" title="Mali-4xx FP" name="Program cache hit count" description="Number of hits in the program cache."/>
+ <event event="0x3d" title="Mali-4xx FP" name="Program cache miss count" description="Number of misses in the program cache."/>
+ </category>
+ <counter_set name="ARM_Mali-4xx_L2_0_cnt" title="Mali-4xx L2" description="Mali GPU L2 Cache Core 0" count="2"/>
+ <category name="Mali-4xx L2" counter_set="ARM_Mali-4xx_L2_0_cnt" per_cpu="no">
+ <event event="0x01" title="Mali L2 Cache" name="Total clock cycles" description="Total clock cycles"/>
+ <event event="0x02" title="Mali L2 Cache" name="Active clock cycles" description="Active clock cycles"/>
+ <option_set name="All">
+ <option event_delta="0x08" name="Master" description="Master"/>
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+ <option_set name="Slaves">
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+ <event event="0x00" option_set="All" title="Mali L2 Cache" name="Read transactions" description="Read transactions"/>
+ <event event="0x01" option_set="All" title="Mali L2 Cache" name="Write transactions" description="Write transactions"/>
+ <event event="0x02" option_set="All" title="Mali L2 Cache" name="Words read" description="Words read"/>
+ <event event="0x03" option_set="All" title="Mali L2 Cache" name="Words written" description="Words written"/>
+ <event event="0x04" option_set="Slaves" title="Mali L2 Cache" name="Read hits" description="Read hits"/>
+ <event event="0x05" option_set="Slaves" title="Mali L2 Cache" name="Read misses" description="Read misses"/>
+ <event event="0x06" option_set="Slaves" title="Mali L2 Cache" name="Write invalidates" description="Write invalidates"/>
+ <event event="0x07" option_set="Slaves" title="Mali L2 Cache" name="Read invalidates" description="Read invalidates"/>
+ <event event="0x08" option_set="Slaves" title="Mali L2 Cache" name="Cacheable read transactions" description="Cacheable read transactions"/>
+ </category>
+ <counter_set name="ARM_Mali-4xx_L2_1_cnt" title="Mali-4xx L2 1" description="Mali GPU L2 Cache Core 1" count="2"/>
+ <category name="Mali-4xx L2_1" counter_set="ARM_Mali-4xx_L2_1_cnt" per_cpu="no">
+ <event event="0x01" title="Mali L2 Cache 1" name="Total clock cycles" description="Total clock cycles"/>
+ <event event="0x02" title="Mali L2 Cache 1" name="Active clock cycles" description="Active clock cycles"/>
+ <option_set name="All">
+ <option event_delta="0x08" name="Master" description="Master"/>
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+ <option_set name="Slaves">
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+ <event event="0x00" option_set="All" title="Mali L2 Cache 1" name="Read transactions" description="Read transactions"/>
+ <event event="0x01" option_set="All" title="Mali L2 Cache 1" name="Write transactions" description="Write transactions"/>
+ <event event="0x02" option_set="All" title="Mali L2 Cache 1" name="Words read" description="Words read"/>
+ <event event="0x03" option_set="All" title="Mali L2 Cache 1" name="Words written" description="Words written"/>
+ <event event="0x04" option_set="Slaves" title="Mali L2 Cache 1" name="Read hits" description="Read hits"/>
+ <event event="0x05" option_set="Slaves" title="Mali L2 Cache 1" name="Read misses" description="Read misses"/>
+ <event event="0x06" option_set="Slaves" title="Mali L2 Cache 1" name="Write invalidates" description="Write invalidates"/>
+ <event event="0x07" option_set="Slaves" title="Mali L2 Cache 1" name="Read invalidates" description="Read invalidates"/>
+ <event event="0x08" option_set="Slaves" title="Mali L2 Cache 1" name="Cacheable read transactions" description="Cacheable read transactions"/>
+ </category>
+ <counter_set name="ARM_Mali-4xx_L2_2_cnt" title="Mali-4xx L2 2" description="Mali GPU L2 Cache Core 2" count="2"/>
+ <category name="Mali-4xx L2_2" counter_set="ARM_Mali-4xx_L2_2_cnt" per_cpu="no">
+ <event event="0x01" title="Mali L2 Cache 2" name="Total clock cycles" description="Total clock cycles"/>
+ <event event="0x02" title="Mali L2 Cache 2" name="Active clock cycles" description="Active clock cycles"/>
+ <option_set name="All">
+ <option event_delta="0x08" name="Master" description="Master"/>
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+ <option_set name="Slaves">
+ <option event_delta="0x10" name="All slaves" description="All slaves"/>
+ <option event_delta="0x20" name="Slave 0" description="Slave 0"/>
+ <option event_delta="0x30" name="Slave 1" description="Slave 1"/>
+ <option event_delta="0x40" name="Slave 2" description="Slave 2"/>
+ <option event_delta="0x50" name="Slave 3" description="Slave 3"/>
+ <option event_delta="0x60" name="Slave 4" description="Slave 4"/>
+ </option_set>
+ <event event="0x00" option_set="All" title="Mali L2 Cache 2" name="Read transactions" description="Read transactions"/>
+ <event event="0x01" option_set="All" title="Mali L2 Cache 2" name="Write transactions" description="Write transactions"/>
+ <event event="0x02" option_set="All" title="Mali L2 Cache 2" name="Words read" description="Words read"/>
+ <event event="0x03" option_set="All" title="Mali L2 Cache 2" name="Words written" description="Words written"/>
+ <event event="0x04" option_set="Slaves" title="Mali L2 Cache 2" name="Read hits" description="Read hits"/>
+ <event event="0x05" option_set="Slaves" title="Mali L2 Cache 2" name="Read misses" description="Read misses"/>
+ <event event="0x06" option_set="Slaves" title="Mali L2 Cache 2" name="Write invalidates" description="Write invalidates"/>
+ <event event="0x07" option_set="Slaves" title="Mali L2 Cache 2" name="Read invalidates" description="Read invalidates"/>
+ <event event="0x08" option_set="Slaves" title="Mali L2 Cache 2" name="Cacheable read transactions" description="Cacheable read transactions"/>
+ </category>
+ <counter_set name="ARM_Mali-4xx_Filmstrip_cnt" count="1"/>
+ <category name="Mali-4xx Filmstrip" counter_set="ARM_Mali-4xx_Filmstrip_cnt" per_cpu="no">
+ <option_set name="fs">
+ <option event_delta="0x3c" name="1:60" description="captures every 60th frame"/>
+ <option event_delta="0x1e" name="1:30" description="captures every 30th frame"/>
+ <option event_delta="0xa" name="1:10" description="captures every 10th frame"/>
+ </option_set>
+ <event event="0x0400" option_set="fs" title="ARM Mali-4xx" name="Filmstrip" description="Scaled framebuffer"/>
+ </category>
+ <category name="ARM_Mali-4xx_Voltage" per_cpu="no">
+ <event counter="ARM_Mali-4xx_Voltage" title="Mali GPU Voltage" name="Voltage" class="absolute" display="average" average_selection="yes" units="mV" description="GPU core voltage."/>
+ </category>
+ <category name="ARM_Mali-4xx_Frequency" per_cpu="no">
+ <event counter="ARM_Mali-4xx_Frequency" title="Mali GPU Frequency" name="Frequency" display="average" average_selection="yes" units="MHz" description="GPU core frequency."/>
+ </category>
+ <category name="Mali-4xx Activity" counter_set="ARM_Mali-4xx_Activity_cnt">
+ <event counter="ARM_Mali-4xx_fragment" title="GPU Fragment" name="Activity" class="activity" activity1="Activity" activity_color1="0x00006fcc" rendering_type="bar" average_selection="yes" average_cores="yes" percentage="yes" description="GPU Fragment Activity"/>
+ <event counter="ARM_Mali-4xx_vertex" title="GPU Vertex" name="Activity" class="activity" activity1="Activity" activity_color1="0x00eda000" rendering_type="bar" average_selection="yes" percentage="yes" description="GPU Vertex Activity"/>
+ </category>
+ <category name="Mali-4xx Software Counters" counter_set="ARM_Mali-4xx_SW_cnt" per_cpu="no">
+ <!-- EGL Counters -->
+ <event counter="ARM_Mali-4xx_SW_0" title="Mali EGL Software Counters" name="Blit Time" description="Time spent blitting the framebuffer from video memory to framebuffer."/>
+ <!-- glDrawElements Counters -->
+ <event counter="ARM_Mali-4xx_SW_1" title="glDrawElements Statistics" name="Calls to glDrawElements" description="Number of calls to glDrawElements."/>
+ <event counter="ARM_Mali-4xx_SW_2" title="glDrawElements Statistics" name="Indices to glDrawElements" description="Number of indices to glDrawElements."/>
+ <event counter="ARM_Mali-4xx_SW_3" title="glDrawElements Statistics" name="Transformed by glDrawElements" description="Number of vertices transformed by glDrawElements."/>
+ <!-- glDrawArrays Counters -->
+ <event counter="ARM_Mali-4xx_SW_4" title="glDrawArrays Statistics" name="Calls to glDrawArrays" description="Number of calls to glDrawArrays."/>
+ <event counter="ARM_Mali-4xx_SW_5" title="glDrawArrays Statistics" name="Transformed by glDrawArrays" description="Number of vertices transformed by glDrawArrays."/>
+ <!-- Draw Call Counters -->
+ <event counter="ARM_Mali-4xx_SW_6" title="Drawcall Statistics" name="Points" description="Number of calls to glDraw* with parameter GL_POINTS."/>
+ <event counter="ARM_Mali-4xx_SW_7" title="Drawcall Statistics" name="Lines" description="Number of calls to glDraw* with parameter GL_LINES."/>
+ <event counter="ARM_Mali-4xx_SW_8" title="Drawcall Statistics" name="Lineloop" description="Number of calls to glDraw* with parameter GL_LINE_LOOP."/>
+ <event counter="ARM_Mali-4xx_SW_9" title="Drawcall Statistics" name="Linestrip" description="Number of calls to glDraw* with parameter GL_LINE_STRIP."/>
+ <event counter="ARM_Mali-4xx_SW_10" title="Drawcall Statistics" name="Triangles" description="Number of calls to glDraw* with parameter GL_TRIANGLES."/>
+ <event counter="ARM_Mali-4xx_SW_11" title="Drawcall Statistics" name="Trianglestrip" description="Number of calls to glDraw* with parameter GL_TRIANGLE_STRIP."/>
+ <event counter="ARM_Mali-4xx_SW_12" title="Drawcall Statistics" name="Trianglefan" description="Number of calls to glDraw* with parameter GL_TRIANGLE_FAN."/>
+ <event counter="ARM_Mali-4xx_SW_13" title="Drawcall Statistics" name="Vertex Upload Time (us)" description="Time spent uploading vertex attributes and faceindex data not present in a VBO."/>
+ <event counter="ARM_Mali-4xx_SW_14" title="Drawcall Statistics" name="Uniform Bytes Copied (bytes)" description="Number of bytes copied to Mali memory as a result of uniforms update."/>
+ <!-- Buffer Profiling Counters -->
+ <event counter="ARM_Mali-4xx_SW_15" title="Buffer Profiling" name="Texture Upload Time (ms)" description="Time spent uploading textures."/>
+ <event counter="ARM_Mali-4xx_SW_16" title="Buffer Profiling" name="VBO Upload Time (ms)" description="Time spent uploading vertex buffer objects."/>
+ <event counter="ARM_Mali-4xx_SW_17" title="Buffer Profiling" name="FBO Flushes" description="Number of flushed on framebuffer attachment."/>
+ <!-- OpenGL ES 1.1 Emulation -->
+ <event counter="ARM_Mali-4xx_SW_18" title="Fixed-function Emulation" name="# Vertex Shaders Generated" description="Number of vertex shaders generated."/>
+ <event counter="ARM_Mali-4xx_SW_19" title="Fixed-function Emulation" name="# Fragment Shaders Generated" description="Number of fragment shaders generated."/>
+ <!-- Geometry Statistics -->
+ <event counter="ARM_Mali-4xx_SW_33" title="Geometry Statistics" name="Triangles" description="The total number of triangles passed to GLES per-frame."/>
+ <event counter="ARM_Mali-4xx_SW_34" title="Geometry Statistics" name="Independent Triangles" description="Number of triangles passed to GLES using the mode GL_TRIANGLES."/>
+ <event counter="ARM_Mali-4xx_SW_35" title="Geometry Statistics" name="Strip Triangles" description="Number of triangles passed to GLES using the mode GL_TRIANGLE_STRIP."/>
+ <event counter="ARM_Mali-4xx_SW_36" title="Geometry Statistics" name="Fan Triangles" description="Number of triangles passed to GLES using the mode GL_TRIANGLE_FAN."/>
+ <event counter="ARM_Mali-4xx_SW_37" title="Geometry Statistics" name="Lines" description="Number of lines passed to GLES per-frame."/>
+ <event counter="ARM_Mali-4xx_SW_38" title="Geometry Statistics" name="Independent Lines" description="Number of lines passed to GLES using the mode GL_LINES."/>
+ <event counter="ARM_Mali-4xx_SW_39" title="Geometry Statistics" name="Strip Lines" description="Number of lines passed to GLES using the mode GL_LINE_STRIP."/>
+ <event counter="ARM_Mali-4xx_SW_40" title="Geometry Statistics" name="Loop Lines" description="Number of lines passed to GLES using the mode GL_LINE_LOOP."/>
+ </category>
diff --git a/tools/gator/daemon/events-Mali-T6xx.xml b/tools/gator/daemon/events-Mali-T6xx.xml
new file mode 100644
index 000000000000..5e8979704870
--- /dev/null
+++ b/tools/gator/daemon/events-Mali-T6xx.xml
@@ -0,0 +1,46 @@
+ <category name="Mali-T6xx Software Counters" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_TOTAL_ALLOC_PAGES" title="Mali Total Alloc Pages" name="Total number of allocated pages" description="Mali total number of allocated pages."/>
+ </category>
+ <category name="Mali-T6xx PM Shader" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_PM_SHADER_0" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 0" description="Mali PM Shader: PM Shader Core 0."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_1" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 1" description="Mali PM Shader: PM Shader Core 1."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_2" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 2" description="Mali PM Shader: PM Shader Core 2."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_3" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 3" description="Mali PM Shader: PM Shader Core 3."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_4" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 4" description="Mali PM Shader: PM Shader Core 4."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_5" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 5" description="Mali PM Shader: PM Shader Core 5."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_6" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 6" description="Mali PM Shader: PM Shader Core 6."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_7" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 7" description="Mali PM Shader: PM Shader Core 7."/>
+ </category>
+ <category name="Mali-T6xx PM Tiler" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_PM_TILER_0" display="average" average_selection="yes" percentage="yes" title="Mali PM Tiler" name="PM Tiler Core 0" description="Mali PM Tiler: PM Tiler Core 0."/>
+ </category>
+ <category name="Mali-T6xx PM L2" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_PM_L2_0" display="average" average_selection="yes" percentage="yes" title="Mali PM L2" name="PM L2 Core 0" description="Mali PM L2: PM L2 Core 0."/>
+ <event counter="ARM_Mali-T6xx_PM_L2_1" display="average" average_selection="yes" percentage="yes" title="Mali PM L2" name="PM L2 Core 1" description="Mali PM L2: PM L2 Core 1."/>
+ </category>
+ <category name="Mali-T6xx MMU Address Space" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_MMU_AS_0" display="average" average_selection="yes" percentage="yes" title="Mali MMU Address Space" name="MMU Address Space 0" description="Mali MMU Address Space 0 usage."/>
+ <event counter="ARM_Mali-T6xx_MMU_AS_1" display="average" average_selection="yes" percentage="yes" title="Mali MMU Address Space" name="MMU Address Space 1" description="Mali MMU Address Space 1 usage."/>
+ <event counter="ARM_Mali-T6xx_MMU_AS_2" display="average" average_selection="yes" percentage="yes" title="Mali MMU Address Space" name="MMU Address Space 2" description="Mali MMU Address Space 2 usage."/>
+ <event counter="ARM_Mali-T6xx_MMU_AS_3" display="average" average_selection="yes" percentage="yes" title="Mali MMU Address Space" name="MMU Address Space 3" description="Mali MMU Address Space 3 usage."/>
+ </category>
+ <category name="Mali-T6xx MMU Page Fault" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_MMU_PAGE_FAULT_0" title="Mali MMU Page Fault Add. Space" name="Mali MMU Page Fault Add. Space 0" description="Reports the number of newly allocated pages after a MMU page fault in address space 0."/>
+ <event counter="ARM_Mali-T6xx_MMU_PAGE_FAULT_1" title="Mali MMU Page Fault Add. Space" name="Mali MMU Page Fault Add. Space 1" description="Reports the number of newly allocated pages after a MMU page fault in address space 1."/>
+ <event counter="ARM_Mali-T6xx_MMU_PAGE_FAULT_2" title="Mali MMU Page Fault Add. Space" name="Mali MMU Page Fault Add. Space 2" description="Reports the number of newly allocated pages after a MMU page fault in address space 2."/>
+ <event counter="ARM_Mali-T6xx_MMU_PAGE_FAULT_3" title="Mali MMU Page Fault Add. Space" name="Mali MMU Page Fault Add. Space 3" description="Reports the number of newly allocated pages after a MMU page fault in address space 3."/>
+ </category>
+ <counter_set name="ARM_Mali-T6xx_Filmstrip_cnt" count="1"/>
+ <category name="Mali-T6xx Filmstrip" counter_set="ARM_Mali-T6xx_Filmstrip_cnt" per_cpu="no">
+ <option_set name="fs">
+ <option event_delta="0x3c" name="1:60" description="captures every 60th frame"/>
+ <option event_delta="0x1e" name="1:30" description="captures every 30th frame"/>
+ <option event_delta="0xa" name="1:10" description="captures every 10th frame"/>
+ </option_set>
+ <event event="0x0400" option_set="fs" title="ARM Mali-T6xx" name="Filmstrip" description="Scaled framebuffer"/>
+ </category>
+ <category name="Mali-T6xx Activity" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_fragment" title="GPU Fragment" name="Activity" class="activity" activity1="Activity" activity_color1="0x00006fcc" rendering_type="bar" average_selection="yes" percentage="yes" cores="1" description="GPU Job Slot 0 Activity"/>
+ <event counter="ARM_Mali-T6xx_vertex" title="GPU Vertex-Tiling-Compute" name="Activity" class="activity" activity1="Activity" activity_color1="0x00eda000" rendering_type="bar" average_selection="yes" percentage="yes" cores="1" description="GPU Job Slot 1 Activity"/>
+ <event counter="ARM_Mali-T6xx_opencl" title="GPU Vertex-Compute" name="Activity" class="activity" activity1="Activity" activity_color1="0x00ef022f" rendering_type="bar" average_selection="yes" percentage="yes" cores="1" description="GPU Job Slot 2 Activity"/>
+ </category>
diff --git a/tools/gator/daemon/events-Mali-T6xx_hw.xml b/tools/gator/daemon/events-Mali-T6xx_hw.xml
new file mode 100644
index 000000000000..df2796262473
--- /dev/null
+++ b/tools/gator/daemon/events-Mali-T6xx_hw.xml
@@ -0,0 +1,91 @@
+ <category name="Mali-T6xx Job Manager" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_GPU_ACTIVE" title="Mali Job Manager Cycles" name="GPU cycles" description="Number of cycles the GPU was active"/>
+ <event counter="ARM_Mali-T6xx_IRQ_ACTIVE" title="Mali Job Manager Cycles" name="IRQ cycles" description="Number of cycles the GPU had a pending interrupt"/>
+ <event counter="ARM_Mali-T6xx_JS0_ACTIVE" title="Mali Job Manager Cycles" name="JS0 cycles" description="Number of cycles JS0 (fragment) was active"/>
+ <event counter="ARM_Mali-T6xx_JS1_ACTIVE" title="Mali Job Manager Cycles" name="JS1 cycles" description="Number of cycles JS1 (vertex/tiler/compute) was active"/>
+ <event counter="ARM_Mali-T6xx_JS2_ACTIVE" title="Mali Job Manager Cycles" name="JS2 cycles" description="Number of cycles JS2 (vertex/compute) was active"/>
+ <event counter="ARM_Mali-T6xx_JS0_JOBS" title="Mali Job Manager Work" name="JS0 jobs" description="Number of Jobs (fragment) completed in JS0"/>
+ <event counter="ARM_Mali-T6xx_JS0_TASKS" title="Mali Job Manager Work" name="JS0 tasks" description="Number of Tasks completed in JS0"/>
+ <event counter="ARM_Mali-T6xx_JS1_JOBS" title="Mali Job Manager Work" name="JS1 jobs" description="Number of Jobs (vertex/tiler/compute) completed in JS1"/>
+ <event counter="ARM_Mali-T6xx_JS1_TASKS" title="Mali Job Manager Work" name="JS1 tasks" description="Number of Tasks completed in JS1"/>
+ <event counter="ARM_Mali-T6xx_JS2_TASKS" title="Mali Job Manager Work" name="JS2 tasks" description="Number of Tasks completed in JS2"/>
+ <event counter="ARM_Mali-T6xx_JS2_JOBS" title="Mali Job Manager Work" name="JS2 jobs" description="Number of Jobs (vertex/compute) completed in JS2"/>
+ </category>
+ <category name="Mali-T6xx Tiler" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_POLYGONS" title="Mali Tiler Primitives" name="Polygons" description="Number of polygons processed"/>
+ <event counter="ARM_Mali-T6xx_QUADS" title="Mali Tiler Primitives" name="Quads" description="Number of quads processed"/>
+ <event counter="ARM_Mali-T6xx_TRIANGLES" title="Mali Tiler Primitives" name="Triangles" description="Number of triangles processed"/>
+ <event counter="ARM_Mali-T6xx_LINES" title="Mali Tiler Primitives" name="Lines" description="Number of lines processed"/>
+ <event counter="ARM_Mali-T6xx_POINTS" title="Mali Tiler Primitives" name="Points" description="Number of points processed"/>
+ <event counter="ARM_Mali-T6xx_FRONT_FACING" title="Mali Tiler Culling" name="Front facing prims" description="Number of front facing primitives"/>
+ <event counter="ARM_Mali-T6xx_BACK_FACING" title="Mali Tiler Culling" name="Back facing prims" description="Number of back facing primitives"/>
+ <event counter="ARM_Mali-T6xx_PRIM_VISIBLE" title="Mali Tiler Culling" name="Visible prims" description="Number of visible primitives"/>
+ <event counter="ARM_Mali-T6xx_PRIM_CULLED" title="Mali Tiler Culling" name="Culled prims" description="Number of culled primitives"/>
+ <event counter="ARM_Mali-T6xx_PRIM_CLIPPED" title="Mali Tiler Culling" name="Clipped prims" description="Number of clipped primitives"/>
+ <event counter="ARM_Mali-T6xx_LEVEL0" title="Mali Tiler Hierarchy" name="L0 prims" description="Number of primitives in hierarchy level 0"/>
+ <event counter="ARM_Mali-T6xx_LEVEL1" title="Mali Tiler Hierarchy" name="L1 prims" description="Number of primitives in hierarchy level 1"/>
+ <event counter="ARM_Mali-T6xx_LEVEL2" title="Mali Tiler Hierarchy" name="L2 prims" description="Number of primitives in hierarchy level 2"/>
+ <event counter="ARM_Mali-T6xx_LEVEL3" title="Mali Tiler Hierarchy" name="L3 prims" description="Number of primitives in hierarchy level 3"/>
+ <event counter="ARM_Mali-T6xx_LEVEL4" title="Mali Tiler Hierarchy" name="L4 prims" description="Number of primitives in hierarchy level 4"/>
+ <event counter="ARM_Mali-T6xx_LEVEL5" title="Mali Tiler Hierarchy" name="L5 prims" description="Number of primitives in hierarchy level 5"/>
+ <event counter="ARM_Mali-T6xx_LEVEL6" title="Mali Tiler Hierarchy" name="L6 prims" description="Number of primitives in hierarchy level 6"/>
+ <event counter="ARM_Mali-T6xx_LEVEL7" title="Mali Tiler Hierarchy" name="L7 prims" description="Number of primitives in hierarchy level 7"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_1" title="Mali Tiler Commands" name="Prims in 1 command" description="Number of primitives producing 1 command"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_2" title="Mali Tiler Commands" name="Prims in 2 command" description="Number of primitives producing 2 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_3" title="Mali Tiler Commands" name="Prims in 3 command" description="Number of primitives producing 3 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_4" title="Mali Tiler Commands" name="Prims in 4 command" description="Number of primitives producing 4 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_4_7" title="Mali Tiler Commands" name="Prims in 4-7 commands" description="Number of primitives producing 4-7 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_5_7" title="Mali Tiler Commands" name="Prims in 5-7 commands" description="Number of primitives producing 5-7 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_8_15" title="Mali Tiler Commands" name="Prims in 8-15 commands" description="Number of primitives producing 8-15 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_16_63" title="Mali Tiler Commands" name="Prims in 16-63 commands" description="Number of primitives producing 16-63 commands"/>
+ <event counter="ARM_Mali-T6xx_COMMAND_64" title="Mali Tiler Commands" name="Prims in &gt;= 64 commands" description="Number of primitives producing &gt;= 64 commands"/>
+ </category>
+ <category name="Mali-T6xx Shader Core" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_TRIPIPE_ACTIVE" title="Mali Core Cycles" name="Tripipe cycles" description="Number of cycles the Tripipe was active"/>
+ <event counter="ARM_Mali-T6xx_FRAG_ACTIVE" title="Mali Core Cycles" name="Fragment cycles" description="Number of cycles fragment processing was active"/>
+ <event counter="ARM_Mali-T6xx_COMPUTE_ACTIVE" title="Mali Core Cycles" name="Compute cycles" description="Number of cycles vertex\compute processing was active"/>
+ <event counter="ARM_Mali-T6xx_FRAG_CYCLE_NO_TILE" title="Mali Core Cycles" name="Fragment cycles waiting for tile" description="Number of cycles spent waiting for a physical tile buffer"/>
+ <event counter="ARM_Mali-T6xx_FRAG_THREADS" title="Mali Core Threads" name="Fragment threads" description="Number of fragment threads started"/>
+ <event counter="ARM_Mali-T6xx_FRAG_DUMMY_THREADS" title="Mali Core Threads" name="Dummy fragment threads" description="Number of dummy fragment threads started"/>
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_LZS_TEST" title="Mali Core Threads" name="Frag threads doing late ZS" description="Number of threads doing late ZS test"/>
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_LZS_KILLED" title="Mali Core Threads" name="Frag threads killed late ZS" description="Number of threads killed by late ZS test"/>
+ <event counter="ARM_Mali-T6xx_FRAG_THREADS_LZS_TEST" title="Mali Core Threads" name="Frag threads doing late ZS" description="Number of threads doing late ZS test"/>
+ <event counter="ARM_Mali-T6xx_FRAG_THREADS_LZS_KILLED" title="Mali Core Threads" name="Frag threads killed late ZS" description="Number of threads killed by late ZS test"/>
+ <event counter="ARM_Mali-T6xx_COMPUTE_TASKS" title="Mali Compute Threads" name="Compute tasks" description="Number of compute tasks"/>
+ <event counter="ARM_Mali-T6xx_COMPUTE_THREADS" title="Mali Compute Threads" name="Compute threads started" description="Number of compute threads started"/>
+ <event counter="ARM_Mali-T6xx_COMPUTE_CYCLES_DESC" title="Mali Compute Threads" name="Compute cycles awaiting descriptors" description="Number of compute cycles spent waiting for descriptors"/>
+ <event counter="ARM_Mali-T6xx_FRAG_PRIMATIVES" title="Mali Fragment Primitives" name="Primitives loaded" description="Number of primitives loaded from tiler"/>
+ <event counter="ARM_Mali-T6xx_FRAG_PRIMATIVES_DROPPED" title="Mali Fragment Primitives" name="Primitives dropped" description="Number of primitives dropped because out of tile"/>
+ <event counter="ARM_Mali-T6xx_FRAG_PRIMITIVES" title="Mali Fragment Primitives" name="Primitives loaded" description="Number of primitives loaded from tiler"/>
+ <event counter="ARM_Mali-T6xx_FRAG_PRIMITIVES_DROPPED" title="Mali Fragment Primitives" name="Primitives dropped" description="Number of primitives dropped because out of tile"/>
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_RAST" title="Mali Fragment Quads" name="Quads rasterized" description="Number of quads rasterized"/>
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_EZS_TEST" title="Mali Fragment Quads" name="Quads doing early ZS" description="Number of quads doing early ZS test"/>
+ <event counter="ARM_Mali-T6xx_FRAG_QUADS_EZS_KILLED" title="Mali Fragment Quads" name="Quads killed early Z" description="Number of quads killed by early ZS test"/>
+ <event counter="ARM_Mali-T6xx_FRAG_NUM_TILES" title="Mali Fragment Tasks" name="Tiles rendered" description="Number of tiles rendered"/>
+ <event counter="ARM_Mali-T6xx_FRAG_TRANS_ELIM" title="Mali Fragment Tasks" name="Tile writes killed by TE" description="Number of tile writes skipped by transaction elimination"/>
+ <event counter="ARM_Mali-T6xx_ARITH_WORDS" title="Mali Arithmetic Pipe" name="A instructions" description="Number of instructions completed by the the A-pipe (normalized per pipeline)"/>
+ <event counter="ARM_Mali-T6xx_LS_WORDS" title="Mali Load/Store Pipe" name="LS instructions" description="Number of instructions completed by the LS-pipe"/>
+ <event counter="ARM_Mali-T6xx_LS_ISSUES" title="Mali Load/Store Pipe" name="LS instruction issues" description="Number of instructions issued to the LS-pipe, including restarts"/>
+ <event counter="ARM_Mali-T6xx_TEX_WORDS" title="Mali Texture Pipe" name="T instructions" description="Number of instructions completed by the T-pipe"/>
+ <event counter="ARM_Mali-T6xx_TEX_THREADS" title="Mali Texture Pipe" name="T instruction issues" description="Number of instructions issused to the T-pipe, including restarts"/>
+ <event counter="ARM_Mali-T6xx_TEX_RECIRC_FMISS" title="Mali Texture Pipe" name="Cache misses" description="Number of instructions in the T-pipe, recirculated due to cache miss"/>
+ <event counter="ARM_Mali-T6xx_LSC_READ_HITS" title="Mali Load/Store Cache" name="Read hits" description="Number of read hits in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_READ_MISSES" title="Mali Load/Store Cache" name="Read misses" description="Number of read misses in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_WRITE_HITS" title="Mali Load/Store Cache" name="Write hits" description="Number of write hits in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_WRITE_MISSES" title="Mali Load/Store Cache" name="Write misses" description="Number of write misses in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_ATOMIC_HITS" title="Mali Load/Store Cache" name="Atomic hits" description="Number of atomic hits in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_ATOMIC_MISSES" title="Mali Load/Store Cache" name="Atomic misses" description="Number of atomic misses in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_LINE_FETCHES" title="Mali Load/Store Cache" name="Line fetches" description="Number of line fetches in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_DIRTY_LINE" title="Mali Load/Store Cache" name="Dirty line evictions" description="Number of dirty line evictions in the Load/Store cache"/>
+ <event counter="ARM_Mali-T6xx_LSC_SNOOPS" title="Mali Load/Store Cache" name="Snoops in to LSC" description="Number of coherent memory snoops in to the Load/Store cache"/>
+ </category>
+ <category name="Mali-T6xx L2 and MMU" per_cpu="no">
+ <event counter="ARM_Mali-T6xx_L2_WRITE_BEATS" title="Mali L2 Cache" name="External write beats" description="Number of external bus write beats"/>
+ <event counter="ARM_Mali-T6xx_L2_READ_BEATS" title="Mali L2 Cache" name="External read beats" description="Number of external bus read beats"/>
+ <event counter="ARM_Mali-T6xx_L2_READ_SNOOP" title="Mali L2 Cache" name="Read snoops" description="Number of read transaction snoops"/>
+ <event counter="ARM_Mali-T6xx_L2_READ_HIT" title="Mali L2 Cache" name="L2 read hits" description="Number of reads hitting in the L2 cache"/>
+ <event counter="ARM_Mali-T6xx_L2_WRITE_SNOOP" title="Mali L2 Cache" name="Write snoops" description="Number of write transaction snoops"/>
+ <event counter="ARM_Mali-T6xx_L2_WRITE_HIT" title="Mali L2 Cache" name="L2 write hits" description="Number of writes hitting in the L2 cache"/>
+ <event counter="ARM_Mali-T6xx_L2_EXT_AR_STALL" title="Mali L2 Cache" name="External bus stalls (AR)" description="Number of cycles a valid read address (AR) is stalled by the external interconnect"/>
+ <event counter="ARM_Mali-T6xx_L2_EXT_W_STALL" title="Mali L2 Cache" name="External bus stalls (W)" description="Number of cycles a valid write data (W channel) is stalled by the external interconnect"/>
+ </category>
diff --git a/tools/gator/daemon/events-Mali-V500.xml b/tools/gator/daemon/events-Mali-V500.xml
new file mode 100644
index 000000000000..d2751e7239b5
--- /dev/null
+++ b/tools/gator/daemon/events-Mali-V500.xml
@@ -0,0 +1,29 @@
+ <category name="Mali-V500">
+ <event counter="ARM_Mali-V500_cnt0" title="Mali Video Engine" name="Samples" class="absolute" description="The number of times we have taken a sample"/>
+ <event counter="ARM_Mali-V500_cnt1" title="Mali Video Engine" name="Queued input-buffers" class="absolute" description="The number of input-buffers that has been queued for consumption by the MVE"/>
+ <event counter="ARM_Mali-V500_cnt2" title="Mali Video Engine" name="Consumed input-buffers" class="absolute" description="The number of input-buffers that has been consumed by the MVE and returned to the application"/>
+ <event counter="ARM_Mali-V500_cnt3" title="Mali Video Engine" name="Queued output-buffers" class="absolute" description="The number of output-buffers that has been queued for usage by the MVE"/>
+ <event counter="ARM_Mali-V500_cnt4" title="Mali Video Engine" name="Consumed output-buffers" class="absolute" description="The number of output-buffers that has been consumed by the MVE and returned to the application"/>
+ <event counter="ARM_Mali-V500_cnt5" title="Mali Video Engine" name="Created Sessions" class="absolute" description="The number of created sessions throughout the lifetime of the process"/>
+ <event counter="ARM_Mali-V500_cnt6" title="Mali Video Engine" name="Active Sessions" description="The number of currently existing sessions"/>
+ <event counter="ARM_Mali-V500_cnt7" title="Mali Video Engine" name="Processed Frames" class="absolute" description="The number of processed frames. A processed frame is one where the encode or decode is complete for that particular frame. Frames can be processed out of order so this is not the same as the number of output-buffers returned"/>
+ <event counter="ARM_Mali-V500_cnt8" title="Mali Video Engine" name="Input Flushes Requested" class="absolute" description="The number of requested flushes of the input queue"/>
+ <event counter="ARM_Mali-V500_cnt9" title="Mali Video Engine" name="Input Flushes Complete" class="absolute" description="The number of completed flushes of the input queue"/>
+ <event counter="ARM_Mali-V500_cnt10" title="Mali Video Engine" name="Output Flushes Requested" class="absolute" description="The number of requested flushes of the output queue"/>
+ <event counter="ARM_Mali-V500_cnt11" title="Mali Video Engine" name="Output Flushes Complete" class="absolute" description="The number of completed flushes of the output queue"/>
+ <event counter="ARM_Mali-V500_cnt12" title="Mali Video Engine" name="Queued Output Buffers (current)" description="The number of output-buffers that are currently queued for usage by the MVE"/>
+ <event counter="ARM_Mali-V500_cnt13" title="Mali Video Engine" name="Queued Input Buffers (current)" description="The number of input-buffers that are currently queued for consumption by the MVE"/>
+ <event counter="ARM_Mali-V500_cnt14" title="Mali Video Engine" name="Output Queue Flushes" description="The number of pending flushes for the MVE output-queue"/>
+ <event counter="ARM_Mali-V500_cnt15" title="Mali Video Engine" name="Input Queue Flushes" description="The number of pending flushes for the MVE input-queue"/>
+ <event counter="ARM_Mali-V500_cnt16" title="Mali Video Engine" name="Errors encountered" class="absolute" description="The number of errors encountered"/>
+ <event counter="ARM_Mali-V500_cnt17" title="Mali Video Engine" name="Bits consumed" class="absolute" description="The number of bits consumed during decode"/>
+ <event counter="ARM_Mali-V500_cnt18" title="Mali Video Engine" name="AFBC bandwidth" class="absolute" description="The amount of AFBC-encoded bytes read or written"/>
+ <event counter="ARM_Mali-V500_cnt19" title="Mali Video Engine" name="Bandwidth (read)" class="absolute" description="The amount of bytes read over the AXI bus"/>
+ <event counter="ARM_Mali-V500_cnt20" title="Mali Video Engine" name="Bandwidth (write)" class="absolute" description="The amount of bytes written over the AXI bus"/>
+ <event counter="ARM_Mali-V500_evn0" title="Mali Video Engine" name="Session created" description="Generated when a session has been created"/>
+ <event counter="ARM_Mali-V500_evn1" title="Mali Video Engine" name="Session destroyed" description="Generated when a session has been destroyed"/>
+ <event counter="ARM_Mali-V500_evn2" title="Mali Video Engine" name="Frame Processed" description="Generated when the MVE has finished processing a frame"/>
+ <event counter="ARM_Mali-V500_evn3" title="Mali Video Engine" name="Output buffer received" description="Generated when an an output buffer is returned to us from the MVE"/>
+ <event counter="ARM_Mali-V500_evn4" title="Mali Video Engine" name="Input buffer received" description="Generated when we an input buffer is returned to us from the MVE"/>
+ <!--event counter="ARM_Mali-V500_act" title="VPU" name="Activity" class="activity" activity1="Parsed" activity_color1="0x000000ff" activity2="Piped" activity_color2="0x0000ff00" rendering_type="bar" average_selection="yes" average_cores="yes" percentage="yes" cores="8" description="Mali-V500 Activity"/-->
+ </category>
diff --git a/tools/gator/daemon/events-Perf-Hardware.xml b/tools/gator/daemon/events-Perf-Hardware.xml
new file mode 100644
index 000000000000..423696f82420
--- /dev/null
+++ b/tools/gator/daemon/events-Perf-Hardware.xml
@@ -0,0 +1,12 @@
+ <counter_set name="Perf_Hardware_cnt" count="6"/>
+ <category name="Perf Hardware" counter_set="Perf_Hardware_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="Perf_Hardware_ccnt" event="0" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="1" title="Instruction" name="Executed" description="Instruction executed"/>
+ <event event="2" title="Cache" name="References" description="Cache References"/>
+ <event event="3" title="Cache" name="Misses" description="Cache Misses"/>
+ <event event="4" title="Branch" name="Instructions" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="5" title="Branch" name="Misses" description="Branch mispredicted or not predicted"/>
+ <event event="6" title="Bus" name="Cycles" description="Bus Cycles"/>
+ <event event="7" title="Instruction" name="Stalled Frontend" description="Stalled Frontend Cycles"/>
+ <event event="8" title="Instruction" name="Stalled Backend" description="Stalled Backend Cycles"/>
+ </category>
diff --git a/tools/gator/daemon/events-Scorpion.xml b/tools/gator/daemon/events-Scorpion.xml
new file mode 100644
index 000000000000..fa716fdc4840
--- /dev/null
+++ b/tools/gator/daemon/events-Scorpion.xml
@@ -0,0 +1,107 @@
+ <counter_set name="Scorpion_cnt" count="4"/>
+ <category name="Scorpion" counter_set="Scorpion_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="Scorpion_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Program Counter" name="SW change" description="Software change of PC, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Branch" name="Procedure Return" description="Procedure return architecturally executed (not by exceptions)"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x4c" title="Scorpion" name="ICACHE_EXPL_INV" description="I-cache explicit invalidates"/>
+ <event event="0x4d" title="Scorpion" name="ICACHE_MISS" description="I-cache misses"/>
+ <event event="0x4e" title="Scorpion" name="ICACHE_ACCESS" description="I-cache accesses"/>
+ <event event="0x4f" title="Scorpion" name="ICACHE_CACHEREQ_L2" description="I-cache cacheable requests to L2"/>
+ <event event="0x50" title="Scorpion" name="ICACHE_NOCACHE_L2" description="I-cache non-cacheable requests to L2"/>
+ <event event="0x51" title="Scorpion" name="HIQUP_NOPED" description="Conditional instructions HIQUPs NOPed"/>
+ <event event="0x52" title="Scorpion" name="DATA_ABORT" description="Interrupts and Exceptions Data Abort"/>
+ <event event="0x53" title="Scorpion" name="IRQ" description="Interrupts and Exceptions IRQ"/>
+ <event event="0x54" title="Scorpion" name="FIQ" description="Interrupts and Exceptions FIQ"/>
+ <event event="0x55" title="Scorpion" name="ALL_EXCPT" description="Interrupts and Exceptions All interrupts"/>
+ <event event="0x56" title="Scorpion" name="UNDEF" description="Interrupts and Exceptions Undefined"/>
+ <event event="0x57" title="Scorpion" name="SVC" description="Interrupts and Exceptions SVC"/>
+ <event event="0x58" title="Scorpion" name="SMC" description="Interrupts and Exceptions SMC"/>
+ <event event="0x59" title="Scorpion" name="PREFETCH_ABORT" description="Interrupts and Exceptions Prefetch Abort"/>
+ <event event="0x5a" title="Scorpion" name="INDEX_CHECK" description="Interrupts and Exceptions Index Check"/>
+ <event event="0x5b" title="Scorpion" name="NULL_CHECK" description="Interrupts and Exceptions Null Check"/>
+ <event event="0x5c" title="Scorpion" name="EXPL_ICIALLU" description="I-cache and BTAC Invalidates Explicit ICIALLU"/>
+ <event event="0x5d" title="Scorpion" name="IMPL_ICIALLU" description="I-cache and BTAC Invalidates Implicit ICIALLU"/>
+ <event event="0x5e" title="Scorpion" name="NONICIALLU_BTAC_INV" description="I-cache and BTAC Invalidates Non-ICIALLU BTAC Invalidate"/>
+ <event event="0x5f" title="Scorpion" name="ICIMVAU_IMPL_ICIALLU" description="I-cache and BTAC Invalidates ICIMVAU-implied ICIALLU"/>
+ <event event="0x60" title="Scorpion" name="SPIPE_ONLY_CYCLES" description="Issue S-pipe only issue cycles"/>
+ <event event="0x61" title="Scorpion" name="XPIPE_ONLY_CYCLES" description="Issue X-pipe only issue cycles"/>
+ <event event="0x62" title="Scorpion" name="DUAL_CYCLES" description="Issue dual issue cycles"/>
+ <event event="0x63" title="Scorpion" name="DISPATCH_ANY_CYCLES" description="Dispatch any dispatch cycles"/>
+ <event event="0x64" title="Scorpion" name="FIFO_FULLBLK_CMT" description="Commits Trace FIFO full Blk CMT"/>
+ <event event="0x65" title="Scorpion" name="FAIL_COND_INST" description="Conditional instructions failing conditional instrs (excluding branches)"/>
+ <event event="0x66" title="Scorpion" name="PASS_COND_INST" description="Conditional instructions passing conditional instrs (excluding branches)"/>
+ <event event="0x67" title="Scorpion" name="ALLOW_VU_CLK" description="Unit Clock Gating Allow VU Clks"/>
+ <event event="0x68" title="Scorpion" name="VU_IDLE" description="Unit Clock Gating VU Idle"/>
+ <event event="0x69" title="Scorpion" name="ALLOW_L2_CLK" description="Unit Clock Gating Allow L2 Clks"/>
+ <event event="0x6a" title="Scorpion" name="L2_IDLE" description="Unit Clock Gating L2 Idle"/>
+ <event event="0x6b" title="Scorpion" name="DTLB_IMPL_INV_SCTLR_DACR" description="DTLB implicit invalidates writes to SCTLR and DACR"/>
+ <event event="0x6c" title="Scorpion" name="DTLB_EXPL_INV" description="DTLB explicit invalidates"/>
+ <event event="0x6d" title="Scorpion" name="DTLB_MISS" description="DTLB misses"/>
+ <event event="0x6e" title="Scorpion" name="DTLB_ACCESS" description="DTLB accesses"/>
+ <event event="0x6f" title="Scorpion" name="ITLB_MISS" description="ITLB misses"/>
+ <event event="0x70" title="Scorpion" name="ITLB_IMPL_INV" description="ITLB implicit ITLB invalidates"/>
+ <event event="0x71" title="Scorpion" name="ITLB_EXPL_INV" description="ITLB explicit ITLB invalidates"/>
+ <event event="0x72" title="Scorpion" name="UTLB_D_MISS" description="UTLB d-side misses"/>
+ <event event="0x73" title="Scorpion" name="UTLB_D_ACCESS" description="UTLB d-side accesses"/>
+ <event event="0x74" title="Scorpion" name="UTLB_I_MISS" description="UTLB i-side misses"/>
+ <event event="0x75" title="Scorpion" name="UTLB_I_ACCESS" description="UTLB i-side accesses"/>
+ <event event="0x76" title="Scorpion" name="UTLB_INV_ASID" description="UTLB invalidate by ASID"/>
+ <event event="0x77" title="Scorpion" name="UTLB_INV_MVA" description="UTLB invalidate by MVA"/>
+ <event event="0x78" title="Scorpion" name="UTLB_INV_ALL" description="UTLB invalidate all"/>
+ <event event="0x79" title="Scorpion" name="S2_HOLD_RDQ_UNAVAIL" description="S2 hold RDQ unavail"/>
+ <event event="0x7a" title="Scorpion" name="S2_HOLD" description="S2 hold"/>
+ <event event="0x7b" title="Scorpion" name="S2_HOLD_DEV_OP" description="S2 hold device op"/>
+ <event event="0x7c" title="Scorpion" name="S2_HOLD_ORDER" description="S2 hold strongly ordered op"/>
+ <event event="0x7d" title="Scorpion" name="S2_HOLD_BARRIER" description="S2 hold barrier"/>
+ <event event="0x7e" title="Scorpion" name="VIU_DUAL_CYCLE" description="Scorpion VIU dual cycle"/>
+ <event event="0x7f" title="Scorpion" name="VIU_SINGLE_CYCLE" description="Scorpion VIU single cycle"/>
+ <event event="0x80" title="Scorpion" name="VX_PIPE_WAR_STALL_CYCLES" description="Scorpion VX pipe WAR cycles"/>
+ <event event="0x81" title="Scorpion" name="VX_PIPE_WAW_STALL_CYCLES" description="Scorpion VX pipe WAW cycles"/>
+ <event event="0x82" title="Scorpion" name="VX_PIPE_RAW_STALL_CYCLES" description="Scorpion VX pipe RAW cycles"/>
+ <event event="0x83" title="Scorpion" name="VX_PIPE_LOAD_USE_STALL" description="Scorpion VX pipe load use stall"/>
+ <event event="0x84" title="Scorpion" name="VS_PIPE_WAR_STALL_CYCLES" description="Scorpion VS pipe WAR stall cycles"/>
+ <event event="0x85" title="Scorpion" name="VS_PIPE_WAW_STALL_CYCLES" description="Scorpion VS pipe WAW stall cycles"/>
+ <event event="0x86" title="Scorpion" name="VS_PIPE_RAW_STALL_CYCLES" description="Scorpion VS pipe RAW stall cycles"/>
+ <event event="0x87" title="Scorpion" name="EXCEPTIONS_INV_OPERATION" description="Scorpion invalid operation exceptions"/>
+ <event event="0x88" title="Scorpion" name="EXCEPTIONS_DIV_BY_ZERO" description="Scorpion divide by zero exceptions"/>
+ <event event="0x89" title="Scorpion" name="COND_INST_FAIL_VX_PIPE" description="Scorpion conditional instruction fail VX pipe"/>
+ <event event="0x8a" title="Scorpion" name="COND_INST_FAIL_VS_PIPE" description="Scorpion conditional instruction fail VS pipe"/>
+ <event event="0x8b" title="Scorpion" name="EXCEPTIONS_OVERFLOW" description="Scorpion overflow exceptions"/>
+ <event event="0x8c" title="Scorpion" name="EXCEPTIONS_UNDERFLOW" description="Scorpion underflow exceptions"/>
+ <event event="0x8d" title="Scorpion" name="EXCEPTIONS_DENORM" description="Scorpion denorm exceptions"/>
+ <event event="0x8e" title="Scorpion" name="BANK_AB_HIT" description="L2 hit rates bank A/B hits"/>
+ <event event="0x8f" title="Scorpion" name="BANK_AB_ACCESS" description="L2 hit rates bank A/B accesses"/>
+ <event event="0x90" title="Scorpion" name="BANK_CD_HIT" description="L2 hit rates bank C/D hits"/>
+ <event event="0x91" title="Scorpion" name="BANK_CD_ACCESS" description="L2 hit rates bank C/D accesses"/>
+ <event event="0x92" title="Scorpion" name="BANK_AB_DSIDE_HIT" description="L2 hit rates bank A/B d-side hits"/>
+ <event event="0x93" title="Scorpion" name="BANK_AB_DSIDE_ACCESS" description="L2 hit rates bank A/B d-side accesses"/>
+ <event event="0x94" title="Scorpion" name="BANK_CD_DSIDE_HIT" description="L2 hit rates bank C/D d-side hits"/>
+ <event event="0x95" title="Scorpion" name="BANK_CD_DSIDE_ACCESS" description="L2 hit rates bank C/D d-side accesses"/>
+ <event event="0x96" title="Scorpion" name="BANK_AB_ISIDE_HIT" description="L2 hit rates bank A/B i-side hits"/>
+ <event event="0x97" title="Scorpion" name="BANK_AB_ISIDE_ACCESS" description="L2 hit rates bank A/B i-side accesses"/>
+ <event event="0x98" title="Scorpion" name="BANK_CD_ISIDE_HIT" description="L2 hit rates bank C/D i-side hits"/>
+ <event event="0x99" title="Scorpion" name="BANK_CD_ISIDE_ACCESS" description="L2 hit rates bank C/D i-side accesses"/>
+ <event event="0x9a" title="Scorpion" name="ISIDE_RD_WAIT" description="fills and castouts cycles that i-side RD requests wait on data from bus"/>
+ <event event="0x9b" title="Scorpion" name="DSIDE_RD_WAIT" description="fills and castouts cycles that d-side RD requests wait on data from bus"/>
+ <event event="0x9c" title="Scorpion" name="BANK_BYPASS_WRITE" description="fills and castouts bank bypass writes"/>
+ <event event="0x9d" title="Scorpion" name="BANK_AB_NON_CASTOUT" description="fills and castouts bank A/B non-castout writes to bus"/>
+ <event event="0x9e" title="Scorpion" name="BANK_AB_L2_CASTOUT" description="fills and castouts bank A/B L2 castouts (granules)"/>
+ <event event="0x9f" title="Scorpion" name="BANK_CD_NON_CASTOUT" description="fills and castouts bank C/D non-castout writes to bus"/>
+ <event event="0xa0" title="Scorpion" name="BANK_CD_L2_CASTOUT" description="fills and castouts bank C/D L2 castouts (granules)"/>
+ </category>
diff --git a/tools/gator/daemon/events-ScorpionMP.xml b/tools/gator/daemon/events-ScorpionMP.xml
new file mode 100644
index 000000000000..c648ccefb287
--- /dev/null
+++ b/tools/gator/daemon/events-ScorpionMP.xml
@@ -0,0 +1,90 @@
+ <counter_set name="ScorpionMP_cnt" count="4"/>
+ <category name="ScorpionMP" counter_set="ScorpionMP_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ScorpionMP_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
+ <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
+ <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
+ <event event="0x04" title="Cache" name="Data access" description="Memory Read or Write operation that causes a cache access to at least the level of data or unified cache closest to the processor"/>
+ <event event="0x05" title="Cache" name="Data TLB refill" description="Memory Read or Write operation that causes a TLB refill of at least the level of TLB closest to the processor"/>
+ <event event="0x06" title="Instruction" name="Memory read" description="Memory-reading instruction architecturally executed"/>
+ <event event="0x07" title="Instruction" name="Memory write" description="Memory-writing instruction architecturally executed"/>
+ <event event="0x08" title="Instruction" name="Executed" description="Instruction architecturally executed"/>
+ <event event="0x09" title="Exception" name="Taken" description="Exceptions taken"/>
+ <event event="0x0a" title="Exception" name="Return" description="Exception return architecturally executed"/>
+ <event event="0x0b" title="Instruction" name="CONTEXTIDR" description="Instruction that writes to the CONTEXTIDR architecturally executed"/>
+ <event event="0x0c" title="Program Counter" name="SW change" description="Software change of PC, except by an exception, architecturally executed"/>
+ <event event="0x0d" title="Branch" name="Immediate" description="Immediate branch architecturally executed"/>
+ <event event="0x0e" title="Branch" name="Procedure Return" description="Procedure return architecturally executed (not by exceptions)"/>
+ <event event="0x0f" title="Memory" name="Unaligned access" description="Unaligned access architecturally executed"/>
+ <event event="0x10" title="Branch" name="Mispredicted" description="Branch mispredicted or not predicted"/>
+ <event event="0x12" title="Branch" name="Potential prediction" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="0x4c" title="Scorpion" name="ICACHE_EXPL_INV" description="I-cache explicit invalidates"/>
+ <event event="0x4d" title="Scorpion" name="ICACHE_MISS" description="I-cache misses"/>
+ <event event="0x4e" title="Scorpion" name="ICACHE_ACCESS" description="I-cache accesses"/>
+ <event event="0x4f" title="Scorpion" name="ICACHE_CACHEREQ_L2" description="I-cache cacheable requests to L2"/>
+ <event event="0x50" title="Scorpion" name="ICACHE_NOCACHE_L2" description="I-cache non-cacheable requests to L2"/>
+ <event event="0x51" title="Scorpion" name="HIQUP_NOPED" description="Conditional instructions HIQUPs NOPed"/>
+ <event event="0x52" title="Scorpion" name="DATA_ABORT" description="Interrupts and Exceptions Data Abort"/>
+ <event event="0x53" title="Scorpion" name="IRQ" description="Interrupts and Exceptions IRQ"/>
+ <event event="0x54" title="Scorpion" name="FIQ" description="Interrupts and Exceptions FIQ"/>
+ <event event="0x55" title="Scorpion" name="ALL_EXCPT" description="Interrupts and Exceptions All interrupts"/>
+ <event event="0x56" title="Scorpion" name="UNDEF" description="Interrupts and Exceptions Undefined"/>
+ <event event="0x57" title="Scorpion" name="SVC" description="Interrupts and Exceptions SVC"/>
+ <event event="0x58" title="Scorpion" name="SMC" description="Interrupts and Exceptions SMC"/>
+ <event event="0x59" title="Scorpion" name="PREFETCH_ABORT" description="Interrupts and Exceptions Prefetch Abort"/>
+ <event event="0x5a" title="Scorpion" name="INDEX_CHECK" description="Interrupts and Exceptions Index Check"/>
+ <event event="0x5b" title="Scorpion" name="NULL_CHECK" description="Interrupts and Exceptions Null Check"/>
+ <event event="0x5c" title="Scorpion" name="EXPL_ICIALLU" description="I-cache and BTAC Invalidates Explicit ICIALLU"/>
+ <event event="0x5d" title="Scorpion" name="IMPL_ICIALLU" description="I-cache and BTAC Invalidates Implicit ICIALLU"/>
+ <event event="0x5e" title="Scorpion" name="NONICIALLU_BTAC_INV" description="I-cache and BTAC Invalidates Non-ICIALLU BTAC Invalidate"/>
+ <event event="0x5f" title="Scorpion" name="ICIMVAU_IMPL_ICIALLU" description="I-cache and BTAC Invalidates ICIMVAU-implied ICIALLU"/>
+ <event event="0x60" title="Scorpion" name="SPIPE_ONLY_CYCLES" description="Issue S-pipe only issue cycles"/>
+ <event event="0x61" title="Scorpion" name="XPIPE_ONLY_CYCLES" description="Issue X-pipe only issue cycles"/>
+ <event event="0x62" title="Scorpion" name="DUAL_CYCLES" description="Issue dual issue cycles"/>
+ <event event="0x63" title="Scorpion" name="DISPATCH_ANY_CYCLES" description="Dispatch any dispatch cycles"/>
+ <event event="0x64" title="Scorpion" name="FIFO_FULLBLK_CMT" description="Commits Trace FIFO full Blk CMT"/>
+ <event event="0x65" title="Scorpion" name="FAIL_COND_INST" description="Conditional instructions failing conditional instrs (excluding branches)"/>
+ <event event="0x66" title="Scorpion" name="PASS_COND_INST" description="Conditional instructions passing conditional instrs (excluding branches)"/>
+ <event event="0x67" title="Scorpion" name="ALLOW_VU_CLK" description="Unit Clock Gating Allow VU Clks"/>
+ <event event="0x68" title="Scorpion" name="VU_IDLE" description="Unit Clock Gating VU Idle"/>
+ <event event="0x69" title="Scorpion" name="ALLOW_L2_CLK" description="Unit Clock Gating Allow L2 Clks"/>
+ <event event="0x6a" title="Scorpion" name="L2_IDLE" description="Unit Clock Gating L2 Idle"/>
+ <event event="0x6b" title="Scorpion" name="DTLB_IMPL_INV_SCTLR_DACR" description="DTLB implicit invalidates writes to SCTLR and DACR"/>
+ <event event="0x6c" title="Scorpion" name="DTLB_EXPL_INV" description="DTLB explicit invalidates"/>
+ <event event="0x6d" title="Scorpion" name="DTLB_MISS" description="DTLB misses"/>
+ <event event="0x6e" title="Scorpion" name="DTLB_ACCESS" description="DTLB accesses"/>
+ <event event="0x6f" title="Scorpion" name="ITLB_MISS" description="ITLB misses"/>
+ <event event="0x70" title="Scorpion" name="ITLB_IMPL_INV" description="ITLB implicit ITLB invalidates"/>
+ <event event="0x71" title="Scorpion" name="ITLB_EXPL_INV" description="ITLB explicit ITLB invalidates"/>
+ <event event="0x72" title="Scorpion" name="UTLB_D_MISS" description="UTLB d-side misses"/>
+ <event event="0x73" title="Scorpion" name="UTLB_D_ACCESS" description="UTLB d-side accesses"/>
+ <event event="0x74" title="Scorpion" name="UTLB_I_MISS" description="UTLB i-side misses"/>
+ <event event="0x75" title="Scorpion" name="UTLB_I_ACCESS" description="UTLB i-side accesses"/>
+ <event event="0x76" title="Scorpion" name="UTLB_INV_ASID" description="UTLB invalidate by ASID"/>
+ <event event="0x77" title="Scorpion" name="UTLB_INV_MVA" description="UTLB invalidate by MVA"/>
+ <event event="0x78" title="Scorpion" name="UTLB_INV_ALL" description="UTLB invalidate all"/>
+ <event event="0x79" title="Scorpion" name="S2_HOLD_RDQ_UNAVAIL" description="S2 hold RDQ unavail"/>
+ <event event="0x7a" title="Scorpion" name="S2_HOLD" description="S2 hold"/>
+ <event event="0x7b" title="Scorpion" name="S2_HOLD_DEV_OP" description="S2 hold device op"/>
+ <event event="0x7c" title="Scorpion" name="S2_HOLD_ORDER" description="S2 hold strongly ordered op"/>
+ <event event="0x7d" title="Scorpion" name="S2_HOLD_BARRIER" description="S2 hold barrier"/>
+ <event event="0x7e" title="Scorpion" name="VIU_DUAL_CYCLE" description="Scorpion VIU dual cycle"/>
+ <event event="0x7f" title="Scorpion" name="VIU_SINGLE_CYCLE" description="Scorpion VIU single cycle"/>
+ <event event="0x80" title="Scorpion" name="VX_PIPE_WAR_STALL_CYCLES" description="Scorpion VX pipe WAR cycles"/>
+ <event event="0x81" title="Scorpion" name="VX_PIPE_WAW_STALL_CYCLES" description="Scorpion VX pipe WAW cycles"/>
+ <event event="0x82" title="Scorpion" name="VX_PIPE_RAW_STALL_CYCLES" description="Scorpion VX pipe RAW cycles"/>
+ <event event="0x83" title="Scorpion" name="VX_PIPE_LOAD_USE_STALL" description="Scorpion VX pipe load use stall"/>
+ <event event="0x84" title="Scorpion" name="VS_PIPE_WAR_STALL_CYCLES" description="Scorpion VS pipe WAR stall cycles"/>
+ <event event="0x85" title="Scorpion" name="VS_PIPE_WAW_STALL_CYCLES" description="Scorpion VS pipe WAW stall cycles"/>
+ <event event="0x86" title="Scorpion" name="VS_PIPE_RAW_STALL_CYCLES" description="Scorpion VS pipe RAW stall cycles"/>
+ <event event="0x87" title="Scorpion" name="EXCEPTIONS_INV_OPERATION" description="Scorpion invalid operation exceptions"/>
+ <event event="0x88" title="Scorpion" name="EXCEPTIONS_DIV_BY_ZERO" description="Scorpion divide by zero exceptions"/>
+ <event event="0x89" title="Scorpion" name="COND_INST_FAIL_VX_PIPE" description="Scorpion conditional instruction fail VX pipe"/>
+ <event event="0x8a" title="Scorpion" name="COND_INST_FAIL_VS_PIPE" description="Scorpion conditional instruction fail VS pipe"/>
+ <event event="0x8b" title="Scorpion" name="EXCEPTIONS_OVERFLOW" description="Scorpion overflow exceptions"/>
+ <event event="0x8c" title="Scorpion" name="EXCEPTIONS_UNDERFLOW" description="Scorpion underflow exceptions"/>
+ <event event="0x8d" title="Scorpion" name="EXCEPTIONS_DENORM" description="Scorpion denorm exceptions"/>
+ <event event="0x8e" title="ScorpionMP" name="NUM_BARRIERS" description="Barriers"/>
+ <event event="0x8f" title="ScorpionMP" name="BARRIER_CYCLES" description="Barrier cycles"/>
+ </category>
diff --git a/tools/gator/daemon/events_footer.xml b/tools/gator/daemon/events_footer.xml
new file mode 100644
index 000000000000..cd2b44665bab
--- /dev/null
+++ b/tools/gator/daemon/events_footer.xml
@@ -0,0 +1 @@
+</events>
diff --git a/tools/gator/daemon/events_header.xml b/tools/gator/daemon/events_header.xml
new file mode 100644
index 000000000000..38ec4c03246e
--- /dev/null
+++ b/tools/gator/daemon/events_header.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<events>
diff --git a/tools/gator/daemon/k/perf_event.3.12.h b/tools/gator/daemon/k/perf_event.3.12.h
new file mode 100644
index 000000000000..e886c48cadf6
--- /dev/null
+++ b/tools/gator/daemon/k/perf_event.3.12.h
@@ -0,0 +1,792 @@
+/*
+ * Performance events:
+ *
+ * Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de>
+ * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar
+ * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra
+ *
+ * Data type definitions, declarations, prototypes.
+ *
+ * Started by: Thomas Gleixner and Ingo Molnar
+ *
+ * For licencing details see kernel-base/COPYING
+ */
+#ifndef _LINUX_PERF_EVENT_H
+#define _LINUX_PERF_EVENT_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <asm/byteorder.h>
+
+/*
+ * User-space ABI bits:
+ */
+
+/*
+ * attr.type
+ */
+enum perf_type_id {
+ PERF_TYPE_HARDWARE = 0,
+ PERF_TYPE_SOFTWARE = 1,
+ PERF_TYPE_TRACEPOINT = 2,
+ PERF_TYPE_HW_CACHE = 3,
+ PERF_TYPE_RAW = 4,
+ PERF_TYPE_BREAKPOINT = 5,
+
+ PERF_TYPE_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized performance event event_id types, used by the
+ * attr.event_id parameter of the sys_perf_event_open()
+ * syscall:
+ */
+enum perf_hw_id {
+ /*
+ * Common hardware events, generalized by the kernel:
+ */
+ PERF_COUNT_HW_CPU_CYCLES = 0,
+ PERF_COUNT_HW_INSTRUCTIONS = 1,
+ PERF_COUNT_HW_CACHE_REFERENCES = 2,
+ PERF_COUNT_HW_CACHE_MISSES = 3,
+ PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
+ PERF_COUNT_HW_BRANCH_MISSES = 5,
+ PERF_COUNT_HW_BUS_CYCLES = 6,
+ PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7,
+ PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8,
+ PERF_COUNT_HW_REF_CPU_CYCLES = 9,
+
+ PERF_COUNT_HW_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized hardware cache events:
+ *
+ * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x
+ * { read, write, prefetch } x
+ * { accesses, misses }
+ */
+enum perf_hw_cache_id {
+ PERF_COUNT_HW_CACHE_L1D = 0,
+ PERF_COUNT_HW_CACHE_L1I = 1,
+ PERF_COUNT_HW_CACHE_LL = 2,
+ PERF_COUNT_HW_CACHE_DTLB = 3,
+ PERF_COUNT_HW_CACHE_ITLB = 4,
+ PERF_COUNT_HW_CACHE_BPU = 5,
+ PERF_COUNT_HW_CACHE_NODE = 6,
+
+ PERF_COUNT_HW_CACHE_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_id {
+ PERF_COUNT_HW_CACHE_OP_READ = 0,
+ PERF_COUNT_HW_CACHE_OP_WRITE = 1,
+ PERF_COUNT_HW_CACHE_OP_PREFETCH = 2,
+
+ PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_result_id {
+ PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0,
+ PERF_COUNT_HW_CACHE_RESULT_MISS = 1,
+
+ PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */
+};
+
+/*
+ * Special "software" events provided by the kernel, even if the hardware
+ * does not support performance events. These events measure various
+ * physical and sw events of the kernel (and allow the profiling of them as
+ * well):
+ */
+enum perf_sw_ids {
+ PERF_COUNT_SW_CPU_CLOCK = 0,
+ PERF_COUNT_SW_TASK_CLOCK = 1,
+ PERF_COUNT_SW_PAGE_FAULTS = 2,
+ PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
+ PERF_COUNT_SW_CPU_MIGRATIONS = 4,
+ PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
+ PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
+ PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
+ PERF_COUNT_SW_EMULATION_FAULTS = 8,
+ PERF_COUNT_SW_DUMMY = 9,
+
+ PERF_COUNT_SW_MAX, /* non-ABI */
+};
+
+/*
+ * Bits that can be set in attr.sample_type to request information
+ * in the overflow packets.
+ */
+enum perf_event_sample_format {
+ PERF_SAMPLE_IP = 1U << 0,
+ PERF_SAMPLE_TID = 1U << 1,
+ PERF_SAMPLE_TIME = 1U << 2,
+ PERF_SAMPLE_ADDR = 1U << 3,
+ PERF_SAMPLE_READ = 1U << 4,
+ PERF_SAMPLE_CALLCHAIN = 1U << 5,
+ PERF_SAMPLE_ID = 1U << 6,
+ PERF_SAMPLE_CPU = 1U << 7,
+ PERF_SAMPLE_PERIOD = 1U << 8,
+ PERF_SAMPLE_STREAM_ID = 1U << 9,
+ PERF_SAMPLE_RAW = 1U << 10,
+ PERF_SAMPLE_BRANCH_STACK = 1U << 11,
+ PERF_SAMPLE_REGS_USER = 1U << 12,
+ PERF_SAMPLE_STACK_USER = 1U << 13,
+ PERF_SAMPLE_WEIGHT = 1U << 14,
+ PERF_SAMPLE_DATA_SRC = 1U << 15,
+ PERF_SAMPLE_IDENTIFIER = 1U << 16,
+
+ PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */
+};
+
+/*
+ * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set
+ *
+ * If the user does not pass priv level information via branch_sample_type,
+ * the kernel uses the event's priv level. Branch and event priv levels do
+ * not have to match. Branch priv level is checked for permissions.
+ *
+ * The branch types can be combined, however BRANCH_ANY covers all types
+ * of branches and therefore it supersedes all the other types.
+ */
+enum perf_branch_sample_type {
+ PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */
+ PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */
+ PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */
+
+ PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */
+ PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */
+ PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */
+ PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */
+ PERF_SAMPLE_BRANCH_ABORT_TX = 1U << 7, /* transaction aborts */
+ PERF_SAMPLE_BRANCH_IN_TX = 1U << 8, /* in transaction */
+ PERF_SAMPLE_BRANCH_NO_TX = 1U << 9, /* not in transaction */
+
+ PERF_SAMPLE_BRANCH_MAX = 1U << 10, /* non-ABI */
+};
+
+#define PERF_SAMPLE_BRANCH_PLM_ALL \
+ (PERF_SAMPLE_BRANCH_USER|\
+ PERF_SAMPLE_BRANCH_KERNEL|\
+ PERF_SAMPLE_BRANCH_HV)
+
+/*
+ * Values to determine ABI of the registers dump.
+ */
+enum perf_sample_regs_abi {
+ PERF_SAMPLE_REGS_ABI_NONE = 0,
+ PERF_SAMPLE_REGS_ABI_32 = 1,
+ PERF_SAMPLE_REGS_ABI_64 = 2,
+};
+
+/*
+ * The format of the data returned by read() on a perf event fd,
+ * as specified by attr.read_format:
+ *
+ * struct read_format {
+ * { u64 value;
+ * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
+ * { u64 id; } && PERF_FORMAT_ID
+ * } && !PERF_FORMAT_GROUP
+ *
+ * { u64 nr;
+ * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
+ * { u64 value;
+ * { u64 id; } && PERF_FORMAT_ID
+ * } cntr[nr];
+ * } && PERF_FORMAT_GROUP
+ * };
+ */
+enum perf_event_read_format {
+ PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
+ PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
+ PERF_FORMAT_ID = 1U << 2,
+ PERF_FORMAT_GROUP = 1U << 3,
+
+ PERF_FORMAT_MAX = 1U << 4, /* non-ABI */
+};
+
+#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
+#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */
+#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
+#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */
+ /* add: sample_stack_user */
+
+/*
+ * Hardware event_id to monitor via a performance monitoring event:
+ */
+struct perf_event_attr {
+
+ /*
+ * Major type: hardware/software/tracepoint/etc.
+ */
+ __u32 type;
+
+ /*
+ * Size of the attr structure, for fwd/bwd compat.
+ */
+ __u32 size;
+
+ /*
+ * Type specific configuration information.
+ */
+ __u64 config;
+
+ union {
+ __u64 sample_period;
+ __u64 sample_freq;
+ };
+
+ __u64 sample_type;
+ __u64 read_format;
+
+ __u64 disabled : 1, /* off by default */
+ inherit : 1, /* children inherit it */
+ pinned : 1, /* must always be on PMU */
+ exclusive : 1, /* only group on PMU */
+ exclude_user : 1, /* don't count user */
+ exclude_kernel : 1, /* ditto kernel */
+ exclude_hv : 1, /* ditto hypervisor */
+ exclude_idle : 1, /* don't count when idle */
+ mmap : 1, /* include mmap data */
+ comm : 1, /* include comm data */
+ freq : 1, /* use freq, not period */
+ inherit_stat : 1, /* per task counts */
+ enable_on_exec : 1, /* next exec enables */
+ task : 1, /* trace fork/exit */
+ watermark : 1, /* wakeup_watermark */
+ /*
+ * precise_ip:
+ *
+ * 0 - SAMPLE_IP can have arbitrary skid
+ * 1 - SAMPLE_IP must have constant skid
+ * 2 - SAMPLE_IP requested to have 0 skid
+ * 3 - SAMPLE_IP must have 0 skid
+ *
+ * See also PERF_RECORD_MISC_EXACT_IP
+ */
+ precise_ip : 2, /* skid constraint */
+ mmap_data : 1, /* non-exec mmap data */
+ sample_id_all : 1, /* sample_type all events */
+
+ exclude_host : 1, /* don't count in host */
+ exclude_guest : 1, /* don't count in guest */
+
+ exclude_callchain_kernel : 1, /* exclude kernel callchains */
+ exclude_callchain_user : 1, /* exclude user callchains */
+ mmap2 : 1, /* include mmap with inode data */
+
+ __reserved_1 : 40;
+
+ union {
+ __u32 wakeup_events; /* wakeup every n events */
+ __u32 wakeup_watermark; /* bytes before wakeup */
+ };
+
+ __u32 bp_type;
+ union {
+ __u64 bp_addr;
+ __u64 config1; /* extension of config */
+ };
+ union {
+ __u64 bp_len;
+ __u64 config2; /* extension of config1 */
+ };
+ __u64 branch_sample_type; /* enum perf_branch_sample_type */
+
+ /*
+ * Defines set of user regs to dump on samples.
+ * See asm/perf_regs.h for details.
+ */
+ __u64 sample_regs_user;
+
+ /*
+ * Defines size of the user stack to dump on samples.
+ */
+ __u32 sample_stack_user;
+
+ /* Align to u64. */
+ __u32 __reserved_2;
+};
+
+#define perf_flags(attr) (*(&(attr)->read_format + 1))
+
+/*
+ * Ioctls that can be done on a perf event fd:
+ */
+#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
+#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
+#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
+#define PERF_EVENT_IOC_RESET _IO ('$', 3)
+#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
+#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
+#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
+#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
+
+enum perf_event_ioc_flags {
+ PERF_IOC_FLAG_GROUP = 1U << 0,
+};
+
+/*
+ * Structure of the page that can be mapped via mmap
+ */
+struct perf_event_mmap_page {
+ __u32 version; /* version number of this structure */
+ __u32 compat_version; /* lowest version this is compat with */
+
+ /*
+ * Bits needed to read the hw events in user-space.
+ *
+ * u32 seq, time_mult, time_shift, idx, width;
+ * u64 count, enabled, running;
+ * u64 cyc, time_offset;
+ * s64 pmc = 0;
+ *
+ * do {
+ * seq = pc->lock;
+ * barrier()
+ *
+ * enabled = pc->time_enabled;
+ * running = pc->time_running;
+ *
+ * if (pc->cap_usr_time && enabled != running) {
+ * cyc = rdtsc();
+ * time_offset = pc->time_offset;
+ * time_mult = pc->time_mult;
+ * time_shift = pc->time_shift;
+ * }
+ *
+ * idx = pc->index;
+ * count = pc->offset;
+ * if (pc->cap_usr_rdpmc && idx) {
+ * width = pc->pmc_width;
+ * pmc = rdpmc(idx - 1);
+ * }
+ *
+ * barrier();
+ * } while (pc->lock != seq);
+ *
+ * NOTE: for obvious reason this only works on self-monitoring
+ * processes.
+ */
+ __u32 lock; /* seqlock for synchronization */
+ __u32 index; /* hardware event identifier */
+ __s64 offset; /* add to hardware event value */
+ __u64 time_enabled; /* time event active */
+ __u64 time_running; /* time event on cpu */
+ union {
+ __u64 capabilities;
+ struct {
+ __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */
+ cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */
+
+ cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */
+ cap_user_time : 1, /* The time_* fields are used */
+ cap_user_time_zero : 1, /* The time_zero field is used */
+ cap_____res : 59;
+ };
+ };
+
+ /*
+ * If cap_usr_rdpmc this field provides the bit-width of the value
+ * read using the rdpmc() or equivalent instruction. This can be used
+ * to sign extend the result like:
+ *
+ * pmc <<= 64 - width;
+ * pmc >>= 64 - width; // signed shift right
+ * count += pmc;
+ */
+ __u16 pmc_width;
+
+ /*
+ * If cap_usr_time the below fields can be used to compute the time
+ * delta since time_enabled (in ns) using rdtsc or similar.
+ *
+ * u64 quot, rem;
+ * u64 delta;
+ *
+ * quot = (cyc >> time_shift);
+ * rem = cyc & ((1 << time_shift) - 1);
+ * delta = time_offset + quot * time_mult +
+ * ((rem * time_mult) >> time_shift);
+ *
+ * Where time_offset,time_mult,time_shift and cyc are read in the
+ * seqcount loop described above. This delta can then be added to
+ * enabled and possible running (if idx), improving the scaling:
+ *
+ * enabled += delta;
+ * if (idx)
+ * running += delta;
+ *
+ * quot = count / running;
+ * rem = count % running;
+ * count = quot * enabled + (rem * enabled) / running;
+ */
+ __u16 time_shift;
+ __u32 time_mult;
+ __u64 time_offset;
+ /*
+ * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated
+ * from sample timestamps.
+ *
+ * time = timestamp - time_zero;
+ * quot = time / time_mult;
+ * rem = time % time_mult;
+ * cyc = (quot << time_shift) + (rem << time_shift) / time_mult;
+ *
+ * And vice versa:
+ *
+ * quot = cyc >> time_shift;
+ * rem = cyc & ((1 << time_shift) - 1);
+ * timestamp = time_zero + quot * time_mult +
+ * ((rem * time_mult) >> time_shift);
+ */
+ __u64 time_zero;
+ __u32 size; /* Header size up to __reserved[] fields. */
+
+ /*
+ * Hole for extension of the self monitor capabilities
+ */
+
+ __u8 __reserved[118*8+4]; /* align to 1k. */
+
+ /*
+ * Control data for the mmap() data buffer.
+ *
+ * User-space reading the @data_head value should issue an smp_rmb(),
+ * after reading this value.
+ *
+ * When the mapping is PROT_WRITE the @data_tail value should be
+ * written by userspace to reflect the last read data, after issueing
+ * an smp_mb() to separate the data read from the ->data_tail store.
+ * In this case the kernel will not over-write unread data.
+ *
+ * See perf_output_put_handle() for the data ordering.
+ */
+ __u64 data_head; /* head in the data section */
+ __u64 data_tail; /* user-space written tail */
+};
+
+#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0)
+#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0)
+#define PERF_RECORD_MISC_KERNEL (1 << 0)
+#define PERF_RECORD_MISC_USER (2 << 0)
+#define PERF_RECORD_MISC_HYPERVISOR (3 << 0)
+#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0)
+#define PERF_RECORD_MISC_GUEST_USER (5 << 0)
+
+#define PERF_RECORD_MISC_MMAP_DATA (1 << 13)
+/*
+ * Indicates that the content of PERF_SAMPLE_IP points to
+ * the actual instruction that triggered the event. See also
+ * perf_event_attr::precise_ip.
+ */
+#define PERF_RECORD_MISC_EXACT_IP (1 << 14)
+/*
+ * Reserve the last bit to indicate some extended misc field
+ */
+#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15)
+
+struct perf_event_header {
+ __u32 type;
+ __u16 misc;
+ __u16 size;
+};
+
+enum perf_event_type {
+
+ /*
+ * If perf_event_attr.sample_id_all is set then all event types will
+ * have the sample_type selected fields related to where/when
+ * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
+ * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
+ * just after the perf_event_header and the fields already present for
+ * the existing fields, i.e. at the end of the payload. That way a newer
+ * perf.data file will be supported by older perf tools, with these new
+ * optional fields being ignored.
+ *
+ * struct sample_id {
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * } && perf_event_attr::sample_id_all
+ *
+ * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The
+ * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
+ * relative to header.size.
+ */
+
+ /*
+ * The MMAP events record the PROT_EXEC mappings so that we can
+ * correlate userspace IPs to code. They have the following structure:
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_MMAP = 1,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 id;
+ * u64 lost;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_LOST = 2,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * char comm[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_COMM = 3,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_EXIT = 4,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 time;
+ * u64 id;
+ * u64 stream_id;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_THROTTLE = 5,
+ PERF_RECORD_UNTHROTTLE = 6,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_FORK = 7,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, tid;
+ *
+ * struct read_format values;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_READ = 8,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * #
+ * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
+ * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
+ * # is fixed relative to header.
+ * #
+ *
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * { u64 ip; } && PERF_SAMPLE_IP
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 addr; } && PERF_SAMPLE_ADDR
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 period; } && PERF_SAMPLE_PERIOD
+ *
+ * { struct read_format values; } && PERF_SAMPLE_READ
+ *
+ * { u64 nr,
+ * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ *
+ * #
+ * # The RAW record below is opaque data wrt the ABI
+ * #
+ * # That is, the ABI doesn't make any promises wrt to
+ * # the stability of its content, it may vary depending
+ * # on event, hardware, kernel version and phase of
+ * # the moon.
+ * #
+ * # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+ * #
+ *
+ * { u32 size;
+ * char data[size];}&& PERF_SAMPLE_RAW
+ *
+ * { u64 nr;
+ * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+ *
+ * { u64 abi; # enum perf_sample_regs_abi
+ * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+ *
+ * { u64 size;
+ * char data[size];
+ * u64 dyn_size; } && PERF_SAMPLE_STACK_USER
+ *
+ * { u64 weight; } && PERF_SAMPLE_WEIGHT
+ * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
+ * };
+ */
+ PERF_RECORD_SAMPLE = 9,
+
+ /*
+ * The MMAP2 records are an augmented version of MMAP, they add
+ * maj, min, ino numbers to be used to uniquely identify each mapping
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * u32 maj;
+ * u32 min;
+ * u64 ino;
+ * u64 ino_generation;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_MMAP2 = 10,
+
+ PERF_RECORD_MAX, /* non-ABI */
+};
+
+#define PERF_MAX_STACK_DEPTH 127
+
+enum perf_callchain_context {
+ PERF_CONTEXT_HV = (__u64)-32,
+ PERF_CONTEXT_KERNEL = (__u64)-128,
+ PERF_CONTEXT_USER = (__u64)-512,
+
+ PERF_CONTEXT_GUEST = (__u64)-2048,
+ PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176,
+ PERF_CONTEXT_GUEST_USER = (__u64)-2560,
+
+ PERF_CONTEXT_MAX = (__u64)-4095,
+};
+
+#define PERF_FLAG_FD_NO_GROUP (1U << 0)
+#define PERF_FLAG_FD_OUTPUT (1U << 1)
+#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */
+
+union perf_mem_data_src {
+ __u64 val;
+ struct {
+ __u64 mem_op:5, /* type of opcode */
+ mem_lvl:14, /* memory hierarchy level */
+ mem_snoop:5, /* snoop mode */
+ mem_lock:2, /* lock instr */
+ mem_dtlb:7, /* tlb access */
+ mem_rsvd:31;
+ };
+};
+
+/* type of opcode (load/store/prefetch,code) */
+#define PERF_MEM_OP_NA 0x01 /* not available */
+#define PERF_MEM_OP_LOAD 0x02 /* load instruction */
+#define PERF_MEM_OP_STORE 0x04 /* store instruction */
+#define PERF_MEM_OP_PFETCH 0x08 /* prefetch */
+#define PERF_MEM_OP_EXEC 0x10 /* code (execution) */
+#define PERF_MEM_OP_SHIFT 0
+
+/* memory hierarchy (memory level, hit or miss) */
+#define PERF_MEM_LVL_NA 0x01 /* not available */
+#define PERF_MEM_LVL_HIT 0x02 /* hit level */
+#define PERF_MEM_LVL_MISS 0x04 /* miss level */
+#define PERF_MEM_LVL_L1 0x08 /* L1 */
+#define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */
+#define PERF_MEM_LVL_L2 0x20 /* L2 */
+#define PERF_MEM_LVL_L3 0x40 /* L3 */
+#define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */
+#define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */
+#define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */
+#define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */
+#define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */
+#define PERF_MEM_LVL_IO 0x1000 /* I/O memory */
+#define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */
+#define PERF_MEM_LVL_SHIFT 5
+
+/* snoop mode */
+#define PERF_MEM_SNOOP_NA 0x01 /* not available */
+#define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */
+#define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */
+#define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */
+#define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */
+#define PERF_MEM_SNOOP_SHIFT 19
+
+/* locked instruction */
+#define PERF_MEM_LOCK_NA 0x01 /* not available */
+#define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */
+#define PERF_MEM_LOCK_SHIFT 24
+
+/* TLB access */
+#define PERF_MEM_TLB_NA 0x01 /* not available */
+#define PERF_MEM_TLB_HIT 0x02 /* hit level */
+#define PERF_MEM_TLB_MISS 0x04 /* miss level */
+#define PERF_MEM_TLB_L1 0x08 /* L1 */
+#define PERF_MEM_TLB_L2 0x10 /* L2 */
+#define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/
+#define PERF_MEM_TLB_OS 0x40 /* OS fault handler */
+#define PERF_MEM_TLB_SHIFT 26
+
+#define PERF_MEM_S(a, s) \
+ (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT)
+
+/*
+ * single taken branch record layout:
+ *
+ * from: source instruction (may not always be a branch insn)
+ * to: branch target
+ * mispred: branch target was mispredicted
+ * predicted: branch target was predicted
+ *
+ * support for mispred, predicted is optional. In case it
+ * is not supported mispred = predicted = 0.
+ *
+ * in_tx: running in a hardware transaction
+ * abort: aborting a hardware transaction
+ */
+struct perf_branch_entry {
+ __u64 from;
+ __u64 to;
+ __u64 mispred:1, /* target mispredicted */
+ predicted:1,/* target predicted */
+ in_tx:1, /* in transaction */
+ abort:1, /* transaction abort */
+ reserved:60;
+};
+
+#endif /* _LINUX_PERF_EVENT_H */
diff --git a/tools/gator/daemon/k/perf_event.h b/tools/gator/daemon/k/perf_event.h
new file mode 120000
index 000000000000..e5dff8c21ef4
--- /dev/null
+++ b/tools/gator/daemon/k/perf_event.h
@@ -0,0 +1 @@
+perf_event.3.12.h \ No newline at end of file
diff --git a/tools/gator/daemon/libsensors/COPYING.LGPL b/tools/gator/daemon/libsensors/COPYING.LGPL
new file mode 100644
index 000000000000..4362b49151d7
--- /dev/null
+++ b/tools/gator/daemon/libsensors/COPYING.LGPL
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/tools/gator/daemon/libsensors/access.c b/tools/gator/daemon/libsensors/access.c
new file mode 100644
index 000000000000..8e227e2550db
--- /dev/null
+++ b/tools/gator/daemon/libsensors/access.c
@@ -0,0 +1,561 @@
+/*
+ access.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "access.h"
+#include "sensors.h"
+#include "data.h"
+#include "error.h"
+#include "sysfs.h"
+
+/* We watch the recursion depth for variables only, as an easy way to
+ detect cycles. */
+#define DEPTH_MAX 8
+
+static int sensors_eval_expr(const sensors_chip_features *chip_features,
+ const sensors_expr *expr,
+ double val, int depth, double *result);
+
+/* Compare two chips name descriptions, to see whether they could match.
+ Return 0 if it does not match, return 1 if it does match. */
+static int sensors_match_chip(const sensors_chip_name *chip1,
+ const sensors_chip_name *chip2)
+{
+ if ((chip1->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) &&
+ (chip2->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) &&
+ strcmp(chip1->prefix, chip2->prefix))
+ return 0;
+
+ if ((chip1->bus.type != SENSORS_BUS_TYPE_ANY) &&
+ (chip2->bus.type != SENSORS_BUS_TYPE_ANY) &&
+ (chip1->bus.type != chip2->bus.type))
+ return 0;
+
+ if ((chip1->bus.nr != SENSORS_BUS_NR_ANY) &&
+ (chip2->bus.nr != SENSORS_BUS_NR_ANY) &&
+ (chip1->bus.nr != chip2->bus.nr))
+ return 0;
+
+ if ((chip1->addr != chip2->addr) &&
+ (chip1->addr != SENSORS_CHIP_NAME_ADDR_ANY) &&
+ (chip2->addr != SENSORS_CHIP_NAME_ADDR_ANY))
+ return 0;
+
+ return 1;
+}
+
+/* Returns, one by one, a pointer to all sensor_chip structs of the
+ config file which match with the given chip name. Last should be
+ the value returned by the last call, or NULL if this is the first
+ call. Returns NULL if no more matches are found. Do not modify
+ the struct the return value points to!
+ Note that this visits the list of chips from last to first. Usually,
+ you want the match that was latest in the config file. */
+static sensors_chip *
+sensors_for_all_config_chips(const sensors_chip_name *name,
+ const sensors_chip *last)
+{
+ int nr, i;
+ sensors_chip_name_list chips;
+
+ for (nr = last ? last - sensors_config_chips - 1 :
+ sensors_config_chips_count - 1; nr >= 0; nr--) {
+
+ chips = sensors_config_chips[nr].chips;
+ for (i = 0; i < chips.fits_count; i++) {
+ if (sensors_match_chip(&chips.fits[i], name))
+ return sensors_config_chips + nr;
+ }
+ }
+ return NULL;
+}
+
+/* Look up a chip in the intern chip list, and return a pointer to it.
+ Do not modify the struct the return value points to! Returns NULL if
+ not found.*/
+static const sensors_chip_features *
+sensors_lookup_chip(const sensors_chip_name *name)
+{
+ int i;
+
+ for (i = 0; i < sensors_proc_chips_count; i++)
+ if (sensors_match_chip(&sensors_proc_chips[i].chip, name))
+ return &sensors_proc_chips[i];
+
+ return NULL;
+}
+
+/* Look up a subfeature of the given chip, and return a pointer to it.
+ Do not modify the struct the return value points to! Returns NULL if
+ not found.*/
+static const sensors_subfeature *
+sensors_lookup_subfeature_nr(const sensors_chip_features *chip,
+ int subfeat_nr)
+{
+ if (subfeat_nr < 0 ||
+ subfeat_nr >= chip->subfeature_count)
+ return NULL;
+ return chip->subfeature + subfeat_nr;
+}
+
+/* Look up a feature of the given chip, and return a pointer to it.
+ Do not modify the struct the return value points to! Returns NULL if
+ not found.*/
+static const sensors_feature *
+sensors_lookup_feature_nr(const sensors_chip_features *chip, int feat_nr)
+{
+ if (feat_nr < 0 ||
+ feat_nr >= chip->feature_count)
+ return NULL;
+ return chip->feature + feat_nr;
+}
+
+/* Look up a subfeature by name, and return a pointer to it.
+ Do not modify the struct the return value points to! Returns NULL if
+ not found.*/
+static const sensors_subfeature *
+sensors_lookup_subfeature_name(const sensors_chip_features *chip,
+ const char *name)
+{
+ int j;
+
+ for (j = 0; j < chip->subfeature_count; j++)
+ if (!strcmp(chip->subfeature[j].name, name))
+ return chip->subfeature + j;
+ return NULL;
+}
+
+/* Check whether the chip name is an 'absolute' name, which can only match
+ one chip, or whether it has wildcards. Returns 0 if it is absolute, 1
+ if there are wildcards. */
+int sensors_chip_name_has_wildcards(const sensors_chip_name *chip)
+{
+ if ((chip->prefix == SENSORS_CHIP_NAME_PREFIX_ANY) ||
+ (chip->bus.type == SENSORS_BUS_TYPE_ANY) ||
+ (chip->bus.nr == SENSORS_BUS_NR_ANY) ||
+ (chip->addr == SENSORS_CHIP_NAME_ADDR_ANY))
+ return 1;
+ else
+ return 0;
+}
+
+/* Look up the label for a given feature. Note that chip should not
+ contain wildcard values! The returned string is newly allocated (free it
+ yourself). On failure, NULL is returned.
+ If no label exists for this feature, its name is returned itself. */
+char *sensors_get_label(const sensors_chip_name *name,
+ const sensors_feature *feature)
+{
+ char *label;
+ const sensors_chip *chip;
+ char buf[PATH_MAX];
+ FILE *f;
+ int i;
+
+ if (sensors_chip_name_has_wildcards(name))
+ return NULL;
+
+ for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));)
+ for (i = 0; i < chip->labels_count; i++)
+ if (!strcmp(feature->name, chip->labels[i].name)) {
+ label = chip->labels[i].value;
+ goto sensors_get_label_exit;
+ }
+
+ /* No user specified label, check for a _label sysfs file */
+ snprintf(buf, PATH_MAX, "%s/%s_label", name->path, feature->name);
+
+ if ((f = fopen(buf, "r"))) {
+ i = fread(buf, 1, sizeof(buf), f);
+ fclose(f);
+ if (i > 0) {
+ /* i - 1 to strip the '\n' at the end */
+ buf[i - 1] = 0;
+ label = buf;
+ goto sensors_get_label_exit;
+ }
+ }
+
+ /* No label, return the feature name instead */
+ label = feature->name;
+
+sensors_get_label_exit:
+ label = strdup(label);
+ if (!label)
+ sensors_fatal_error(__func__, "Allocating label text");
+ return label;
+}
+
+/* Looks up whether a feature should be ignored. Returns
+ 1 if it should be ignored, 0 if not. */
+static int sensors_get_ignored(const sensors_chip_name *name,
+ const sensors_feature *feature)
+{
+ const sensors_chip *chip;
+ int i;
+
+ for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));)
+ for (i = 0; i < chip->ignores_count; i++)
+ if (!strcmp(feature->name, chip->ignores[i].name))
+ return 1;
+ return 0;
+}
+
+/* Read the value of a subfeature of a certain chip. Note that chip should not
+ contain wildcard values! This function will return 0 on success, and <0
+ on failure. */
+static int __sensors_get_value(const sensors_chip_name *name, int subfeat_nr,
+ int depth, double *result)
+{
+ const sensors_chip_features *chip_features;
+ const sensors_subfeature *subfeature;
+ const sensors_expr *expr = NULL;
+ double val;
+ int res, i;
+
+ if (depth >= DEPTH_MAX)
+ return -SENSORS_ERR_RECURSION;
+ if (sensors_chip_name_has_wildcards(name))
+ return -SENSORS_ERR_WILDCARDS;
+ if (!(chip_features = sensors_lookup_chip(name)))
+ return -SENSORS_ERR_NO_ENTRY;
+ if (!(subfeature = sensors_lookup_subfeature_nr(chip_features,
+ subfeat_nr)))
+ return -SENSORS_ERR_NO_ENTRY;
+ if (!(subfeature->flags & SENSORS_MODE_R))
+ return -SENSORS_ERR_ACCESS_R;
+
+ /* Apply compute statement if it exists */
+ if (subfeature->flags & SENSORS_COMPUTE_MAPPING) {
+ const sensors_feature *feature;
+ const sensors_chip *chip;
+
+ feature = sensors_lookup_feature_nr(chip_features,
+ subfeature->mapping);
+
+ chip = NULL;
+ while (!expr &&
+ (chip = sensors_for_all_config_chips(name, chip)))
+ for (i = 0; i < chip->computes_count; i++) {
+ if (!strcmp(feature->name,
+ chip->computes[i].name)) {
+ expr = chip->computes[i].from_proc;
+ break;
+ }
+ }
+ }
+
+ res = sensors_read_sysfs_attr(name, subfeature, &val);
+ if (res)
+ return res;
+ if (!expr)
+ *result = val;
+ else if ((res = sensors_eval_expr(chip_features, expr, val, depth,
+ result)))
+ return res;
+ return 0;
+}
+
+int sensors_get_value(const sensors_chip_name *name, int subfeat_nr,
+ double *result)
+{
+ return __sensors_get_value(name, subfeat_nr, 0, result);
+}
+
+/* Set the value of a subfeature of a certain chip. Note that chip should not
+ contain wildcard values! This function will return 0 on success, and <0
+ on failure. */
+int sensors_set_value(const sensors_chip_name *name, int subfeat_nr,
+ double value)
+{
+ const sensors_chip_features *chip_features;
+ const sensors_subfeature *subfeature;
+ const sensors_expr *expr = NULL;
+ int i, res;
+ double to_write;
+
+ if (sensors_chip_name_has_wildcards(name))
+ return -SENSORS_ERR_WILDCARDS;
+ if (!(chip_features = sensors_lookup_chip(name)))
+ return -SENSORS_ERR_NO_ENTRY;
+ if (!(subfeature = sensors_lookup_subfeature_nr(chip_features,
+ subfeat_nr)))
+ return -SENSORS_ERR_NO_ENTRY;
+ if (!(subfeature->flags & SENSORS_MODE_W))
+ return -SENSORS_ERR_ACCESS_W;
+
+ /* Apply compute statement if it exists */
+ if (subfeature->flags & SENSORS_COMPUTE_MAPPING) {
+ const sensors_feature *feature;
+ const sensors_chip *chip;
+
+ feature = sensors_lookup_feature_nr(chip_features,
+ subfeature->mapping);
+
+ chip = NULL;
+ while (!expr &&
+ (chip = sensors_for_all_config_chips(name, chip)))
+ for (i = 0; i < chip->computes_count; i++) {
+ if (!strcmp(feature->name,
+ chip->computes[i].name)) {
+ expr = chip->computes[i].to_proc;
+ break;
+ }
+ }
+ }
+
+ to_write = value;
+ if (expr)
+ if ((res = sensors_eval_expr(chip_features, expr,
+ value, 0, &to_write)))
+ return res;
+ return sensors_write_sysfs_attr(name, subfeature, to_write);
+}
+
+const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name
+ *match, int *nr)
+{
+ const sensors_chip_name *res;
+
+ while (*nr < sensors_proc_chips_count) {
+ res = &sensors_proc_chips[(*nr)++].chip;
+ if (!match || sensors_match_chip(res, match))
+ return res;
+ }
+ return NULL;
+}
+
+const char *sensors_get_adapter_name(const sensors_bus_id *bus)
+{
+ int i;
+
+ /* bus types with a single instance */
+ switch (bus->type) {
+ case SENSORS_BUS_TYPE_ISA:
+ return "ISA adapter";
+ case SENSORS_BUS_TYPE_PCI:
+ return "PCI adapter";
+ /* SPI should not be here, but for now SPI adapters have no name
+ so we don't have any custom string to return. */
+ case SENSORS_BUS_TYPE_SPI:
+ return "SPI adapter";
+ case SENSORS_BUS_TYPE_VIRTUAL:
+ return "Virtual device";
+ case SENSORS_BUS_TYPE_ACPI:
+ return "ACPI interface";
+ /* HID should probably not be there either, but I don't know if
+ HID buses have a name nor where to find it. */
+ case SENSORS_BUS_TYPE_HID:
+ return "HID adapter";
+ }
+
+ /* bus types with several instances */
+ for (i = 0; i < sensors_proc_bus_count; i++)
+ if (sensors_proc_bus[i].bus.type == bus->type &&
+ sensors_proc_bus[i].bus.nr == bus->nr)
+ return sensors_proc_bus[i].adapter;
+ return NULL;
+}
+
+const sensors_feature *
+sensors_get_features(const sensors_chip_name *name, int *nr)
+{
+ const sensors_chip_features *chip;
+
+ if (!(chip = sensors_lookup_chip(name)))
+ return NULL; /* No such chip */
+
+ while (*nr < chip->feature_count
+ && sensors_get_ignored(name, &chip->feature[*nr]))
+ (*nr)++;
+ if (*nr >= chip->feature_count)
+ return NULL;
+ return &chip->feature[(*nr)++];
+}
+
+const sensors_subfeature *
+sensors_get_all_subfeatures(const sensors_chip_name *name,
+ const sensors_feature *feature, int *nr)
+{
+ const sensors_chip_features *chip;
+ const sensors_subfeature *subfeature;
+
+ if (!(chip = sensors_lookup_chip(name)))
+ return NULL; /* No such chip */
+
+ /* Seek directly to the first subfeature */
+ if (*nr < feature->first_subfeature)
+ *nr = feature->first_subfeature;
+
+ if (*nr >= chip->subfeature_count)
+ return NULL; /* end of list */
+ subfeature = &chip->subfeature[(*nr)++];
+ if (subfeature->mapping == feature->number)
+ return subfeature;
+ return NULL; /* end of subfeature list */
+}
+
+const sensors_subfeature *
+sensors_get_subfeature(const sensors_chip_name *name,
+ const sensors_feature *feature,
+ sensors_subfeature_type type)
+{
+ const sensors_chip_features *chip;
+ int i;
+
+ if (!(chip = sensors_lookup_chip(name)))
+ return NULL; /* No such chip */
+
+ for (i = feature->first_subfeature; i < chip->subfeature_count &&
+ chip->subfeature[i].mapping == feature->number; i++) {
+ if (chip->subfeature[i].type == type)
+ return &chip->subfeature[i];
+ }
+ return NULL; /* No such subfeature */
+}
+
+/* Evaluate an expression */
+int sensors_eval_expr(const sensors_chip_features *chip_features,
+ const sensors_expr *expr,
+ double val, int depth, double *result)
+{
+ double res1, res2;
+ int res;
+ const sensors_subfeature *subfeature;
+
+ if (expr->kind == sensors_kind_val) {
+ *result = expr->data.val;
+ return 0;
+ }
+ if (expr->kind == sensors_kind_source) {
+ *result = val;
+ return 0;
+ }
+ if (expr->kind == sensors_kind_var) {
+ if (!(subfeature = sensors_lookup_subfeature_name(chip_features,
+ expr->data.var)))
+ return -SENSORS_ERR_NO_ENTRY;
+ return __sensors_get_value(&chip_features->chip,
+ subfeature->number, depth + 1,
+ result);
+ }
+ if ((res = sensors_eval_expr(chip_features, expr->data.subexpr.sub1,
+ val, depth, &res1)))
+ return res;
+ if (expr->data.subexpr.sub2 &&
+ (res = sensors_eval_expr(chip_features, expr->data.subexpr.sub2,
+ val, depth, &res2)))
+ return res;
+ switch (expr->data.subexpr.op) {
+ case sensors_add:
+ *result = res1 + res2;
+ return 0;
+ case sensors_sub:
+ *result = res1 - res2;
+ return 0;
+ case sensors_multiply:
+ *result = res1 * res2;
+ return 0;
+ case sensors_divide:
+ if (res2 == 0.0)
+ return -SENSORS_ERR_DIV_ZERO;
+ *result = res1 / res2;
+ return 0;
+ case sensors_negate:
+ *result = -res1;
+ return 0;
+ case sensors_exp:
+ *result = exp(res1);
+ return 0;
+ case sensors_log:
+ if (res1 < 0.0)
+ return -SENSORS_ERR_DIV_ZERO;
+ *result = log(res1);
+ return 0;
+ }
+ return 0;
+}
+
+/* Execute all set statements for this particular chip. The chip may not
+ contain wildcards! This function will return 0 on success, and <0 on
+ failure. */
+static int sensors_do_this_chip_sets(const sensors_chip_name *name)
+{
+ const sensors_chip_features *chip_features;
+ sensors_chip *chip;
+ double value;
+ int i;
+ int err = 0, res;
+ const sensors_subfeature *subfeature;
+
+ chip_features = sensors_lookup_chip(name); /* Can't fail */
+
+ for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));)
+ for (i = 0; i < chip->sets_count; i++) {
+ subfeature = sensors_lookup_subfeature_name(chip_features,
+ chip->sets[i].name);
+ if (!subfeature) {
+ sensors_parse_error_wfn("Unknown feature name",
+ chip->sets[i].line.filename,
+ chip->sets[i].line.lineno);
+ err = -SENSORS_ERR_NO_ENTRY;
+ continue;
+ }
+
+ res = sensors_eval_expr(chip_features,
+ chip->sets[i].value, 0,
+ 0, &value);
+ if (res) {
+ sensors_parse_error_wfn("Error parsing expression",
+ chip->sets[i].line.filename,
+ chip->sets[i].line.lineno);
+ err = res;
+ continue;
+ }
+ if ((res = sensors_set_value(name, subfeature->number,
+ value))) {
+ sensors_parse_error_wfn("Failed to set value",
+ chip->sets[i].line.filename,
+ chip->sets[i].line.lineno);
+ err = res;
+ continue;
+ }
+ }
+ return err;
+}
+
+/* Execute all set statements for this particular chip. The chip may contain
+ wildcards! This function will return 0 on success, and <0 on failure. */
+int sensors_do_chip_sets(const sensors_chip_name *name)
+{
+ int nr, this_res;
+ const sensors_chip_name *found_name;
+ int res = 0;
+
+ for (nr = 0; (found_name = sensors_get_detected_chips(name, &nr));) {
+ this_res = sensors_do_this_chip_sets(found_name);
+ if (this_res)
+ res = this_res;
+ }
+ return res;
+}
diff --git a/tools/gator/daemon/libsensors/access.h b/tools/gator/daemon/libsensors/access.h
new file mode 100644
index 000000000000..1d3784340757
--- /dev/null
+++ b/tools/gator/daemon/libsensors/access.h
@@ -0,0 +1,33 @@
+/*
+ access.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 LIB_SENSORS_ACCESS_H
+#define LIB_SENSORS_ACCESS_H
+
+#include "sensors.h"
+#include "data.h"
+
+/* Check whether the chip name is an 'absolute' name, which can only match
+ one chip, or whether it has wildcards. Returns 0 if it is absolute, 1
+ if there are wildcards. */
+int sensors_chip_name_has_wildcards(const sensors_chip_name *chip);
+
+#endif /* def LIB_SENSORS_ACCESS_H */
diff --git a/tools/gator/daemon/libsensors/conf-lex.c b/tools/gator/daemon/libsensors/conf-lex.c
new file mode 100644
index 000000000000..a54664b3d77b
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-lex.c
@@ -0,0 +1,2881 @@
+
+#line 3 "<stdout>"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define yy_create_buffer sensors_yy_create_buffer
+#define yy_delete_buffer sensors_yy_delete_buffer
+#define yy_flex_debug sensors_yy_flex_debug
+#define yy_init_buffer sensors_yy_init_buffer
+#define yy_flush_buffer sensors_yy_flush_buffer
+#define yy_load_buffer_state sensors_yy_load_buffer_state
+#define yy_switch_to_buffer sensors_yy_switch_to_buffer
+#define yyin sensors_yyin
+#define yyleng sensors_yyleng
+#define yylex sensors_yylex
+#define yylineno sensors_yylineno
+#define yyout sensors_yyout
+#define yyrestart sensors_yyrestart
+#define yytext sensors_yytext
+#define yywrap sensors_yywrap
+#define yyalloc sensors_yyalloc
+#define yyrealloc sensors_yyrealloc
+#define yyfree sensors_yyfree
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE sensors_yyrestart(sensors_yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int sensors_yyleng;
+
+extern FILE *sensors_yyin, *sensors_yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up sensors_yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up sensors_yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via sensors_yyrestart()), so that the user can continue scanning by
+ * just pointing sensors_yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when sensors_yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int sensors_yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow sensors_yywrap()'s to do buffer switches
+ * instead of setting up a fresh sensors_yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void sensors_yyrestart (FILE *input_file );
+void sensors_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE sensors_yy_create_buffer (FILE *file,int size );
+void sensors_yy_delete_buffer (YY_BUFFER_STATE b );
+void sensors_yy_flush_buffer (YY_BUFFER_STATE b );
+void sensors_yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void sensors_yypop_buffer_state (void );
+
+static void sensors_yyensure_buffer_stack (void );
+static void sensors_yy_load_buffer_state (void );
+static void sensors_yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER sensors_yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE sensors_yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE sensors_yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE sensors_yy_scan_bytes (yyconst char *bytes,int len );
+
+void *sensors_yyalloc (yy_size_t );
+void *sensors_yyrealloc (void *,yy_size_t );
+void sensors_yyfree (void * );
+
+#define yy_new_buffer sensors_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ sensors_yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ sensors_yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define sensors_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *sensors_yyin = (FILE *) 0, *sensors_yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int sensors_yylineno;
+
+int sensors_yylineno = 1;
+
+extern char *sensors_yytext;
+#define yytext_ptr sensors_yytext
+static yyconst flex_int16_t yy_nxt[][39] =
+ {
+ {
+ 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, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
+ },
+
+ {
+ 9, 10, 11, 12, 10, 13, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 14, 15, 16, 14, 14, 14, 14, 14, 17, 18,
+ 14, 14, 14, 14, 14, 19, 14, 14, 14
+ },
+
+ {
+ 9, 10, 11, 12, 10, 13, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+
+ 14, 15, 16, 14, 14, 14, 14, 14, 17, 18,
+ 14, 14, 14, 14, 14, 19, 14, 14, 14
+ },
+
+ {
+ 9, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35
+ },
+
+ {
+ 9, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35
+
+ },
+
+ {
+ 9, 39, 39, 40, 41, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 42, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39
+ },
+
+ {
+ 9, 39, 39, 40, 41, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 42, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39
+ },
+
+ {
+ 9, 43, 43, 44, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43
+ },
+
+ {
+ 9, 43, 43, 44, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43
+ },
+
+ {
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9, -9, -9
+
+ },
+
+ {
+ 9, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
+ -10, -10, -10, -10, -10, -10, -10, -10, -10
+ },
+
+ {
+ 9, -11, 45, 46, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11, -11, -11
+ },
+
+ {
+ 9, -12, -12, -12, -12, -12, -12, -12, -12, -12,
+ -12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
+
+ -12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
+ -12, -12, -12, -12, -12, -12, -12, -12, -12
+ },
+
+ {
+ 9, 47, 47, 48, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47
+ },
+
+ {
+ 9, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ -14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -15, -15, -15, -15, -15, -15, -15, -15, -15,
+ -15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 50, 49
+ },
+
+ {
+ 9, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ 49, 49, 49, 49, 49, 49, 49, 51, 49, 49,
+ 49, 49, 52, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+
+ 49, 49, 49, 49, 49, 49, 53, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ 54, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ -19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
+ 49, 49, 49, 49, 55, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20
+ },
+
+ {
+ 9, -21, 56, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
+ -21, -21, -21, -21, -21, -21, -21, -21, -21
+ },
+
+ {
+ 9, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+
+ -22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
+ -22, -22, -22, -22, -22, -22, -22, -22, -22
+ },
+
+ {
+ 9, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23
+ },
+
+ {
+ 9, 57, 57, 58, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57
+
+ },
+
+ {
+ 9, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -25, -25, -25, -25
+ },
+
+ {
+ 9, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26, -26, -26
+ },
+
+ {
+ 9, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, -27
+ },
+
+ {
+ 9, -28, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, -28, -28, -28, -28, -28, -28, -28, -28
+ },
+
+ {
+ 9, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29
+
+ },
+
+ {
+ 9, -30, -30, -30, -30, -30, -30, -30, -30, -30,
+ -30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
+ -30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
+ -30, -30, -30, -30, -30, -30, -30, -30, -30
+ },
+
+ {
+ 9, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, 59, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31, -31, -31
+ },
+
+ {
+ 9, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32, -32, -32
+ },
+
+ {
+ 9, -33, -33, -33, -33, -33, -33, -33, -33, -33,
+ -33, -33, 60, -33, 61, -33, 62, -33, -33, -33,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62
+ },
+
+ {
+ 9, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, -34, -34, -34, -34, -34, -34, -34, -34
+
+ },
+
+ {
+ 9, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, 62, -35, 62, -35, -35, -35,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62
+ },
+
+ {
+ 9, -36, 63, 64, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36, -36, -36
+ },
+
+ {
+ 9, -37, -37, -37, -37, -37, -37, -37, -37, -37,
+ -37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
+
+ -37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
+ -37, -37, -37, -37, -37, -37, -37, -37, -37
+ },
+
+ {
+ 9, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, -38, -38, -38
+ },
+
+ {
+ 9, 65, 65, -39, -39, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, -39, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65
+
+ },
+
+ {
+ 9, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40, -40, -40
+ },
+
+ {
+ 9, -41, -41, -41, 66, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
+ -41, -41, -41, -41, -41, -41, -41, -41, -41
+ },
+
+ {
+ 9, 67, 67, 68, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+
+ 69, 70, 67, 67, 67, 71, 67, 67, 67, 67,
+ 67, 72, 67, 67, 73, 67, 74, 67, 75
+ },
+
+ {
+ 9, 76, 76, -43, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76
+ },
+
+ {
+ 9, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, -44, -44, -44, -44, -44, -44
+
+ },
+
+ {
+ 9, -45, 45, 46, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -45, -45
+ },
+
+ {
+ 9, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46, -46, -46
+ },
+
+ {
+ 9, 47, 47, 48, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47
+ },
+
+ {
+ 9, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48, -48, -48
+ },
+
+ {
+ 9, -49, -49, -49, -49, -49, -49, -49, -49, -49,
+ -49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 77, 49, 49, 49
+ },
+
+ {
+ 9, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
+ 49, 49, 49, 49, 49, 49, 49, 49, 78, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -52, -52, -52, -52, -52, -52, -52, -52, -52,
+ -52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 79, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 80, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -54, -54, -54, -54, -54, -54, -54, -54, -54,
+ -54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
+ 49, 81, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -55, -55, -55, -55, -55, -55, -55, -55, -55,
+ -55, -55, -55, -55, -55, -55, -55, -55, -55, -55,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 82, 49, 49
+ },
+
+ {
+ 9, -56, 56, -56, -56, -56, -56, -56, -56, -56,
+ -56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
+ -56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
+ -56, -56, -56, -56, -56, -56, -56, -56, -56
+ },
+
+ {
+ 9, 57, 57, 58, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57
+ },
+
+ {
+ 9, -58, -58, -58, -58, -58, -58, -58, -58, -58,
+ -58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
+ -58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
+ -58, -58, -58, -58, -58, -58, -58, -58, -58
+ },
+
+ {
+ 9, -59, -59, -59, -59, -59, -59, -59, -59, -59,
+ -59, -59, -59, -59, 59, -59, -59, -59, -59, -59,
+ -59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
+ -59, -59, -59, -59, -59, -59, -59, -59, -59
+
+ },
+
+ {
+ 9, -60, -60, -60, -60, -60, -60, -60, -60, -60,
+ -60, -60, -60, -60, 59, -60, -60, -60, -60, -60,
+ -60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
+ -60, -60, -60, -60, -60, -60, -60, -60, -60
+ },
+
+ {
+ 9, -61, -61, -61, -61, -61, -61, -61, -61, -61,
+ -61, -61, 60, -61, 61, -61, 62, -61, -61, -61,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62
+ },
+
+ {
+ 9, -62, -62, -62, -62, -62, -62, -62, -62, -62,
+ -62, -62, -62, -62, 62, -62, 62, -62, -62, -62,
+
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62
+ },
+
+ {
+ 9, -63, 63, 64, -63, -63, -63, -63, -63, -63,
+ -63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
+ -63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
+ -63, -63, -63, -63, -63, -63, -63, -63, -63
+ },
+
+ {
+ 9, -64, -64, -64, -64, -64, -64, -64, -64, -64,
+ -64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
+ -64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
+ -64, -64, -64, -64, -64, -64, -64, -64, -64
+
+ },
+
+ {
+ 9, 65, 65, -65, -65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, -65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65
+ },
+
+ {
+ 9, -66, -66, -66, -66, -66, -66, -66, -66, -66,
+ -66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
+ -66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
+ -66, -66, -66, -66, -66, -66, -66, -66, -66
+ },
+
+ {
+ 9, -67, -67, -67, -67, -67, -67, -67, -67, -67,
+ -67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
+
+ -67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
+ -67, -67, -67, -67, -67, -67, -67, -67, -67
+ },
+
+ {
+ 9, -68, -68, -68, -68, -68, -68, -68, -68, -68,
+ -68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
+ -68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
+ -68, -68, -68, -68, -68, -68, -68, -68, -68
+ },
+
+ {
+ 9, -69, -69, -69, -69, -69, -69, -69, -69, -69,
+ -69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
+ -69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
+ -69, -69, -69, -69, -69, -69, -69, -69, -69
+
+ },
+
+ {
+ 9, -70, -70, -70, -70, -70, -70, -70, -70, -70,
+ -70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
+ -70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
+ -70, -70, -70, -70, -70, -70, -70, -70, -70
+ },
+
+ {
+ 9, -71, -71, -71, -71, -71, -71, -71, -71, -71,
+ -71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
+ -71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
+ -71, -71, -71, -71, -71, -71, -71, -71, -71
+ },
+
+ {
+ 9, -72, -72, -72, -72, -72, -72, -72, -72, -72,
+ -72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
+
+ -72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
+ -72, -72, -72, -72, -72, -72, -72, -72, -72
+ },
+
+ {
+ 9, -73, -73, -73, -73, -73, -73, -73, -73, -73,
+ -73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
+ -73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
+ -73, -73, -73, -73, -73, -73, -73, -73, -73
+ },
+
+ {
+ 9, -74, -74, -74, -74, -74, -74, -74, -74, -74,
+ -74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
+ -74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
+ -74, -74, -74, -74, -74, -74, -74, -74, -74
+
+ },
+
+ {
+ 9, -75, -75, -75, -75, -75, -75, -75, -75, -75,
+ -75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
+ -75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
+ -75, -75, -75, -75, -75, -75, -75, -75, -75
+ },
+
+ {
+ 9, 76, 76, -76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 76, 76
+ },
+
+ {
+ 9, -77, 83, -77, -77, -77, -77, -77, -77, -77,
+ -77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 84, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -79, -79, -79, -79, -79, -79, -79, -79, -79,
+ -79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 85, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -80, -80, -80, -80, -80, -80, -80, -80, -80,
+ -80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 86, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -81, -81, -81, -81, -81, -81, -81, -81, -81,
+ -81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
+ 49, 49, 49, 49, 87, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -82, 88, -82, -82, -82, -82, -82, -82, -82,
+ -82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -83, 83, -83, -83, -83, -83, -83, -83, -83,
+ -83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
+ -83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
+ -83, -83, -83, -83, -83, -83, -83, -83, -83
+ },
+
+ {
+ 9, -84, 89, -84, -84, -84, -84, -84, -84, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -85, -85, -85, -85, -85, -85, -85, -85, -85,
+ -85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 90, 49
+ },
+
+ {
+ 9, -86, -86, -86, -86, -86, -86, -86, -86, -86,
+ -86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 91, 49, 49, 49, 49
+ },
+
+ {
+ 9, -87, -87, -87, -87, -87, -87, -87, -87, -87,
+ -87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 92,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -88, 88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88
+ },
+
+ {
+ 9, -89, 89, -89, -89, -89, -89, -89, -89, -89,
+ -89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
+ -89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
+ -89, -89, -89, -89, -89, -89, -89, -89, -89
+
+ },
+
+ {
+ 9, -90, -90, -90, -90, -90, -90, -90, -90, -90,
+ -90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 93, 49, 49
+ },
+
+ {
+ 9, -91, -91, -91, -91, -91, -91, -91, -91, -91,
+ -91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
+ 49, 49, 49, 49, 94, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -92, 95, -92, -92, -92, -92, -92, -92, -92,
+ -92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
+
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -93, -93, -93, -93, -93, -93, -93, -93, -93,
+ -93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
+ 49, 49, 49, 49, 96, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -94, 97, -94, -94, -94, -94, -94, -94, -94,
+ -94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+
+ },
+
+ {
+ 9, -95, 95, -95, -95, -95, -95, -95, -95, -95,
+ -95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
+ -95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
+ -95, -95, -95, -95, -95, -95, -95, -95, -95
+ },
+
+ {
+ 9, -96, 98, -96, -96, -96, -96, -96, -96, -96,
+ -96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49
+ },
+
+ {
+ 9, -97, 97, -97, -97, -97, -97, -97, -97, -97,
+ -97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
+
+ -97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
+ -97, -97, -97, -97, -97, -97, -97, -97, -97
+ },
+
+ {
+ 9, -98, 98, -98, -98, -98, -98, -98, -98, -98,
+ -98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
+ -98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
+ -98, -98, -98, -98, -98, -98, -98, -98, -98
+ },
+
+ } ;
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up sensors_yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ sensors_yyleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 50
+#define YY_END_OF_BUFFER 51
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[99] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 13, 13, 51, 12,
+ 1, 2, 3, 11, 11, 11, 11, 11, 11, 33,
+ 15, 16, 31, 18, 25, 26, 23, 21, 27, 22,
+ 33, 24, 20, 28, 32, 33, 29, 30, 49, 36,
+ 39, 48, 13, 14, 1, 2, 3, 4, 11, 11,
+ 11, 11, 11, 11, 11, 15, 18, 19, 20, 34,
+ 20, 32, 35, 17, 49, 38, 47, 37, 40, 41,
+ 42, 43, 44, 45, 46, 13, 8, 11, 11, 11,
+ 11, 6, 8, 9, 11, 11, 11, 6, 9, 11,
+ 11, 5, 11, 10, 5, 7, 10, 7
+
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 4, 5, 1, 1, 1, 1, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 1, 1, 1,
+ 1, 1, 1, 15, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 1, 17, 1, 18, 16, 19, 20, 21, 22, 23,
+
+ 24, 25, 26, 27, 28, 23, 23, 29, 30, 31,
+ 32, 33, 23, 34, 35, 36, 37, 38, 23, 23,
+ 23, 23, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+extern int sensors_yy_flex_debug;
+int sensors_yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *sensors_yytext;
+#line 1 "lib/conf-lex.l"
+#line 2 "lib/conf-lex.l"
+/*
+ conf-lex.l - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "general.h"
+#include "data.h"
+#include "conf-parse.h"
+#include "error.h"
+#include "scanner.h"
+
+static int buffer_count;
+static int buffer_max;
+static char *buffer;
+
+char sensors_lex_error[100];
+
+const char *sensors_yyfilename;
+int sensors_yylineno;
+
+#define buffer_malloc() sensors_malloc_array(&buffer,&buffer_count,\
+ &buffer_max,1)
+#define buffer_free() sensors_free_array(&buffer,&buffer_count,\
+ &buffer_max)
+#define buffer_add_char(c) sensors_add_array_el(c,&buffer,\
+ &buffer_count,\
+ &buffer_max,1)
+#define buffer_add_string(s) sensors_add_array_els(s,strlen(s),\
+ &buffer, \
+ &buffer_count,&buffer_max,1)
+
+/* Scanner for configuration files */
+/* All states are exclusive */
+
+
+
+/* Any whitespace-like character */
+/* Note: `10', `10.4' and `.4' are valid, `10.' is not */
+/* Only positive whole numbers are recognized here */
+#line 1255 "<stdout>"
+
+#define INITIAL 0
+#define MIDDLE 1
+#define STRING 2
+#define ERR 3
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int sensors_yylex_destroy (void );
+
+int sensors_yyget_debug (void );
+
+void sensors_yyset_debug (int debug_flag );
+
+YY_EXTRA_TYPE sensors_yyget_extra (void );
+
+void sensors_yyset_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *sensors_yyget_in (void );
+
+void sensors_yyset_in (FILE * in_str );
+
+FILE *sensors_yyget_out (void );
+
+void sensors_yyset_out (FILE * out_str );
+
+int sensors_yyget_leng (void );
+
+char *sensors_yyget_text (void );
+
+int sensors_yyget_lineno (void );
+
+void sensors_yyset_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int sensors_yywrap (void );
+#else
+extern int sensors_yywrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( sensors_yytext, sensors_yyleng, 1, sensors_yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( sensors_yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( sensors_yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, sensors_yyin))==0 && ferror(sensors_yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(sensors_yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int sensors_yylex (void);
+
+#define YY_DECL int sensors_yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after sensors_yytext and sensors_yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 80 "lib/conf-lex.l"
+
+
+ /*
+ * STATE: INITIAL
+ */
+
+#line 1450 "<stdout>"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! sensors_yyin )
+ sensors_yyin = stdin;
+
+ if ( ! sensors_yyout )
+ sensors_yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ sensors_yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE );
+ }
+
+ sensors_yy_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of sensors_yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 )
+ ++yy_cp;
+
+ yy_current_state = -yy_current_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+
+case YY_STATE_EOF(INITIAL):
+#line 88 "lib/conf-lex.l"
+{ /* EOF from this state terminates */
+ return 0;
+ }
+ YY_BREAK
+case 1:
+YY_RULE_SETUP
+#line 92 "lib/conf-lex.l"
+; /* eat as many blanks as possible at once */
+ YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 94 "lib/conf-lex.l"
+{ /* eat a bare newline (possibly preceded by blanks) */
+ sensors_yylineno++;
+ }
+ YY_BREAK
+/* comments */
+case 3:
+YY_RULE_SETUP
+#line 100 "lib/conf-lex.l"
+; /* eat the rest of the line after comment char */
+ YY_BREAK
+case 4:
+/* rule 4 can match eol */
+YY_RULE_SETUP
+#line 102 "lib/conf-lex.l"
+{ /* eat the rest of the line after comment char */
+ sensors_yylineno++;
+ }
+ YY_BREAK
+/*
+ * Keywords must be followed by whitespace - eat that too.
+ * If there isn't trailing whitespace, we still need to
+ * accept it as lexically correct (even though the parser
+ * will reject it anyway.)
+ */
+case 5:
+YY_RULE_SETUP
+#line 113 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return LABEL;
+ }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 120 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return SET;
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 127 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return COMPUTE;
+ }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 134 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return BUS;
+ }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 141 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return CHIP;
+ }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 148 "lib/conf-lex.l"
+{
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return IGNORE;
+ }
+ YY_BREAK
+/* Anything else at the beginning of a line is an error */
+case 11:
+#line 158 "lib/conf-lex.l"
+case 12:
+YY_RULE_SETUP
+#line 158 "lib/conf-lex.l"
+{
+ BEGIN(ERR);
+ strcpy(sensors_lex_error,"Invalid keyword");
+ return ERROR;
+ }
+ YY_BREAK
+
+/*
+ * STATE: ERROR
+ */
+
+case 13:
+YY_RULE_SETUP
+#line 171 "lib/conf-lex.l"
+; /* eat whatever is left on this line */
+ YY_BREAK
+case 14:
+/* rule 14 can match eol */
+YY_RULE_SETUP
+#line 173 "lib/conf-lex.l"
+{
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+ YY_BREAK
+
+/*
+ * STATE: MIDDLE
+ */
+
+case 15:
+YY_RULE_SETUP
+#line 186 "lib/conf-lex.l"
+; /* eat as many blanks as possible at once */
+ YY_BREAK
+case 16:
+/* rule 16 can match eol */
+YY_RULE_SETUP
+#line 188 "lib/conf-lex.l"
+{ /* newline here sends EOL token to parser */
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+ YY_BREAK
+case YY_STATE_EOF(MIDDLE):
+#line 194 "lib/conf-lex.l"
+{ /* EOF here sends EOL token to parser also */
+ BEGIN(INITIAL);
+ return EOL;
+ }
+ YY_BREAK
+case 17:
+/* rule 17 can match eol */
+YY_RULE_SETUP
+#line 199 "lib/conf-lex.l"
+{ /* eat an escaped newline with no state change */
+ sensors_yylineno++;
+ }
+ YY_BREAK
+/* comments */
+case 18:
+YY_RULE_SETUP
+#line 205 "lib/conf-lex.l"
+; /* eat the rest of the line after comment char */
+ YY_BREAK
+case 19:
+/* rule 19 can match eol */
+YY_RULE_SETUP
+#line 207 "lib/conf-lex.l"
+{ /* eat the rest of the line after comment char */
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+ YY_BREAK
+/* A number */
+case 20:
+YY_RULE_SETUP
+#line 215 "lib/conf-lex.l"
+{
+ sensors_yylval.value = atof(sensors_yytext);
+ return FLOAT;
+ }
+ YY_BREAK
+/* Some operators */
+case 21:
+YY_RULE_SETUP
+#line 222 "lib/conf-lex.l"
+return '+';
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 223 "lib/conf-lex.l"
+return '-';
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 224 "lib/conf-lex.l"
+return '*';
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 225 "lib/conf-lex.l"
+return '/';
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 226 "lib/conf-lex.l"
+return '(';
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 227 "lib/conf-lex.l"
+return ')';
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 228 "lib/conf-lex.l"
+return ',';
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 229 "lib/conf-lex.l"
+return '@';
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 230 "lib/conf-lex.l"
+return '^';
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 231 "lib/conf-lex.l"
+return '`';
+ YY_BREAK
+/* Quoted string */
+case 31:
+YY_RULE_SETUP
+#line 235 "lib/conf-lex.l"
+{
+ buffer_malloc();
+ BEGIN(STRING);
+ }
+ YY_BREAK
+/* A normal, unquoted identifier */
+case 32:
+YY_RULE_SETUP
+#line 242 "lib/conf-lex.l"
+{
+ sensors_yylval.name = strdup(sensors_yytext);
+ if (! sensors_yylval.name)
+ sensors_fatal_error("conf-lex.l",
+ "Allocating a new string");
+
+ return NAME;
+ }
+ YY_BREAK
+/* anything else is bogus */
+case 33:
+#line 254 "lib/conf-lex.l"
+case 34:
+#line 255 "lib/conf-lex.l"
+case 35:
+YY_RULE_SETUP
+#line 255 "lib/conf-lex.l"
+{
+ BEGIN(ERR);
+ return ERROR;
+ }
+ YY_BREAK
+
+/*
+ * STATE: STRING
+ */
+
+/* Oops, newline or EOF while in a string is not good */
+case 36:
+/* rule 36 can match eol */
+#line 270 "lib/conf-lex.l"
+case 37:
+/* rule 37 can match eol */
+YY_RULE_SETUP
+#line 270 "lib/conf-lex.l"
+{
+ buffer_add_char("\0");
+ strcpy(sensors_lex_error,
+ "No matching double quote.");
+ buffer_free();
+ yyless(0);
+ BEGIN(ERR);
+ return ERROR;
+ }
+ YY_BREAK
+case YY_STATE_EOF(STRING):
+#line 280 "lib/conf-lex.l"
+{
+ strcpy(sensors_lex_error,
+ "Reached end-of-file without a matching double quote.");
+ buffer_free();
+ BEGIN(MIDDLE);
+ return ERROR;
+ }
+ YY_BREAK
+/* At the end */
+case 38:
+YY_RULE_SETUP
+#line 290 "lib/conf-lex.l"
+{
+ buffer_add_char("\0");
+ strcpy(sensors_lex_error,
+ "Quoted strings must be separated by whitespace.");
+ buffer_free();
+ BEGIN(ERR);
+ return ERROR;
+ }
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 299 "lib/conf-lex.l"
+{
+ buffer_add_char("\0");
+ sensors_yylval.name = strdup(buffer);
+ if (! sensors_yylval.name)
+ sensors_fatal_error("conf-lex.l",
+ "Allocating a new string");
+ buffer_free();
+ BEGIN(MIDDLE);
+ return NAME;
+ }
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 310 "lib/conf-lex.l"
+buffer_add_char("\a");
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 311 "lib/conf-lex.l"
+buffer_add_char("\b");
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 312 "lib/conf-lex.l"
+buffer_add_char("\f");
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 313 "lib/conf-lex.l"
+buffer_add_char("\n");
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 314 "lib/conf-lex.l"
+buffer_add_char("\r");
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 315 "lib/conf-lex.l"
+buffer_add_char("\t");
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 316 "lib/conf-lex.l"
+buffer_add_char("\v");
+ YY_BREAK
+/* Other escapes: just copy the character behind the slash */
+case 47:
+YY_RULE_SETUP
+#line 320 "lib/conf-lex.l"
+{
+ buffer_add_char(&sensors_yytext[1]);
+ }
+ YY_BREAK
+/* Anything else (including a bare '\' which may be followed by EOF) */
+case 48:
+#line 327 "lib/conf-lex.l"
+case 49:
+YY_RULE_SETUP
+#line 327 "lib/conf-lex.l"
+{
+ buffer_add_string(sensors_yytext);
+ }
+ YY_BREAK
+
+case 50:
+YY_RULE_SETUP
+#line 332 "lib/conf-lex.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+#line 1903 "<stdout>"
+ case YY_STATE_EOF(ERR):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed sensors_yyin at a new source and called
+ * sensors_yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = sensors_yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( sensors_yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * sensors_yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of sensors_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ sensors_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ sensors_yyrestart(sensors_yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) sensors_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+
+ yy_current_state = yy_nxt[yy_current_state][1];
+ yy_is_jam = (yy_current_state <= 0);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ sensors_yyrestart(sensors_yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( sensors_yywrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve sensors_yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void sensors_yyrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ sensors_yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE );
+ }
+
+ sensors_yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+ sensors_yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void sensors_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * sensors_yypop_buffer_state();
+ * sensors_yypush_buffer_state(new_buffer);
+ */
+ sensors_yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ sensors_yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (sensors_yywrap()) processing, but the only time this flag
+ * is looked at is after sensors_yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void sensors_yy_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ sensors_yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE sensors_yy_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) sensors_yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) sensors_yyalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ sensors_yy_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with sensors_yy_create_buffer()
+ *
+ */
+ void sensors_yy_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ sensors_yyfree((void *) b->yy_ch_buf );
+
+ sensors_yyfree((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a sensors_yyrestart() or at EOF.
+ */
+ static void sensors_yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ sensors_yy_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then sensors_yy_init_buffer was _probably_
+ * called from sensors_yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void sensors_yy_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ sensors_yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void sensors_yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ sensors_yyensure_buffer_stack();
+
+ /* This block is copied from sensors_yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from sensors_yy_switch_to_buffer. */
+ sensors_yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void sensors_yypop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ sensors_yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ sensors_yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void sensors_yyensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)sensors_yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)sensors_yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE sensors_yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) sensors_yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ sensors_yy_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to sensors_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * sensors_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE sensors_yy_scan_string (yyconst char * yystr )
+{
+
+ return sensors_yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to sensors_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE sensors_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) sensors_yyalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = sensors_yy_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in sensors_yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up sensors_yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ sensors_yytext[sensors_yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = sensors_yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ sensors_yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int sensors_yyget_lineno (void)
+{
+
+ return sensors_yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *sensors_yyget_in (void)
+{
+ return sensors_yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *sensors_yyget_out (void)
+{
+ return sensors_yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int sensors_yyget_leng (void)
+{
+ return sensors_yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *sensors_yyget_text (void)
+{
+ return sensors_yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void sensors_yyset_lineno (int line_number )
+{
+
+ sensors_yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see sensors_yy_switch_to_buffer
+ */
+void sensors_yyset_in (FILE * in_str )
+{
+ sensors_yyin = in_str ;
+}
+
+void sensors_yyset_out (FILE * out_str )
+{
+ sensors_yyout = out_str ;
+}
+
+int sensors_yyget_debug (void)
+{
+ return sensors_yy_flex_debug;
+}
+
+void sensors_yyset_debug (int bdebug )
+{
+ sensors_yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from sensors_yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ sensors_yyin = stdin;
+ sensors_yyout = stdout;
+#else
+ sensors_yyin = (FILE *) 0;
+ sensors_yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * sensors_yylex_init()
+ */
+ return 0;
+}
+
+/* sensors_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int sensors_yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ sensors_yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ sensors_yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ sensors_yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * sensors_yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *sensors_yyalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *sensors_yyrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void sensors_yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see sensors_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 332 "lib/conf-lex.l"
+
+
+
+/*
+ Do the buffer handling manually. This allows us to scan as many
+ config files as we need to, while cleaning up properly after each
+ one. The "BEGIN(0)" line ensures that we start in the default state,
+ even if e.g. the previous config file was syntactically broken.
+
+ Returns 0 if successful, !0 otherwise.
+*/
+
+static YY_BUFFER_STATE scan_buf = (YY_BUFFER_STATE)0;
+
+int sensors_scanner_init(FILE *input, const char *filename)
+{
+ BEGIN(0);
+ if (!(scan_buf = sensors_yy_create_buffer(input, YY_BUF_SIZE)))
+ return -1;
+
+ sensors_yy_switch_to_buffer(scan_buf);
+ sensors_yyfilename = filename;
+ sensors_yylineno = 1;
+ return 0;
+}
+
+void sensors_scanner_exit(void)
+{
+ sensors_yy_delete_buffer(scan_buf);
+ scan_buf = (YY_BUFFER_STATE)0;
+
+/* As of flex 2.5.9, sensors_yylex_destroy() must be called when done with the
+ scaller, otherwise we'll leak memory. */
+#if defined(YY_FLEX_MAJOR_VERSION) && defined(YY_FLEX_MINOR_VERSION) && defined(YY_FLEX_SUBMINOR_VERSION)
+#if YY_FLEX_MAJOR_VERSION > 2 || \
+ (YY_FLEX_MAJOR_VERSION == 2 && (YY_FLEX_MINOR_VERSION > 5 || \
+ (YY_FLEX_MINOR_VERSION == 5 && YY_FLEX_SUBMINOR_VERSION >= 9)))
+ sensors_yylex_destroy();
+#endif
+#endif
+}
+
+
diff --git a/tools/gator/daemon/libsensors/conf-lex.l b/tools/gator/daemon/libsensors/conf-lex.l
new file mode 100644
index 000000000000..43ddbd8f5bd5
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-lex.l
@@ -0,0 +1,372 @@
+%{
+/*
+ conf-lex.l - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "general.h"
+#include "data.h"
+#include "conf-parse.h"
+#include "error.h"
+#include "scanner.h"
+
+static int buffer_count;
+static int buffer_max;
+static char *buffer;
+
+char sensors_lex_error[100];
+
+const char *sensors_yyfilename;
+int sensors_yylineno;
+
+#define buffer_malloc() sensors_malloc_array(&buffer,&buffer_count,\
+ &buffer_max,1)
+#define buffer_free() sensors_free_array(&buffer,&buffer_count,\
+ &buffer_max)
+#define buffer_add_char(c) sensors_add_array_el(c,&buffer,\
+ &buffer_count,\
+ &buffer_max,1)
+#define buffer_add_string(s) sensors_add_array_els(s,strlen(s),\
+ &buffer, \
+ &buffer_count,&buffer_max,1)
+
+%}
+
+ /* Scanner for configuration files */
+
+%option nodefault
+%option noyywrap
+%option nounput
+
+ /* All states are exclusive */
+
+%x MIDDLE
+%x STRING
+%x ERR
+
+ /* Any whitespace-like character */
+
+BLANK [ \f\r\t\v]
+
+IDCHAR [[:alnum:]_]
+
+ /* Note: `10', `10.4' and `.4' are valid, `10.' is not */
+
+FLOAT [[:digit:]]*\.?[[:digit:]]+
+
+ /* Only positive whole numbers are recognized here */
+
+NUM 0|([1-9][[:digit:]]*)
+
+
+%%
+
+ /*
+ * STATE: INITIAL
+ */
+
+<INITIAL>{
+
+<<EOF>> { /* EOF from this state terminates */
+ return 0;
+ }
+
+{BLANK}+ ; /* eat as many blanks as possible at once */
+
+{BLANK}*\n { /* eat a bare newline (possibly preceded by blanks) */
+ sensors_yylineno++;
+ }
+
+ /* comments */
+
+#.* ; /* eat the rest of the line after comment char */
+
+#.*\n { /* eat the rest of the line after comment char */
+ sensors_yylineno++;
+ }
+
+ /*
+ * Keywords must be followed by whitespace - eat that too.
+ * If there isn't trailing whitespace, we still need to
+ * accept it as lexically correct (even though the parser
+ * will reject it anyway.)
+ */
+
+label{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return LABEL;
+ }
+
+set{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return SET;
+ }
+
+compute{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return COMPUTE;
+ }
+
+bus{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return BUS;
+ }
+
+chip{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return CHIP;
+ }
+
+ignore{BLANK}* {
+ sensors_yylval.line.filename = sensors_yyfilename;
+ sensors_yylval.line.lineno = sensors_yylineno;
+ BEGIN(MIDDLE);
+ return IGNORE;
+ }
+
+ /* Anything else at the beginning of a line is an error */
+
+[a-z]+ |
+. {
+ BEGIN(ERR);
+ strcpy(sensors_lex_error,"Invalid keyword");
+ return ERROR;
+ }
+}
+
+ /*
+ * STATE: ERROR
+ */
+
+<ERR>{
+
+.* ; /* eat whatever is left on this line */
+
+\n {
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+}
+
+ /*
+ * STATE: MIDDLE
+ */
+
+<MIDDLE>{
+
+{BLANK}+ ; /* eat as many blanks as possible at once */
+
+\n { /* newline here sends EOL token to parser */
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+
+<<EOF>> { /* EOF here sends EOL token to parser also */
+ BEGIN(INITIAL);
+ return EOL;
+ }
+
+\\{BLANK}*\n { /* eat an escaped newline with no state change */
+ sensors_yylineno++;
+ }
+
+ /* comments */
+
+#.* ; /* eat the rest of the line after comment char */
+
+#.*\n { /* eat the rest of the line after comment char */
+ BEGIN(INITIAL);
+ sensors_yylineno++;
+ return EOL;
+ }
+
+ /* A number */
+
+{FLOAT} {
+ sensors_yylval.value = atof(sensors_yytext);
+ return FLOAT;
+ }
+
+ /* Some operators */
+
+"+" return '+';
+"-" return '-';
+"*" return '*';
+"/" return '/';
+"(" return '(';
+")" return ')';
+"," return ',';
+"@" return '@';
+"^" return '^';
+"`" return '`';
+
+ /* Quoted string */
+
+\" {
+ buffer_malloc();
+ BEGIN(STRING);
+ }
+
+ /* A normal, unquoted identifier */
+
+{IDCHAR}+ {
+ sensors_yylval.name = strdup(sensors_yytext);
+ if (! sensors_yylval.name)
+ sensors_fatal_error("conf-lex.l",
+ "Allocating a new string");
+
+ return NAME;
+ }
+
+ /* anything else is bogus */
+
+. |
+[[:digit:]]*\. |
+\\{BLANK}* {
+ BEGIN(ERR);
+ return ERROR;
+ }
+}
+
+ /*
+ * STATE: STRING
+ */
+
+<STRING>{
+
+ /* Oops, newline or EOF while in a string is not good */
+
+\n |
+\\\n {
+ buffer_add_char("\0");
+ strcpy(sensors_lex_error,
+ "No matching double quote.");
+ buffer_free();
+ yyless(0);
+ BEGIN(ERR);
+ return ERROR;
+ }
+
+<<EOF>> {
+ strcpy(sensors_lex_error,
+ "Reached end-of-file without a matching double quote.");
+ buffer_free();
+ BEGIN(MIDDLE);
+ return ERROR;
+ }
+
+ /* At the end */
+
+\"\" {
+ buffer_add_char("\0");
+ strcpy(sensors_lex_error,
+ "Quoted strings must be separated by whitespace.");
+ buffer_free();
+ BEGIN(ERR);
+ return ERROR;
+ }
+
+\" {
+ buffer_add_char("\0");
+ sensors_yylval.name = strdup(buffer);
+ if (! sensors_yylval.name)
+ sensors_fatal_error("conf-lex.l",
+ "Allocating a new string");
+ buffer_free();
+ BEGIN(MIDDLE);
+ return NAME;
+ }
+
+\\a buffer_add_char("\a");
+\\b buffer_add_char("\b");
+\\f buffer_add_char("\f");
+\\n buffer_add_char("\n");
+\\r buffer_add_char("\r");
+\\t buffer_add_char("\t");
+\\v buffer_add_char("\v");
+
+ /* Other escapes: just copy the character behind the slash */
+
+\\. {
+ buffer_add_char(&sensors_yytext[1]);
+ }
+
+ /* Anything else (including a bare '\' which may be followed by EOF) */
+
+\\ |
+[^\\\n\"]+ {
+ buffer_add_string(sensors_yytext);
+ }
+}
+
+%%
+
+/*
+ Do the buffer handling manually. This allows us to scan as many
+ config files as we need to, while cleaning up properly after each
+ one. The "BEGIN(0)" line ensures that we start in the default state,
+ even if e.g. the previous config file was syntactically broken.
+
+ Returns 0 if successful, !0 otherwise.
+*/
+
+static YY_BUFFER_STATE scan_buf = (YY_BUFFER_STATE)0;
+
+int sensors_scanner_init(FILE *input, const char *filename)
+{
+ BEGIN(0);
+ if (!(scan_buf = sensors_yy_create_buffer(input, YY_BUF_SIZE)))
+ return -1;
+
+ sensors_yy_switch_to_buffer(scan_buf);
+ sensors_yyfilename = filename;
+ sensors_yylineno = 1;
+ return 0;
+}
+
+void sensors_scanner_exit(void)
+{
+ sensors_yy_delete_buffer(scan_buf);
+ scan_buf = (YY_BUFFER_STATE)0;
+
+/* As of flex 2.5.9, yylex_destroy() must be called when done with the
+ scaller, otherwise we'll leak memory. */
+#if defined(YY_FLEX_MAJOR_VERSION) && defined(YY_FLEX_MINOR_VERSION) && defined(YY_FLEX_SUBMINOR_VERSION)
+#if YY_FLEX_MAJOR_VERSION > 2 || \
+ (YY_FLEX_MAJOR_VERSION == 2 && (YY_FLEX_MINOR_VERSION > 5 || \
+ (YY_FLEX_MINOR_VERSION == 5 && YY_FLEX_SUBMINOR_VERSION >= 9)))
+ sensors_yylex_destroy();
+#endif
+#endif
+}
+
diff --git a/tools/gator/daemon/libsensors/conf-parse.c b/tools/gator/daemon/libsensors/conf-parse.c
new file mode 100644
index 000000000000..fb775460a1c3
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-parse.c
@@ -0,0 +1,2042 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.5"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse sensors_yyparse
+#define yylex sensors_yylex
+#define yyerror sensors_yyerror
+#define yylval sensors_yylval
+#define yychar sensors_yychar
+#define yydebug sensors_yydebug
+#define yynerrs sensors_yynerrs
+
+
+/* Copy the first part of user declarations. */
+
+/* Line 268 of yacc.c */
+#line 1 "lib/conf-parse.y"
+
+/*
+ conf-parse.y - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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.
+*/
+
+#define YYERROR_VERBOSE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "data.h"
+#include "general.h"
+#include "error.h"
+#include "conf.h"
+#include "access.h"
+#include "init.h"
+
+static void sensors_yyerror(const char *err);
+static sensors_expr *malloc_expr(void);
+
+static sensors_chip *current_chip = NULL;
+
+#define bus_add_el(el) sensors_add_array_el(el,\
+ &sensors_config_busses,\
+ &sensors_config_busses_count,\
+ &sensors_config_busses_max,\
+ sizeof(sensors_bus))
+#define label_add_el(el) sensors_add_array_el(el,\
+ &current_chip->labels,\
+ &current_chip->labels_count,\
+ &current_chip->labels_max,\
+ sizeof(sensors_label));
+#define set_add_el(el) sensors_add_array_el(el,\
+ &current_chip->sets,\
+ &current_chip->sets_count,\
+ &current_chip->sets_max,\
+ sizeof(sensors_set));
+#define compute_add_el(el) sensors_add_array_el(el,\
+ &current_chip->computes,\
+ &current_chip->computes_count,\
+ &current_chip->computes_max,\
+ sizeof(sensors_compute));
+#define ignore_add_el(el) sensors_add_array_el(el,\
+ &current_chip->ignores,\
+ &current_chip->ignores_count,\
+ &current_chip->ignores_max,\
+ sizeof(sensors_ignore));
+#define chip_add_el(el) sensors_add_array_el(el,\
+ &sensors_config_chips,\
+ &sensors_config_chips_count,\
+ &sensors_config_chips_max,\
+ sizeof(sensors_chip));
+
+#define fits_add_el(el,list) sensors_add_array_el(el,\
+ &(list).fits,\
+ &(list).fits_count,\
+ &(list).fits_max, \
+ sizeof(sensors_chip_name));
+
+
+
+/* Line 268 of yacc.c */
+#line 158 "lib/conf-parse.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ NEG = 258,
+ EOL = 259,
+ BUS = 260,
+ LABEL = 261,
+ SET = 262,
+ CHIP = 263,
+ COMPUTE = 264,
+ IGNORE = 265,
+ FLOAT = 266,
+ NAME = 267,
+ ERROR = 268
+ };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 293 of yacc.c */
+#line 79 "lib/conf-parse.y"
+
+ double value;
+ char *name;
+ void *nothing;
+ sensors_chip_name_list chips;
+ sensors_expr *expr;
+ sensors_bus_id bus;
+ sensors_chip_name chip;
+ sensors_config_line line;
+
+
+
+/* Line 293 of yacc.c */
+#line 220 "lib/conf-parse.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 343 of yacc.c */
+#line 232 "lib/conf-parse.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 58
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 24
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 16
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 34
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 63
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 268
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 22, 23, 5, 4, 10, 3, 2, 6, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 21, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 8, 2, 9, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 7, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 4, 7, 10, 13, 16, 19, 22,
+ 25, 28, 32, 36, 40, 46, 49, 52, 54, 57,
+ 59, 61, 63, 67, 71, 75, 79, 82, 86, 89,
+ 92, 94, 96, 98, 100
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 25, 0, -1, -1, 25, 26, -1, 27, 11, -1,
+ 28, 11, -1, 29, 11, -1, 32, 11, -1, 30,
+ 11, -1, 31, 11, -1, 1, 11, -1, 12, 35,
+ 36, -1, 13, 37, 38, -1, 14, 37, 34, -1,
+ 16, 37, 34, 10, 34, -1, 17, 37, -1, 15,
+ 33, -1, 39, -1, 33, 39, -1, 18, -1, 19,
+ -1, 21, -1, 34, 4, 34, -1, 34, 3, 34,
+ -1, 34, 5, 34, -1, 34, 6, 34, -1, 3,
+ 34, -1, 22, 34, 23, -1, 8, 34, -1, 9,
+ 34, -1, 19, -1, 19, -1, 19, -1, 19, -1,
+ 19, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 119, 119, 120, 123, 124, 125, 126, 127, 128,
+ 129, 132, 141, 156, 171, 188, 201, 219, 225, 231,
+ 236, 241, 245, 252, 259, 266, 273, 280, 282, 289,
+ 298, 308, 312, 316, 320
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "'-'", "'+'", "'*'", "'/'", "NEG", "'^'",
+ "'`'", "','", "EOL", "BUS", "LABEL", "SET", "CHIP", "COMPUTE", "IGNORE",
+ "FLOAT", "NAME", "ERROR", "'@'", "'('", "')'", "$accept", "input",
+ "line", "bus_statement", "label_statement", "set_statement",
+ "compute_statement", "ignore_statement", "chip_statement",
+ "chip_name_list", "expression", "bus_id", "adapter_name",
+ "function_name", "string", "chip_name", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 45, 43, 42, 47, 258, 94, 96,
+ 44, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 64, 40, 41
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 24, 25, 25, 26, 26, 26, 26, 26, 26,
+ 26, 27, 28, 29, 30, 31, 32, 33, 33, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 35, 36, 37, 38, 39
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 2, 2, 2, 2, 2, 2, 2,
+ 2, 3, 3, 3, 5, 2, 2, 1, 2, 1,
+ 1, 1, 3, 3, 3, 3, 2, 3, 2, 2,
+ 1, 1, 1, 1, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 2, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0, 0, 10, 30, 0,
+ 32, 0, 0, 34, 16, 17, 0, 15, 4, 5,
+ 6, 8, 9, 7, 31, 11, 33, 12, 0, 0,
+ 0, 19, 20, 21, 0, 13, 18, 0, 26, 28,
+ 29, 0, 0, 0, 0, 0, 0, 27, 23, 22,
+ 24, 25, 14
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 1, 10, 11, 12, 13, 14, 15, 16, 24,
+ 45, 19, 35, 21, 37, 25
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -27
+static const yytype_int8 yypact[] =
+{
+ -27, 37, -27, -4, -3, 1, 1, 6, 1, 1,
+ -27, 8, 13, 20, 23, 32, 34, -27, -27, 29,
+ -27, 39, 14, -27, 6, -27, 14, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27, -27, 14, 14,
+ 14, -27, -27, -27, 14, 36, -27, 5, -27, -27,
+ -27, -2, 14, 14, 14, 14, 14, -27, 0, 0,
+ -27, -27, 36
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -26, -27, -27, 38, -27, 31
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 47, 52, 53, 54, 55, 54, 55, 17, 52, 53,
+ 54, 55, 48, 49, 50, 56, 18, 38, 51, 28,
+ 20, 57, 39, 40, 29, 23, 58, 59, 60, 61,
+ 62, 30, 41, 42, 31, 43, 44, 2, 3, 52,
+ 53, 54, 55, 32, 22, 33, 26, 27, 34, 4,
+ 5, 6, 7, 8, 9, 46, 0, 0, 36
+};
+
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-27))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+ 26, 3, 4, 5, 6, 5, 6, 11, 3, 4,
+ 5, 6, 38, 39, 40, 10, 19, 3, 44, 11,
+ 19, 23, 8, 9, 11, 19, 52, 53, 54, 55,
+ 56, 11, 18, 19, 11, 21, 22, 0, 1, 3,
+ 4, 5, 6, 11, 6, 11, 8, 9, 19, 12,
+ 13, 14, 15, 16, 17, 24, -1, -1, 19
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 25, 0, 1, 12, 13, 14, 15, 16, 17,
+ 26, 27, 28, 29, 30, 31, 32, 11, 19, 35,
+ 19, 37, 37, 19, 33, 39, 37, 37, 11, 11,
+ 11, 11, 11, 11, 19, 36, 19, 38, 3, 8,
+ 9, 18, 19, 21, 22, 34, 39, 34, 34, 34,
+ 34, 34, 3, 4, 5, 6, 10, 23, 34, 34,
+ 34, 34, 34
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
+
+#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = 0;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 11:
+
+/* Line 1806 of yacc.c */
+#line 133 "lib/conf-parse.y"
+ { sensors_bus new_el;
+ new_el.line = (yyvsp[(1) - (3)].line);
+ new_el.bus = (yyvsp[(2) - (3)].bus);
+ new_el.adapter = (yyvsp[(3) - (3)].name);
+ bus_add_el(&new_el);
+ }
+ break;
+
+ case 12:
+
+/* Line 1806 of yacc.c */
+#line 142 "lib/conf-parse.y"
+ { sensors_label new_el;
+ if (!current_chip) {
+ sensors_yyerror("Label statement before first chip statement");
+ free((yyvsp[(2) - (3)].name));
+ free((yyvsp[(3) - (3)].name));
+ YYERROR;
+ }
+ new_el.line = (yyvsp[(1) - (3)].line);
+ new_el.name = (yyvsp[(2) - (3)].name);
+ new_el.value = (yyvsp[(3) - (3)].name);
+ label_add_el(&new_el);
+ }
+ break;
+
+ case 13:
+
+/* Line 1806 of yacc.c */
+#line 157 "lib/conf-parse.y"
+ { sensors_set new_el;
+ if (!current_chip) {
+ sensors_yyerror("Set statement before first chip statement");
+ free((yyvsp[(2) - (3)].name));
+ sensors_free_expr((yyvsp[(3) - (3)].expr));
+ YYERROR;
+ }
+ new_el.line = (yyvsp[(1) - (3)].line);
+ new_el.name = (yyvsp[(2) - (3)].name);
+ new_el.value = (yyvsp[(3) - (3)].expr);
+ set_add_el(&new_el);
+ }
+ break;
+
+ case 14:
+
+/* Line 1806 of yacc.c */
+#line 172 "lib/conf-parse.y"
+ { sensors_compute new_el;
+ if (!current_chip) {
+ sensors_yyerror("Compute statement before first chip statement");
+ free((yyvsp[(2) - (5)].name));
+ sensors_free_expr((yyvsp[(3) - (5)].expr));
+ sensors_free_expr((yyvsp[(5) - (5)].expr));
+ YYERROR;
+ }
+ new_el.line = (yyvsp[(1) - (5)].line);
+ new_el.name = (yyvsp[(2) - (5)].name);
+ new_el.from_proc = (yyvsp[(3) - (5)].expr);
+ new_el.to_proc = (yyvsp[(5) - (5)].expr);
+ compute_add_el(&new_el);
+ }
+ break;
+
+ case 15:
+
+/* Line 1806 of yacc.c */
+#line 189 "lib/conf-parse.y"
+ { sensors_ignore new_el;
+ if (!current_chip) {
+ sensors_yyerror("Ignore statement before first chip statement");
+ free((yyvsp[(2) - (2)].name));
+ YYERROR;
+ }
+ new_el.line = (yyvsp[(1) - (2)].line);
+ new_el.name = (yyvsp[(2) - (2)].name);
+ ignore_add_el(&new_el);
+ }
+ break;
+
+ case 16:
+
+/* Line 1806 of yacc.c */
+#line 202 "lib/conf-parse.y"
+ { sensors_chip new_el;
+ new_el.line = (yyvsp[(1) - (2)].line);
+ new_el.labels = NULL;
+ new_el.sets = NULL;
+ new_el.computes = NULL;
+ new_el.ignores = NULL;
+ new_el.labels_count = new_el.labels_max = 0;
+ new_el.sets_count = new_el.sets_max = 0;
+ new_el.computes_count = new_el.computes_max = 0;
+ new_el.ignores_count = new_el.ignores_max = 0;
+ new_el.chips = (yyvsp[(2) - (2)].chips);
+ chip_add_el(&new_el);
+ current_chip = sensors_config_chips +
+ sensors_config_chips_count - 1;
+ }
+ break;
+
+ case 17:
+
+/* Line 1806 of yacc.c */
+#line 220 "lib/conf-parse.y"
+ {
+ (yyval.chips).fits = NULL;
+ (yyval.chips).fits_count = (yyval.chips).fits_max = 0;
+ fits_add_el(&(yyvsp[(1) - (1)].chip),(yyval.chips));
+ }
+ break;
+
+ case 18:
+
+/* Line 1806 of yacc.c */
+#line 226 "lib/conf-parse.y"
+ { (yyval.chips) = (yyvsp[(1) - (2)].chips);
+ fits_add_el(&(yyvsp[(2) - (2)].chip),(yyval.chips));
+ }
+ break;
+
+ case 19:
+
+/* Line 1806 of yacc.c */
+#line 232 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->data.val = (yyvsp[(1) - (1)].value);
+ (yyval.expr)->kind = sensors_kind_val;
+ }
+ break;
+
+ case 20:
+
+/* Line 1806 of yacc.c */
+#line 237 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->data.var = (yyvsp[(1) - (1)].name);
+ (yyval.expr)->kind = sensors_kind_var;
+ }
+ break;
+
+ case 21:
+
+/* Line 1806 of yacc.c */
+#line 242 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_source;
+ }
+ break;
+
+ case 22:
+
+/* Line 1806 of yacc.c */
+#line 246 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_add;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr);
+ (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr);
+ }
+ break;
+
+ case 23:
+
+/* Line 1806 of yacc.c */
+#line 253 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_sub;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr);
+ (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr);
+ }
+ break;
+
+ case 24:
+
+/* Line 1806 of yacc.c */
+#line 260 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_multiply;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr);
+ (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr);
+ }
+ break;
+
+ case 25:
+
+/* Line 1806 of yacc.c */
+#line 267 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_divide;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr);
+ (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr);
+ }
+ break;
+
+ case 26:
+
+/* Line 1806 of yacc.c */
+#line 274 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_negate;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr);
+ (yyval.expr)->data.subexpr.sub2 = NULL;
+ }
+ break;
+
+ case 27:
+
+/* Line 1806 of yacc.c */
+#line 281 "lib/conf-parse.y"
+ { (yyval.expr) = (yyvsp[(2) - (3)].expr); }
+ break;
+
+ case 28:
+
+/* Line 1806 of yacc.c */
+#line 283 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_exp;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr);
+ (yyval.expr)->data.subexpr.sub2 = NULL;
+ }
+ break;
+
+ case 29:
+
+/* Line 1806 of yacc.c */
+#line 290 "lib/conf-parse.y"
+ { (yyval.expr) = malloc_expr();
+ (yyval.expr)->kind = sensors_kind_sub;
+ (yyval.expr)->data.subexpr.op = sensors_log;
+ (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr);
+ (yyval.expr)->data.subexpr.sub2 = NULL;
+ }
+ break;
+
+ case 30:
+
+/* Line 1806 of yacc.c */
+#line 299 "lib/conf-parse.y"
+ { int res = sensors_parse_bus_id((yyvsp[(1) - (1)].name),&(yyval.bus));
+ free((yyvsp[(1) - (1)].name));
+ if (res) {
+ sensors_yyerror("Parse error in bus id");
+ YYERROR;
+ }
+ }
+ break;
+
+ case 31:
+
+/* Line 1806 of yacc.c */
+#line 309 "lib/conf-parse.y"
+ { (yyval.name) = (yyvsp[(1) - (1)].name); }
+ break;
+
+ case 32:
+
+/* Line 1806 of yacc.c */
+#line 313 "lib/conf-parse.y"
+ { (yyval.name) = (yyvsp[(1) - (1)].name); }
+ break;
+
+ case 33:
+
+/* Line 1806 of yacc.c */
+#line 317 "lib/conf-parse.y"
+ { (yyval.name) = (yyvsp[(1) - (1)].name); }
+ break;
+
+ case 34:
+
+/* Line 1806 of yacc.c */
+#line 321 "lib/conf-parse.y"
+ { int res = sensors_parse_chip_name((yyvsp[(1) - (1)].name),&(yyval.chip));
+ free((yyvsp[(1) - (1)].name));
+ if (res) {
+ sensors_yyerror("Parse error in chip name");
+ YYERROR;
+ }
+ }
+ break;
+
+
+
+/* Line 1806 of yacc.c */
+#line 1793 "lib/conf-parse.c"
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
+/* Line 2067 of yacc.c */
+#line 330 "lib/conf-parse.y"
+
+
+void sensors_yyerror(const char *err)
+{
+ if (sensors_lex_error[0]) {
+ sensors_parse_error_wfn(sensors_lex_error, sensors_yyfilename, sensors_yylineno);
+ sensors_lex_error[0] = '\0';
+ } else
+ sensors_parse_error_wfn(err, sensors_yyfilename, sensors_yylineno);
+}
+
+sensors_expr *malloc_expr(void)
+{
+ sensors_expr *res = malloc(sizeof(sensors_expr));
+ if (! res)
+ sensors_fatal_error(__func__, "Allocating a new expression");
+ return res;
+}
+
diff --git a/tools/gator/daemon/libsensors/conf-parse.h b/tools/gator/daemon/libsensors/conf-parse.h
new file mode 100644
index 000000000000..89c9c1a0d59f
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-parse.h
@@ -0,0 +1,84 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ NEG = 258,
+ EOL = 259,
+ BUS = 260,
+ LABEL = 261,
+ SET = 262,
+ CHIP = 263,
+ COMPUTE = 264,
+ IGNORE = 265,
+ FLOAT = 266,
+ NAME = 267,
+ ERROR = 268
+ };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 2068 of yacc.c */
+#line 79 "lib/conf-parse.y"
+
+ double value;
+ char *name;
+ void *nothing;
+ sensors_chip_name_list chips;
+ sensors_expr *expr;
+ sensors_bus_id bus;
+ sensors_chip_name chip;
+ sensors_config_line line;
+
+
+
+/* Line 2068 of yacc.c */
+#line 76 "lib/conf-parse.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE sensors_yylval;
+
+
diff --git a/tools/gator/daemon/libsensors/conf-parse.y b/tools/gator/daemon/libsensors/conf-parse.y
new file mode 100644
index 000000000000..1937f544dc66
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf-parse.y
@@ -0,0 +1,347 @@
+%{
+/*
+ conf-parse.y - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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.
+*/
+
+#define YYERROR_VERBOSE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "data.h"
+#include "general.h"
+#include "error.h"
+#include "conf.h"
+#include "access.h"
+#include "init.h"
+
+static void sensors_yyerror(const char *err);
+static sensors_expr *malloc_expr(void);
+
+static sensors_chip *current_chip = NULL;
+
+#define bus_add_el(el) sensors_add_array_el(el,\
+ &sensors_config_busses,\
+ &sensors_config_busses_count,\
+ &sensors_config_busses_max,\
+ sizeof(sensors_bus))
+#define label_add_el(el) sensors_add_array_el(el,\
+ &current_chip->labels,\
+ &current_chip->labels_count,\
+ &current_chip->labels_max,\
+ sizeof(sensors_label));
+#define set_add_el(el) sensors_add_array_el(el,\
+ &current_chip->sets,\
+ &current_chip->sets_count,\
+ &current_chip->sets_max,\
+ sizeof(sensors_set));
+#define compute_add_el(el) sensors_add_array_el(el,\
+ &current_chip->computes,\
+ &current_chip->computes_count,\
+ &current_chip->computes_max,\
+ sizeof(sensors_compute));
+#define ignore_add_el(el) sensors_add_array_el(el,\
+ &current_chip->ignores,\
+ &current_chip->ignores_count,\
+ &current_chip->ignores_max,\
+ sizeof(sensors_ignore));
+#define chip_add_el(el) sensors_add_array_el(el,\
+ &sensors_config_chips,\
+ &sensors_config_chips_count,\
+ &sensors_config_chips_max,\
+ sizeof(sensors_chip));
+
+#define fits_add_el(el,list) sensors_add_array_el(el,\
+ &(list).fits,\
+ &(list).fits_count,\
+ &(list).fits_max, \
+ sizeof(sensors_chip_name));
+
+%}
+
+%union {
+ double value;
+ char *name;
+ void *nothing;
+ sensors_chip_name_list chips;
+ sensors_expr *expr;
+ sensors_bus_id bus;
+ sensors_chip_name chip;
+ sensors_config_line line;
+}
+
+%left <nothing> '-' '+'
+%left <nothing> '*' '/'
+%left <nothing> NEG
+%right <nothing> '^' '`'
+
+%token <nothing> ','
+%token <nothing> EOL
+%token <line> BUS
+%token <line> LABEL
+%token <line> SET
+%token <line> CHIP
+%token <line> COMPUTE
+%token <line> IGNORE
+%token <value> FLOAT
+%token <name> NAME
+%token <nothing> ERROR
+
+%type <chips> chip_name_list
+%type <expr> expression
+%type <bus> bus_id
+%type <name> adapter_name
+%type <name> function_name
+%type <name> string
+%type <chip> chip_name
+
+%start input
+
+%%
+
+input: /* empty */
+ | input line
+;
+
+line: bus_statement EOL
+ | label_statement EOL
+ | set_statement EOL
+ | chip_statement EOL
+ | compute_statement EOL
+ | ignore_statement EOL
+ | error EOL
+;
+
+bus_statement: BUS bus_id adapter_name
+ { sensors_bus new_el;
+ new_el.line = $1;
+ new_el.bus = $2;
+ new_el.adapter = $3;
+ bus_add_el(&new_el);
+ }
+;
+
+label_statement: LABEL function_name string
+ { sensors_label new_el;
+ if (!current_chip) {
+ sensors_yyerror("Label statement before first chip statement");
+ free($2);
+ free($3);
+ YYERROR;
+ }
+ new_el.line = $1;
+ new_el.name = $2;
+ new_el.value = $3;
+ label_add_el(&new_el);
+ }
+;
+
+set_statement: SET function_name expression
+ { sensors_set new_el;
+ if (!current_chip) {
+ sensors_yyerror("Set statement before first chip statement");
+ free($2);
+ sensors_free_expr($3);
+ YYERROR;
+ }
+ new_el.line = $1;
+ new_el.name = $2;
+ new_el.value = $3;
+ set_add_el(&new_el);
+ }
+;
+
+compute_statement: COMPUTE function_name expression ',' expression
+ { sensors_compute new_el;
+ if (!current_chip) {
+ sensors_yyerror("Compute statement before first chip statement");
+ free($2);
+ sensors_free_expr($3);
+ sensors_free_expr($5);
+ YYERROR;
+ }
+ new_el.line = $1;
+ new_el.name = $2;
+ new_el.from_proc = $3;
+ new_el.to_proc = $5;
+ compute_add_el(&new_el);
+ }
+;
+
+ignore_statement: IGNORE function_name
+ { sensors_ignore new_el;
+ if (!current_chip) {
+ sensors_yyerror("Ignore statement before first chip statement");
+ free($2);
+ YYERROR;
+ }
+ new_el.line = $1;
+ new_el.name = $2;
+ ignore_add_el(&new_el);
+ }
+;
+
+chip_statement: CHIP chip_name_list
+ { sensors_chip new_el;
+ new_el.line = $1;
+ new_el.labels = NULL;
+ new_el.sets = NULL;
+ new_el.computes = NULL;
+ new_el.ignores = NULL;
+ new_el.labels_count = new_el.labels_max = 0;
+ new_el.sets_count = new_el.sets_max = 0;
+ new_el.computes_count = new_el.computes_max = 0;
+ new_el.ignores_count = new_el.ignores_max = 0;
+ new_el.chips = $2;
+ chip_add_el(&new_el);
+ current_chip = sensors_config_chips +
+ sensors_config_chips_count - 1;
+ }
+;
+
+chip_name_list: chip_name
+ {
+ $$.fits = NULL;
+ $$.fits_count = $$.fits_max = 0;
+ fits_add_el(&$1,$$);
+ }
+ | chip_name_list chip_name
+ { $$ = $1;
+ fits_add_el(&$2,$$);
+ }
+;
+
+expression: FLOAT
+ { $$ = malloc_expr();
+ $$->data.val = $1;
+ $$->kind = sensors_kind_val;
+ }
+ | NAME
+ { $$ = malloc_expr();
+ $$->data.var = $1;
+ $$->kind = sensors_kind_var;
+ }
+ | '@'
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_source;
+ }
+ | expression '+' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_add;
+ $$->data.subexpr.sub1 = $1;
+ $$->data.subexpr.sub2 = $3;
+ }
+ | expression '-' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_sub;
+ $$->data.subexpr.sub1 = $1;
+ $$->data.subexpr.sub2 = $3;
+ }
+ | expression '*' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_multiply;
+ $$->data.subexpr.sub1 = $1;
+ $$->data.subexpr.sub2 = $3;
+ }
+ | expression '/' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_divide;
+ $$->data.subexpr.sub1 = $1;
+ $$->data.subexpr.sub2 = $3;
+ }
+ | '-' expression %prec NEG
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_negate;
+ $$->data.subexpr.sub1 = $2;
+ $$->data.subexpr.sub2 = NULL;
+ }
+ | '(' expression ')'
+ { $$ = $2; }
+ | '^' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_exp;
+ $$->data.subexpr.sub1 = $2;
+ $$->data.subexpr.sub2 = NULL;
+ }
+ | '`' expression
+ { $$ = malloc_expr();
+ $$->kind = sensors_kind_sub;
+ $$->data.subexpr.op = sensors_log;
+ $$->data.subexpr.sub1 = $2;
+ $$->data.subexpr.sub2 = NULL;
+ }
+;
+
+bus_id: NAME
+ { int res = sensors_parse_bus_id($1,&$$);
+ free($1);
+ if (res) {
+ sensors_yyerror("Parse error in bus id");
+ YYERROR;
+ }
+ }
+;
+
+adapter_name: NAME
+ { $$ = $1; }
+;
+
+function_name: NAME
+ { $$ = $1; }
+;
+
+string: NAME
+ { $$ = $1; }
+;
+
+chip_name: NAME
+ { int res = sensors_parse_chip_name($1,&$$);
+ free($1);
+ if (res) {
+ sensors_yyerror("Parse error in chip name");
+ YYERROR;
+ }
+ }
+;
+
+%%
+
+void sensors_yyerror(const char *err)
+{
+ if (sensors_lex_error[0]) {
+ sensors_parse_error_wfn(sensors_lex_error, sensors_yyfilename, sensors_yylineno);
+ sensors_lex_error[0] = '\0';
+ } else
+ sensors_parse_error_wfn(err, sensors_yyfilename, sensors_yylineno);
+}
+
+sensors_expr *malloc_expr(void)
+{
+ sensors_expr *res = malloc(sizeof(sensors_expr));
+ if (! res)
+ sensors_fatal_error(__func__, "Allocating a new expression");
+ return res;
+}
diff --git a/tools/gator/daemon/libsensors/conf.h b/tools/gator/daemon/libsensors/conf.h
new file mode 100644
index 000000000000..b7ce4f7ca833
--- /dev/null
+++ b/tools/gator/daemon/libsensors/conf.h
@@ -0,0 +1,34 @@
+/*
+ conf.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 LIB_SENSORS_CONF_H
+#define LIB_SENSORS_CONF_H
+
+/* This is defined in conf-lex.l */
+int sensors_yylex(void);
+extern char sensors_lex_error[];
+extern const char *sensors_yyfilename;
+extern int sensors_yylineno;
+extern FILE *sensors_yyin;
+
+/* This is defined in conf-parse.y */
+int sensors_yyparse(void);
+
+#endif /* LIB_SENSORS_CONF_H */
diff --git a/tools/gator/daemon/libsensors/data.c b/tools/gator/daemon/libsensors/data.c
new file mode 100644
index 000000000000..cac9c8dc6eb1
--- /dev/null
+++ b/tools/gator/daemon/libsensors/data.c
@@ -0,0 +1,278 @@
+/*
+ data.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007, 2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 file modified by ARM on Jan 23, 2013 to move version.h to the current directory. ***/
+
+/* this define needed for strndup() */
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "access.h"
+#include "error.h"
+#include "data.h"
+#include "sensors.h"
+#include "version.h"
+
+const char *libsensors_version = LM_VERSION;
+
+char **sensors_config_files = NULL;
+int sensors_config_files_count = 0;
+int sensors_config_files_max = 0;
+
+sensors_chip *sensors_config_chips = NULL;
+int sensors_config_chips_count = 0;
+int sensors_config_chips_subst = 0;
+int sensors_config_chips_max = 0;
+
+sensors_bus *sensors_config_busses = NULL;
+int sensors_config_busses_count = 0;
+int sensors_config_busses_max = 0;
+
+sensors_chip_features *sensors_proc_chips = NULL;
+int sensors_proc_chips_count = 0;
+int sensors_proc_chips_max = 0;
+
+sensors_bus *sensors_proc_bus = NULL;
+int sensors_proc_bus_count = 0;
+int sensors_proc_bus_max = 0;
+
+void sensors_free_chip_name(sensors_chip_name *chip)
+{
+ free(chip->prefix);
+}
+
+/*
+ Parse a chip name to the internal representation. These are valid names:
+
+ lm78-i2c-10-5e *-i2c-10-5e
+ lm78-i2c-10-* *-i2c-10-*
+ lm78-i2c-*-5e *-i2c-*-5e
+ lm78-i2c-*-* *-i2c-*-*
+ lm78-isa-10dd *-isa-10dd
+ lm78-isa-* *-isa-*
+ lm78-* *-*
+
+ Here 'lm78' can be any prefix. 'i2c' and 'isa' are
+ literal strings, just like all dashes '-' and wildcards '*'. '10' can
+ be any decimal i2c bus number. '5e' can be any hexadecimal i2c device
+ address, and '10dd' any hexadecimal isa address.
+
+ The 'prefix' part in the result is freshly allocated. All old contents
+ of res is overwritten. res itself is not allocated. In case of an error
+ return (ie. != 0), res is undefined, but all allocations are undone.
+*/
+
+int sensors_parse_chip_name(const char *name, sensors_chip_name *res)
+{
+ char *dash;
+
+ /* First, the prefix. It's either "*" or a real chip name. */
+ if (!strncmp(name, "*-", 2)) {
+ res->prefix = SENSORS_CHIP_NAME_PREFIX_ANY;
+ name += 2;
+ } else {
+ if (!(dash = strchr(name, '-')))
+ return -SENSORS_ERR_CHIP_NAME;
+ res->prefix = strndup(name, dash - name);
+ if (!res->prefix)
+ sensors_fatal_error(__func__,
+ "Allocating name prefix");
+ name = dash + 1;
+ }
+
+ /* Then we have either a sole "*" (all chips with this name) or a bus
+ type and an address. */
+ if (!strcmp(name, "*")) {
+ res->bus.type = SENSORS_BUS_TYPE_ANY;
+ res->bus.nr = SENSORS_BUS_NR_ANY;
+ res->addr = SENSORS_CHIP_NAME_ADDR_ANY;
+ return 0;
+ }
+
+ if (!(dash = strchr(name, '-')))
+ goto ERROR;
+ if (!strncmp(name, "i2c", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_I2C;
+ else if (!strncmp(name, "isa", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_ISA;
+ else if (!strncmp(name, "pci", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_PCI;
+ else if (!strncmp(name, "spi", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_SPI;
+ else if (!strncmp(name, "virtual", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_VIRTUAL;
+ else if (!strncmp(name, "acpi", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_ACPI;
+ else if (!strncmp(name, "hid", dash - name))
+ res->bus.type = SENSORS_BUS_TYPE_HID;
+ else
+ goto ERROR;
+ name = dash + 1;
+
+ /* Some bus types (i2c, spi) have an additional bus number.
+ For these, the next part is either a "*" (any bus of that type)
+ or a decimal number. */
+ switch (res->bus.type) {
+ case SENSORS_BUS_TYPE_I2C:
+ case SENSORS_BUS_TYPE_SPI:
+ case SENSORS_BUS_TYPE_HID:
+ if (!strncmp(name, "*-", 2)) {
+ res->bus.nr = SENSORS_BUS_NR_ANY;
+ name += 2;
+ break;
+ }
+
+ res->bus.nr = strtoul(name, &dash, 10);
+ if (*name == '\0' || *dash != '-' || res->bus.nr < 0)
+ goto ERROR;
+ name = dash + 1;
+ break;
+ default:
+ res->bus.nr = SENSORS_BUS_NR_ANY;
+ }
+
+ /* Last part is the chip address, or "*" for any address. */
+ if (!strcmp(name, "*")) {
+ res->addr = SENSORS_CHIP_NAME_ADDR_ANY;
+ } else {
+ res->addr = strtoul(name, &dash, 16);
+ if (*name == '\0' || *dash != '\0' || res->addr < 0)
+ goto ERROR;
+ }
+
+ return 0;
+
+ERROR:
+ free(res->prefix);
+ return -SENSORS_ERR_CHIP_NAME;
+}
+
+int sensors_snprintf_chip_name(char *str, size_t size,
+ const sensors_chip_name *chip)
+{
+ if (sensors_chip_name_has_wildcards(chip))
+ return -SENSORS_ERR_WILDCARDS;
+
+ switch (chip->bus.type) {
+ case SENSORS_BUS_TYPE_ISA:
+ return snprintf(str, size, "%s-isa-%04x", chip->prefix,
+ chip->addr);
+ case SENSORS_BUS_TYPE_PCI:
+ return snprintf(str, size, "%s-pci-%04x", chip->prefix,
+ chip->addr);
+ case SENSORS_BUS_TYPE_I2C:
+ return snprintf(str, size, "%s-i2c-%hd-%02x", chip->prefix,
+ chip->bus.nr, chip->addr);
+ case SENSORS_BUS_TYPE_SPI:
+ return snprintf(str, size, "%s-spi-%hd-%x", chip->prefix,
+ chip->bus.nr, chip->addr);
+ case SENSORS_BUS_TYPE_VIRTUAL:
+ return snprintf(str, size, "%s-virtual-%x", chip->prefix,
+ chip->addr);
+ case SENSORS_BUS_TYPE_ACPI:
+ return snprintf(str, size, "%s-acpi-%x", chip->prefix,
+ chip->addr);
+ case SENSORS_BUS_TYPE_HID:
+ return snprintf(str, size, "%s-hid-%hd-%x", chip->prefix,
+ chip->bus.nr, chip->addr);
+ }
+
+ return -SENSORS_ERR_CHIP_NAME;
+}
+
+int sensors_parse_bus_id(const char *name, sensors_bus_id *bus)
+{
+ char *endptr;
+
+ if (strncmp(name, "i2c-", 4)) {
+ return -SENSORS_ERR_BUS_NAME;
+ }
+ name += 4;
+ bus->type = SENSORS_BUS_TYPE_I2C;
+ bus->nr = strtoul(name, &endptr, 10);
+ if (*name == '\0' || *endptr != '\0' || bus->nr < 0)
+ return -SENSORS_ERR_BUS_NAME;
+ return 0;
+}
+
+static int sensors_substitute_chip(sensors_chip_name *name,
+ const char *filename, int lineno)
+{
+ int i, j;
+ for (i = 0; i < sensors_config_busses_count; i++)
+ if (sensors_config_busses[i].bus.type == name->bus.type &&
+ sensors_config_busses[i].bus.nr == name->bus.nr)
+ break;
+
+ if (i == sensors_config_busses_count) {
+ sensors_parse_error_wfn("Undeclared bus id referenced",
+ filename, lineno);
+ name->bus.nr = SENSORS_BUS_NR_IGNORE;
+ return -SENSORS_ERR_BUS_NAME;
+ }
+
+ /* Compare the adapter names */
+ for (j = 0; j < sensors_proc_bus_count; j++) {
+ if (!strcmp(sensors_config_busses[i].adapter,
+ sensors_proc_bus[j].adapter)) {
+ name->bus.nr = sensors_proc_bus[j].bus.nr;
+ return 0;
+ }
+ }
+
+ /* We did not find a matching bus name, simply ignore this chip
+ config entry. */
+ name->bus.nr = SENSORS_BUS_NR_IGNORE;
+ return 0;
+}
+
+/* Bus substitution is on a per-configuration file basis, so we keep
+ memory (in sensors_config_chips_subst) of which chip entries have been
+ already substituted. */
+int sensors_substitute_busses(void)
+{
+ int err, i, j, lineno;
+ sensors_chip_name_list *chips;
+ const char *filename;
+ int res = 0;
+
+ for (i = sensors_config_chips_subst;
+ i < sensors_config_chips_count; i++) {
+ filename = sensors_config_chips[i].line.filename;
+ lineno = sensors_config_chips[i].line.lineno;
+ chips = &sensors_config_chips[i].chips;
+ for (j = 0; j < chips->fits_count; j++) {
+ /* We can only substitute if a specific bus number
+ is given. */
+ if (chips->fits[j].bus.nr == SENSORS_BUS_NR_ANY)
+ continue;
+
+ err = sensors_substitute_chip(&chips->fits[j],
+ filename, lineno);
+ if (err)
+ res = err;
+ }
+ }
+ sensors_config_chips_subst = sensors_config_chips_count;
+ return res;
+}
diff --git a/tools/gator/daemon/libsensors/data.h b/tools/gator/daemon/libsensors/data.h
new file mode 100644
index 000000000000..4a23eabe1414
--- /dev/null
+++ b/tools/gator/daemon/libsensors/data.h
@@ -0,0 +1,184 @@
+/*
+ data.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007, 2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 LIB_SENSORS_DATA_H
+#define LIB_SENSORS_DATA_H
+
+#include "sensors.h"
+#include "general.h"
+
+/* This header file contains all kinds of data structures which are used
+ for the representation of the config file data and the sensors
+ data. */
+
+/* Kinds of expression operators recognized */
+typedef enum sensors_operation {
+ sensors_add, sensors_sub, sensors_multiply, sensors_divide,
+ sensors_negate, sensors_exp, sensors_log,
+} sensors_operation;
+
+/* An expression can have several forms */
+typedef enum sensors_expr_kind {
+ sensors_kind_val, sensors_kind_source, sensors_kind_var,
+ sensors_kind_sub
+} sensors_expr_kind;
+
+/* An expression. It is either a floating point value, a variable name,
+ an operation on subexpressions, or the special value 'sub' } */
+struct sensors_expr;
+
+typedef struct sensors_subexpr {
+ sensors_operation op;
+ struct sensors_expr *sub1;
+ struct sensors_expr *sub2;
+} sensors_subexpr;
+
+typedef struct sensors_expr {
+ sensors_expr_kind kind;
+ union {
+ double val;
+ char *var;
+ sensors_subexpr subexpr;
+ } data;
+} sensors_expr;
+
+/* Config file line reference */
+typedef struct sensors_config_line {
+ const char *filename;
+ int lineno;
+} sensors_config_line;
+
+/* Config file label declaration: a feature name, combined with the label
+ value */
+typedef struct sensors_label {
+ char *name;
+ char *value;
+ sensors_config_line line;
+} sensors_label;
+
+/* Config file set declaration: a subfeature name, combined with an
+ expression */
+typedef struct sensors_set {
+ char *name;
+ sensors_expr *value;
+ sensors_config_line line;
+} sensors_set;
+
+/* Config file compute declaration: a feature name, combined with two
+ expressions */
+typedef struct sensors_compute {
+ char *name;
+ sensors_expr *from_proc;
+ sensors_expr *to_proc;
+ sensors_config_line line;
+} sensors_compute;
+
+/* Config file ignore declaration: a feature name */
+typedef struct sensors_ignore {
+ char *name;
+ sensors_config_line line;
+} sensors_ignore;
+
+/* A list of chip names, used to represent a config file chips declaration */
+typedef struct sensors_chip_name_list {
+ sensors_chip_name *fits;
+ int fits_count;
+ int fits_max;
+} sensors_chip_name_list;
+
+/* A config file chip block */
+typedef struct sensors_chip {
+ sensors_chip_name_list chips;
+ sensors_label *labels;
+ int labels_count;
+ int labels_max;
+ sensors_set *sets;
+ int sets_count;
+ int sets_max;
+ sensors_compute *computes;
+ int computes_count;
+ int computes_max;
+ sensors_ignore *ignores;
+ int ignores_count;
+ int ignores_max;
+ sensors_config_line line;
+} sensors_chip;
+
+/* Config file bus declaration: the bus type and number, combined with adapter
+ name */
+typedef struct sensors_bus {
+ char *adapter;
+ sensors_bus_id bus;
+ sensors_config_line line;
+} sensors_bus;
+
+/* Internal data about all features and subfeatures of a chip */
+typedef struct sensors_chip_features {
+ struct sensors_chip_name chip;
+ struct sensors_feature *feature;
+ struct sensors_subfeature *subfeature;
+ int feature_count;
+ int subfeature_count;
+} sensors_chip_features;
+
+extern char **sensors_config_files;
+extern int sensors_config_files_count;
+extern int sensors_config_files_max;
+
+#define sensors_add_config_files(el) sensors_add_array_el( \
+ (el), &sensors_config_files, &sensors_config_files_count, \
+ &sensors_config_files_max, sizeof(char *))
+
+extern sensors_chip *sensors_config_chips;
+extern int sensors_config_chips_count;
+extern int sensors_config_chips_subst;
+extern int sensors_config_chips_max;
+
+extern sensors_bus *sensors_config_busses;
+extern int sensors_config_busses_count;
+extern int sensors_config_busses_max;
+
+extern sensors_chip_features *sensors_proc_chips;
+extern int sensors_proc_chips_count;
+extern int sensors_proc_chips_max;
+
+#define sensors_add_proc_chips(el) sensors_add_array_el( \
+ (el), &sensors_proc_chips, &sensors_proc_chips_count,\
+ &sensors_proc_chips_max, sizeof(struct sensors_chip_features))
+
+extern sensors_bus *sensors_proc_bus;
+extern int sensors_proc_bus_count;
+extern int sensors_proc_bus_max;
+
+#define sensors_add_proc_bus(el) sensors_add_array_el( \
+ (el), &sensors_proc_bus, &sensors_proc_bus_count,\
+ &sensors_proc_bus_max, sizeof(struct sensors_bus))
+
+/* Substitute configuration bus numbers with real-world bus numbers
+ in the chips lists */
+int sensors_substitute_busses(void);
+
+
+/* Parse a bus id into its components. Returns 0 on success, a value from
+ error.h on failure. */
+int sensors_parse_bus_id(const char *name, sensors_bus_id *bus);
+
+#endif /* def LIB_SENSORS_DATA_H */
diff --git a/tools/gator/daemon/libsensors/error.c b/tools/gator/daemon/libsensors/error.c
new file mode 100644
index 000000000000..55bde81295fb
--- /dev/null
+++ b/tools/gator/daemon/libsensors/error.c
@@ -0,0 +1,92 @@
+/*
+ error.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "error.h"
+#include "general.h"
+
+static void sensors_default_parse_error(const char *err, int lineno);
+static void sensors_default_parse_error_wfn(const char *err,
+ const char *filename, int lineno);
+static void sensors_default_fatal_error(const char *proc, const char *err);
+
+void (*sensors_parse_error) (const char *err, int lineno) =
+ sensors_default_parse_error;
+void (*sensors_parse_error_wfn) (const char *err, const char *filename,
+ int lineno) = sensors_default_parse_error_wfn;
+void (*sensors_fatal_error) (const char *proc, const char *err) =
+ sensors_default_fatal_error;
+
+static const char *errorlist[] = {
+ /* Invalid error code */ "Unknown error",
+ /* SENSORS_ERR_WILDCARDS */ "Wildcard found in chip name",
+ /* SENSORS_ERR_NO_ENTRY */ "No such subfeature known",
+ /* SENSORS_ERR_ACCESS_R */ "Can't read",
+ /* SENSORS_ERR_KERNEL */ "Kernel interface error",
+ /* SENSORS_ERR_DIV_ZERO */ "Divide by zero",
+ /* SENSORS_ERR_CHIP_NAME */ "Can't parse chip name",
+ /* SENSORS_ERR_BUS_NAME */ "Can't parse bus name",
+ /* SENSORS_ERR_PARSE */ "General parse error",
+ /* SENSORS_ERR_ACCESS_W */ "Can't write",
+ /* SENSORS_ERR_IO */ "I/O error",
+ /* SENSORS_ERR_RECURSION */ "Evaluation recurses too deep",
+};
+
+const char *sensors_strerror(int errnum)
+{
+ if (errnum < 0)
+ errnum = -errnum;
+ if (errnum >= ARRAY_SIZE(errorlist))
+ errnum = 0;
+ return errorlist[errnum];
+}
+
+void sensors_default_parse_error(const char *err, int lineno)
+{
+ if (lineno)
+ fprintf(stderr, "Error: Line %d: %s\n", lineno, err);
+ else
+ fprintf(stderr, "Error: %s\n", err);
+}
+
+void sensors_default_parse_error_wfn(const char *err,
+ const char *filename, int lineno)
+{
+ /* If application provided a custom parse error reporting function
+ but not the variant with the filename, fall back to the original
+ variant without the filename, for backwards compatibility. */
+ if (sensors_parse_error != sensors_default_parse_error ||
+ !filename)
+ return sensors_parse_error(err, lineno);
+
+ if (lineno)
+ fprintf(stderr, "Error: File %s, line %d: %s\n", filename,
+ lineno, err);
+ else
+ fprintf(stderr, "Error: File %s: %s\n", filename, err);
+}
+
+void sensors_default_fatal_error(const char *proc, const char *err)
+{
+ fprintf(stderr, "Fatal error in `%s': %s\n", proc, err);
+ exit(1);
+}
diff --git a/tools/gator/daemon/libsensors/error.h b/tools/gator/daemon/libsensors/error.h
new file mode 100644
index 000000000000..37cdc956151d
--- /dev/null
+++ b/tools/gator/daemon/libsensors/error.h
@@ -0,0 +1,74 @@
+/*
+ error.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 LIB_SENSORS_ERROR_H
+#define LIB_SENSORS_ERROR_H
+
+#define SENSORS_ERR_WILDCARDS 1 /* Wildcard found in chip name */
+#define SENSORS_ERR_NO_ENTRY 2 /* No such subfeature known */
+#define SENSORS_ERR_ACCESS_R 3 /* Can't read */
+#define SENSORS_ERR_KERNEL 4 /* Kernel interface error */
+#define SENSORS_ERR_DIV_ZERO 5 /* Divide by zero */
+#define SENSORS_ERR_CHIP_NAME 6 /* Can't parse chip name */
+#define SENSORS_ERR_BUS_NAME 7 /* Can't parse bus name */
+#define SENSORS_ERR_PARSE 8 /* General parse error */
+#define SENSORS_ERR_ACCESS_W 9 /* Can't write */
+#define SENSORS_ERR_IO 10 /* I/O error */
+#define SENSORS_ERR_RECURSION 11 /* Evaluation recurses too deep */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* This function returns a pointer to a string which describes the error.
+ errnum may be negative (the corresponding positive error is returned).
+ You may not modify the result! */
+const char *sensors_strerror(int errnum);
+
+/* These functions are called when a parse error is detected. Give them new
+ values, and your own functions are called instead of the default (which
+ print to stderr). These functions may terminate the program, but they
+ usually output an error and return. The first function is the original
+ one, the second one was added later when support for multiple
+ configuration files was added.
+ The library code now only calls the second function. However, for
+ backwards compatibility, if an application provides a custom handling
+ function for the first function but not the second, then all parse
+ errors will be reported using the first function (that is, the filename
+ is never reported.)
+ Note that filename can be NULL (if filename isn't known) and lineno
+ can be 0 (if the error occurs before the actual parsing starts.) */
+extern void (*sensors_parse_error) (const char *err, int lineno);
+extern void (*sensors_parse_error_wfn) (const char *err,
+ const char *filename, int lineno);
+
+/* This function is called when an immediately fatal error (like no
+ memory left) is detected. Give it a new value, and your own function
+ is called instead of the default (which prints to stderr and ends
+ the program). Never let it return! */
+extern void (*sensors_fatal_error) (const char *proc, const char *err);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* def LIB_SENSORS_ERROR_H */
diff --git a/tools/gator/daemon/libsensors/general.c b/tools/gator/daemon/libsensors/general.c
new file mode 100644
index 000000000000..f237e3b59c22
--- /dev/null
+++ b/tools/gator/daemon/libsensors/general.c
@@ -0,0 +1,85 @@
+/*
+ general.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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.
+*/
+
+#include "error.h"
+#include "general.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+#define A_BUNCH 16
+
+void sensors_malloc_array(void *list, int *num_el, int *max_el, int el_size)
+{
+ void **my_list = (void **)list;
+
+ *my_list = malloc(el_size*A_BUNCH);
+ if (! *my_list)
+ sensors_fatal_error(__func__, "Allocating new elements");
+ *max_el = A_BUNCH;
+ *num_el = 0;
+}
+
+void sensors_free_array(void *list, int *num_el, int *max_el)
+{
+ void **my_list = (void **)list;
+
+ free(*my_list);
+ *my_list = NULL;
+ *num_el = 0;
+ *max_el = 0;
+}
+
+void sensors_add_array_el(const void *el, void *list, int *num_el,
+ int *max_el, int el_size)
+{
+ int new_max_el;
+ void **my_list = (void *)list;
+ if (*num_el + 1 > *max_el) {
+ new_max_el = *max_el + A_BUNCH;
+ *my_list = realloc(*my_list, new_max_el * el_size);
+ if (! *my_list)
+ sensors_fatal_error(__func__,
+ "Allocating new elements");
+ *max_el = new_max_el;
+ }
+ memcpy(((char *) *my_list) + *num_el * el_size, el, el_size);
+ (*num_el) ++;
+}
+
+void sensors_add_array_els(const void *els, int nr_els, void *list,
+ int *num_el, int *max_el, int el_size)
+{
+ int new_max_el;
+ void **my_list = (void *)list;
+ if (*num_el + nr_els > *max_el) {
+ new_max_el = (*max_el + nr_els + A_BUNCH);
+ new_max_el -= new_max_el % A_BUNCH;
+ *my_list = realloc(*my_list, new_max_el * el_size);
+ if (! *my_list)
+ sensors_fatal_error(__func__,
+ "Allocating new elements");
+ *max_el = new_max_el;
+ }
+ memcpy(((char *)*my_list) + *num_el * el_size, els, el_size * nr_els);
+ *num_el += nr_els;
+}
diff --git a/tools/gator/daemon/libsensors/general.h b/tools/gator/daemon/libsensors/general.h
new file mode 100644
index 000000000000..a3971e02e1b7
--- /dev/null
+++ b/tools/gator/daemon/libsensors/general.h
@@ -0,0 +1,39 @@
+/*
+ general.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 LIB_SENSORS_GENERAL
+#define LIB_SENSORS_GENERAL
+
+/* These are general purpose functions. They allow you to use variable-
+ length arrays, which are extended automatically. A distinction is
+ made between the current number of elements and the maximum number.
+ You can only add elements at the end. Primitive, but very useful
+ for internal use. */
+void sensors_malloc_array(void *list, int *num_el, int *max_el,
+ int el_size);
+void sensors_free_array(void *list, int *num_el, int *max_el);
+void sensors_add_array_el(const void *el, void *list, int *num_el,
+ int *max_el, int el_size);
+void sensors_add_array_els(const void *els, int nr_els, void *list,
+ int *num_el, int *max_el, int el_size);
+
+#define ARRAY_SIZE(arr) (int)(sizeof(arr) / sizeof((arr)[0]))
+
+#endif /* LIB_SENSORS_GENERAL */
diff --git a/tools/gator/daemon/libsensors/init.c b/tools/gator/daemon/libsensors/init.c
new file mode 100644
index 000000000000..558046e76c24
--- /dev/null
+++ b/tools/gator/daemon/libsensors/init.c
@@ -0,0 +1,341 @@
+/*
+ init.c - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007, 2009 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 file modified by ARM on Jan 23, 2013 to cast alphasort to supress a warning as it's prototype is different on android. ***/
+
+/* Needed for scandir() and alphasort() */
+#define _BSD_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "sensors.h"
+#include "data.h"
+#include "error.h"
+#include "access.h"
+#include "conf.h"
+#include "sysfs.h"
+#include "scanner.h"
+#include "init.h"
+
+#define DEFAULT_CONFIG_FILE ETCDIR "/sensors3.conf"
+#define ALT_CONFIG_FILE ETCDIR "/sensors.conf"
+#define DEFAULT_CONFIG_DIR ETCDIR "/sensors.d"
+
+/* Wrapper around sensors_yyparse(), which clears the locale so that
+ the decimal numbers are always parsed properly. */
+static int sensors_parse(void)
+{
+ int res;
+ char *locale;
+
+ /* Remember the current locale and clear it */
+ locale = setlocale(LC_ALL, NULL);
+ if (locale) {
+ locale = strdup(locale);
+ if (!locale)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ setlocale(LC_ALL, "C");
+ }
+
+ res = sensors_yyparse();
+
+ /* Restore the old locale */
+ if (locale) {
+ setlocale(LC_ALL, locale);
+ free(locale);
+ }
+
+ return res;
+}
+
+static void free_bus(sensors_bus *bus)
+{
+ free(bus->adapter);
+}
+
+static void free_config_busses(void)
+{
+ int i;
+
+ for (i = 0; i < sensors_config_busses_count; i++)
+ free_bus(&sensors_config_busses[i]);
+ free(sensors_config_busses);
+ sensors_config_busses = NULL;
+ sensors_config_busses_count = sensors_config_busses_max = 0;
+}
+
+static int parse_config(FILE *input, const char *name)
+{
+ int err;
+ char *name_copy;
+
+ if (name) {
+ /* Record configuration file name for error reporting */
+ name_copy = strdup(name);
+ if (!name_copy)
+ sensors_fatal_error(__func__, "Out of memory");
+ sensors_add_config_files(&name_copy);
+ } else
+ name_copy = NULL;
+
+ if (sensors_scanner_init(input, name_copy)) {
+ err = -SENSORS_ERR_PARSE;
+ goto exit_cleanup;
+ }
+ err = sensors_parse();
+ sensors_scanner_exit();
+ if (err) {
+ err = -SENSORS_ERR_PARSE;
+ goto exit_cleanup;
+ }
+
+ err = sensors_substitute_busses();
+
+exit_cleanup:
+ free_config_busses();
+ return err;
+}
+
+static int config_file_filter(const struct dirent *entry)
+{
+ return entry->d_name[0] != '.'; /* Skip hidden files */
+}
+
+static int add_config_from_dir(const char *dir)
+{
+ int count, res, i;
+ struct dirent **namelist;
+
+ count = scandir(dir, &namelist, config_file_filter, (int (*)(const struct dirent **, const struct dirent **))alphasort);
+ if (count < 0) {
+ /* Do not return an error if directory does not exist */
+ if (errno == ENOENT)
+ return 0;
+
+ sensors_parse_error_wfn(strerror(errno), NULL, 0);
+ return -SENSORS_ERR_PARSE;
+ }
+
+ for (res = 0, i = 0; !res && i < count; i++) {
+ int len;
+ char path[PATH_MAX];
+ FILE *input;
+ struct stat st;
+
+ len = snprintf(path, sizeof(path), "%s/%s", dir,
+ namelist[i]->d_name);
+ if (len < 0 || len >= (int)sizeof(path)) {
+ res = -SENSORS_ERR_PARSE;
+ continue;
+ }
+
+ /* Only accept regular files */
+ if (stat(path, &st) < 0 || !S_ISREG(st.st_mode))
+ continue;
+
+ input = fopen(path, "r");
+ if (input) {
+ res = parse_config(input, path);
+ fclose(input);
+ } else {
+ res = -SENSORS_ERR_PARSE;
+ sensors_parse_error_wfn(strerror(errno), path, 0);
+ }
+ }
+
+ /* Free memory allocated by scandir() */
+ for (i = 0; i < count; i++)
+ free(namelist[i]);
+ free(namelist);
+
+ return res;
+}
+
+int sensors_init(FILE *input)
+{
+ int res;
+
+ if (!sensors_init_sysfs())
+ return -SENSORS_ERR_KERNEL;
+ if ((res = sensors_read_sysfs_bus()) ||
+ (res = sensors_read_sysfs_chips()))
+ goto exit_cleanup;
+
+ if (input) {
+ res = parse_config(input, NULL);
+ if (res)
+ goto exit_cleanup;
+ } else {
+ const char* name;
+
+ /* No configuration provided, use default */
+ input = fopen(name = DEFAULT_CONFIG_FILE, "r");
+ if (!input && errno == ENOENT)
+ input = fopen(name = ALT_CONFIG_FILE, "r");
+ if (input) {
+ res = parse_config(input, name);
+ fclose(input);
+ if (res)
+ goto exit_cleanup;
+
+ } else if (errno != ENOENT) {
+ sensors_parse_error_wfn(strerror(errno), name, 0);
+ res = -SENSORS_ERR_PARSE;
+ goto exit_cleanup;
+ }
+
+ /* Also check for files in default directory */
+ res = add_config_from_dir(DEFAULT_CONFIG_DIR);
+ if (res)
+ goto exit_cleanup;
+ }
+
+ return 0;
+
+exit_cleanup:
+ sensors_cleanup();
+ return res;
+}
+
+static void free_chip_name(sensors_chip_name *name)
+{
+ free(name->prefix);
+ free(name->path);
+}
+
+static void free_chip_features(sensors_chip_features *features)
+{
+ int i;
+
+ for (i = 0; i < features->subfeature_count; i++)
+ free(features->subfeature[i].name);
+ free(features->subfeature);
+ for (i = 0; i < features->feature_count; i++)
+ free(features->feature[i].name);
+ free(features->feature);
+}
+
+static void free_label(sensors_label *label)
+{
+ free(label->name);
+ free(label->value);
+}
+
+void sensors_free_expr(sensors_expr *expr)
+{
+ if (expr->kind == sensors_kind_var)
+ free(expr->data.var);
+ else if (expr->kind == sensors_kind_sub) {
+ if (expr->data.subexpr.sub1)
+ sensors_free_expr(expr->data.subexpr.sub1);
+ if (expr->data.subexpr.sub2)
+ sensors_free_expr(expr->data.subexpr.sub2);
+ }
+ free(expr);
+}
+
+static void free_set(sensors_set *set)
+{
+ free(set->name);
+ sensors_free_expr(set->value);
+}
+
+static void free_compute(sensors_compute *compute)
+{
+ free(compute->name);
+ sensors_free_expr(compute->from_proc);
+ sensors_free_expr(compute->to_proc);
+}
+
+static void free_ignore(sensors_ignore *ignore)
+{
+ free(ignore->name);
+}
+
+static void free_chip(sensors_chip *chip)
+{
+ int i;
+
+ for (i = 0; i < chip->chips.fits_count; i++)
+ free_chip_name(&chip->chips.fits[i]);
+ free(chip->chips.fits);
+ chip->chips.fits_count = chip->chips.fits_max = 0;
+
+ for (i = 0; i < chip->labels_count; i++)
+ free_label(&chip->labels[i]);
+ free(chip->labels);
+ chip->labels_count = chip->labels_max = 0;
+
+ for (i = 0; i < chip->sets_count; i++)
+ free_set(&chip->sets[i]);
+ free(chip->sets);
+ chip->sets_count = chip->sets_max = 0;
+
+ for (i = 0; i < chip->computes_count; i++)
+ free_compute(&chip->computes[i]);
+ free(chip->computes);
+ chip->computes_count = chip->computes_max = 0;
+
+ for (i = 0; i < chip->ignores_count; i++)
+ free_ignore(&chip->ignores[i]);
+ free(chip->ignores);
+ chip->ignores_count = chip->ignores_max = 0;
+}
+
+void sensors_cleanup(void)
+{
+ int i;
+
+ for (i = 0; i < sensors_proc_chips_count; i++) {
+ free_chip_name(&sensors_proc_chips[i].chip);
+ free_chip_features(&sensors_proc_chips[i]);
+ }
+ free(sensors_proc_chips);
+ sensors_proc_chips = NULL;
+ sensors_proc_chips_count = sensors_proc_chips_max = 0;
+
+ for (i = 0; i < sensors_config_chips_count; i++)
+ free_chip(&sensors_config_chips[i]);
+ free(sensors_config_chips);
+ sensors_config_chips = NULL;
+ sensors_config_chips_count = sensors_config_chips_max = 0;
+ sensors_config_chips_subst = 0;
+
+ for (i = 0; i < sensors_proc_bus_count; i++)
+ free_bus(&sensors_proc_bus[i]);
+ free(sensors_proc_bus);
+ sensors_proc_bus = NULL;
+ sensors_proc_bus_count = sensors_proc_bus_max = 0;
+
+ for (i = 0; i < sensors_config_files_count; i++)
+ free(sensors_config_files[i]);
+ free(sensors_config_files);
+ sensors_config_files = NULL;
+ sensors_config_files_count = sensors_config_files_max = 0;
+}
diff --git a/tools/gator/daemon/libsensors/init.h b/tools/gator/daemon/libsensors/init.h
new file mode 100644
index 000000000000..47006a62db63
--- /dev/null
+++ b/tools/gator/daemon/libsensors/init.h
@@ -0,0 +1,28 @@
+/*
+ init.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 LIB_SENSORS_INIT_H
+#define LIB_SENSORS_INIT_H
+
+#include "data.h"
+
+void sensors_free_expr(sensors_expr *expr);
+
+#endif /* def LIB_SENSORS_INIT_H */
diff --git a/tools/gator/daemon/libsensors/scanner.h b/tools/gator/daemon/libsensors/scanner.h
new file mode 100644
index 000000000000..4c415167de5c
--- /dev/null
+++ b/tools/gator/daemon/libsensors/scanner.h
@@ -0,0 +1,32 @@
+/*
+ scanner.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 2006 Mark M. Hoffman <mhoffman@lightlink.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 file modified by ARM on Jan 23, 2013 to fix input defined but not used warning from conf-lex.c. ***/
+
+#ifndef LIB_SENSORS_SCANNER_H
+#define LIB_SENSORS_SCANNER_H
+
+int sensors_scanner_init(FILE *input, const char *filename);
+void sensors_scanner_exit(void);
+
+#define YY_NO_INPUT
+
+#endif
+
diff --git a/tools/gator/daemon/libsensors/sensors.h b/tools/gator/daemon/libsensors/sensors.h
new file mode 100644
index 000000000000..7874d028dc82
--- /dev/null
+++ b/tools/gator/daemon/libsensors/sensors.h
@@ -0,0 +1,311 @@
+/*
+ sensors.h - Part of libsensors, a Linux library for reading sensor data.
+ Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2007, 2010 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 file modified by ARM on Jan 23, 2013 to read non-scaled values. ***/
+
+#ifndef LIB_SENSORS_SENSORS_H
+#define LIB_SENSORS_SENSORS_H
+
+#include <stdio.h>
+#include <limits.h>
+
+/* Publicly accessible library functions */
+
+/* libsensors API version define, first digit is the major version (changed
+ when the API + ABI breaks), the third digit is incremented to track small
+ API additions like new flags / enum values. The second digit is for tracking
+ larger additions like new methods. */
+#define SENSORS_API_VERSION 0x432
+
+#define SENSORS_CHIP_NAME_PREFIX_ANY NULL
+#define SENSORS_CHIP_NAME_ADDR_ANY (-1)
+
+#define SENSORS_BUS_TYPE_ANY (-1)
+#define SENSORS_BUS_TYPE_I2C 0
+#define SENSORS_BUS_TYPE_ISA 1
+#define SENSORS_BUS_TYPE_PCI 2
+#define SENSORS_BUS_TYPE_SPI 3
+#define SENSORS_BUS_TYPE_VIRTUAL 4
+#define SENSORS_BUS_TYPE_ACPI 5
+#define SENSORS_BUS_TYPE_HID 6
+#define SENSORS_BUS_NR_ANY (-1)
+#define SENSORS_BUS_NR_IGNORE (-2)
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern const char *libsensors_version;
+
+extern int sensors_sysfs_no_scaling;
+
+typedef struct sensors_bus_id {
+ short type;
+ short nr;
+} sensors_bus_id;
+
+/* A chip name is encoded in this structure */
+typedef struct sensors_chip_name {
+ char *prefix;
+ sensors_bus_id bus;
+ int addr;
+ char *path;
+} sensors_chip_name;
+
+/* Load the configuration file and the detected chips list. If this
+ returns a value unequal to zero, you are in trouble; you can not
+ assume anything will be initialized properly. If you want to
+ reload the configuration file, call sensors_cleanup() below before
+ calling sensors_init() again. */
+int sensors_init(FILE *input);
+
+/* Clean-up function: You can't access anything after
+ this, until the next sensors_init() call! */
+void sensors_cleanup(void);
+
+/* Parse a chip name to the internal representation. Return 0 on success, <0
+ on error. */
+int sensors_parse_chip_name(const char *orig_name, sensors_chip_name *res);
+
+/* Free memory allocated for the internal representation of a chip name. */
+void sensors_free_chip_name(sensors_chip_name *chip);
+
+/* Print a chip name from its internal representation. Note that chip should
+ not contain wildcard values! Return the number of characters printed on
+ success (same as snprintf), <0 on error. */
+int sensors_snprintf_chip_name(char *str, size_t size,
+ const sensors_chip_name *chip);
+
+/* This function returns the adapter name of a bus,
+ as used within the sensors_chip_name structure. If it could not be found,
+ it returns NULL */
+const char *sensors_get_adapter_name(const sensors_bus_id *bus);
+
+typedef struct sensors_feature sensors_feature;
+
+/* Look up the label for a given feature. Note that chip should not
+ contain wildcard values! The returned string is newly allocated (free it
+ yourself). On failure, NULL is returned.
+ If no label exists for this feature, its name is returned itself. */
+char *sensors_get_label(const sensors_chip_name *name,
+ const sensors_feature *feature);
+
+/* Read the value of a subfeature of a certain chip. Note that chip should not
+ contain wildcard values! This function will return 0 on success, and <0
+ on failure. */
+int sensors_get_value(const sensors_chip_name *name, int subfeat_nr,
+ double *value);
+
+/* Set the value of a subfeature of a certain chip. Note that chip should not
+ contain wildcard values! This function will return 0 on success, and <0
+ on failure. */
+int sensors_set_value(const sensors_chip_name *name, int subfeat_nr,
+ double value);
+
+/* Execute all set statements for this particular chip. The chip may contain
+ wildcards! This function will return 0 on success, and <0 on failure. */
+int sensors_do_chip_sets(const sensors_chip_name *name);
+
+/* This function returns all detected chips that match a given chip name,
+ one by one. If no chip name is provided, all detected chips are returned.
+ To start at the beginning of the list, use 0 for nr; NULL is returned if
+ we are at the end of the list. Do not try to change these chip names, as
+ they point to internal structures! */
+const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name
+ *match, int *nr);
+
+/* These defines are used in the flags field of sensors_subfeature */
+#define SENSORS_MODE_R 1
+#define SENSORS_MODE_W 2
+#define SENSORS_COMPUTE_MAPPING 4
+
+typedef enum sensors_feature_type {
+ SENSORS_FEATURE_IN = 0x00,
+ SENSORS_FEATURE_FAN = 0x01,
+ SENSORS_FEATURE_TEMP = 0x02,
+ SENSORS_FEATURE_POWER = 0x03,
+ SENSORS_FEATURE_ENERGY = 0x04,
+ SENSORS_FEATURE_CURR = 0x05,
+ SENSORS_FEATURE_HUMIDITY = 0x06,
+ SENSORS_FEATURE_MAX_MAIN,
+ SENSORS_FEATURE_VID = 0x10,
+ SENSORS_FEATURE_INTRUSION = 0x11,
+ SENSORS_FEATURE_MAX_OTHER,
+ SENSORS_FEATURE_BEEP_ENABLE = 0x18,
+ SENSORS_FEATURE_UNKNOWN = INT_MAX,
+} sensors_feature_type;
+
+/* All the sensor types (in, fan, temp, vid) are a multiple of 0x100 apart,
+ and sensor subfeatures which have no compute mapping have bit 7 set. */
+typedef enum sensors_subfeature_type {
+ SENSORS_SUBFEATURE_IN_INPUT = SENSORS_FEATURE_IN << 8,
+ SENSORS_SUBFEATURE_IN_MIN,
+ SENSORS_SUBFEATURE_IN_MAX,
+ SENSORS_SUBFEATURE_IN_LCRIT,
+ SENSORS_SUBFEATURE_IN_CRIT,
+ SENSORS_SUBFEATURE_IN_AVERAGE,
+ SENSORS_SUBFEATURE_IN_LOWEST,
+ SENSORS_SUBFEATURE_IN_HIGHEST,
+ SENSORS_SUBFEATURE_IN_ALARM = (SENSORS_FEATURE_IN << 8) | 0x80,
+ SENSORS_SUBFEATURE_IN_MIN_ALARM,
+ SENSORS_SUBFEATURE_IN_MAX_ALARM,
+ SENSORS_SUBFEATURE_IN_BEEP,
+ SENSORS_SUBFEATURE_IN_LCRIT_ALARM,
+ SENSORS_SUBFEATURE_IN_CRIT_ALARM,
+
+ SENSORS_SUBFEATURE_FAN_INPUT = SENSORS_FEATURE_FAN << 8,
+ SENSORS_SUBFEATURE_FAN_MIN,
+ SENSORS_SUBFEATURE_FAN_MAX,
+ SENSORS_SUBFEATURE_FAN_ALARM = (SENSORS_FEATURE_FAN << 8) | 0x80,
+ SENSORS_SUBFEATURE_FAN_FAULT,
+ SENSORS_SUBFEATURE_FAN_DIV,
+ SENSORS_SUBFEATURE_FAN_BEEP,
+ SENSORS_SUBFEATURE_FAN_PULSES,
+ SENSORS_SUBFEATURE_FAN_MIN_ALARM,
+ SENSORS_SUBFEATURE_FAN_MAX_ALARM,
+
+ SENSORS_SUBFEATURE_TEMP_INPUT = SENSORS_FEATURE_TEMP << 8,
+ SENSORS_SUBFEATURE_TEMP_MAX,
+ SENSORS_SUBFEATURE_TEMP_MAX_HYST,
+ SENSORS_SUBFEATURE_TEMP_MIN,
+ SENSORS_SUBFEATURE_TEMP_CRIT,
+ SENSORS_SUBFEATURE_TEMP_CRIT_HYST,
+ SENSORS_SUBFEATURE_TEMP_LCRIT,
+ SENSORS_SUBFEATURE_TEMP_EMERGENCY,
+ SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST,
+ SENSORS_SUBFEATURE_TEMP_LOWEST,
+ SENSORS_SUBFEATURE_TEMP_HIGHEST,
+ SENSORS_SUBFEATURE_TEMP_ALARM = (SENSORS_FEATURE_TEMP << 8) | 0x80,
+ SENSORS_SUBFEATURE_TEMP_MAX_ALARM,
+ SENSORS_SUBFEATURE_TEMP_MIN_ALARM,
+ SENSORS_SUBFEATURE_TEMP_CRIT_ALARM,
+ SENSORS_SUBFEATURE_TEMP_FAULT,
+ SENSORS_SUBFEATURE_TEMP_TYPE,
+ SENSORS_SUBFEATURE_TEMP_OFFSET,
+ SENSORS_SUBFEATURE_TEMP_BEEP,
+ SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM,
+ SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM,
+
+ SENSORS_SUBFEATURE_POWER_AVERAGE = SENSORS_FEATURE_POWER << 8,
+ SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST,
+ SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST,
+ SENSORS_SUBFEATURE_POWER_INPUT,
+ SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST,
+ SENSORS_SUBFEATURE_POWER_INPUT_LOWEST,
+ SENSORS_SUBFEATURE_POWER_CAP,
+ SENSORS_SUBFEATURE_POWER_CAP_HYST,
+ SENSORS_SUBFEATURE_POWER_MAX,
+ SENSORS_SUBFEATURE_POWER_CRIT,
+ SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL = (SENSORS_FEATURE_POWER << 8) | 0x80,
+ SENSORS_SUBFEATURE_POWER_ALARM,
+ SENSORS_SUBFEATURE_POWER_CAP_ALARM,
+ SENSORS_SUBFEATURE_POWER_MAX_ALARM,
+ SENSORS_SUBFEATURE_POWER_CRIT_ALARM,
+
+ SENSORS_SUBFEATURE_ENERGY_INPUT = SENSORS_FEATURE_ENERGY << 8,
+
+ SENSORS_SUBFEATURE_CURR_INPUT = SENSORS_FEATURE_CURR << 8,
+ SENSORS_SUBFEATURE_CURR_MIN,
+ SENSORS_SUBFEATURE_CURR_MAX,
+ SENSORS_SUBFEATURE_CURR_LCRIT,
+ SENSORS_SUBFEATURE_CURR_CRIT,
+ SENSORS_SUBFEATURE_CURR_AVERAGE,
+ SENSORS_SUBFEATURE_CURR_LOWEST,
+ SENSORS_SUBFEATURE_CURR_HIGHEST,
+ SENSORS_SUBFEATURE_CURR_ALARM = (SENSORS_FEATURE_CURR << 8) | 0x80,
+ SENSORS_SUBFEATURE_CURR_MIN_ALARM,
+ SENSORS_SUBFEATURE_CURR_MAX_ALARM,
+ SENSORS_SUBFEATURE_CURR_BEEP,
+ SENSORS_SUBFEATURE_CURR_LCRIT_ALARM,
+ SENSORS_SUBFEATURE_CURR_CRIT_ALARM,
+
+ SENSORS_SUBFEATURE_HUMIDITY_INPUT = SENSORS_FEATURE_HUMIDITY << 8,
+
+ SENSORS_SUBFEATURE_VID = SENSORS_FEATURE_VID << 8,
+
+ SENSORS_SUBFEATURE_INTRUSION_ALARM = SENSORS_FEATURE_INTRUSION << 8,
+ SENSORS_SUBFEATURE_INTRUSION_BEEP,
+
+ SENSORS_SUBFEATURE_BEEP_ENABLE = SENSORS_FEATURE_BEEP_ENABLE << 8,
+
+ SENSORS_SUBFEATURE_UNKNOWN = INT_MAX,
+} sensors_subfeature_type;
+
+/* Data about a single chip feature (or category leader) */
+struct sensors_feature {
+ char *name;
+ int number;
+ sensors_feature_type type;
+ /* Members below are for libsensors internal use only */
+ int first_subfeature;
+ int padding1;
+};
+
+/* Data about a single chip subfeature:
+ name is the string name used to refer to this subfeature (in config files)
+ number is the internal subfeature number, used in many functions to refer
+ to this subfeature
+ type is the subfeature type
+ mapping is the number of a main feature this subfeature belongs to
+ (for example subfeatures fan1_input, fan1_min, fan1_div and fan1_alarm
+ are mapped to main feature fan1)
+ flags is a bitfield, its value is a combination of SENSORS_MODE_R (readable),
+ SENSORS_MODE_W (writable) and SENSORS_COMPUTE_MAPPING (affected by the
+ computation rules of the main feature) */
+typedef struct sensors_subfeature {
+ char *name;
+ int number;
+ sensors_subfeature_type type;
+ int mapping;
+ unsigned int flags;
+} sensors_subfeature;
+
+/* This returns all main features of a specific chip. nr is an internally
+ used variable. Set it to zero to start at the begin of the list. If no
+ more features are found NULL is returned.
+ Do not try to change the returned structure; you will corrupt internal
+ data structures. */
+const sensors_feature *
+sensors_get_features(const sensors_chip_name *name, int *nr);
+
+/* This returns all subfeatures of a given main feature. nr is an internally
+ used variable. Set it to zero to start at the begin of the list. If no
+ more features are found NULL is returned.
+ Do not try to change the returned structure; you will corrupt internal
+ data structures. */
+const sensors_subfeature *
+sensors_get_all_subfeatures(const sensors_chip_name *name,
+ const sensors_feature *feature, int *nr);
+
+/* This returns the subfeature of the given type for a given main feature,
+ if it exists, NULL otherwise.
+ Do not try to change the returned structure; you will corrupt internal
+ data structures. */
+const sensors_subfeature *
+sensors_get_subfeature(const sensors_chip_name *name,
+ const sensors_feature *feature,
+ sensors_subfeature_type type);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* def LIB_SENSORS_ERROR_H */
diff --git a/tools/gator/daemon/libsensors/sysfs.c b/tools/gator/daemon/libsensors/sysfs.c
new file mode 100644
index 000000000000..2b494c9975b4
--- /dev/null
+++ b/tools/gator/daemon/libsensors/sysfs.c
@@ -0,0 +1,926 @@
+/*
+ sysfs.c - Part of libsensors, a library for reading Linux sensor data
+ Copyright (c) 2005 Mark M. Hoffman <mhoffman@lightlink.com>
+ Copyright (C) 2007-2010 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 file modified by ARM on Jan 23, 2013 to improve performance by substituting calls to fread() with calls to read() and to read non-scaled values. ***/
+
+/* this define needed for strndup() */
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include "data.h"
+#include "error.h"
+#include "access.h"
+#include "general.h"
+#include "sysfs.h"
+
+
+/****************************************************************************/
+
+#define ATTR_MAX 128
+#define SYSFS_MAGIC 0x62656572
+
+int sensors_sysfs_no_scaling;
+
+/*
+ * Read an attribute from sysfs
+ * Returns a pointer to a freshly allocated string; free it yourself.
+ * If the file doesn't exist or can't be read, NULL is returned.
+ */
+static char *sysfs_read_attr(const char *device, const char *attr)
+{
+ char path[NAME_MAX];
+ char buf[ATTR_MAX], *p;
+ FILE *f;
+
+ snprintf(path, NAME_MAX, "%s/%s", device, attr);
+
+ if (!(f = fopen(path, "r")))
+ return NULL;
+ p = fgets(buf, ATTR_MAX, f);
+ fclose(f);
+ if (!p)
+ return NULL;
+
+ /* Last byte is a '\n'; chop that off */
+ p = strndup(buf, strlen(buf) - 1);
+ if (!p)
+ sensors_fatal_error(__func__, "Out of memory");
+ return p;
+}
+
+/*
+ * Call an arbitrary function for each class device of the given class
+ * Returns 0 on success (all calls returned 0), a positive errno for
+ * local errors, or a negative error value if any call fails.
+ */
+static int sysfs_foreach_classdev(const char *class_name,
+ int (*func)(const char *, const char *))
+{
+ char path[NAME_MAX];
+ int path_off, ret;
+ DIR *dir;
+ struct dirent *ent;
+
+ path_off = snprintf(path, NAME_MAX, "%s/class/%s",
+ sensors_sysfs_mount, class_name);
+ if (!(dir = opendir(path)))
+ return errno;
+
+ ret = 0;
+ while (!ret && (ent = readdir(dir))) {
+ if (ent->d_name[0] == '.') /* skip hidden entries */
+ continue;
+
+ snprintf(path + path_off, NAME_MAX - path_off, "/%s",
+ ent->d_name);
+ ret = func(path, ent->d_name);
+ }
+
+ closedir(dir);
+ return ret;
+}
+
+/*
+ * Call an arbitrary function for each device of the given bus type
+ * Returns 0 on success (all calls returned 0), a positive errno for
+ * local errors, or a negative error value if any call fails.
+ */
+static int sysfs_foreach_busdev(const char *bus_type,
+ int (*func)(const char *, const char *))
+{
+ char path[NAME_MAX];
+ int path_off, ret;
+ DIR *dir;
+ struct dirent *ent;
+
+ path_off = snprintf(path, NAME_MAX, "%s/bus/%s/devices",
+ sensors_sysfs_mount, bus_type);
+ if (!(dir = opendir(path)))
+ return errno;
+
+ ret = 0;
+ while (!ret && (ent = readdir(dir))) {
+ if (ent->d_name[0] == '.') /* skip hidden entries */
+ continue;
+
+ snprintf(path + path_off, NAME_MAX - path_off, "/%s",
+ ent->d_name);
+ ret = func(path, ent->d_name);
+ }
+
+ closedir(dir);
+ return ret;
+}
+
+/****************************************************************************/
+
+char sensors_sysfs_mount[NAME_MAX];
+
+#define MAX_MAIN_SENSOR_TYPES (SENSORS_FEATURE_MAX_MAIN - SENSORS_FEATURE_IN)
+#define MAX_OTHER_SENSOR_TYPES (SENSORS_FEATURE_MAX_OTHER - SENSORS_FEATURE_VID)
+#define MAX_SENSORS_PER_TYPE 24
+/* max_subfeatures is now computed dynamically */
+#define FEATURE_SIZE (max_subfeatures * 2)
+#define FEATURE_TYPE_SIZE (MAX_SENSORS_PER_TYPE * FEATURE_SIZE)
+
+/*
+ * Room for all 7 main types (in, fan, temp, power, energy, current, humidity)
+ * and 2 other types (VID, intrusion) with all their subfeatures + misc features
+ */
+#define SUB_OFFSET_OTHER (MAX_MAIN_SENSOR_TYPES * FEATURE_TYPE_SIZE)
+#define SUB_OFFSET_MISC (SUB_OFFSET_OTHER + \
+ MAX_OTHER_SENSOR_TYPES * FEATURE_TYPE_SIZE)
+#define ALL_POSSIBLE_SUBFEATURES (SUB_OFFSET_MISC + 1)
+
+static
+int get_type_scaling(sensors_subfeature_type type)
+{
+ /* Multipliers for subfeatures */
+ switch (type & 0xFF80) {
+ case SENSORS_SUBFEATURE_IN_INPUT:
+ case SENSORS_SUBFEATURE_TEMP_INPUT:
+ case SENSORS_SUBFEATURE_CURR_INPUT:
+ case SENSORS_SUBFEATURE_HUMIDITY_INPUT:
+ return 1000;
+ case SENSORS_SUBFEATURE_FAN_INPUT:
+ return 1;
+ case SENSORS_SUBFEATURE_POWER_AVERAGE:
+ case SENSORS_SUBFEATURE_ENERGY_INPUT:
+ return 1000000;
+ }
+
+ /* Multipliers for second class subfeatures
+ that need their own multiplier */
+ switch (type) {
+ case SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL:
+ case SENSORS_SUBFEATURE_VID:
+ case SENSORS_SUBFEATURE_TEMP_OFFSET:
+ return 1000;
+ default:
+ return 1;
+ }
+}
+
+static
+char *get_feature_name(sensors_feature_type ftype, char *sfname)
+{
+ char *name, *underscore;
+
+ switch (ftype) {
+ case SENSORS_FEATURE_IN:
+ case SENSORS_FEATURE_FAN:
+ case SENSORS_FEATURE_TEMP:
+ case SENSORS_FEATURE_POWER:
+ case SENSORS_FEATURE_ENERGY:
+ case SENSORS_FEATURE_CURR:
+ case SENSORS_FEATURE_HUMIDITY:
+ case SENSORS_FEATURE_INTRUSION:
+ underscore = strchr(sfname, '_');
+ name = strndup(sfname, underscore - sfname);
+ if (!name)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ break;
+ default:
+ name = strdup(sfname);
+ if (!name)
+ sensors_fatal_error(__func__, "Out of memory");
+ }
+
+ return name;
+}
+
+/* Static mappings for use by sensors_subfeature_get_type() */
+struct subfeature_type_match
+{
+ const char *name;
+ sensors_subfeature_type type;
+};
+
+struct feature_type_match
+{
+ const char *name;
+ const struct subfeature_type_match *submatches;
+};
+
+static const struct subfeature_type_match temp_matches[] = {
+ { "input", SENSORS_SUBFEATURE_TEMP_INPUT },
+ { "max", SENSORS_SUBFEATURE_TEMP_MAX },
+ { "max_hyst", SENSORS_SUBFEATURE_TEMP_MAX_HYST },
+ { "min", SENSORS_SUBFEATURE_TEMP_MIN },
+ { "crit", SENSORS_SUBFEATURE_TEMP_CRIT },
+ { "crit_hyst", SENSORS_SUBFEATURE_TEMP_CRIT_HYST },
+ { "lcrit", SENSORS_SUBFEATURE_TEMP_LCRIT },
+ { "emergency", SENSORS_SUBFEATURE_TEMP_EMERGENCY },
+ { "emergency_hyst", SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST },
+ { "lowest", SENSORS_SUBFEATURE_TEMP_LOWEST },
+ { "highest", SENSORS_SUBFEATURE_TEMP_HIGHEST },
+ { "alarm", SENSORS_SUBFEATURE_TEMP_ALARM },
+ { "min_alarm", SENSORS_SUBFEATURE_TEMP_MIN_ALARM },
+ { "max_alarm", SENSORS_SUBFEATURE_TEMP_MAX_ALARM },
+ { "crit_alarm", SENSORS_SUBFEATURE_TEMP_CRIT_ALARM },
+ { "emergency_alarm", SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM },
+ { "lcrit_alarm", SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM },
+ { "fault", SENSORS_SUBFEATURE_TEMP_FAULT },
+ { "type", SENSORS_SUBFEATURE_TEMP_TYPE },
+ { "offset", SENSORS_SUBFEATURE_TEMP_OFFSET },
+ { "beep", SENSORS_SUBFEATURE_TEMP_BEEP },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match in_matches[] = {
+ { "input", SENSORS_SUBFEATURE_IN_INPUT },
+ { "min", SENSORS_SUBFEATURE_IN_MIN },
+ { "max", SENSORS_SUBFEATURE_IN_MAX },
+ { "lcrit", SENSORS_SUBFEATURE_IN_LCRIT },
+ { "crit", SENSORS_SUBFEATURE_IN_CRIT },
+ { "average", SENSORS_SUBFEATURE_IN_AVERAGE },
+ { "lowest", SENSORS_SUBFEATURE_IN_LOWEST },
+ { "highest", SENSORS_SUBFEATURE_IN_HIGHEST },
+ { "alarm", SENSORS_SUBFEATURE_IN_ALARM },
+ { "min_alarm", SENSORS_SUBFEATURE_IN_MIN_ALARM },
+ { "max_alarm", SENSORS_SUBFEATURE_IN_MAX_ALARM },
+ { "lcrit_alarm", SENSORS_SUBFEATURE_IN_LCRIT_ALARM },
+ { "crit_alarm", SENSORS_SUBFEATURE_IN_CRIT_ALARM },
+ { "beep", SENSORS_SUBFEATURE_IN_BEEP },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match fan_matches[] = {
+ { "input", SENSORS_SUBFEATURE_FAN_INPUT },
+ { "min", SENSORS_SUBFEATURE_FAN_MIN },
+ { "max", SENSORS_SUBFEATURE_FAN_MAX },
+ { "div", SENSORS_SUBFEATURE_FAN_DIV },
+ { "pulses", SENSORS_SUBFEATURE_FAN_PULSES },
+ { "alarm", SENSORS_SUBFEATURE_FAN_ALARM },
+ { "min_alarm", SENSORS_SUBFEATURE_FAN_MIN_ALARM },
+ { "max_alarm", SENSORS_SUBFEATURE_FAN_MAX_ALARM },
+ { "fault", SENSORS_SUBFEATURE_FAN_FAULT },
+ { "beep", SENSORS_SUBFEATURE_FAN_BEEP },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match power_matches[] = {
+ { "average", SENSORS_SUBFEATURE_POWER_AVERAGE },
+ { "average_highest", SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST },
+ { "average_lowest", SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST },
+ { "input", SENSORS_SUBFEATURE_POWER_INPUT },
+ { "input_highest", SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST },
+ { "input_lowest", SENSORS_SUBFEATURE_POWER_INPUT_LOWEST },
+ { "cap", SENSORS_SUBFEATURE_POWER_CAP },
+ { "cap_hyst", SENSORS_SUBFEATURE_POWER_CAP_HYST },
+ { "cap_alarm", SENSORS_SUBFEATURE_POWER_CAP_ALARM },
+ { "alarm", SENSORS_SUBFEATURE_POWER_ALARM },
+ { "max", SENSORS_SUBFEATURE_POWER_MAX },
+ { "max_alarm", SENSORS_SUBFEATURE_POWER_MAX_ALARM },
+ { "crit", SENSORS_SUBFEATURE_POWER_CRIT },
+ { "crit_alarm", SENSORS_SUBFEATURE_POWER_CRIT_ALARM },
+ { "average_interval", SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match energy_matches[] = {
+ { "input", SENSORS_SUBFEATURE_ENERGY_INPUT },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match curr_matches[] = {
+ { "input", SENSORS_SUBFEATURE_CURR_INPUT },
+ { "min", SENSORS_SUBFEATURE_CURR_MIN },
+ { "max", SENSORS_SUBFEATURE_CURR_MAX },
+ { "lcrit", SENSORS_SUBFEATURE_CURR_LCRIT },
+ { "crit", SENSORS_SUBFEATURE_CURR_CRIT },
+ { "average", SENSORS_SUBFEATURE_CURR_AVERAGE },
+ { "lowest", SENSORS_SUBFEATURE_CURR_LOWEST },
+ { "highest", SENSORS_SUBFEATURE_CURR_HIGHEST },
+ { "alarm", SENSORS_SUBFEATURE_CURR_ALARM },
+ { "min_alarm", SENSORS_SUBFEATURE_CURR_MIN_ALARM },
+ { "max_alarm", SENSORS_SUBFEATURE_CURR_MAX_ALARM },
+ { "lcrit_alarm", SENSORS_SUBFEATURE_CURR_LCRIT_ALARM },
+ { "crit_alarm", SENSORS_SUBFEATURE_CURR_CRIT_ALARM },
+ { "beep", SENSORS_SUBFEATURE_CURR_BEEP },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match humidity_matches[] = {
+ { "input", SENSORS_SUBFEATURE_HUMIDITY_INPUT },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match cpu_matches[] = {
+ { "vid", SENSORS_SUBFEATURE_VID },
+ { NULL, 0 }
+};
+
+static const struct subfeature_type_match intrusion_matches[] = {
+ { "alarm", SENSORS_SUBFEATURE_INTRUSION_ALARM },
+ { "beep", SENSORS_SUBFEATURE_INTRUSION_BEEP },
+ { NULL, 0 }
+};
+static struct feature_type_match matches[] = {
+ { "temp%d%c", temp_matches },
+ { "in%d%c", in_matches },
+ { "fan%d%c", fan_matches },
+ { "cpu%d%c", cpu_matches },
+ { "power%d%c", power_matches },
+ { "curr%d%c", curr_matches },
+ { "energy%d%c", energy_matches },
+ { "intrusion%d%c", intrusion_matches },
+ { "humidity%d%c", humidity_matches },
+};
+
+/* Return the subfeature type and channel number based on the subfeature
+ name */
+static
+sensors_subfeature_type sensors_subfeature_get_type(const char *name, int *nr)
+{
+ char c;
+ int i, count;
+ const struct subfeature_type_match *submatches;
+
+ /* Special case */
+ if (!strcmp(name, "beep_enable")) {
+ *nr = 0;
+ return SENSORS_SUBFEATURE_BEEP_ENABLE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(matches); i++)
+ if ((count = sscanf(name, matches[i].name, nr, &c)))
+ break;
+
+ if (i == ARRAY_SIZE(matches) || count != 2 || c != '_')
+ return SENSORS_SUBFEATURE_UNKNOWN; /* no match */
+
+ submatches = matches[i].submatches;
+ name = strchr(name + 3, '_') + 1;
+ for (i = 0; submatches[i].name != NULL; i++)
+ if (!strcmp(name, submatches[i].name))
+ return submatches[i].type;
+
+ return SENSORS_SUBFEATURE_UNKNOWN;
+}
+
+static int sensors_compute_max(void)
+{
+ int i, j, max, offset;
+ const struct subfeature_type_match *submatches;
+ sensors_feature_type ftype;
+
+ max = 0;
+ for (i = 0; i < ARRAY_SIZE(matches); i++) {
+ submatches = matches[i].submatches;
+ for (j = 0; submatches[j].name != NULL; j++) {
+ ftype = submatches[j].type >> 8;
+
+ if (ftype < SENSORS_FEATURE_VID) {
+ offset = submatches[j].type & 0x7F;
+ if (offset >= max)
+ max = offset + 1;
+ } else {
+ offset = submatches[j].type & 0xFF;
+ if (offset >= max * 2)
+ max = ((offset + 1) + 1) / 2;
+ }
+ }
+ }
+
+ return max;
+}
+
+static int sensors_get_attr_mode(const char *device, const char *attr)
+{
+ char path[NAME_MAX];
+ struct stat st;
+ int mode = 0;
+
+ snprintf(path, NAME_MAX, "%s/%s", device, attr);
+ if (!stat(path, &st)) {
+ if (st.st_mode & S_IRUSR)
+ mode |= SENSORS_MODE_R;
+ if (st.st_mode & S_IWUSR)
+ mode |= SENSORS_MODE_W;
+ }
+ return mode;
+}
+
+static int sensors_read_dynamic_chip(sensors_chip_features *chip,
+ const char *dev_path)
+{
+ int i, fnum = 0, sfnum = 0, prev_slot;
+ static int max_subfeatures;
+ DIR *dir;
+ struct dirent *ent;
+ sensors_subfeature *all_subfeatures;
+ sensors_subfeature *dyn_subfeatures;
+ sensors_feature *dyn_features;
+ sensors_feature_type ftype;
+ sensors_subfeature_type sftype;
+
+ if (!(dir = opendir(dev_path)))
+ return -errno;
+
+ /* Dynamically figure out the max number of subfeatures */
+ if (!max_subfeatures)
+ max_subfeatures = sensors_compute_max();
+
+ /* We use a large sparse table at first to store all found
+ subfeatures, so that we can store them sorted at type and index
+ and then later create a dense sorted table. */
+ all_subfeatures = calloc(ALL_POSSIBLE_SUBFEATURES,
+ sizeof(sensors_subfeature));
+ if (!all_subfeatures)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ while ((ent = readdir(dir))) {
+ char *name;
+ int nr;
+
+ /* Skip directories and symlinks */
+ if (ent->d_type != DT_REG)
+ continue;
+
+ name = ent->d_name;
+
+ sftype = sensors_subfeature_get_type(name, &nr);
+ if (sftype == SENSORS_SUBFEATURE_UNKNOWN)
+ continue;
+ ftype = sftype >> 8;
+
+ /* Adjust the channel number */
+ switch (ftype) {
+ case SENSORS_FEATURE_FAN:
+ case SENSORS_FEATURE_TEMP:
+ case SENSORS_FEATURE_POWER:
+ case SENSORS_FEATURE_ENERGY:
+ case SENSORS_FEATURE_CURR:
+ case SENSORS_FEATURE_HUMIDITY:
+ nr--;
+ break;
+ default:
+ break;
+ }
+
+ if (nr < 0 || nr >= MAX_SENSORS_PER_TYPE) {
+ /* More sensors of one type than MAX_SENSORS_PER_TYPE,
+ we have to ignore it */
+#ifdef DEBUG
+ sensors_fatal_error(__func__,
+ "Increase MAX_SENSORS_PER_TYPE!");
+#endif
+ continue;
+ }
+
+ /* "calculate" a place to store the subfeature in our sparse,
+ sorted table */
+ switch (ftype) {
+ case SENSORS_FEATURE_VID:
+ case SENSORS_FEATURE_INTRUSION:
+ i = SUB_OFFSET_OTHER +
+ (ftype - SENSORS_FEATURE_VID) * FEATURE_TYPE_SIZE +
+ nr * FEATURE_SIZE + (sftype & 0xFF);
+ break;
+ case SENSORS_FEATURE_BEEP_ENABLE:
+ i = SUB_OFFSET_MISC +
+ (ftype - SENSORS_FEATURE_BEEP_ENABLE);
+ break;
+ default:
+ i = ftype * FEATURE_TYPE_SIZE +
+ nr * FEATURE_SIZE +
+ ((sftype & 0x80) >> 7) * max_subfeatures +
+ (sftype & 0x7F);
+ }
+
+ if (all_subfeatures[i].name) {
+#ifdef DEBUG
+ sensors_fatal_error(__func__, "Duplicate subfeature");
+#endif
+ continue;
+ }
+
+ /* fill in the subfeature members */
+ all_subfeatures[i].type = sftype;
+ all_subfeatures[i].name = strdup(name);
+ if (!all_subfeatures[i].name)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ /* Other and misc subfeatures are never scaled */
+ if (sftype < SENSORS_SUBFEATURE_VID && !(sftype & 0x80))
+ all_subfeatures[i].flags |= SENSORS_COMPUTE_MAPPING;
+ all_subfeatures[i].flags |= sensors_get_attr_mode(dev_path, name);
+
+ sfnum++;
+ }
+ closedir(dir);
+
+ if (!sfnum) { /* No subfeature */
+ chip->subfeature = NULL;
+ goto exit_free;
+ }
+
+ /* How many main features? */
+ prev_slot = -1;
+ for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
+ if (!all_subfeatures[i].name)
+ continue;
+
+ if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) {
+ fnum++;
+ prev_slot = i / FEATURE_SIZE;
+ }
+ }
+
+ dyn_subfeatures = calloc(sfnum, sizeof(sensors_subfeature));
+ dyn_features = calloc(fnum, sizeof(sensors_feature));
+ if (!dyn_subfeatures || !dyn_features)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ /* Copy from the sparse array to the compact array */
+ sfnum = 0;
+ fnum = -1;
+ prev_slot = -1;
+ for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
+ if (!all_subfeatures[i].name)
+ continue;
+
+ /* New main feature? */
+ if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) {
+ ftype = all_subfeatures[i].type >> 8;
+ fnum++;
+ prev_slot = i / FEATURE_SIZE;
+
+ dyn_features[fnum].name = get_feature_name(ftype,
+ all_subfeatures[i].name);
+ dyn_features[fnum].number = fnum;
+ dyn_features[fnum].first_subfeature = sfnum;
+ dyn_features[fnum].type = ftype;
+ }
+
+ dyn_subfeatures[sfnum] = all_subfeatures[i];
+ dyn_subfeatures[sfnum].number = sfnum;
+ /* Back to the feature */
+ dyn_subfeatures[sfnum].mapping = fnum;
+
+ sfnum++;
+ }
+
+ chip->subfeature = dyn_subfeatures;
+ chip->subfeature_count = sfnum;
+ chip->feature = dyn_features;
+ chip->feature_count = ++fnum;
+
+exit_free:
+ free(all_subfeatures);
+ return 0;
+}
+
+/* returns !0 if sysfs filesystem was found, 0 otherwise */
+int sensors_init_sysfs(void)
+{
+ struct statfs statfsbuf;
+
+ snprintf(sensors_sysfs_mount, NAME_MAX, "%s", "/sys");
+ if (statfs(sensors_sysfs_mount, &statfsbuf) < 0
+ || statfsbuf.f_type != SYSFS_MAGIC)
+ return 0;
+
+ return 1;
+}
+
+/* returns: number of devices added (0 or 1) if successful, <0 otherwise */
+static int sensors_read_one_sysfs_chip(const char *dev_path,
+ const char *dev_name,
+ const char *hwmon_path)
+{
+ int domain, bus, slot, fn, vendor, product, id;
+ int err = -SENSORS_ERR_KERNEL;
+ char *bus_attr;
+ char bus_path[NAME_MAX];
+ char linkpath[NAME_MAX];
+ char subsys_path[NAME_MAX], *subsys;
+ int sub_len;
+ sensors_chip_features entry;
+
+ /* ignore any device without name attribute */
+ if (!(entry.chip.prefix = sysfs_read_attr(hwmon_path, "name")))
+ return 0;
+
+ entry.chip.path = strdup(hwmon_path);
+ if (!entry.chip.path)
+ sensors_fatal_error(__func__, "Out of memory");
+
+ if (dev_path == NULL) {
+ /* Virtual device */
+ entry.chip.bus.type = SENSORS_BUS_TYPE_VIRTUAL;
+ entry.chip.bus.nr = 0;
+ /* For now we assume that virtual devices are unique */
+ entry.chip.addr = 0;
+ goto done;
+ }
+
+ /* Find bus type */
+ snprintf(linkpath, NAME_MAX, "%s/subsystem", dev_path);
+ sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
+ if (sub_len < 0 && errno == ENOENT) {
+ /* Fallback to "bus" link for kernels <= 2.6.17 */
+ snprintf(linkpath, NAME_MAX, "%s/bus", dev_path);
+ sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
+ }
+ if (sub_len < 0) {
+ /* Older kernels (<= 2.6.11) have neither the subsystem
+ symlink nor the bus symlink */
+ if (errno == ENOENT)
+ subsys = NULL;
+ else
+ goto exit_free;
+ } else {
+ subsys_path[sub_len] = '\0';
+ subsys = strrchr(subsys_path, '/') + 1;
+ }
+
+ if ((!subsys || !strcmp(subsys, "i2c")) &&
+ sscanf(dev_name, "%hd-%x", &entry.chip.bus.nr,
+ &entry.chip.addr) == 2) {
+ /* find out if legacy ISA or not */
+ if (entry.chip.bus.nr == 9191) {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
+ entry.chip.bus.nr = 0;
+ } else {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_I2C;
+ snprintf(bus_path, sizeof(bus_path),
+ "%s/class/i2c-adapter/i2c-%d/device",
+ sensors_sysfs_mount, entry.chip.bus.nr);
+
+ if ((bus_attr = sysfs_read_attr(bus_path, "name"))) {
+ if (!strncmp(bus_attr, "ISA ", 4)) {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
+ entry.chip.bus.nr = 0;
+ }
+
+ free(bus_attr);
+ }
+ }
+ } else
+ if ((!subsys || !strcmp(subsys, "spi")) &&
+ sscanf(dev_name, "spi%hd.%d", &entry.chip.bus.nr,
+ &entry.chip.addr) == 2) {
+ /* SPI */
+ entry.chip.bus.type = SENSORS_BUS_TYPE_SPI;
+ } else
+ if ((!subsys || !strcmp(subsys, "pci")) &&
+ sscanf(dev_name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
+ /* PCI */
+ entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
+ entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
+ entry.chip.bus.nr = 0;
+ } else
+ if ((!subsys || !strcmp(subsys, "platform") ||
+ !strcmp(subsys, "of_platform"))) {
+ /* must be new ISA (platform driver) */
+ if (sscanf(dev_name, "%*[a-z0-9_].%d", &entry.chip.addr) != 1)
+ entry.chip.addr = 0;
+ entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
+ entry.chip.bus.nr = 0;
+ } else if (subsys && !strcmp(subsys, "acpi")) {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_ACPI;
+ /* For now we assume that acpi devices are unique */
+ entry.chip.bus.nr = 0;
+ entry.chip.addr = 0;
+ } else
+ if (subsys && !strcmp(subsys, "hid") &&
+ sscanf(dev_name, "%x:%x:%x.%x", &bus, &vendor, &product, &id) == 4) {
+ entry.chip.bus.type = SENSORS_BUS_TYPE_HID;
+ /* As of kernel 2.6.32, the hid device names don't look good */
+ entry.chip.bus.nr = bus;
+ entry.chip.addr = id;
+ } else {
+ /* Ignore unknown device */
+ err = 0;
+ goto exit_free;
+ }
+
+done:
+ if (sensors_read_dynamic_chip(&entry, hwmon_path) < 0)
+ goto exit_free;
+ if (!entry.subfeature) { /* No subfeature, discard chip */
+ err = 0;
+ goto exit_free;
+ }
+ sensors_add_proc_chips(&entry);
+
+ return 1;
+
+exit_free:
+ free(entry.chip.prefix);
+ free(entry.chip.path);
+ return err;
+}
+
+static int sensors_add_hwmon_device_compat(const char *path,
+ const char *dev_name)
+{
+ int err;
+
+ err = sensors_read_one_sysfs_chip(path, dev_name, path);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* returns 0 if successful, !0 otherwise */
+static int sensors_read_sysfs_chips_compat(void)
+{
+ int ret;
+
+ ret = sysfs_foreach_busdev("i2c", sensors_add_hwmon_device_compat);
+ if (ret && ret != ENOENT)
+ return -SENSORS_ERR_KERNEL;
+
+ return 0;
+}
+
+static int sensors_add_hwmon_device(const char *path, const char *classdev)
+{
+ char linkpath[NAME_MAX];
+ char device[NAME_MAX], *device_p;
+ int dev_len, err;
+ (void)classdev; /* hide warning */
+
+ snprintf(linkpath, NAME_MAX, "%s/device", path);
+ dev_len = readlink(linkpath, device, NAME_MAX - 1);
+ if (dev_len < 0) {
+ /* No device link? Treat as virtual */
+ err = sensors_read_one_sysfs_chip(NULL, NULL, path);
+ } else {
+ device[dev_len] = '\0';
+ device_p = strrchr(device, '/') + 1;
+
+ /* The attributes we want might be those of the hwmon class
+ device, or those of the device itself. */
+ err = sensors_read_one_sysfs_chip(linkpath, device_p, path);
+ if (err == 0)
+ err = sensors_read_one_sysfs_chip(linkpath, device_p,
+ linkpath);
+ }
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* returns 0 if successful, !0 otherwise */
+int sensors_read_sysfs_chips(void)
+{
+ int ret;
+
+ ret = sysfs_foreach_classdev("hwmon", sensors_add_hwmon_device);
+ if (ret == ENOENT) {
+ /* compatibility function for kernel 2.6.n where n <= 13 */
+ return sensors_read_sysfs_chips_compat();
+ }
+
+ if (ret > 0)
+ ret = -SENSORS_ERR_KERNEL;
+ return ret;
+}
+
+/* returns 0 if successful, !0 otherwise */
+static int sensors_add_i2c_bus(const char *path, const char *classdev)
+{
+ sensors_bus entry;
+
+ if (sscanf(classdev, "i2c-%hd", &entry.bus.nr) != 1 ||
+ entry.bus.nr == 9191) /* legacy ISA */
+ return 0;
+ entry.bus.type = SENSORS_BUS_TYPE_I2C;
+
+ /* Get the adapter name from the classdev "name" attribute
+ * (Linux 2.6.20 and later). If it fails, fall back to
+ * the device "name" attribute (for older kernels). */
+ entry.adapter = sysfs_read_attr(path, "name");
+ if (!entry.adapter)
+ entry.adapter = sysfs_read_attr(path, "device/name");
+ if (entry.adapter)
+ sensors_add_proc_bus(&entry);
+
+ return 0;
+}
+
+/* returns 0 if successful, !0 otherwise */
+int sensors_read_sysfs_bus(void)
+{
+ int ret;
+
+ ret = sysfs_foreach_classdev("i2c-adapter", sensors_add_i2c_bus);
+ if (ret == ENOENT)
+ ret = sysfs_foreach_busdev("i2c", sensors_add_i2c_bus);
+ if (ret && ret != ENOENT)
+ return -SENSORS_ERR_KERNEL;
+
+ return 0;
+}
+
+int sensors_read_sysfs_attr(const sensors_chip_name *name,
+ const sensors_subfeature *subfeature,
+ double *value)
+{
+ char n[NAME_MAX];
+ int f;
+
+ snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
+ if ((f = open(n, O_RDONLY)) != -1) {
+ int res, err = 0;
+ char buf[512];
+ int count;
+
+ errno = 0;
+ if ((count = read(f, buf, sizeof(buf) - 1)) == -1) {
+ if (errno == EIO)
+ err = -SENSORS_ERR_IO;
+ else
+ err = -SENSORS_ERR_ACCESS_R;
+ } else {
+ buf[count] = '\0';
+ errno = 0;
+ res = sscanf(buf, "%lf", value);
+ if (res == EOF && errno == EIO)
+ err = -SENSORS_ERR_IO;
+ else if (res != 1)
+ err = -SENSORS_ERR_ACCESS_R;
+ }
+ res = close(f);
+ if (err)
+ return err;
+
+ if (res != 0) {
+ if (errno == EIO)
+ return -SENSORS_ERR_IO;
+ else
+ return -SENSORS_ERR_ACCESS_R;
+ }
+ if (!sensors_sysfs_no_scaling)
+ *value /= get_type_scaling(subfeature->type);
+ } else
+ return -SENSORS_ERR_KERNEL;
+
+ return 0;
+}
+
+int sensors_write_sysfs_attr(const sensors_chip_name *name,
+ const sensors_subfeature *subfeature,
+ double value)
+{
+ char n[NAME_MAX];
+ FILE *f;
+
+ snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
+ if ((f = fopen(n, "w"))) {
+ int res, err = 0;
+
+ if (!sensors_sysfs_no_scaling)
+ value *= get_type_scaling(subfeature->type);
+ res = fprintf(f, "%d", (int) value);
+ if (res == -EIO)
+ err = -SENSORS_ERR_IO;
+ else if (res < 0)
+ err = -SENSORS_ERR_ACCESS_W;
+ res = fclose(f);
+ if (err)
+ return err;
+
+ if (res == EOF) {
+ if (errno == EIO)
+ return -SENSORS_ERR_IO;
+ else
+ return -SENSORS_ERR_ACCESS_W;
+ }
+ } else
+ return -SENSORS_ERR_KERNEL;
+
+ return 0;
+}
diff --git a/tools/gator/daemon/libsensors/sysfs.h b/tools/gator/daemon/libsensors/sysfs.h
new file mode 100644
index 000000000000..38584afd028e
--- /dev/null
+++ b/tools/gator/daemon/libsensors/sysfs.h
@@ -0,0 +1,43 @@
+/*
+ sysfs.h - part of libsensors, a library for reading Linux sensor data
+ Copyright (C) Mark M. Hoffman <mhoffman@lightlink.com>
+ Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser 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 SENSORS_LIB_SYSFS_H
+#define SENSORS_LIB_SYSFS_H
+
+extern char sensors_sysfs_mount[];
+
+int sensors_init_sysfs(void);
+
+int sensors_read_sysfs_chips(void);
+
+int sensors_read_sysfs_bus(void);
+
+/* Read a value out of a sysfs attribute file */
+int sensors_read_sysfs_attr(const sensors_chip_name *name,
+ const sensors_subfeature *subfeature,
+ double *value);
+
+/* Write a value to a sysfs attribute file */
+int sensors_write_sysfs_attr(const sensors_chip_name *name,
+ const sensors_subfeature *subfeature,
+ double value);
+
+#endif /* !SENSORS_LIB_SYSFS_H */
diff --git a/tools/gator/daemon/libsensors/version.h b/tools/gator/daemon/libsensors/version.h
new file mode 100644
index 000000000000..76ceb08c29d0
--- /dev/null
+++ b/tools/gator/daemon/libsensors/version.h
@@ -0,0 +1 @@
+#define LM_VERSION "3.3.2"
diff --git a/tools/gator/daemon/main.cpp b/tools/gator/daemon/main.cpp
new file mode 100644
index 000000000000..2998c7012221
--- /dev/null
+++ b/tools/gator/daemon/main.cpp
@@ -0,0 +1,547 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * 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 <arpa/inet.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "Child.h"
+#include "EventsXML.h"
+#include "KMod.h"
+#include "Logging.h"
+#include "Monitor.h"
+#include "OlySocket.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
+
+#define DEBUG false
+
+extern Child* child;
+static int shutdownFilesystem();
+static pthread_mutex_t numSessions_mutex;
+static OlyServerSocket* sock = NULL;
+static Monitor monitor;
+static int numSessions = 0;
+static bool driverRunningAtStart = false;
+static bool driverMountedAtStart = false;
+
+struct cmdline_t {
+ int port;
+ char* module;
+};
+
+#define DEFAULT_PORT 8080
+
+void cleanUp() {
+ if (shutdownFilesystem() == -1) {
+ logg->logMessage("Error shutting down gator filesystem");
+ }
+ delete sock;
+ delete util;
+ delete logg;
+}
+
+// CTRL C Signal Handler
+static void handler(int signum) {
+ logg->logMessage("Received signal %d, gator daemon exiting", signum);
+
+ // Case 1: both child and parent receive the signal
+ if (numSessions > 0) {
+ // Arbitrary sleep of 1 second to give time for the child to exit;
+ // if something bad happens, continue the shutdown process regardless
+ sleep(1);
+ }
+
+ // Case 2: only the parent received the signal
+ if (numSessions > 0) {
+ // Kill child threads - the first signal exits gracefully
+ logg->logMessage("Killing process group as %d child was running when signal was received", numSessions);
+ kill(0, SIGINT);
+
+ // Give time for the child to exit
+ sleep(1);
+
+ if (numSessions > 0) {
+ // The second signal force kills the child
+ logg->logMessage("Force kill the child");
+ kill(0, SIGINT);
+ // Again, sleep for 1 second
+ sleep(1);
+
+ if (numSessions > 0) {
+ // Something bad has really happened; the child is not exiting and therefore may hold the /dev/gator resource open
+ printf("Unable to kill the gatord child process, thus gator.ko may still be loaded.\n");
+ }
+ }
+ }
+
+ cleanUp();
+ exit(0);
+}
+
+// Child exit Signal Handler
+static void child_exit(int) {
+ int status;
+ int pid = wait(&status);
+ if (pid != -1) {
+ pthread_mutex_lock(&numSessions_mutex);
+ numSessions--;
+ pthread_mutex_unlock(&numSessions_mutex);
+ logg->logMessage("Child process %d exited with status %d", pid, status);
+ }
+}
+
+static const int UDP_ANS_PORT = 30000;
+static const int UDP_REQ_PORT = 30001;
+
+typedef struct {
+ char rviHeader[8];
+ uint32_t messageID;
+ uint8_t ethernetAddress[8];
+ uint32_t ethernetType;
+ uint32_t dhcp;
+ char dhcpName[40];
+ uint32_t ipAddress;
+ uint32_t defaultGateway;
+ uint32_t subnetMask;
+ uint32_t activeConnections;
+} RVIConfigureInfo;
+
+static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 };
+
+class UdpListener {
+public:
+ UdpListener() : mDstAns(), mReq(-1), mAns(-1) {}
+
+ void setup(int port) {
+ mReq = udpPort(UDP_REQ_PORT);
+ mAns = udpPort(UDP_ANS_PORT);
+
+ // Format the answer buffer
+ memset(&mDstAns, 0, sizeof(mDstAns));
+ memcpy(mDstAns.rviHeader, "STR_ANS ", sizeof(mDstAns.rviHeader));
+ if (gethostname(mDstAns.dhcpName, sizeof(mDstAns.dhcpName) - 1) != 0) {
+ logg->logError(__FILE__, __LINE__, "gethostname failed");
+ handleException();
+ }
+ // Subvert the defaultGateway field for the port number
+ if (port != DEFAULT_PORT) {
+ mDstAns.defaultGateway = port;
+ }
+ // Subvert the subnetMask field for the protocol version
+ mDstAns.subnetMask = PROTOCOL_VERSION;
+ }
+
+ int getReq() const {
+ return mReq;
+ }
+
+ void handle() {
+ char buf[128];
+ struct sockaddr_in6 sockaddr;
+ socklen_t addrlen;
+ int read;
+ addrlen = sizeof(sockaddr);
+ read = recvfrom(mReq, &buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr, &addrlen);
+ if (read < 0) {
+ logg->logError(__FILE__, __LINE__, "recvfrom failed");
+ handleException();
+ } else if ((read == 12) && (memcmp(buf, DST_REQ, sizeof(DST_REQ)) == 0)) {
+ if (sendto(mAns, &mDstAns, sizeof(mDstAns), 0, (struct sockaddr *)&sockaddr, addrlen) != sizeof(mDstAns)) {
+ logg->logError(__FILE__, __LINE__, "sendto failed");
+ handleException();
+ }
+ }
+ }
+
+ void close() {
+ ::close(mReq);
+ ::close(mAns);
+ }
+
+private:
+ int udpPort(int port) {
+ int s;
+ struct sockaddr_in6 sockaddr;
+ int on;
+ int family = AF_INET6;
+
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s == -1) {
+ family = AF_INET;
+ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (s == -1) {
+ logg->logError(__FILE__, __LINE__, "socket failed");
+ handleException();
+ }
+ }
+
+ on = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) {
+ logg->logError(__FILE__, __LINE__, "setsockopt failed");
+ handleException();
+ }
+
+ memset((void*)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sin6_family = family;
+ sockaddr.sin6_port = htons(port);
+ sockaddr.sin6_addr = in6addr_any;
+ if (bind(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
+ logg->logError(__FILE__, __LINE__, "socket failed");
+ handleException();
+ }
+
+ return s;
+ }
+
+ RVIConfigureInfo mDstAns;
+ int mReq;
+ int mAns;
+};
+
+static UdpListener udpListener;
+
+// retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted
+static int mountGatorFS() {
+ // If already mounted,
+ if (access("/dev/gator/buffer", F_OK) == 0) {
+ return 0;
+ }
+
+ // else, mount the filesystem
+ mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+static bool init_module (const char * const location) {
+ bool ret(false);
+ const int fd = open(location, O_RDONLY);
+ if (fd >= 0) {
+ struct stat st;
+ if (fstat(fd, &st) == 0) {
+ void * const p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (p != MAP_FAILED) {
+ if (syscall(__NR_init_module, p, (unsigned long)st.st_size, "") == 0) {
+ ret = true;
+ }
+ munmap(p, st.st_size);
+ }
+ }
+ close(fd);
+ }
+
+ return ret;
+}
+
+static bool setupFilesystem(char* module) {
+ if (module) {
+ // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running
+ shutdownFilesystem();
+
+ // if still mounted
+ if (access("/dev/gator/buffer", F_OK) == 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to remove the running gator.ko. Manually remove the module or use the running module by not specifying one on the commandline");
+ handleException();
+ }
+ }
+
+ const int retval = mountGatorFS();
+ if (retval == 1) {
+ logg->logMessage("Driver already running at startup");
+ driverRunningAtStart = true;
+ } else if (retval == 0) {
+ logg->logMessage("Driver already mounted at startup");
+ driverRunningAtStart = driverMountedAtStart = true;
+ } else {
+ char command[256]; // arbitrarily large amount
+ char location[256]; // arbitrarily large amount
+
+ if (module) {
+ strncpy(location, module, sizeof(location));
+ } else {
+ // Is the driver co-located in the same directory?
+ if (util->getApplicationFullPath(location, sizeof(location)) != 0) { // allow some buffer space
+ logg->logMessage("Unable to determine the full path of gatord, the cwd will be used");
+ }
+ strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1);
+ }
+
+ if (access(location, F_OK) == -1) {
+ if (module == NULL) {
+ // The gator kernel is not already loaded and unable to locate gator.ko in the default location
+ return false;
+ } else {
+ // gator location specified on the command line but it was not found
+ logg->logError(__FILE__, __LINE__, "gator module not found at %s", location);
+ handleException();
+ }
+ }
+
+ // Load driver
+ bool success = init_module(location);
+ if (!success) {
+ logg->logMessage("init_module failed, trying insmod");
+ snprintf(command, sizeof(command), "insmod %s >/dev/null 2>&1", location);
+ if (system(command) != 0) {
+ logg->logMessage("Unable to load gator.ko driver with command: %s", command);
+ logg->logError(__FILE__, __LINE__, "Unable to load (insmod) gator.ko driver:\n >>> gator.ko must be built against the current kernel version & configuration\n >>> See dmesg for more details");
+ handleException();
+ }
+ }
+
+ if (mountGatorFS() == -1) {
+ logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling.");
+ handleException();
+ }
+ }
+
+ return true;
+}
+
+static int shutdownFilesystem() {
+ if (driverMountedAtStart == false) {
+ umount("/dev/gator");
+ }
+ if (driverRunningAtStart == false) {
+ if (syscall(__NR_delete_module, "gator", O_NONBLOCK) != 0) {
+ logg->logMessage("delete_module failed, trying rmmod");
+ if (system("rmmod gator >/dev/null 2>&1") != 0) {
+ return -1;
+ }
+ }
+ }
+
+ return 0; // success
+}
+
+static struct cmdline_t parseCommandLine(int argc, char** argv) {
+ struct cmdline_t cmdline;
+ cmdline.port = DEFAULT_PORT;
+ cmdline.module = NULL;
+ char version_string[256]; // arbitrary length to hold the version information
+ int c;
+
+ // build the version string
+ if (PROTOCOL_VERSION < PROTOCOL_DEV) {
+ snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION);
+ } else {
+ snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION);
+ }
+
+ while ((c = getopt(argc, argv, "hvp:s:c:e:m:o:")) != -1) {
+ switch(c) {
+ case 'c':
+ gSessionData->mConfigurationXMLPath = optarg;
+ break;
+ case 'e':
+ gSessionData->mEventsXMLPath = optarg;
+ break;
+ case 'm':
+ cmdline.module = optarg;
+ break;
+ case 'p':
+ cmdline.port = strtol(optarg, NULL, 10);
+ break;
+ case 's':
+ gSessionData->mSessionXMLPath = optarg;
+ break;
+ case 'o':
+ gSessionData->mTargetPath = optarg;
+ break;
+ case 'h':
+ case '?':
+ logg->logError(__FILE__, __LINE__,
+ "%s. All parameters are optional:\n"
+ "-c config_xml path and filename of the configuration.xml to use\n"
+ "-e events_xml path and filename of the events.xml to use\n"
+ "-h this help page\n"
+ "-m module path and filename of gator.ko\n"
+ "-p port_number port upon which the server listens; default is 8080\n"
+ "-s session_xml path and filename of a session xml used for local capture\n"
+ "-o apc_dir path and name of the output for a local capture\n"
+ "-v version information\n"
+ , version_string);
+ handleException();
+ break;
+ case 'v':
+ logg->logError(__FILE__, __LINE__, version_string);
+ handleException();
+ break;
+ }
+ }
+
+ // Error checking
+ if (cmdline.port != DEFAULT_PORT && gSessionData->mSessionXMLPath != NULL) {
+ logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both");
+ handleException();
+ }
+
+ if (gSessionData->mTargetPath != NULL && gSessionData->mSessionXMLPath == NULL) {
+ logg->logError(__FILE__, __LINE__, "Missing -s command line option required for a local capture.");
+ handleException();
+ }
+
+ if (optind < argc) {
+ logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]);
+ handleException();
+ }
+
+ return cmdline;
+}
+
+void handleClient() {
+ OlySocket client(sock->acceptConnection());
+
+ int pid = fork();
+ if (pid < 0) {
+ // Error
+ logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists.");
+ } else if (pid == 0) {
+ // Child
+ sock->closeServerSocket();
+ udpListener.close();
+ monitor.close();
+ child = new Child(&client, numSessions + 1);
+ child->run();
+ delete child;
+ exit(0);
+ } else {
+ // Parent
+ client.closeSocket();
+
+ pthread_mutex_lock(&numSessions_mutex);
+ numSessions++;
+ pthread_mutex_unlock(&numSessions_mutex);
+
+ // Maximum number of connections is 2
+ int wait = 0;
+ while (numSessions > 1) {
+ // Throttle until one of the children exits before continuing to accept another socket connection
+ logg->logMessage("%d sessions active!", numSessions);
+ if (wait++ >= 10) { // Wait no more than 10 seconds
+ // Kill last created child
+ kill(pid, SIGALRM);
+ break;
+ }
+ sleep(1);
+ }
+ }
+}
+
+// Gator data flow: collector -> collector fifo -> sender
+int main(int argc, char** argv) {
+ // Ensure proper signal handling by making gatord the process group leader
+ // e.g. it may not be the group leader when launched as 'sudo gatord'
+ setsid();
+
+ logg = new Logging(DEBUG); // Set up global thread-safe logging
+ gSessionData = new SessionData(); // Global data class
+ util = new OlyUtility(); // Set up global utility class
+
+ // Initialize drivers
+ new KMod();
+
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0);
+ pthread_mutex_init(&numSessions_mutex, NULL);
+
+ signal(SIGINT, handler);
+ signal(SIGTERM, handler);
+ signal(SIGABRT, handler);
+
+ // Set to high priority
+ if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) {
+ logg->logMessage("setpriority() failed");
+ }
+
+ // Parse the command line parameters
+ struct cmdline_t cmdline = parseCommandLine(argc, argv);
+
+ // Verify root permissions
+ uid_t euid = geteuid();
+ if (euid) {
+ logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
+ handleException();
+ }
+
+ // Call before setting up the SIGCHLD handler, as system() spawns child processes
+ if (!setupFilesystem(cmdline.module)) {
+ logg->logMessage("Unable to setup gatorfs, trying perf");
+ if (!gSessionData->perf.setup()) {
+ logg->logError(__FILE__, __LINE__,
+ "Unable to locate gator.ko driver:\n"
+ " >>> gator.ko should be co-located with gatord in the same directory\n"
+ " >>> OR insmod gator.ko prior to launching gatord\n"
+ " >>> OR specify the location of gator.ko on the command line\n"
+ " >>> OR run Linux 3.4 or later with perf (CONFIG_PERF_EVENTS and CONFIG_HW_PERF_EVENTS) and tracing (CONFIG_TRACING) support to collect data via userspace only");
+ handleException();
+ }
+ }
+
+ gSessionData->hwmon.setup();
+ {
+ EventsXML eventsXML;
+ mxml_node_t *xml = eventsXML.getTree();
+ gSessionData->fsDriver.setup(xml);
+ gSessionData->maliVideo.setup(xml);
+ mxmlDelete(xml);
+ }
+
+ // Handle child exit codes
+ signal(SIGCHLD, child_exit);
+
+ // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal
+ // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler
+ signal(SIGPIPE, SIG_IGN);
+
+ // If the command line argument is a session xml file, no need to open a socket
+ if (gSessionData->mSessionXMLPath) {
+ child = new Child();
+ child->run();
+ delete child;
+ } else {
+ sock = new OlyServerSocket(cmdline.port);
+ udpListener.setup(cmdline.port);
+ if (!monitor.init() || !monitor.add(sock->getFd()) || !monitor.add(udpListener.getReq())) {
+ logg->logError(__FILE__, __LINE__, "Monitor setup failed");
+ handleException();
+ }
+ // Forever loop, can be exited via a signal or exception
+ while (1) {
+ struct epoll_event events[2];
+ logg->logMessage("Waiting on connection...");
+ int ready = monitor.wait(events, ARRAY_LENGTH(events), -1);
+ if (ready < 0) {
+ logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
+ handleException();
+ }
+ for (int i = 0; i < ready; ++i) {
+ if (events[i].data.fd == sock->getFd()) {
+ handleClient();
+ } else if (events[i].data.fd == udpListener.getReq()) {
+ udpListener.handle();
+ }
+ }
+ }
+ }
+
+ cleanUp();
+ return 0;
+}
diff --git a/tools/gator/daemon/mxml/COPYING b/tools/gator/daemon/mxml/COPYING
new file mode 100644
index 000000000000..4d0aa78af224
--- /dev/null
+++ b/tools/gator/daemon/mxml/COPYING
@@ -0,0 +1,507 @@
+ Mini-XML License
+ September 18, 2010
+
+
+The Mini-XML library and included programs are provided under the
+terms of the GNU Library General Public License version 2 (LGPL2)
+with the following exceptions:
+
+ 1. Static linking of applications to the Mini-XML library
+does not constitute a derivative work and does not require
+the author to provide source code for the application, use
+the shared Mini-XML libraries, or link their applications
+against a user-supplied version of Mini-XML.
+
+If you link the application to a modified version of
+Mini-XML, then the changes to Mini-XML must be provided
+under the terms of the LGPL2 in sections 1, 2, and 4.
+
+ 2. You do not have to provide a copy of the Mini-XML license
+with programs that are linked to the Mini-XML library, nor
+do you have to identify the Mini-XML license in your
+program or documentation as required by section 6 of the
+LGPL2.
+
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ [This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/tools/gator/daemon/mxml/config.h b/tools/gator/daemon/mxml/config.h
new file mode 100644
index 000000000000..1f59ba34a474
--- /dev/null
+++ b/tools/gator/daemon/mxml/config.h
@@ -0,0 +1,96 @@
+/* config.h. Generated from config.h.in by configure. */
+/*
+ * "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $"
+ *
+ * Configuration file for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+
+/*
+ * Version number...
+ */
+
+#define MXML_VERSION "Mini-XML v2.7"
+
+
+/*
+ * Inline function support...
+ */
+
+#define inline
+
+
+/*
+ * Long long support...
+ */
+
+#define HAVE_LONG_LONG 1
+
+
+/*
+ * Do we have the snprintf() and vsnprintf() functions?
+ */
+
+#define HAVE_SNPRINTF 1
+#define HAVE_VSNPRINTF 1
+
+
+/*
+ * Do we have the strXXX() functions?
+ */
+
+#define HAVE_STRDUP 1
+
+
+/*
+ * Do we have threading support?
+ */
+
+#define HAVE_PTHREAD_H 1
+
+
+/*
+ * Define prototypes for string functions as needed...
+ */
+
+# ifndef HAVE_STRDUP
+extern char *_mxml_strdup(const char *);
+# define strdup _mxml_strdup
+# endif /* !HAVE_STRDUP */
+
+extern char *_mxml_strdupf(const char *, ...);
+extern char *_mxml_vstrdupf(const char *, va_list);
+
+# ifndef HAVE_SNPRINTF
+extern int _mxml_snprintf(char *, size_t, const char *, ...);
+# define snprintf _mxml_snprintf
+# endif /* !HAVE_SNPRINTF */
+
+# ifndef HAVE_VSNPRINTF
+extern int _mxml_vsnprintf(char *, size_t, const char *, va_list);
+# define vsnprintf _mxml_vsnprintf
+# endif /* !HAVE_VSNPRINTF */
+
+/*
+ * End of "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-attr.c b/tools/gator/daemon/mxml/mxml-attr.c
new file mode 100644
index 000000000000..c9950f5fb732
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-attr.c
@@ -0,0 +1,319 @@
+/*
+ * "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $"
+ *
+ * Attribute support code for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlElementDeleteAttr() - Delete an attribute.
+ * mxmlElementGetAttr() - Get an attribute.
+ * mxmlElementSetAttr() - Set an attribute.
+ * mxmlElementSetAttrf() - Set an attribute with a formatted value.
+ * mxml_set_attr() - Set or add an attribute name/value pair.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int mxml_set_attr(mxml_node_t *node, const char *name,
+ char *value);
+
+
+/*
+ * 'mxmlElementDeleteAttr()' - Delete an attribute.
+ *
+ * @since Mini-XML 2.4@
+ */
+
+void
+mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */
+ const char *name)/* I - Attribute name */
+{
+ int i; /* Looping var */
+ mxml_attr_t *attr; /* Cirrent attribute */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlElementDeleteAttr(node=%p, name=\"%s\")\n",
+ node, name ? name : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name)
+ return;
+
+ /*
+ * Look for the attribute...
+ */
+
+ for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
+ i > 0;
+ i --, attr ++)
+ {
+#ifdef DEBUG
+ printf(" %s=\"%s\"\n", attr->name, attr->value);
+#endif /* DEBUG */
+
+ if (!strcmp(attr->name, name))
+ {
+ /*
+ * Delete this attribute...
+ */
+
+ free(attr->name);
+ free(attr->value);
+
+ i --;
+ if (i > 0)
+ memmove(attr, attr + 1, i * sizeof(mxml_attr_t));
+
+ node->value.element.num_attrs --;
+ return;
+ }
+ }
+}
+
+
+/*
+ * 'mxmlElementGetAttr()' - Get an attribute.
+ *
+ * This function returns NULL if the node is not an element or the
+ * named attribute does not exist.
+ */
+
+const char * /* O - Attribute value or NULL */
+mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */
+ const char *name) /* I - Name of attribute */
+{
+ int i; /* Looping var */
+ mxml_attr_t *attr; /* Cirrent attribute */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlElementGetAttr(node=%p, name=\"%s\")\n",
+ node, name ? name : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name)
+ return (NULL);
+
+ /*
+ * Look for the attribute...
+ */
+
+ for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
+ i > 0;
+ i --, attr ++)
+ {
+#ifdef DEBUG
+ printf(" %s=\"%s\"\n", attr->name, attr->value);
+#endif /* DEBUG */
+
+ if (!strcmp(attr->name, name))
+ {
+#ifdef DEBUG
+ printf(" Returning \"%s\"!\n", attr->value);
+#endif /* DEBUG */
+ return (attr->value);
+ }
+ }
+
+ /*
+ * Didn't find attribute, so return NULL...
+ */
+
+#ifdef DEBUG
+ puts(" Returning NULL!\n");
+#endif /* DEBUG */
+
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlElementSetAttr()' - Set an attribute.
+ *
+ * If the named attribute already exists, the value of the attribute
+ * is replaced by the new string value. The string value is copied
+ * into the element node. This function does nothing if the node is
+ * not an element.
+ */
+
+void
+mxmlElementSetAttr(mxml_node_t *node, /* I - Element node */
+ const char *name, /* I - Name of attribute */
+ const char *value) /* I - Attribute value */
+{
+ char *valuec; /* Copy of value */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n",
+ node, name ? name : "(null)", value ? value : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name)
+ return;
+
+ if (value)
+ valuec = strdup(value);
+ else
+ valuec = NULL;
+
+ if (mxml_set_attr(node, name, valuec))
+ free(valuec);
+}
+
+
+/*
+ * 'mxmlElementSetAttrf()' - Set an attribute with a formatted value.
+ *
+ * If the named attribute already exists, the value of the attribute
+ * is replaced by the new formatted string. The formatted string value is
+ * copied into the element node. This function does nothing if the node
+ * is not an element.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+void
+mxmlElementSetAttrf(mxml_node_t *node, /* I - Element node */
+ const char *name, /* I - Name of attribute */
+ const char *format,/* I - Printf-style attribute value */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Argument pointer */
+ char *value; /* Value */
+
+
+#ifdef DEBUG
+ fprintf(stderr,
+ "mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n",
+ node, name ? name : "(null)", format ? format : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name || !format)
+ return;
+
+ /*
+ * Format the value...
+ */
+
+ va_start(ap, format);
+ value = _mxml_vstrdupf(format, ap);
+ va_end(ap);
+
+ if (!value)
+ mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ else if (mxml_set_attr(node, name, value))
+ free(value);
+}
+
+
+/*
+ * 'mxml_set_attr()' - Set or add an attribute name/value pair.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_set_attr(mxml_node_t *node, /* I - Element node */
+ const char *name, /* I - Attribute name */
+ char *value) /* I - Attribute value */
+{
+ int i; /* Looping var */
+ mxml_attr_t *attr; /* New attribute */
+
+
+ /*
+ * Look for the attribute...
+ */
+
+ for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
+ i > 0;
+ i --, attr ++)
+ if (!strcmp(attr->name, name))
+ {
+ /*
+ * Free the old value as needed...
+ */
+
+ if (attr->value)
+ free(attr->value);
+
+ attr->value = value;
+
+ return (0);
+ }
+
+ /*
+ * Add a new attribute...
+ */
+
+ if (node->value.element.num_attrs == 0)
+ attr = malloc(sizeof(mxml_attr_t));
+ else
+ attr = realloc(node->value.element.attrs,
+ (node->value.element.num_attrs + 1) * sizeof(mxml_attr_t));
+
+ if (!attr)
+ {
+ mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ return (-1);
+ }
+
+ node->value.element.attrs = attr;
+ attr += node->value.element.num_attrs;
+
+ if ((attr->name = strdup(name)) == NULL)
+ {
+ mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ return (-1);
+ }
+
+ attr->value = value;
+
+ node->value.element.num_attrs ++;
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-entity.c b/tools/gator/daemon/mxml/mxml-entity.c
new file mode 100644
index 000000000000..c5c9f61f73c2
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-entity.c
@@ -0,0 +1,460 @@
+/*
+ * "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $"
+ *
+ * Character entity support code for Mini-XML, a small XML-like
+ * file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlEntityAddCallback() - Add a callback to convert entities to
+ * Unicode.
+ * mxmlEntityGetName() - Get the name that corresponds to the
+ * character value.
+ * mxmlEntityGetValue() - Get the character corresponding to a named
+ * entity.
+ * mxmlEntityRemoveCallback() - Remove a callback.
+ * _mxml_entity_cb() - Lookup standard (X)HTML entities.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "mxml-private.h"
+
+
+/*
+ * 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlEntityAddCallback(
+ mxml_entity_cb_t cb) /* I - Callback function to add */
+{
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0])))
+ {
+ global->entity_cbs[global->num_entity_cbs] = cb;
+ global->num_entity_cbs ++;
+
+ return (0);
+ }
+ else
+ {
+ mxml_error("Unable to add entity callback!");
+
+ return (-1);
+ }
+}
+
+
+/*
+ * 'mxmlEntityGetName()' - Get the name that corresponds to the character value.
+ *
+ * If val does not need to be represented by a named entity, NULL is returned.
+ */
+
+const char * /* O - Entity name or NULL */
+mxmlEntityGetName(int val) /* I - Character value */
+{
+ switch (val)
+ {
+ case '&' :
+ return ("amp");
+
+ case '<' :
+ return ("lt");
+
+ case '>' :
+ return ("gt");
+
+ case '\"' :
+ return ("quot");
+
+ default :
+ return (NULL);
+ }
+}
+
+
+/*
+ * 'mxmlEntityGetValue()' - Get the character corresponding to a named entity.
+ *
+ * The entity name can also be a numeric constant. -1 is returned if the
+ * name is not known.
+ */
+
+int /* O - Character value or -1 on error */
+mxmlEntityGetValue(const char *name) /* I - Entity name */
+{
+ int i; /* Looping var */
+ int ch; /* Character value */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ for (i = 0; i < global->num_entity_cbs; i ++)
+ if ((ch = (global->entity_cbs[i])(name)) >= 0)
+ return (ch);
+
+ return (-1);
+}
+
+
+/*
+ * 'mxmlEntityRemoveCallback()' - Remove a callback.
+ */
+
+void
+mxmlEntityRemoveCallback(
+ mxml_entity_cb_t cb) /* I - Callback function to remove */
+{
+ int i; /* Looping var */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ for (i = 0; i < global->num_entity_cbs; i ++)
+ if (cb == global->entity_cbs[i])
+ {
+ /*
+ * Remove the callback...
+ */
+
+ global->num_entity_cbs --;
+
+ if (i < global->num_entity_cbs)
+ memmove(global->entity_cbs + i, global->entity_cbs + i + 1,
+ (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0]));
+
+ return;
+ }
+}
+
+
+/*
+ * '_mxml_entity_cb()' - Lookup standard (X)HTML entities.
+ */
+
+int /* O - Unicode value or -1 */
+_mxml_entity_cb(const char *name) /* I - Entity name */
+{
+ int diff, /* Difference between names */
+ current, /* Current entity in search */
+ first, /* First entity in search */
+ last; /* Last entity in search */
+ static const struct
+ {
+ const char *name; /* Entity name */
+ int val; /* Character value */
+ } entities[] =
+ {
+ { "AElig", 198 },
+ { "Aacute", 193 },
+ { "Acirc", 194 },
+ { "Agrave", 192 },
+ { "Alpha", 913 },
+ { "Aring", 197 },
+ { "Atilde", 195 },
+ { "Auml", 196 },
+ { "Beta", 914 },
+ { "Ccedil", 199 },
+ { "Chi", 935 },
+ { "Dagger", 8225 },
+ { "Delta", 916 },
+ { "Dstrok", 208 },
+ { "ETH", 208 },
+ { "Eacute", 201 },
+ { "Ecirc", 202 },
+ { "Egrave", 200 },
+ { "Epsilon", 917 },
+ { "Eta", 919 },
+ { "Euml", 203 },
+ { "Gamma", 915 },
+ { "Iacute", 205 },
+ { "Icirc", 206 },
+ { "Igrave", 204 },
+ { "Iota", 921 },
+ { "Iuml", 207 },
+ { "Kappa", 922 },
+ { "Lambda", 923 },
+ { "Mu", 924 },
+ { "Ntilde", 209 },
+ { "Nu", 925 },
+ { "OElig", 338 },
+ { "Oacute", 211 },
+ { "Ocirc", 212 },
+ { "Ograve", 210 },
+ { "Omega", 937 },
+ { "Omicron", 927 },
+ { "Oslash", 216 },
+ { "Otilde", 213 },
+ { "Ouml", 214 },
+ { "Phi", 934 },
+ { "Pi", 928 },
+ { "Prime", 8243 },
+ { "Psi", 936 },
+ { "Rho", 929 },
+ { "Scaron", 352 },
+ { "Sigma", 931 },
+ { "THORN", 222 },
+ { "Tau", 932 },
+ { "Theta", 920 },
+ { "Uacute", 218 },
+ { "Ucirc", 219 },
+ { "Ugrave", 217 },
+ { "Upsilon", 933 },
+ { "Uuml", 220 },
+ { "Xi", 926 },
+ { "Yacute", 221 },
+ { "Yuml", 376 },
+ { "Zeta", 918 },
+ { "aacute", 225 },
+ { "acirc", 226 },
+ { "acute", 180 },
+ { "aelig", 230 },
+ { "agrave", 224 },
+ { "alefsym", 8501 },
+ { "alpha", 945 },
+ { "amp", '&' },
+ { "and", 8743 },
+ { "ang", 8736 },
+ { "apos", '\'' },
+ { "aring", 229 },
+ { "asymp", 8776 },
+ { "atilde", 227 },
+ { "auml", 228 },
+ { "bdquo", 8222 },
+ { "beta", 946 },
+ { "brkbar", 166 },
+ { "brvbar", 166 },
+ { "bull", 8226 },
+ { "cap", 8745 },
+ { "ccedil", 231 },
+ { "cedil", 184 },
+ { "cent", 162 },
+ { "chi", 967 },
+ { "circ", 710 },
+ { "clubs", 9827 },
+ { "cong", 8773 },
+ { "copy", 169 },
+ { "crarr", 8629 },
+ { "cup", 8746 },
+ { "curren", 164 },
+ { "dArr", 8659 },
+ { "dagger", 8224 },
+ { "darr", 8595 },
+ { "deg", 176 },
+ { "delta", 948 },
+ { "diams", 9830 },
+ { "die", 168 },
+ { "divide", 247 },
+ { "eacute", 233 },
+ { "ecirc", 234 },
+ { "egrave", 232 },
+ { "empty", 8709 },
+ { "emsp", 8195 },
+ { "ensp", 8194 },
+ { "epsilon", 949 },
+ { "equiv", 8801 },
+ { "eta", 951 },
+ { "eth", 240 },
+ { "euml", 235 },
+ { "euro", 8364 },
+ { "exist", 8707 },
+ { "fnof", 402 },
+ { "forall", 8704 },
+ { "frac12", 189 },
+ { "frac14", 188 },
+ { "frac34", 190 },
+ { "frasl", 8260 },
+ { "gamma", 947 },
+ { "ge", 8805 },
+ { "gt", '>' },
+ { "hArr", 8660 },
+ { "harr", 8596 },
+ { "hearts", 9829 },
+ { "hellip", 8230 },
+ { "hibar", 175 },
+ { "iacute", 237 },
+ { "icirc", 238 },
+ { "iexcl", 161 },
+ { "igrave", 236 },
+ { "image", 8465 },
+ { "infin", 8734 },
+ { "int", 8747 },
+ { "iota", 953 },
+ { "iquest", 191 },
+ { "isin", 8712 },
+ { "iuml", 239 },
+ { "kappa", 954 },
+ { "lArr", 8656 },
+ { "lambda", 955 },
+ { "lang", 9001 },
+ { "laquo", 171 },
+ { "larr", 8592 },
+ { "lceil", 8968 },
+ { "ldquo", 8220 },
+ { "le", 8804 },
+ { "lfloor", 8970 },
+ { "lowast", 8727 },
+ { "loz", 9674 },
+ { "lrm", 8206 },
+ { "lsaquo", 8249 },
+ { "lsquo", 8216 },
+ { "lt", '<' },
+ { "macr", 175 },
+ { "mdash", 8212 },
+ { "micro", 181 },
+ { "middot", 183 },
+ { "minus", 8722 },
+ { "mu", 956 },
+ { "nabla", 8711 },
+ { "nbsp", 160 },
+ { "ndash", 8211 },
+ { "ne", 8800 },
+ { "ni", 8715 },
+ { "not", 172 },
+ { "notin", 8713 },
+ { "nsub", 8836 },
+ { "ntilde", 241 },
+ { "nu", 957 },
+ { "oacute", 243 },
+ { "ocirc", 244 },
+ { "oelig", 339 },
+ { "ograve", 242 },
+ { "oline", 8254 },
+ { "omega", 969 },
+ { "omicron", 959 },
+ { "oplus", 8853 },
+ { "or", 8744 },
+ { "ordf", 170 },
+ { "ordm", 186 },
+ { "oslash", 248 },
+ { "otilde", 245 },
+ { "otimes", 8855 },
+ { "ouml", 246 },
+ { "para", 182 },
+ { "part", 8706 },
+ { "permil", 8240 },
+ { "perp", 8869 },
+ { "phi", 966 },
+ { "pi", 960 },
+ { "piv", 982 },
+ { "plusmn", 177 },
+ { "pound", 163 },
+ { "prime", 8242 },
+ { "prod", 8719 },
+ { "prop", 8733 },
+ { "psi", 968 },
+ { "quot", '\"' },
+ { "rArr", 8658 },
+ { "radic", 8730 },
+ { "rang", 9002 },
+ { "raquo", 187 },
+ { "rarr", 8594 },
+ { "rceil", 8969 },
+ { "rdquo", 8221 },
+ { "real", 8476 },
+ { "reg", 174 },
+ { "rfloor", 8971 },
+ { "rho", 961 },
+ { "rlm", 8207 },
+ { "rsaquo", 8250 },
+ { "rsquo", 8217 },
+ { "sbquo", 8218 },
+ { "scaron", 353 },
+ { "sdot", 8901 },
+ { "sect", 167 },
+ { "shy", 173 },
+ { "sigma", 963 },
+ { "sigmaf", 962 },
+ { "sim", 8764 },
+ { "spades", 9824 },
+ { "sub", 8834 },
+ { "sube", 8838 },
+ { "sum", 8721 },
+ { "sup", 8835 },
+ { "sup1", 185 },
+ { "sup2", 178 },
+ { "sup3", 179 },
+ { "supe", 8839 },
+ { "szlig", 223 },
+ { "tau", 964 },
+ { "there4", 8756 },
+ { "theta", 952 },
+ { "thetasym", 977 },
+ { "thinsp", 8201 },
+ { "thorn", 254 },
+ { "tilde", 732 },
+ { "times", 215 },
+ { "trade", 8482 },
+ { "uArr", 8657 },
+ { "uacute", 250 },
+ { "uarr", 8593 },
+ { "ucirc", 251 },
+ { "ugrave", 249 },
+ { "uml", 168 },
+ { "upsih", 978 },
+ { "upsilon", 965 },
+ { "uuml", 252 },
+ { "weierp", 8472 },
+ { "xi", 958 },
+ { "yacute", 253 },
+ { "yen", 165 },
+ { "yuml", 255 },
+ { "zeta", 950 },
+ { "zwj", 8205 },
+ { "zwnj", 8204 }
+ };
+
+
+ /*
+ * Do a binary search for the named entity...
+ */
+
+ first = 0;
+ last = (int)(sizeof(entities) / sizeof(entities[0]) - 1);
+
+ while ((last - first) > 1)
+ {
+ current = (first + last) / 2;
+
+ if ((diff = strcmp(name, entities[current].name)) == 0)
+ return (entities[current].val);
+ else if (diff < 0)
+ last = current;
+ else
+ first = current;
+ }
+
+ /*
+ * If we get here, there is a small chance that there is still
+ * a match; check first and last...
+ */
+
+ if (!strcmp(name, entities[first].name))
+ return (entities[first].val);
+ else if (!strcmp(name, entities[last].name))
+ return (entities[last].val);
+ else
+ return (-1);
+}
+
+
+/*
+ * End of "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-file.c b/tools/gator/daemon/mxml/mxml-file.c
new file mode 100644
index 000000000000..7860ee5f8370
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-file.c
@@ -0,0 +1,3082 @@
+/*
+ * "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $"
+ *
+ * File loading code for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlLoadFd() - Load a file descriptor into an XML node tree.
+ * mxmlLoadFile() - Load a file into an XML node tree.
+ * mxmlLoadString() - Load a string into an XML node tree.
+ * mxmlSaveAllocString() - Save an XML tree to an allocated string.
+ * mxmlSaveFd() - Save an XML tree to a file descriptor.
+ * mxmlSaveFile() - Save an XML tree to a file.
+ * mxmlSaveString() - Save an XML node tree to a string.
+ * mxmlSAXLoadFd() - Load a file descriptor into an XML node tree
+ * using a SAX callback.
+ * mxmlSAXLoadFile() - Load a file into an XML node tree
+ * using a SAX callback.
+ * mxmlSAXLoadString() - Load a string into an XML node tree
+ * using a SAX callback.
+ * mxmlSetCustomHandlers() - Set the handling functions for custom data.
+ * mxmlSetErrorCallback() - Set the error message callback.
+ * mxmlSetWrapMargin() - Set the wrap margin when saving XML data.
+ * mxml_add_char() - Add a character to a buffer, expanding as needed.
+ * mxml_fd_getc() - Read a character from a file descriptor.
+ * mxml_fd_putc() - Write a character to a file descriptor.
+ * mxml_fd_read() - Read a buffer of data from a file descriptor.
+ * mxml_fd_write() - Write a buffer of data to a file descriptor.
+ * mxml_file_getc() - Get a character from a file.
+ * mxml_file_putc() - Write a character to a file.
+ * mxml_get_entity() - Get the character corresponding to an entity...
+ * mxml_load_data() - Load data into an XML node tree.
+ * mxml_parse_element() - Parse an element for any attributes...
+ * mxml_string_getc() - Get a character from a string.
+ * mxml_string_putc() - Write a character to a string.
+ * mxml_write_name() - Write a name string.
+ * mxml_write_node() - Save an XML node to a file.
+ * mxml_write_string() - Write a string, escaping & and < as needed.
+ * mxml_write_ws() - Do whitespace callback...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#ifndef WIN32
+# include <unistd.h>
+#endif /* !WIN32 */
+#include "mxml-private.h"
+
+
+/*
+ * Character encoding...
+ */
+
+#define ENCODE_UTF8 0 /* UTF-8 */
+#define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */
+#define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian */
+
+
+/*
+ * Macro to test for a bad XML character...
+ */
+
+#define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t')
+
+
+/*
+ * Types and structures...
+ */
+
+typedef int (*_mxml_getc_cb_t)(void *, int *);
+typedef int (*_mxml_putc_cb_t)(int, void *);
+
+typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/
+{
+ int fd; /* File descriptor */
+ unsigned char *current, /* Current position in buffer */
+ *end, /* End of buffer */
+ buffer[8192]; /* Character buffer */
+} _mxml_fdbuf_t;
+
+
+/*
+ * Local functions...
+ */
+
+static int mxml_add_char(int ch, char **ptr, char **buffer,
+ int *bufsize);
+static int mxml_fd_getc(void *p, int *encoding);
+static int mxml_fd_putc(int ch, void *p);
+static int mxml_fd_read(_mxml_fdbuf_t *buf);
+static int mxml_fd_write(_mxml_fdbuf_t *buf);
+static int mxml_file_getc(void *p, int *encoding);
+static int mxml_file_putc(int ch, void *p);
+static int mxml_get_entity(mxml_node_t *parent, void *p,
+ int *encoding,
+ _mxml_getc_cb_t getc_cb);
+static inline int mxml_isspace(int ch)
+ {
+ return (ch == ' ' || ch == '\t' || ch == '\r' ||
+ ch == '\n');
+ }
+static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p,
+ mxml_load_cb_t cb,
+ _mxml_getc_cb_t getc_cb,
+ mxml_sax_cb_t sax_cb, void *sax_data);
+static int mxml_parse_element(mxml_node_t *node, void *p,
+ int *encoding,
+ _mxml_getc_cb_t getc_cb);
+static int mxml_string_getc(void *p, int *encoding);
+static int mxml_string_putc(int ch, void *p);
+static int mxml_write_name(const char *s, void *p,
+ _mxml_putc_cb_t putc_cb);
+static int mxml_write_node(mxml_node_t *node, void *p,
+ mxml_save_cb_t cb, int col,
+ _mxml_putc_cb_t putc_cb,
+ _mxml_global_t *global);
+static int mxml_write_string(const char *s, void *p,
+ _mxml_putc_cb_t putc_cb);
+static int mxml_write_ws(mxml_node_t *node, void *p,
+ mxml_save_cb_t cb, int ws,
+ int col, _mxml_putc_cb_t putc_cb);
+
+
+/*
+ * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree.
+ *
+ * The nodes in the specified file are added to the specified top node.
+ * If no top node is provided, the XML file MUST be well-formed with a
+ * single parent node like <?xml> for the entire file. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ */
+
+mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxmlLoadFd(mxml_node_t *top, /* I - Top node */
+ int fd, /* I - File descriptor to read from */
+ mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
+{
+ _mxml_fdbuf_t buf; /* File descriptor buffer */
+
+
+ /*
+ * Initialize the file descriptor buffer...
+ */
+
+ buf.fd = fd;
+ buf.current = buf.buffer;
+ buf.end = buf.buffer;
+
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, &buf, cb, mxml_fd_getc, MXML_NO_CALLBACK, NULL));
+}
+
+
+/*
+ * 'mxmlLoadFile()' - Load a file into an XML node tree.
+ *
+ * The nodes in the specified file are added to the specified top node.
+ * If no top node is provided, the XML file MUST be well-formed with a
+ * single parent node like <?xml> for the entire file. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ */
+
+mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxmlLoadFile(mxml_node_t *top, /* I - Top node */
+ FILE *fp, /* I - File to read from */
+ mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
+{
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, fp, cb, mxml_file_getc, MXML_NO_CALLBACK, NULL));
+}
+
+
+/*
+ * 'mxmlLoadString()' - Load a string into an XML node tree.
+ *
+ * The nodes in the specified string are added to the specified top node.
+ * If no top node is provided, the XML string MUST be well-formed with a
+ * single parent node like <?xml> for the entire string. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ */
+
+mxml_node_t * /* O - First node or NULL if the string has errors. */
+mxmlLoadString(mxml_node_t *top, /* I - Top node */
+ const char *s, /* I - String to load */
+ mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
+{
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, MXML_NO_CALLBACK,
+ NULL));
+}
+
+
+/*
+ * 'mxmlSaveAllocString()' - Save an XML tree to an allocated string.
+ *
+ * This function returns a pointer to a string containing the textual
+ * representation of the XML node tree. The string should be freed
+ * using the free() function when you are done with it. NULL is returned
+ * if the node would produce an empty string or if the string cannot be
+ * allocated.
+ *
+ * The callback argument specifies a function that returns a whitespace
+ * string or NULL before and after each element. If MXML_NO_CALLBACK
+ * is specified, whitespace will only be added before MXML_TEXT nodes
+ * with leading whitespace and before attribute names inside opening
+ * element tags.
+ */
+
+char * /* O - Allocated string or NULL */
+mxmlSaveAllocString(
+ mxml_node_t *node, /* I - Node to write */
+ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
+{
+ int bytes; /* Required bytes */
+ char buffer[8192]; /* Temporary buffer */
+ char *s; /* Allocated string */
+
+
+ /*
+ * Write the node to the temporary buffer...
+ */
+
+ bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb);
+
+ if (bytes <= 0)
+ return (NULL);
+
+ if (bytes < (int)(sizeof(buffer) - 1))
+ {
+ /*
+ * Node fit inside the buffer, so just duplicate that string and
+ * return...
+ */
+
+ return (strdup(buffer));
+ }
+
+ /*
+ * Allocate a buffer of the required size and save the node to the
+ * new buffer...
+ */
+
+ if ((s = malloc(bytes + 1)) == NULL)
+ return (NULL);
+
+ mxmlSaveString(node, s, bytes + 1, cb);
+
+ /*
+ * Return the allocated string...
+ */
+
+ return (s);
+}
+
+
+/*
+ * 'mxmlSaveFd()' - Save an XML tree to a file descriptor.
+ *
+ * The callback argument specifies a function that returns a whitespace
+ * string or NULL before and after each element. If MXML_NO_CALLBACK
+ * is specified, whitespace will only be added before MXML_TEXT nodes
+ * with leading whitespace and before attribute names inside opening
+ * element tags.
+ */
+
+int /* O - 0 on success, -1 on error. */
+mxmlSaveFd(mxml_node_t *node, /* I - Node to write */
+ int fd, /* I - File descriptor to write to */
+ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
+{
+ int col; /* Final column */
+ _mxml_fdbuf_t buf; /* File descriptor buffer */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ /*
+ * Initialize the file descriptor buffer...
+ */
+
+ buf.fd = fd;
+ buf.current = buf.buffer;
+ buf.end = buf.buffer + sizeof(buf.buffer);
+
+ /*
+ * Write the node...
+ */
+
+ if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc, global)) < 0)
+ return (-1);
+
+ if (col > 0)
+ if (mxml_fd_putc('\n', &buf) < 0)
+ return (-1);
+
+ /*
+ * Flush and return...
+ */
+
+ return (mxml_fd_write(&buf));
+}
+
+
+/*
+ * 'mxmlSaveFile()' - Save an XML tree to a file.
+ *
+ * The callback argument specifies a function that returns a whitespace
+ * string or NULL before and after each element. If MXML_NO_CALLBACK
+ * is specified, whitespace will only be added before MXML_TEXT nodes
+ * with leading whitespace and before attribute names inside opening
+ * element tags.
+ */
+
+int /* O - 0 on success, -1 on error. */
+mxmlSaveFile(mxml_node_t *node, /* I - Node to write */
+ FILE *fp, /* I - File to write to */
+ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
+{
+ int col; /* Final column */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ /*
+ * Write the node...
+ */
+
+ if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc, global)) < 0)
+ return (-1);
+
+ if (col > 0)
+ if (putc('\n', fp) < 0)
+ return (-1);
+
+ /*
+ * Return 0 (success)...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSaveString()' - Save an XML node tree to a string.
+ *
+ * This function returns the total number of bytes that would be
+ * required for the string but only copies (bufsize - 1) characters
+ * into the specified buffer.
+ *
+ * The callback argument specifies a function that returns a whitespace
+ * string or NULL before and after each element. If MXML_NO_CALLBACK
+ * is specified, whitespace will only be added before MXML_TEXT nodes
+ * with leading whitespace and before attribute names inside opening
+ * element tags.
+ */
+
+int /* O - Size of string */
+mxmlSaveString(mxml_node_t *node, /* I - Node to write */
+ char *buffer, /* I - String buffer */
+ int bufsize, /* I - Size of string buffer */
+ mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
+{
+ int col; /* Final column */
+ char *ptr[2]; /* Pointers for putc_cb */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ /*
+ * Write the node...
+ */
+
+ ptr[0] = buffer;
+ ptr[1] = buffer + bufsize;
+
+ if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc, global)) < 0)
+ return (-1);
+
+ if (col > 0)
+ mxml_string_putc('\n', ptr);
+
+ /*
+ * Nul-terminate the buffer...
+ */
+
+ if (ptr[0] >= ptr[1])
+ buffer[bufsize - 1] = '\0';
+ else
+ ptr[0][0] = '\0';
+
+ /*
+ * Return the number of characters...
+ */
+
+ return (ptr[0] - buffer);
+}
+
+
+/*
+ * 'mxmlSAXLoadFd()' - Load a file descriptor into an XML node tree
+ * using a SAX callback.
+ *
+ * The nodes in the specified file are added to the specified top node.
+ * If no top node is provided, the XML file MUST be well-formed with a
+ * single parent node like <?xml> for the entire file. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ *
+ * The SAX callback must call mxmlRetain() for any nodes that need to
+ * be kept for later use. Otherwise, nodes are deleted when the parent
+ * node is closed or after each data, comment, CDATA, or directive node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */
+ int fd, /* I - File descriptor to read from */
+ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
+ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
+ void *sax_data) /* I - SAX user data */
+{
+ _mxml_fdbuf_t buf; /* File descriptor buffer */
+
+
+ /*
+ * Initialize the file descriptor buffer...
+ */
+
+ buf.fd = fd;
+ buf.current = buf.buffer;
+ buf.end = buf.buffer;
+
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, &buf, cb, mxml_fd_getc, sax_cb, sax_data));
+}
+
+
+/*
+ * 'mxmlSAXLoadFile()' - Load a file into an XML node tree
+ * using a SAX callback.
+ *
+ * The nodes in the specified file are added to the specified top node.
+ * If no top node is provided, the XML file MUST be well-formed with a
+ * single parent node like <?xml> for the entire file. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ *
+ * The SAX callback must call mxmlRetain() for any nodes that need to
+ * be kept for later use. Otherwise, nodes are deleted when the parent
+ * node is closed or after each data, comment, CDATA, or directive node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxmlSAXLoadFile(
+ mxml_node_t *top, /* I - Top node */
+ FILE *fp, /* I - File to read from */
+ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
+ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
+ void *sax_data) /* I - SAX user data */
+{
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, fp, cb, mxml_file_getc, sax_cb, sax_data));
+}
+
+
+/*
+ * 'mxmlSAXLoadString()' - Load a string into an XML node tree
+ * using a SAX callback.
+ *
+ * The nodes in the specified string are added to the specified top node.
+ * If no top node is provided, the XML string MUST be well-formed with a
+ * single parent node like <?xml> for the entire string. The callback
+ * function returns the value type that should be used for child nodes.
+ * If MXML_NO_CALLBACK is specified then all child nodes will be either
+ * MXML_ELEMENT or MXML_TEXT nodes.
+ *
+ * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
+ * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
+ * child nodes of the specified type.
+ *
+ * The SAX callback must call mxmlRetain() for any nodes that need to
+ * be kept for later use. Otherwise, nodes are deleted when the parent
+ * node is closed or after each data, comment, CDATA, or directive node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - First node or NULL if the string has errors. */
+mxmlSAXLoadString(
+ mxml_node_t *top, /* I - Top node */
+ const char *s, /* I - String to load */
+ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
+ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
+ void *sax_data) /* I - SAX user data */
+{
+ /*
+ * Read the XML data...
+ */
+
+ return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, sax_cb, sax_data));
+}
+
+
+/*
+ * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data.
+ *
+ * The load function accepts a node pointer and a data string and must
+ * return 0 on success and non-zero on error.
+ *
+ * The save function accepts a node pointer and must return a malloc'd
+ * string on success and NULL on error.
+ *
+ */
+
+void
+mxmlSetCustomHandlers(
+ mxml_custom_load_cb_t load, /* I - Load function */
+ mxml_custom_save_cb_t save) /* I - Save function */
+{
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ global->custom_load_cb = load;
+ global->custom_save_cb = save;
+}
+
+
+/*
+ * 'mxmlSetErrorCallback()' - Set the error message callback.
+ */
+
+void
+mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */
+{
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ global->error_cb = cb;
+}
+
+
+/*
+ * 'mxmlSetWrapMargin()' - Set the wrap margin when saving XML data.
+ *
+ * Wrapping is disabled when "column" is 0.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+void
+mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping */
+{
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ global->wrap = column;
+}
+
+
+/*
+ * 'mxml_add_char()' - Add a character to a buffer, expanding as needed.
+ */
+
+static int /* O - 0 on success, -1 on error */
+mxml_add_char(int ch, /* I - Character to add */
+ char **bufptr, /* IO - Current position in buffer */
+ char **buffer, /* IO - Current buffer */
+ int *bufsize) /* IO - Current buffer size */
+{
+ char *newbuffer; /* New buffer value */
+
+
+ if (*bufptr >= (*buffer + *bufsize - 4))
+ {
+ /*
+ * Increase the size of the buffer...
+ */
+
+ if (*bufsize < 1024)
+ (*bufsize) *= 2;
+ else
+ (*bufsize) += 1024;
+
+ if ((newbuffer = realloc(*buffer, *bufsize)) == NULL)
+ {
+ free(*buffer);
+
+ mxml_error("Unable to expand string buffer to %d bytes!", *bufsize);
+
+ return (-1);
+ }
+
+ *bufptr = newbuffer + (*bufptr - *buffer);
+ *buffer = newbuffer;
+ }
+
+ if (ch < 0x80)
+ {
+ /*
+ * Single byte ASCII...
+ */
+
+ *(*bufptr)++ = ch;
+ }
+ else if (ch < 0x800)
+ {
+ /*
+ * Two-byte UTF-8...
+ */
+
+ *(*bufptr)++ = 0xc0 | (ch >> 6);
+ *(*bufptr)++ = 0x80 | (ch & 0x3f);
+ }
+ else if (ch < 0x10000)
+ {
+ /*
+ * Three-byte UTF-8...
+ */
+
+ *(*bufptr)++ = 0xe0 | (ch >> 12);
+ *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
+ *(*bufptr)++ = 0x80 | (ch & 0x3f);
+ }
+ else
+ {
+ /*
+ * Four-byte UTF-8...
+ */
+
+ *(*bufptr)++ = 0xf0 | (ch >> 18);
+ *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f);
+ *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
+ *(*bufptr)++ = 0x80 | (ch & 0x3f);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_fd_getc()' - Read a character from a file descriptor.
+ */
+
+static int /* O - Character or EOF */
+mxml_fd_getc(void *p, /* I - File descriptor buffer */
+ int *encoding) /* IO - Encoding */
+{
+ _mxml_fdbuf_t *buf; /* File descriptor buffer */
+ int ch, /* Current character */
+ temp; /* Temporary character */
+
+
+ /*
+ * Grab the next character in the buffer...
+ */
+
+ buf = (_mxml_fdbuf_t *)p;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ ch = *(buf->current)++;
+
+ switch (*encoding)
+ {
+ case ENCODE_UTF8 :
+ /*
+ * Got a UTF-8 character; convert UTF-8 to Unicode and return...
+ */
+
+ if (!(ch & 0x80))
+ {
+#if DEBUG > 1
+ printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+
+ return (ch);
+ }
+ else if (ch == 0xfe)
+ {
+ /*
+ * UTF-16 big-endian BOM?
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ ch = *(buf->current)++;
+
+ if (ch != 0xff)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16BE;
+
+ return (mxml_fd_getc(p, encoding));
+ }
+ else if (ch == 0xff)
+ {
+ /*
+ * UTF-16 little-endian BOM?
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ ch = *(buf->current)++;
+
+ if (ch != 0xfe)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16LE;
+
+ return (mxml_fd_getc(p, encoding));
+ }
+ else if ((ch & 0xe0) == 0xc0)
+ {
+ /*
+ * Two-byte value...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
+
+ if (ch < 0x80)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+ }
+ else if ((ch & 0xf0) == 0xe0)
+ {
+ /*
+ * Three-byte value...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (ch < 0x800)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+ /*
+ * Ignore (strip) Byte Order Mark (BOM)...
+ */
+
+ if (ch == 0xfeff)
+ return (mxml_fd_getc(p, encoding));
+ }
+ else if ((ch & 0xf8) == 0xf0)
+ {
+ /*
+ * Four-byte value...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x07) << 6) | (temp & 0x3f);
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ if ((temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (ch < 0x10000)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+ }
+ else
+ return (EOF);
+ break;
+
+ case ENCODE_UTF16BE :
+ /*
+ * Read UTF-16 big-endian char...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ ch = (ch << 8) | temp;
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ lch = *(buf->current)++;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ lch = (lch << 8) | temp;
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+ break;
+
+ case ENCODE_UTF16LE :
+ /*
+ * Read UTF-16 little-endian char...
+ */
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ ch |= (temp << 8);
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ lch = *(buf->current)++;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_read(buf) < 0)
+ return (EOF);
+
+ temp = *(buf->current)++;
+
+ lch |= (temp << 8);
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+ break;
+ }
+
+#if DEBUG > 1
+ printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+}
+
+
+/*
+ * 'mxml_fd_putc()' - Write a character to a file descriptor.
+ */
+
+static int /* O - 0 on success, -1 on error */
+mxml_fd_putc(int ch, /* I - Character */
+ void *p) /* I - File descriptor buffer */
+{
+ _mxml_fdbuf_t *buf; /* File descriptor buffer */
+
+
+ /*
+ * Flush the write buffer as needed...
+ */
+
+ buf = (_mxml_fdbuf_t *)p;
+
+ if (buf->current >= buf->end)
+ if (mxml_fd_write(buf) < 0)
+ return (-1);
+
+ *(buf->current)++ = ch;
+
+ /*
+ * Return successfully...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_fd_read()' - Read a buffer of data from a file descriptor.
+ */
+
+static int /* O - 0 on success, -1 on error */
+mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */
+{
+ int bytes; /* Bytes read... */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!buf)
+ return (-1);
+
+ /*
+ * Read from the file descriptor...
+ */
+
+ while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0)
+#ifdef EINTR
+ if (errno != EAGAIN && errno != EINTR)
+#else
+ if (errno != EAGAIN)
+#endif /* EINTR */
+ return (-1);
+
+ if (bytes == 0)
+ return (-1);
+
+ /*
+ * Update the pointers and return success...
+ */
+
+ buf->current = buf->buffer;
+ buf->end = buf->buffer + bytes;
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_fd_write()' - Write a buffer of data to a file descriptor.
+ */
+
+static int /* O - 0 on success, -1 on error */
+mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */
+{
+ int bytes; /* Bytes written */
+ unsigned char *ptr; /* Pointer into buffer */
+
+
+ /*
+ * Range check...
+ */
+
+ if (!buf)
+ return (-1);
+
+ /*
+ * Return 0 if there is nothing to write...
+ */
+
+ if (buf->current == buf->buffer)
+ return (0);
+
+ /*
+ * Loop until we have written everything...
+ */
+
+ for (ptr = buf->buffer; ptr < buf->current; ptr += bytes)
+ if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0)
+ return (-1);
+
+ /*
+ * All done, reset pointers and return success...
+ */
+
+ buf->current = buf->buffer;
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_file_getc()' - Get a character from a file.
+ */
+
+static int /* O - Character or EOF */
+mxml_file_getc(void *p, /* I - Pointer to file */
+ int *encoding) /* IO - Encoding */
+{
+ int ch, /* Character from file */
+ temp; /* Temporary character */
+ FILE *fp; /* Pointer to file */
+
+
+ /*
+ * Read a character from the file and see if it is EOF or ASCII...
+ */
+
+ fp = (FILE *)p;
+ ch = getc(fp);
+
+ if (ch == EOF)
+ return (EOF);
+
+ switch (*encoding)
+ {
+ case ENCODE_UTF8 :
+ /*
+ * Got a UTF-8 character; convert UTF-8 to Unicode and return...
+ */
+
+ if (!(ch & 0x80))
+ {
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+
+#if DEBUG > 1
+ printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ else if (ch == 0xfe)
+ {
+ /*
+ * UTF-16 big-endian BOM?
+ */
+
+ ch = getc(fp);
+ if (ch != 0xff)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16BE;
+
+ return (mxml_file_getc(p, encoding));
+ }
+ else if (ch == 0xff)
+ {
+ /*
+ * UTF-16 little-endian BOM?
+ */
+
+ ch = getc(fp);
+ if (ch != 0xfe)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16LE;
+
+ return (mxml_file_getc(p, encoding));
+ }
+ else if ((ch & 0xe0) == 0xc0)
+ {
+ /*
+ * Two-byte value...
+ */
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
+
+ if (ch < 0x80)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+ }
+ else if ((ch & 0xf0) == 0xe0)
+ {
+ /*
+ * Three-byte value...
+ */
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (ch < 0x800)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+ /*
+ * Ignore (strip) Byte Order Mark (BOM)...
+ */
+
+ if (ch == 0xfeff)
+ return (mxml_file_getc(p, encoding));
+ }
+ else if ((ch & 0xf8) == 0xf0)
+ {
+ /*
+ * Four-byte value...
+ */
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x07) << 6) | (temp & 0x3f);
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = (ch << 6) | (temp & 0x3f);
+
+ if (ch < 0x10000)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+ }
+ else
+ return (EOF);
+ break;
+
+ case ENCODE_UTF16BE :
+ /*
+ * Read UTF-16 big-endian char...
+ */
+
+ ch = (ch << 8) | getc(fp);
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch = (getc(fp) << 8);
+ lch |= getc(fp);
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+ break;
+
+ case ENCODE_UTF16LE :
+ /*
+ * Read UTF-16 little-endian char...
+ */
+
+ ch |= (getc(fp) << 8);
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch = getc(fp);
+ lch |= (getc(fp) << 8);
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+ break;
+ }
+
+#if DEBUG > 1
+ printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+}
+
+
+/*
+ * 'mxml_file_putc()' - Write a character to a file.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_file_putc(int ch, /* I - Character to write */
+ void *p) /* I - Pointer to file */
+{
+ return (putc(ch, (FILE *)p) == EOF ? -1 : 0);
+}
+
+
+/*
+ * 'mxml_get_entity()' - Get the character corresponding to an entity...
+ */
+
+static int /* O - Character value or EOF on error */
+mxml_get_entity(mxml_node_t *parent, /* I - Parent node */
+ void *p, /* I - Pointer to source */
+ int *encoding, /* IO - Character encoding */
+ int (*getc_cb)(void *, int *))
+ /* I - Get character function */
+{
+ int ch; /* Current character */
+ char entity[64], /* Entity string */
+ *entptr; /* Pointer into entity */
+
+
+ entptr = entity;
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ if (ch > 126 || (!isalnum(ch) && ch != '#'))
+ break;
+ else if (entptr < (entity + sizeof(entity) - 1))
+ *entptr++ = ch;
+ else
+ {
+ mxml_error("Entity name too long under parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ break;
+ }
+
+ *entptr = '\0';
+
+ if (ch != ';')
+ {
+ mxml_error("Character entity \"%s\" not terminated under parent <%s>!",
+ entity, parent ? parent->value.element.name : "null");
+ return (EOF);
+ }
+
+ if (entity[0] == '#')
+ {
+ if (entity[1] == 'x')
+ ch = strtol(entity + 2, NULL, 16);
+ else
+ ch = strtol(entity + 1, NULL, 10);
+ }
+ else if ((ch = mxmlEntityGetValue(entity)) < 0)
+ mxml_error("Entity name \"%s;\" not supported under parent <%s>!",
+ entity, parent ? parent->value.element.name : "null");
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!",
+ ch, parent ? parent->value.element.name : "null");
+ return (EOF);
+ }
+
+ return (ch);
+}
+
+
+/*
+ * 'mxml_load_data()' - Load data into an XML node tree.
+ */
+
+static mxml_node_t * /* O - First node or NULL if the file could not be read. */
+mxml_load_data(
+ mxml_node_t *top, /* I - Top node */
+ void *p, /* I - Pointer to data */
+ mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
+ _mxml_getc_cb_t getc_cb, /* I - Read function */
+ mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
+ void *sax_data) /* I - SAX user data */
+{
+ mxml_node_t *node, /* Current node */
+ *first, /* First node added */
+ *parent; /* Current parent node */
+ int ch, /* Character from file */
+ whitespace; /* Non-zero if whitespace seen */
+ char *buffer, /* String buffer */
+ *bufptr; /* Pointer into buffer */
+ int bufsize; /* Size of buffer */
+ mxml_type_t type; /* Current node type */
+ int encoding; /* Character encoding */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+ static const char * const types[] = /* Type strings... */
+ {
+ "MXML_ELEMENT", /* XML element with attributes */
+ "MXML_INTEGER", /* Integer value */
+ "MXML_OPAQUE", /* Opaque string */
+ "MXML_REAL", /* Real value */
+ "MXML_TEXT", /* Text fragment */
+ "MXML_CUSTOM" /* Custom data */
+ };
+
+
+ /*
+ * Read elements and other nodes from the file...
+ */
+
+ if ((buffer = malloc(64)) == NULL)
+ {
+ mxml_error("Unable to allocate string buffer!");
+ return (NULL);
+ }
+
+ bufsize = 64;
+ bufptr = buffer;
+ parent = top;
+ first = NULL;
+ whitespace = 0;
+ encoding = ENCODE_UTF8;
+
+ if (cb && parent)
+ type = (*cb)(parent);
+ else
+ type = MXML_TEXT;
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ {
+ if ((ch == '<' ||
+ (mxml_isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) &&
+ bufptr > buffer)
+ {
+ /*
+ * Add a new value node...
+ */
+
+ *bufptr = '\0';
+
+ switch (type)
+ {
+ case MXML_INTEGER :
+ node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0));
+ break;
+
+ case MXML_OPAQUE :
+ node = mxmlNewOpaque(parent, buffer);
+ break;
+
+ case MXML_REAL :
+ node = mxmlNewReal(parent, strtod(buffer, &bufptr));
+ break;
+
+ case MXML_TEXT :
+ node = mxmlNewText(parent, whitespace, buffer);
+ break;
+
+ case MXML_CUSTOM :
+ if (global->custom_load_cb)
+ {
+ /*
+ * Use the callback to fill in the custom data...
+ */
+
+ node = mxmlNewCustom(parent, NULL, NULL);
+
+ if ((*global->custom_load_cb)(node, buffer))
+ {
+ mxml_error("Bad custom value '%s' in parent <%s>!",
+ buffer, parent ? parent->value.element.name : "null");
+ mxmlDelete(node);
+ node = NULL;
+ }
+ break;
+ }
+
+ default : /* Ignore... */
+ node = NULL;
+ break;
+ }
+
+ if (*bufptr)
+ {
+ /*
+ * Bad integer/real number value...
+ */
+
+ mxml_error("Bad %s value '%s' in parent <%s>!",
+ type == MXML_INTEGER ? "integer" : "real", buffer,
+ parent ? parent->value.element.name : "null");
+ break;
+ }
+
+ bufptr = buffer;
+ whitespace = mxml_isspace(ch) && type == MXML_TEXT;
+
+ if (!node && type != MXML_IGNORE)
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Unable to add value node of type %s to parent <%s>!",
+ types[type], parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_DATA, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (!first && node)
+ first = node;
+ }
+ else if (mxml_isspace(ch) && type == MXML_TEXT)
+ whitespace = 1;
+
+ /*
+ * Add lone whitespace node if we have an element and existing
+ * whitespace...
+ */
+
+ if (ch == '<' && whitespace && type == MXML_TEXT)
+ {
+ if (parent)
+ {
+ node = mxmlNewText(parent, whitespace, "");
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_DATA, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (!first && node)
+ first = node;
+ }
+
+ whitespace = 0;
+ }
+
+ if (ch == '<')
+ {
+ /*
+ * Start of open/close tag...
+ */
+
+ bufptr = buffer;
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer))
+ break;
+ else if (ch == '<')
+ {
+ mxml_error("Bare < in element!");
+ goto error;
+ }
+ else if (ch == '&')
+ {
+ if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+ else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ else if (((bufptr - buffer) == 1 && buffer[0] == '?') ||
+ ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) ||
+ ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8)))
+ break;
+
+ *bufptr = '\0';
+
+ if (!strcmp(buffer, "!--"))
+ {
+ /*
+ * Gather rest of comment...
+ */
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ {
+ if (ch == '>' && bufptr > (buffer + 4) &&
+ bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-')
+ break;
+ else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+
+ /*
+ * Error out if we didn't get the whole comment...
+ */
+
+ if (ch != '>')
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Early EOF in comment node!");
+ goto error;
+ }
+
+
+ /*
+ * Otherwise add this as an element under the current parent...
+ */
+
+ *bufptr = '\0';
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Just print error for now...
+ */
+
+ mxml_error("Unable to add comment node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ break;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_COMMENT, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (node && !first)
+ first = node;
+ }
+ else if (!strcmp(buffer, "![CDATA["))
+ {
+ /*
+ * Gather CDATA section...
+ */
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ {
+ if (ch == '>' && !strncmp(bufptr - 2, "]]", 2))
+ break;
+ else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+
+ /*
+ * Error out if we didn't get the whole comment...
+ */
+
+ if (ch != '>')
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Early EOF in CDATA node!");
+ goto error;
+ }
+
+
+ /*
+ * Otherwise add this as an element under the current parent...
+ */
+
+ *bufptr = '\0';
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Unable to add CDATA node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_CDATA, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (node && !first)
+ first = node;
+ }
+ else if (buffer[0] == '?')
+ {
+ /*
+ * Gather rest of processing instruction...
+ */
+
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF)
+ {
+ if (ch == '>' && bufptr > buffer && bufptr[-1] == '?')
+ break;
+ else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+
+ /*
+ * Error out if we didn't get the whole processing instruction...
+ */
+
+ if (ch != '>')
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Early EOF in processing instruction node!");
+ goto error;
+ }
+
+ /*
+ * Otherwise add this as an element under the current parent...
+ */
+
+ *bufptr = '\0';
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Unable to add processing instruction node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (node)
+ {
+ if (!first)
+ first = node;
+
+ if (!parent)
+ {
+ parent = node;
+
+ if (cb)
+ type = (*cb)(parent);
+ }
+ }
+ }
+ else if (buffer[0] == '!')
+ {
+ /*
+ * Gather rest of declaration...
+ */
+
+ do
+ {
+ if (ch == '>')
+ break;
+ else
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+ }
+ while ((ch = (*getc_cb)(p, &encoding)) != EOF);
+
+ /*
+ * Error out if we didn't get the whole declaration...
+ */
+
+ if (ch != '>')
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Early EOF in declaration node!");
+ goto error;
+ }
+
+ /*
+ * Otherwise add this as an element under the current parent...
+ */
+
+ *bufptr = '\0';
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Print error and return...
+ */
+
+ mxml_error("Unable to add declaration node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
+
+ if (!mxmlRelease(node))
+ node = NULL;
+ }
+
+ if (node)
+ {
+ if (!first)
+ first = node;
+
+ if (!parent)
+ {
+ parent = node;
+
+ if (cb)
+ type = (*cb)(parent);
+ }
+ }
+ }
+ else if (buffer[0] == '/')
+ {
+ /*
+ * Handle close tag...
+ */
+
+ if (!parent || strcmp(buffer + 1, parent->value.element.name))
+ {
+ /*
+ * Close tag doesn't match tree; print an error for now...
+ */
+
+ mxml_error("Mismatched close tag <%s> under parent <%s>!",
+ buffer, parent ? parent->value.element.name : "(null)");
+ goto error;
+ }
+
+ /*
+ * Keep reading until we see >...
+ */
+
+ while (ch != '>' && ch != EOF)
+ ch = (*getc_cb)(p, &encoding);
+
+ node = parent;
+ parent = parent->parent;
+
+ if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
+
+ if (!mxmlRelease(node) && first == node)
+ first = NULL;
+ }
+
+ /*
+ * Ascend into the parent and set the value type as needed...
+ */
+
+ if (cb && parent)
+ type = (*cb)(parent);
+ }
+ else
+ {
+ /*
+ * Handle open tag...
+ */
+
+ if (!parent && first)
+ {
+ /*
+ * There can only be one root element!
+ */
+
+ mxml_error("<%s> cannot be a second root node after <%s>",
+ buffer, first->value.element.name);
+ goto error;
+ }
+
+ if ((node = mxmlNewElement(parent, buffer)) == NULL)
+ {
+ /*
+ * Just print error for now...
+ */
+
+ mxml_error("Unable to add element node to parent <%s>!",
+ parent ? parent->value.element.name : "null");
+ goto error;
+ }
+
+ if (mxml_isspace(ch))
+ {
+ if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF)
+ goto error;
+ }
+ else if (ch == '/')
+ {
+ if ((ch = (*getc_cb)(p, &encoding)) != '>')
+ {
+ mxml_error("Expected > but got '%c' instead for element <%s/>!",
+ ch, buffer);
+ mxmlDelete(node);
+ goto error;
+ }
+
+ ch = '/';
+ }
+
+ if (sax_cb)
+ (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data);
+
+ if (!first)
+ first = node;
+
+ if (ch == EOF)
+ break;
+
+ if (ch != '/')
+ {
+ /*
+ * Descend into this node, setting the value type as needed...
+ */
+
+ parent = node;
+
+ if (cb && parent)
+ type = (*cb)(parent);
+ }
+ else if (sax_cb)
+ {
+ (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
+
+ if (!mxmlRelease(node) && first == node)
+ first = NULL;
+ }
+ }
+
+ bufptr = buffer;
+ }
+ else if (ch == '&')
+ {
+ /*
+ * Add character entity to current buffer...
+ */
+
+ if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+ else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch))
+ {
+ /*
+ * Add character to current buffer...
+ */
+
+ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
+ goto error;
+ }
+ }
+
+ /*
+ * Free the string buffer - we don't need it anymore...
+ */
+
+ free(buffer);
+
+ /*
+ * Find the top element and return it...
+ */
+
+ if (parent)
+ {
+ node = parent;
+
+ while (parent->parent != top && parent->parent)
+ parent = parent->parent;
+
+ if (node != parent)
+ {
+ mxml_error("Missing close tag </%s> under parent <%s>!",
+ node->value.element.name,
+ node->parent ? node->parent->value.element.name : "(null)");
+
+ mxmlDelete(first);
+
+ return (NULL);
+ }
+ }
+
+ if (parent)
+ return (parent);
+ else
+ return (first);
+
+ /*
+ * Common error return...
+ */
+
+error:
+
+ mxmlDelete(first);
+
+ free(buffer);
+
+ return (NULL);
+}
+
+
+/*
+ * 'mxml_parse_element()' - Parse an element for any attributes...
+ */
+
+static int /* O - Terminating character */
+mxml_parse_element(
+ mxml_node_t *node, /* I - Element node */
+ void *p, /* I - Data to read from */
+ int *encoding, /* IO - Encoding */
+ _mxml_getc_cb_t getc_cb) /* I - Data callback */
+{
+ int ch, /* Current character in file */
+ quote; /* Quoting character */
+ char *name, /* Attribute name */
+ *value, /* Attribute value */
+ *ptr; /* Pointer into name/value */
+ int namesize, /* Size of name string */
+ valsize; /* Size of value string */
+
+
+ /*
+ * Initialize the name and value buffers...
+ */
+
+ if ((name = malloc(64)) == NULL)
+ {
+ mxml_error("Unable to allocate memory for name!");
+ return (EOF);
+ }
+
+ namesize = 64;
+
+ if ((value = malloc(64)) == NULL)
+ {
+ free(name);
+ mxml_error("Unable to allocate memory for value!");
+ return (EOF);
+ }
+
+ valsize = 64;
+
+ /*
+ * Loop until we hit a >, /, ?, or EOF...
+ */
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ {
+#if DEBUG > 1
+ fprintf(stderr, "parse_element: ch='%c'\n", ch);
+#endif /* DEBUG > 1 */
+
+ /*
+ * Skip leading whitespace...
+ */
+
+ if (mxml_isspace(ch))
+ continue;
+
+ /*
+ * Stop at /, ?, or >...
+ */
+
+ if (ch == '/' || ch == '?')
+ {
+ /*
+ * Grab the > character and print an error if it isn't there...
+ */
+
+ quote = (*getc_cb)(p, encoding);
+
+ if (quote != '>')
+ {
+ mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
+ ch, node->value.element.name, quote);
+ goto error;
+ }
+
+ break;
+ }
+ else if (ch == '<')
+ {
+ mxml_error("Bare < in element %s!", node->value.element.name);
+ goto error;
+ }
+ else if (ch == '>')
+ break;
+
+ /*
+ * Read the attribute name...
+ */
+
+ name[0] = ch;
+ ptr = name + 1;
+
+ if (ch == '\"' || ch == '\'')
+ {
+ /*
+ * Name is in quotes, so get a quoted string...
+ */
+
+ quote = ch;
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &ptr, &name, &namesize))
+ goto error;
+
+ if (ch == quote)
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Grab an normal, non-quoted name...
+ */
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' ||
+ ch == '?')
+ break;
+ else
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &ptr, &name, &namesize))
+ goto error;
+ }
+ }
+
+ *ptr = '\0';
+
+ if (mxmlElementGetAttr(node, name))
+ goto error;
+
+ while (ch != EOF && mxml_isspace(ch))
+ ch = (*getc_cb)(p, encoding);
+
+ if (ch == '=')
+ {
+ /*
+ * Read the attribute value...
+ */
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch));
+
+ if (ch == EOF)
+ {
+ mxml_error("Missing value for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ goto error;
+ }
+
+ if (ch == '\'' || ch == '\"')
+ {
+ /*
+ * Read quoted value...
+ */
+
+ quote = ch;
+ ptr = value;
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ if (ch == quote)
+ break;
+ else
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &ptr, &value, &valsize))
+ goto error;
+ }
+
+ *ptr = '\0';
+ }
+ else
+ {
+ /*
+ * Read unquoted value...
+ */
+
+ value[0] = ch;
+ ptr = value + 1;
+
+ while ((ch = (*getc_cb)(p, encoding)) != EOF)
+ if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>')
+ break;
+ else
+ {
+ if (ch == '&')
+ if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
+ goto error;
+
+ if (mxml_add_char(ch, &ptr, &value, &valsize))
+ goto error;
+ }
+
+ *ptr = '\0';
+ }
+
+ /*
+ * Set the attribute with the given string value...
+ */
+
+ mxmlElementSetAttr(node, name, value);
+ }
+ else
+ {
+ mxml_error("Missing value for attribute '%s' in element %s!",
+ name, node->value.element.name);
+ goto error;
+ }
+
+ /*
+ * Check the end character...
+ */
+
+ if (ch == '/' || ch == '?')
+ {
+ /*
+ * Grab the > character and print an error if it isn't there...
+ */
+
+ quote = (*getc_cb)(p, encoding);
+
+ if (quote != '>')
+ {
+ mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
+ ch, node->value.element.name, quote);
+ ch = EOF;
+ }
+
+ break;
+ }
+ else if (ch == '>')
+ break;
+ }
+
+ /*
+ * Free the name and value buffers and return...
+ */
+
+ free(name);
+ free(value);
+
+ return (ch);
+
+ /*
+ * Common error return point...
+ */
+
+error:
+
+ free(name);
+ free(value);
+
+ return (EOF);
+}
+
+
+/*
+ * 'mxml_string_getc()' - Get a character from a string.
+ */
+
+static int /* O - Character or EOF */
+mxml_string_getc(void *p, /* I - Pointer to file */
+ int *encoding) /* IO - Encoding */
+{
+ int ch; /* Character */
+ const char **s; /* Pointer to string pointer */
+
+
+ s = (const char **)p;
+
+ if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE)
+ {
+ /*
+ * Got character; convert UTF-8 to integer and return...
+ */
+
+ (*s)++;
+
+ switch (*encoding)
+ {
+ case ENCODE_UTF8 :
+ if (!(ch & 0x80))
+ {
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+
+ return (ch);
+ }
+ else if (ch == 0xfe)
+ {
+ /*
+ * UTF-16 big-endian BOM?
+ */
+
+ if (((*s)[0] & 255) != 0xff)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16BE;
+ (*s)++;
+
+ return (mxml_string_getc(p, encoding));
+ }
+ else if (ch == 0xff)
+ {
+ /*
+ * UTF-16 little-endian BOM?
+ */
+
+ if (((*s)[0] & 255) != 0xfe)
+ return (EOF);
+
+ *encoding = ENCODE_UTF16LE;
+ (*s)++;
+
+ return (mxml_string_getc(p, encoding));
+ }
+ else if ((ch & 0xe0) == 0xc0)
+ {
+ /*
+ * Two-byte value...
+ */
+
+ if (((*s)[0] & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f);
+
+ (*s)++;
+
+ if (ch < 0x80)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ else if ((ch & 0xf0) == 0xe0)
+ {
+ /*
+ * Three-byte value...
+ */
+
+ if (((*s)[0] & 0xc0) != 0x80 ||
+ ((*s)[1] & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f);
+
+ (*s) += 2;
+
+ if (ch < 0x800)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+ /*
+ * Ignore (strip) Byte Order Mark (BOM)...
+ */
+
+ if (ch == 0xfeff)
+ return (mxml_string_getc(p, encoding));
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ else if ((ch & 0xf8) == 0xf0)
+ {
+ /*
+ * Four-byte value...
+ */
+
+ if (((*s)[0] & 0xc0) != 0x80 ||
+ ((*s)[1] & 0xc0) != 0x80 ||
+ ((*s)[2] & 0xc0) != 0x80)
+ return (EOF);
+
+ ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) |
+ ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f);
+
+ (*s) += 3;
+
+ if (ch < 0x10000)
+ {
+ mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
+ return (EOF);
+ }
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ else
+ return (EOF);
+
+ case ENCODE_UTF16BE :
+ /*
+ * Read UTF-16 big-endian char...
+ */
+
+ ch = (ch << 8) | ((*s)[0] & 255);
+ (*s) ++;
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch; /* Lower word */
+
+
+ if (!(*s)[0])
+ return (EOF);
+
+ lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255);
+ (*s) += 2;
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+
+ case ENCODE_UTF16LE :
+ /*
+ * Read UTF-16 little-endian char...
+ */
+
+ ch = ch | (((*s)[0] & 255) << 8);
+
+ if (!ch)
+ {
+ (*s) --;
+ return (EOF);
+ }
+
+ (*s) ++;
+
+ if (mxml_bad_char(ch))
+ {
+ mxml_error("Bad control character 0x%02x not allowed by XML standard!",
+ ch);
+ return (EOF);
+ }
+ else if (ch >= 0xd800 && ch <= 0xdbff)
+ {
+ /*
+ * Multi-word UTF-16 char...
+ */
+
+ int lch; /* Lower word */
+
+
+ if (!(*s)[1])
+ return (EOF);
+
+ lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255);
+ (*s) += 2;
+
+ if (lch < 0xdc00 || lch >= 0xdfff)
+ return (EOF);
+
+ ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
+ }
+
+#if DEBUG > 1
+ printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
+#endif /* DEBUG > 1 */
+
+ return (ch);
+ }
+ }
+
+ return (EOF);
+}
+
+
+/*
+ * 'mxml_string_putc()' - Write a character to a string.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_string_putc(int ch, /* I - Character to write */
+ void *p) /* I - Pointer to string pointers */
+{
+ char **pp; /* Pointer to string pointers */
+
+
+ pp = (char **)p;
+
+ if (pp[0] < pp[1])
+ pp[0][0] = ch;
+
+ pp[0] ++;
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_write_name()' - Write a name string.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_write_name(const char *s, /* I - Name to write */
+ void *p, /* I - Write pointer */
+ int (*putc_cb)(int, void *))
+ /* I - Write callback */
+{
+ char quote; /* Quote character */
+ const char *name; /* Entity name */
+
+
+ if (*s == '\"' || *s == '\'')
+ {
+ /*
+ * Write a quoted name string...
+ */
+
+ if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+
+ quote = *s++;
+
+ while (*s && *s != quote)
+ {
+ if ((name = mxmlEntityGetName(*s)) != NULL)
+ {
+ if ((*putc_cb)('&', p) < 0)
+ return (-1);
+
+ while (*name)
+ {
+ if ((*putc_cb)(*name, p) < 0)
+ return (-1);
+
+ name ++;
+ }
+
+ if ((*putc_cb)(';', p) < 0)
+ return (-1);
+ }
+ else if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+
+ s ++;
+ }
+
+ /*
+ * Write the end quote...
+ */
+
+ if ((*putc_cb)(quote, p) < 0)
+ return (-1);
+ }
+ else
+ {
+ /*
+ * Write a non-quoted name string...
+ */
+
+ while (*s)
+ {
+ if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+
+ s ++;
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_write_node()' - Save an XML node to a file.
+ */
+
+static int /* O - Column or -1 on error */
+mxml_write_node(mxml_node_t *node, /* I - Node to write */
+ void *p, /* I - File to write to */
+ mxml_save_cb_t cb, /* I - Whitespace callback */
+ int col, /* I - Current column */
+ _mxml_putc_cb_t putc_cb,/* I - Output callback */
+ _mxml_global_t *global)/* I - Global data */
+{
+ int i, /* Looping var */
+ width; /* Width of attr + value */
+ mxml_attr_t *attr; /* Current attribute */
+ char s[255]; /* Temporary string */
+
+
+ /*
+ * Print the node value...
+ */
+
+ switch (node->type)
+ {
+ case MXML_ELEMENT :
+ col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
+
+ if ((*putc_cb)('<', p) < 0)
+ return (-1);
+ if (node->value.element.name[0] == '?' ||
+ !strncmp(node->value.element.name, "!--", 3) ||
+ !strncmp(node->value.element.name, "![CDATA[", 8))
+ {
+ /*
+ * Comments, CDATA, and processing instructions do not
+ * use character entities.
+ */
+
+ const char *ptr; /* Pointer into name */
+
+
+ for (ptr = node->value.element.name; *ptr; ptr ++)
+ if ((*putc_cb)(*ptr, p) < 0)
+ return (-1);
+ }
+ else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(node->value.element.name) + 1;
+
+ for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
+ i > 0;
+ i --, attr ++)
+ {
+ width = strlen(attr->name);
+
+ if (attr->value)
+ width += strlen(attr->value) + 3;
+
+ if (global->wrap > 0 && (col + width) > global->wrap)
+ {
+ if ((*putc_cb)('\n', p) < 0)
+ return (-1);
+
+ col = 0;
+ }
+ else
+ {
+ if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+
+ col ++;
+ }
+
+ if (mxml_write_name(attr->name, p, putc_cb) < 0)
+ return (-1);
+
+ if (attr->value)
+ {
+ if ((*putc_cb)('=', p) < 0)
+ return (-1);
+ if ((*putc_cb)('\"', p) < 0)
+ return (-1);
+ if (mxml_write_string(attr->value, p, putc_cb) < 0)
+ return (-1);
+ if ((*putc_cb)('\"', p) < 0)
+ return (-1);
+ }
+
+ col += width;
+ }
+
+ if (node->child)
+ {
+ /*
+ * Write children...
+ */
+
+ mxml_node_t *child; /* Current child */
+
+
+ if ((*putc_cb)('>', p) < 0)
+ return (-1);
+ else
+ col ++;
+
+ col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
+
+ for (child = node->child; child; child = child->next)
+ {
+ if ((col = mxml_write_node(child, p, cb, col, putc_cb, global)) < 0)
+ return (-1);
+ }
+
+ /*
+ * The ? and ! elements are special-cases and have no end tags...
+ */
+
+ if (node->value.element.name[0] != '!' &&
+ node->value.element.name[0] != '?')
+ {
+ col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
+
+ if ((*putc_cb)('<', p) < 0)
+ return (-1);
+ if ((*putc_cb)('/', p) < 0)
+ return (-1);
+ if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
+ return (-1);
+ if ((*putc_cb)('>', p) < 0)
+ return (-1);
+
+ col += strlen(node->value.element.name) + 3;
+
+ col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
+ }
+ }
+ else if (node->value.element.name[0] == '!' ||
+ node->value.element.name[0] == '?')
+ {
+ /*
+ * The ? and ! elements are special-cases...
+ */
+
+ if ((*putc_cb)('>', p) < 0)
+ return (-1);
+ else
+ col ++;
+
+ col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
+ }
+ else
+ {
+ if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+ if ((*putc_cb)('/', p) < 0)
+ return (-1);
+ if ((*putc_cb)('>', p) < 0)
+ return (-1);
+
+ col += 3;
+
+ col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
+ }
+ break;
+
+ case MXML_INTEGER :
+ if (node->prev)
+ {
+ if (global->wrap > 0 && col > global->wrap)
+ {
+ if ((*putc_cb)('\n', p) < 0)
+ return (-1);
+
+ col = 0;
+ }
+ else if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+ else
+ col ++;
+ }
+
+ sprintf(s, "%d", node->value.integer);
+ if (mxml_write_string(s, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(s);
+ break;
+
+ case MXML_OPAQUE :
+ if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(node->value.opaque);
+ break;
+
+ case MXML_REAL :
+ if (node->prev)
+ {
+ if (global->wrap > 0 && col > global->wrap)
+ {
+ if ((*putc_cb)('\n', p) < 0)
+ return (-1);
+
+ col = 0;
+ }
+ else if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+ else
+ col ++;
+ }
+
+ sprintf(s, "%f", node->value.real);
+ if (mxml_write_string(s, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(s);
+ break;
+
+ case MXML_TEXT :
+ if (node->value.text.whitespace && col > 0)
+ {
+ if (global->wrap > 0 && col > global->wrap)
+ {
+ if ((*putc_cb)('\n', p) < 0)
+ return (-1);
+
+ col = 0;
+ }
+ else if ((*putc_cb)(' ', p) < 0)
+ return (-1);
+ else
+ col ++;
+ }
+
+ if (mxml_write_string(node->value.text.string, p, putc_cb) < 0)
+ return (-1);
+
+ col += strlen(node->value.text.string);
+ break;
+
+ case MXML_CUSTOM :
+ if (global->custom_save_cb)
+ {
+ char *data; /* Custom data string */
+ const char *newline; /* Last newline in string */
+
+
+ if ((data = (*global->custom_save_cb)(node)) == NULL)
+ return (-1);
+
+ if (mxml_write_string(data, p, putc_cb) < 0)
+ return (-1);
+
+ if ((newline = strrchr(data, '\n')) == NULL)
+ col += strlen(data);
+ else
+ col = strlen(newline);
+
+ free(data);
+ break;
+ }
+
+ default : /* Should never happen */
+ return (-1);
+ }
+
+ return (col);
+}
+
+
+/*
+ * 'mxml_write_string()' - Write a string, escaping & and < as needed.
+ */
+
+static int /* O - 0 on success, -1 on failure */
+mxml_write_string(
+ const char *s, /* I - String to write */
+ void *p, /* I - Write pointer */
+ _mxml_putc_cb_t putc_cb) /* I - Write callback */
+{
+ const char *name; /* Entity name, if any */
+
+
+ while (*s)
+ {
+ if ((name = mxmlEntityGetName(*s)) != NULL)
+ {
+ if ((*putc_cb)('&', p) < 0)
+ return (-1);
+
+ while (*name)
+ {
+ if ((*putc_cb)(*name, p) < 0)
+ return (-1);
+ name ++;
+ }
+
+ if ((*putc_cb)(';', p) < 0)
+ return (-1);
+ }
+ else if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+
+ s ++;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'mxml_write_ws()' - Do whitespace callback...
+ */
+
+static int /* O - New column */
+mxml_write_ws(mxml_node_t *node, /* I - Current node */
+ void *p, /* I - Write pointer */
+ mxml_save_cb_t cb, /* I - Callback function */
+ int ws, /* I - Where value */
+ int col, /* I - Current column */
+ _mxml_putc_cb_t putc_cb) /* I - Write callback */
+{
+ const char *s; /* Whitespace string */
+
+
+ if (cb && (s = (*cb)(node, ws)) != NULL)
+ {
+ while (*s)
+ {
+ if ((*putc_cb)(*s, p) < 0)
+ return (-1);
+ else if (*s == '\n')
+ col = 0;
+ else if (*s == '\t')
+ {
+ col += MXML_TAB;
+ col = col - (col % MXML_TAB);
+ }
+ else
+ col ++;
+
+ s ++;
+ }
+ }
+
+ return (col);
+}
+
+
+/*
+ * End of "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-get.c b/tools/gator/daemon/mxml/mxml-get.c
new file mode 100644
index 000000000000..a5356d57e186
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-get.c
@@ -0,0 +1,471 @@
+/*
+ * "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $"
+ *
+ * Node get functions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlGetCDATA() - Get the value for a CDATA node.
+ * mxmlGetCustom() - Get the value for a custom node.
+ * mxmlGetElement() - Get the name for an element node.
+ * mxmlGetFirstChild() - Get the first child of an element node.
+ * mxmlGetInteger() - Get the integer value from the specified node or its
+ * first child.
+ * mxmlGetLastChild() - Get the last child of an element node.
+ * mxmlGetNextSibling() - Get the next node for the current parent.
+ * mxmlGetOpaque() - Get an opaque string value for a node or its first
+ * child.
+ * mxmlGetParent() - Get the parent node.
+ * mxmlGetPrevSibling() - Get the previous node for the current parent.
+ * mxmlGetReal() - Get the real value for a node or its first child.
+ * mxmlGetText() - Get the text value for a node or its first child.
+ * mxmlGetType() - Get the node type.
+ * mxmlGetUserData() - Get the user data pointer for a node.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * 'mxmlGetCDATA()' - Get the value for a CDATA node.
+ *
+ * @code NULL@ is returned if the node is not a CDATA element.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const char * /* O - CDATA value or NULL */
+mxmlGetCDATA(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT ||
+ strncmp(node->value.element.name, "![CDATA[", 8))
+ return (NULL);
+
+ /*
+ * Return the text following the CDATA declaration...
+ */
+
+ return (node->value.element.name + 8);
+}
+
+
+/*
+ * 'mxmlGetCustom()' - Get the value for a custom node.
+ *
+ * @code NULL@ is returned if the node (or its first child) is not a custom
+ * value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const void * /* O - Custom value or NULL */
+mxmlGetCustom(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_CUSTOM)
+ return (node->value.custom.data);
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_CUSTOM)
+ return (node->child->value.custom.data);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlGetElement()' - Get the name for an element node.
+ *
+ * @code NULL@ is returned if the node is not an element node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const char * /* O - Element name or NULL */
+mxmlGetElement(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT)
+ return (NULL);
+
+ /*
+ * Return the element name...
+ */
+
+ return (node->value.element.name);
+}
+
+
+/*
+ * 'mxmlGetFirstChild()' - Get the first child of an element node.
+ *
+ * @code NULL@ is returned if the node is not an element node or if the node
+ * has no children.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - First child or NULL */
+mxmlGetFirstChild(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT)
+ return (NULL);
+
+ /*
+ * Return the first child node...
+ */
+
+ return (node->child);
+}
+
+
+/*
+ * 'mxmlGetInteger()' - Get the integer value from the specified node or its
+ * first child.
+ *
+ * 0 is returned if the node (or its first child) is not an integer value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+int /* O - Integer value or 0 */
+mxmlGetInteger(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (0);
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_INTEGER)
+ return (node->value.integer);
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_INTEGER)
+ return (node->child->value.integer);
+ else
+ return (0);
+}
+
+
+/*
+ * 'mxmlGetLastChild()' - Get the last child of an element node.
+ *
+ * @code NULL@ is returned if the node is not an element node or if the node
+ * has no children.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - Last child or NULL */
+mxmlGetLastChild(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT)
+ return (NULL);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->last_child);
+}
+
+
+/*
+ * 'mxmlGetNextSibling()' - Get the next node for the current parent.
+ *
+ * @code NULL@ is returned if this is the last child for the current parent.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t *
+mxmlGetNextSibling(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->next);
+}
+
+
+/*
+ * 'mxmlGetOpaque()' - Get an opaque string value for a node or its first child.
+ *
+ * @code NULL@ is returned if the node (or its first child) is not an opaque
+ * value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const char * /* O - Opaque string or NULL */
+mxmlGetOpaque(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_OPAQUE)
+ return (node->value.opaque);
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_OPAQUE)
+ return (node->child->value.opaque);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlGetParent()' - Get the parent node.
+ *
+ * @code NULL@ is returned for a root node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - Parent node or NULL */
+mxmlGetParent(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->parent);
+}
+
+
+/*
+ * 'mxmlGetPrevSibling()' - Get the previous node for the current parent.
+ *
+ * @code NULL@ is returned if this is the first child for the current parent.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - Previous node or NULL */
+mxmlGetPrevSibling(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->prev);
+}
+
+
+/*
+ * 'mxmlGetReal()' - Get the real value for a node or its first child.
+ *
+ * 0.0 is returned if the node (or its first child) is not a real value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+double /* O - Real value or 0.0 */
+mxmlGetReal(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (0.0);
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_REAL)
+ return (node->value.real);
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_REAL)
+ return (node->child->value.real);
+ else
+ return (0.0);
+}
+
+
+/*
+ * 'mxmlGetText()' - Get the text value for a node or its first child.
+ *
+ * @code NULL@ is returned if the node (or its first child) is not a text node.
+ * The "whitespace" argument can be NULL.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+const char * /* O - Text string or NULL */
+mxmlGetText(mxml_node_t *node, /* I - Node to get */
+ int *whitespace) /* O - 1 if string is preceded by whitespace, 0 otherwise */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ {
+ if (whitespace)
+ *whitespace = 0;
+
+ return (NULL);
+ }
+
+ /*
+ * Return the integer value...
+ */
+
+ if (node->type == MXML_TEXT)
+ {
+ if (whitespace)
+ *whitespace = node->value.text.whitespace;
+
+ return (node->value.text.string);
+ }
+ else if (node->type == MXML_ELEMENT &&
+ node->child &&
+ node->child->type == MXML_TEXT)
+ {
+ if (whitespace)
+ *whitespace = node->child->value.text.whitespace;
+
+ return (node->child->value.text.string);
+ }
+ else
+ {
+ if (whitespace)
+ *whitespace = 0;
+
+ return (NULL);
+ }
+}
+
+
+/*
+ * 'mxmlGetType()' - Get the node type.
+ *
+ * @code MXML_IGNORE@ is returned if "node" is @code NULL@.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_type_t /* O - Type of node */
+mxmlGetType(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (MXML_IGNORE);
+
+ /*
+ * Return the node type...
+ */
+
+ return (node->type);
+}
+
+
+/*
+ * 'mxmlGetUserData()' - Get the user data pointer for a node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+void * /* O - User data pointer */
+mxmlGetUserData(mxml_node_t *node) /* I - Node to get */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Return the user data pointer...
+ */
+
+ return (node->user_data);
+}
+
+
+/*
+ * End of "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-index.c b/tools/gator/daemon/mxml/mxml-index.c
new file mode 100644
index 000000000000..b6efc66f055c
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-index.c
@@ -0,0 +1,662 @@
+/*
+ * "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $"
+ *
+ * Index support code for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * Sort functions...
+ */
+
+static int index_compare(mxml_index_t *ind, mxml_node_t *first,
+ mxml_node_t *second);
+static int index_find(mxml_index_t *ind, const char *element,
+ const char *value, mxml_node_t *node);
+static void index_sort(mxml_index_t *ind, int left, int right);
+
+
+/*
+ * 'mxmlIndexDelete()' - Delete an index.
+ */
+
+void
+mxmlIndexDelete(mxml_index_t *ind) /* I - Index to delete */
+{
+ /*
+ * Range check input..
+ */
+
+ if (!ind)
+ return;
+
+ /*
+ * Free memory...
+ */
+
+ if (ind->attr)
+ free(ind->attr);
+
+ if (ind->alloc_nodes)
+ free(ind->nodes);
+
+ free(ind);
+}
+
+
+/*
+ * 'mxmlIndexEnum()' - Return the next node in the index.
+ *
+ * Nodes are returned in the sorted order of the index.
+ */
+
+mxml_node_t * /* O - Next node or NULL if there is none */
+mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ind)
+ return (NULL);
+
+ /*
+ * Return the next node...
+ */
+
+ if (ind->cur_node < ind->num_nodes)
+ return (ind->nodes[ind->cur_node ++]);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlIndexFind()' - Find the next matching node.
+ *
+ * You should call mxmlIndexReset() prior to using this function for
+ * the first time with a particular set of "element" and "value"
+ * strings. Passing NULL for both "element" and "value" is equivalent
+ * to calling mxmlIndexEnum().
+ */
+
+mxml_node_t * /* O - Node or NULL if none found */
+mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */
+ const char *element, /* I - Element name to find, if any */
+ const char *value) /* I - Attribute value, if any */
+{
+ int diff, /* Difference between names */
+ current, /* Current entity in search */
+ first, /* First entity in search */
+ last; /* Last entity in search */
+
+
+#ifdef DEBUG
+ printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n",
+ ind, element ? element : "(null)", value ? value : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!ind || (!ind->attr && value))
+ {
+#ifdef DEBUG
+ puts(" returning NULL...");
+ printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)");
+#endif /* DEBUG */
+
+ return (NULL);
+ }
+
+ /*
+ * If both element and value are NULL, just enumerate the nodes in the
+ * index...
+ */
+
+ if (!element && !value)
+ return (mxmlIndexEnum(ind));
+
+ /*
+ * If there are no nodes in the index, return NULL...
+ */
+
+ if (!ind->num_nodes)
+ {
+#ifdef DEBUG
+ puts(" returning NULL...");
+ puts(" no nodes!");
+#endif /* DEBUG */
+
+ return (NULL);
+ }
+
+ /*
+ * If cur_node == 0, then find the first matching node...
+ */
+
+ if (ind->cur_node == 0)
+ {
+ /*
+ * Find the first node using a modified binary search algorithm...
+ */
+
+ first = 0;
+ last = ind->num_nodes - 1;
+
+#ifdef DEBUG
+ printf(" find first time, num_nodes=%d...\n", ind->num_nodes);
+#endif /* DEBUG */
+
+ while ((last - first) > 1)
+ {
+ current = (first + last) / 2;
+
+#ifdef DEBUG
+ printf(" first=%d, last=%d, current=%d\n", first, last, current);
+#endif /* DEBUG */
+
+ if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0)
+ {
+ /*
+ * Found a match, move back to find the first...
+ */
+
+#ifdef DEBUG
+ puts(" match!");
+#endif /* DEBUG */
+
+ while (current > 0 &&
+ !index_find(ind, element, value, ind->nodes[current - 1]))
+ current --;
+
+#ifdef DEBUG
+ printf(" returning first match=%d\n", current);
+#endif /* DEBUG */
+
+ /*
+ * Return the first match and save the index to the next...
+ */
+
+ ind->cur_node = current + 1;
+
+ return (ind->nodes[current]);
+ }
+ else if (diff < 0)
+ last = current;
+ else
+ first = current;
+
+#ifdef DEBUG
+ printf(" diff=%d\n", diff);
+#endif /* DEBUG */
+ }
+
+ /*
+ * If we get this far, then we found exactly 0 or 1 matches...
+ */
+
+ for (current = first; current <= last; current ++)
+ if (!index_find(ind, element, value, ind->nodes[current]))
+ {
+ /*
+ * Found exactly one (or possibly two) match...
+ */
+
+#ifdef DEBUG
+ printf(" returning only match %d...\n", current);
+#endif /* DEBUG */
+
+ ind->cur_node = current + 1;
+
+ return (ind->nodes[current]);
+ }
+
+ /*
+ * No matches...
+ */
+
+ ind->cur_node = ind->num_nodes;
+
+#ifdef DEBUG
+ puts(" returning NULL...");
+#endif /* DEBUG */
+
+ return (NULL);
+ }
+ else if (ind->cur_node < ind->num_nodes &&
+ !index_find(ind, element, value, ind->nodes[ind->cur_node]))
+ {
+ /*
+ * Return the next matching node...
+ */
+
+#ifdef DEBUG
+ printf(" returning next match %d...\n", ind->cur_node);
+#endif /* DEBUG */
+
+ return (ind->nodes[ind->cur_node ++]);
+ }
+
+ /*
+ * If we get this far, then we have no matches...
+ */
+
+ ind->cur_node = ind->num_nodes;
+
+#ifdef DEBUG
+ puts(" returning NULL...");
+#endif /* DEBUG */
+
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlIndexGetCount()' - Get the number of nodes in an index.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+int /* I - Number of nodes in index */
+mxmlIndexGetCount(mxml_index_t *ind) /* I - Index of nodes */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ind)
+ return (0);
+
+ /*
+ * Return the number of nodes in the index...
+ */
+
+ return (ind->num_nodes);
+}
+
+
+/*
+ * 'mxmlIndexNew()' - Create a new index.
+ *
+ * The index will contain all nodes that contain the named element and/or
+ * attribute. If both "element" and "attr" are NULL, then the index will
+ * contain a sorted list of the elements in the node tree. Nodes are
+ * sorted by element name and optionally by attribute value if the "attr"
+ * argument is not NULL.
+ */
+
+mxml_index_t * /* O - New index */
+mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */
+ const char *element, /* I - Element to index or NULL for all */
+ const char *attr) /* I - Attribute to index or NULL for none */
+{
+ mxml_index_t *ind; /* New index */
+ mxml_node_t *current, /* Current node in index */
+ **temp; /* Temporary node pointer array */
+
+
+ /*
+ * Range check input...
+ */
+
+#ifdef DEBUG
+ printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n",
+ node, element ? element : "(null)", attr ? attr : "(null)");
+#endif /* DEBUG */
+
+ if (!node)
+ return (NULL);
+
+ /*
+ * Create a new index...
+ */
+
+ if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL)
+ {
+ mxml_error("Unable to allocate %d bytes for index - %s",
+ sizeof(mxml_index_t), strerror(errno));
+ return (NULL);
+ }
+
+ if (attr)
+ ind->attr = strdup(attr);
+
+ if (!element && !attr)
+ current = node;
+ else
+ current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND);
+
+ while (current)
+ {
+ if (ind->num_nodes >= ind->alloc_nodes)
+ {
+ if (!ind->alloc_nodes)
+ temp = malloc(64 * sizeof(mxml_node_t *));
+ else
+ temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *));
+
+ if (!temp)
+ {
+ /*
+ * Unable to allocate memory for the index, so abort...
+ */
+
+ mxml_error("Unable to allocate %d bytes for index: %s",
+ (ind->alloc_nodes + 64) * sizeof(mxml_node_t *),
+ strerror(errno));
+
+ mxmlIndexDelete(ind);
+ return (NULL);
+ }
+
+ ind->nodes = temp;
+ ind->alloc_nodes += 64;
+ }
+
+ ind->nodes[ind->num_nodes ++] = current;
+
+ current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND);
+ }
+
+ /*
+ * Sort nodes based upon the search criteria...
+ */
+
+#ifdef DEBUG
+ {
+ int i; /* Looping var */
+
+
+ printf("%d node(s) in index.\n\n", ind->num_nodes);
+
+ if (attr)
+ {
+ printf("Node Address Element %s\n", attr);
+ puts("-------- -------- -------------- ------------------------------");
+
+ for (i = 0; i < ind->num_nodes; i ++)
+ printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i],
+ ind->nodes[i]->value.element.name,
+ mxmlElementGetAttr(ind->nodes[i], attr));
+ }
+ else
+ {
+ puts("Node Address Element");
+ puts("-------- -------- --------------");
+
+ for (i = 0; i < ind->num_nodes; i ++)
+ printf("%8d %-8p %s\n", i, ind->nodes[i],
+ ind->nodes[i]->value.element.name);
+ }
+
+ putchar('\n');
+ }
+#endif /* DEBUG */
+
+ if (ind->num_nodes > 1)
+ index_sort(ind, 0, ind->num_nodes - 1);
+
+#ifdef DEBUG
+ {
+ int i; /* Looping var */
+
+
+ puts("After sorting:\n");
+
+ if (attr)
+ {
+ printf("Node Address Element %s\n", attr);
+ puts("-------- -------- -------------- ------------------------------");
+
+ for (i = 0; i < ind->num_nodes; i ++)
+ printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i],
+ ind->nodes[i]->value.element.name,
+ mxmlElementGetAttr(ind->nodes[i], attr));
+ }
+ else
+ {
+ puts("Node Address Element");
+ puts("-------- -------- --------------");
+
+ for (i = 0; i < ind->num_nodes; i ++)
+ printf("%8d %-8p %s\n", i, ind->nodes[i],
+ ind->nodes[i]->value.element.name);
+ }
+
+ putchar('\n');
+ }
+#endif /* DEBUG */
+
+ /*
+ * Return the new index...
+ */
+
+ return (ind);
+}
+
+
+/*
+ * 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and
+ * return the first node in the index.
+ *
+ * This function should be called prior to using mxmlIndexEnum() or
+ * mxmlIndexFind() for the first time.
+ */
+
+mxml_node_t * /* O - First node or NULL if there is none */
+mxmlIndexReset(mxml_index_t *ind) /* I - Index to reset */
+{
+#ifdef DEBUG
+ printf("mxmlIndexReset(ind=%p)\n", ind);
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!ind)
+ return (NULL);
+
+ /*
+ * Set the index to the first element...
+ */
+
+ ind->cur_node = 0;
+
+ /*
+ * Return the first node...
+ */
+
+ if (ind->num_nodes)
+ return (ind->nodes[0]);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'index_compare()' - Compare two nodes.
+ */
+
+static int /* O - Result of comparison */
+index_compare(mxml_index_t *ind, /* I - Index */
+ mxml_node_t *first, /* I - First node */
+ mxml_node_t *second) /* I - Second node */
+{
+ int diff; /* Difference */
+
+
+ /*
+ * Check the element name...
+ */
+
+ if ((diff = strcmp(first->value.element.name,
+ second->value.element.name)) != 0)
+ return (diff);
+
+ /*
+ * Check the attribute value...
+ */
+
+ if (ind->attr)
+ {
+ if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr),
+ mxmlElementGetAttr(second, ind->attr))) != 0)
+ return (diff);
+ }
+
+ /*
+ * No difference, return 0...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'index_find()' - Compare a node with index values.
+ */
+
+static int /* O - Result of comparison */
+index_find(mxml_index_t *ind, /* I - Index */
+ const char *element, /* I - Element name or NULL */
+ const char *value, /* I - Attribute value or NULL */
+ mxml_node_t *node) /* I - Node */
+{
+ int diff; /* Difference */
+
+
+ /*
+ * Check the element name...
+ */
+
+ if (element)
+ {
+ if ((diff = strcmp(element, node->value.element.name)) != 0)
+ return (diff);
+ }
+
+ /*
+ * Check the attribute value...
+ */
+
+ if (value)
+ {
+ if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0)
+ return (diff);
+ }
+
+ /*
+ * No difference, return 0...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'index_sort()' - Sort the nodes in the index...
+ *
+ * This function implements the classic quicksort algorithm...
+ */
+
+static void
+index_sort(mxml_index_t *ind, /* I - Index to sort */
+ int left, /* I - Left node in partition */
+ int right) /* I - Right node in partition */
+{
+ mxml_node_t *pivot, /* Pivot node */
+ *temp; /* Swap node */
+ int templ, /* Temporary left node */
+ tempr; /* Temporary right node */
+
+
+ /*
+ * Loop until we have sorted all the way to the right...
+ */
+
+ do
+ {
+ /*
+ * Sort the pivot in the current partition...
+ */
+
+ pivot = ind->nodes[left];
+
+ for (templ = left, tempr = right; templ < tempr;)
+ {
+ /*
+ * Move left while left node <= pivot node...
+ */
+
+ while ((templ < right) &&
+ index_compare(ind, ind->nodes[templ], pivot) <= 0)
+ templ ++;
+
+ /*
+ * Move right while right node > pivot node...
+ */
+
+ while ((tempr > left) &&
+ index_compare(ind, ind->nodes[tempr], pivot) > 0)
+ tempr --;
+
+ /*
+ * Swap nodes if needed...
+ */
+
+ if (templ < tempr)
+ {
+ temp = ind->nodes[templ];
+ ind->nodes[templ] = ind->nodes[tempr];
+ ind->nodes[tempr] = temp;
+ }
+ }
+
+ /*
+ * When we get here, the right (tempr) node is the new position for the
+ * pivot node...
+ */
+
+ if (index_compare(ind, pivot, ind->nodes[tempr]) > 0)
+ {
+ ind->nodes[left] = ind->nodes[tempr];
+ ind->nodes[tempr] = pivot;
+ }
+
+ /*
+ * Recursively sort the left partition as needed...
+ */
+
+ if (left < (tempr - 1))
+ index_sort(ind, left, tempr - 1);
+ }
+ while (right > (left = tempr + 1));
+}
+
+
+/*
+ * End of "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-node.c b/tools/gator/daemon/mxml/mxml-node.c
new file mode 100644
index 000000000000..44af759f9de3
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-node.c
@@ -0,0 +1,807 @@
+/*
+ * "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $"
+ *
+ * Node support code for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlAdd() - Add a node to a tree.
+ * mxmlDelete() - Delete a node and all of its children.
+ * mxmlGetRefCount() - Get the current reference (use) count for a node.
+ * mxmlNewCDATA() - Create a new CDATA node.
+ * mxmlNewCustom() - Create a new custom data node.
+ * mxmlNewElement() - Create a new element node.
+ * mxmlNewInteger() - Create a new integer node.
+ * mxmlNewOpaque() - Create a new opaque string.
+ * mxmlNewReal() - Create a new real number node.
+ * mxmlNewText() - Create a new text fragment node.
+ * mxmlNewTextf() - Create a new formatted text fragment node.
+ * mxmlRemove() - Remove a node from its parent.
+ * mxmlNewXML() - Create a new XML document tree.
+ * mxmlRelease() - Release a node.
+ * mxmlRetain() - Retain a node.
+ * mxml_new() - Create a new node.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * Local functions...
+ */
+
+static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type);
+
+
+/*
+ * 'mxmlAdd()' - Add a node to a tree.
+ *
+ * Adds the specified node to the parent. If the child argument is not
+ * NULL, puts the new node before or after the specified child depending
+ * on the value of the where argument. If the child argument is NULL,
+ * puts the new node at the beginning of the child list (MXML_ADD_BEFORE)
+ * or at the end of the child list (MXML_ADD_AFTER). The constant
+ * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.
+ */
+
+void
+mxmlAdd(mxml_node_t *parent, /* I - Parent node */
+ int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */
+ mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */
+ mxml_node_t *node) /* I - Node to add */
+{
+#ifdef DEBUG
+ fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent,
+ where, child, node);
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!parent || !node)
+ return;
+
+#if DEBUG > 1
+ fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
+ if (parent)
+ {
+ fprintf(stderr, " BEFORE: parent->child=%p\n", parent->child);
+ fprintf(stderr, " BEFORE: parent->last_child=%p\n", parent->last_child);
+ fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev);
+ fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next);
+ }
+#endif /* DEBUG > 1 */
+
+ /*
+ * Remove the node from any existing parent...
+ */
+
+ if (node->parent)
+ mxmlRemove(node);
+
+ /*
+ * Reset pointers...
+ */
+
+ node->parent = parent;
+
+ switch (where)
+ {
+ case MXML_ADD_BEFORE :
+ if (!child || child == parent->child || child->parent != parent)
+ {
+ /*
+ * Insert as first node under parent...
+ */
+
+ node->next = parent->child;
+
+ if (parent->child)
+ parent->child->prev = node;
+ else
+ parent->last_child = node;
+
+ parent->child = node;
+ }
+ else
+ {
+ /*
+ * Insert node before this child...
+ */
+
+ node->next = child;
+ node->prev = child->prev;
+
+ if (child->prev)
+ child->prev->next = node;
+ else
+ parent->child = node;
+
+ child->prev = node;
+ }
+ break;
+
+ case MXML_ADD_AFTER :
+ if (!child || child == parent->last_child || child->parent != parent)
+ {
+ /*
+ * Insert as last node under parent...
+ */
+
+ node->parent = parent;
+ node->prev = parent->last_child;
+
+ if (parent->last_child)
+ parent->last_child->next = node;
+ else
+ parent->child = node;
+
+ parent->last_child = node;
+ }
+ else
+ {
+ /*
+ * Insert node after this child...
+ */
+
+ node->prev = child;
+ node->next = child->next;
+
+ if (child->next)
+ child->next->prev = node;
+ else
+ parent->last_child = node;
+
+ child->next = node;
+ }
+ break;
+ }
+
+#if DEBUG > 1
+ fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
+ if (parent)
+ {
+ fprintf(stderr, " AFTER: parent->child=%p\n", parent->child);
+ fprintf(stderr, " AFTER: parent->last_child=%p\n", parent->last_child);
+ fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev);
+ fprintf(stderr, " AFTER: parent->next=%p\n", parent->next);
+ }
+#endif /* DEBUG > 1 */
+}
+
+
+/*
+ * 'mxmlDelete()' - Delete a node and all of its children.
+ *
+ * If the specified node has a parent, this function first removes the
+ * node from its parent using the mxmlRemove() function.
+ */
+
+void
+mxmlDelete(mxml_node_t *node) /* I - Node to delete */
+{
+ int i; /* Looping var */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlDelete(node=%p)\n", node);
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return;
+
+ /*
+ * Remove the node from its parent, if any...
+ */
+
+ mxmlRemove(node);
+
+ /*
+ * Delete children...
+ */
+
+ while (node->child)
+ mxmlDelete(node->child);
+
+ /*
+ * Now delete any node data...
+ */
+
+ switch (node->type)
+ {
+ case MXML_ELEMENT :
+ if (node->value.element.name)
+ free(node->value.element.name);
+
+ if (node->value.element.num_attrs)
+ {
+ for (i = 0; i < node->value.element.num_attrs; i ++)
+ {
+ if (node->value.element.attrs[i].name)
+ free(node->value.element.attrs[i].name);
+ if (node->value.element.attrs[i].value)
+ free(node->value.element.attrs[i].value);
+ }
+
+ free(node->value.element.attrs);
+ }
+ break;
+ case MXML_INTEGER :
+ /* Nothing to do */
+ break;
+ case MXML_OPAQUE :
+ if (node->value.opaque)
+ free(node->value.opaque);
+ break;
+ case MXML_REAL :
+ /* Nothing to do */
+ break;
+ case MXML_TEXT :
+ if (node->value.text.string)
+ free(node->value.text.string);
+ break;
+ case MXML_CUSTOM :
+ if (node->value.custom.data &&
+ node->value.custom.destroy)
+ (*(node->value.custom.destroy))(node->value.custom.data);
+ break;
+ default :
+ break;
+ }
+
+ /*
+ * Free this node...
+ */
+
+ free(node);
+}
+
+
+/*
+ * 'mxmlGetRefCount()' - Get the current reference (use) count for a node.
+ *
+ * The initial reference count of new nodes is 1. Use the @link mxmlRetain@
+ * and @link mxmlRelease@ functions to increment and decrement a node's
+ * reference count.
+ *
+ * @since Mini-XML 2.7@.
+ */
+
+int /* O - Reference count */
+mxmlGetRefCount(mxml_node_t *node) /* I - Node */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (0);
+
+ /*
+ * Return the reference count...
+ */
+
+ return (node->ref_count);
+}
+
+
+/*
+ * 'mxmlNewCDATA()' - Create a new CDATA node.
+ *
+ * The new CDATA node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * CDATA node has no parent. The data string must be nul-terminated and
+ * is copied into the new node. CDATA nodes use the MXML_ELEMENT type.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ const char *data) /* I - Data string */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n",
+ parent, data ? data : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!data)
+ return (NULL);
+
+ /*
+ * Create the node and set the name value...
+ */
+
+ if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
+ node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewCustom()' - Create a new custom data node.
+ *
+ * The new custom node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * element node has no parent. NULL can be passed when the data in the
+ * node is not dynamically allocated or is separately managed.
+ *
+ * @since Mini-XML 2.1@
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewCustom(
+ mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ void *data, /* I - Pointer to data */
+ mxml_custom_destroy_cb_t destroy) /* I - Function to destroy data */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent,
+ data, destroy);
+#endif /* DEBUG */
+
+ /*
+ * Create the node and set the value...
+ */
+
+ if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL)
+ {
+ node->value.custom.data = data;
+ node->value.custom.destroy = destroy;
+ }
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewElement()' - Create a new element node.
+ *
+ * The new element node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * element node has no parent.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ const char *name) /* I - Name of element */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent,
+ name ? name : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!name)
+ return (NULL);
+
+ /*
+ * Create the node and set the element name...
+ */
+
+ if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
+ node->value.element.name = strdup(name);
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewInteger()' - Create a new integer node.
+ *
+ * The new integer node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * integer node has no parent.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ int integer) /* I - Integer value */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer);
+#endif /* DEBUG */
+
+ /*
+ * Create the node and set the element name...
+ */
+
+ if ((node = mxml_new(parent, MXML_INTEGER)) != NULL)
+ node->value.integer = integer;
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewOpaque()' - Create a new opaque string.
+ *
+ * The new opaque node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * opaque node has no parent. The opaque string must be nul-terminated and
+ * is copied into the new node.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ const char *opaque) /* I - Opaque string */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent,
+ opaque ? opaque : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!opaque)
+ return (NULL);
+
+ /*
+ * Create the node and set the element name...
+ */
+
+ if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL)
+ node->value.opaque = strdup(opaque);
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewReal()' - Create a new real number node.
+ *
+ * The new real number node is added to the end of the specified parent's
+ * child list. The constant MXML_NO_PARENT can be used to specify that
+ * the new real number node has no parent.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ double real) /* I - Real number value */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real);
+#endif /* DEBUG */
+
+ /*
+ * Create the node and set the element name...
+ */
+
+ if ((node = mxml_new(parent, MXML_REAL)) != NULL)
+ node->value.real = real;
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewText()' - Create a new text fragment node.
+ *
+ * The new text node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * text node has no parent. The whitespace parameter is used to specify
+ * whether leading whitespace is present before the node. The text
+ * string must be nul-terminated and is copied into the new node.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
+ const char *string) /* I - String */
+{
+ mxml_node_t *node; /* New node */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n",
+ parent, whitespace, string ? string : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!string)
+ return (NULL);
+
+ /*
+ * Create the node and set the text value...
+ */
+
+ if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
+ {
+ node->value.text.whitespace = whitespace;
+ node->value.text.string = strdup(string);
+ }
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlNewTextf()' - Create a new formatted text fragment node.
+ *
+ * The new text node is added to the end of the specified parent's child
+ * list. The constant MXML_NO_PARENT can be used to specify that the new
+ * text node has no parent. The whitespace parameter is used to specify
+ * whether leading whitespace is present before the node. The format
+ * string must be nul-terminated and is formatted into the new node.
+ */
+
+mxml_node_t * /* O - New node */
+mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
+ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
+ const char *format, /* I - Printf-style frmat string */
+ ...) /* I - Additional args as needed */
+{
+ mxml_node_t *node; /* New node */
+ va_list ap; /* Pointer to arguments */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n",
+ parent, whitespace, format ? format : "(null)");
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!format)
+ return (NULL);
+
+ /*
+ * Create the node and set the text value...
+ */
+
+ if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
+ {
+ va_start(ap, format);
+
+ node->value.text.whitespace = whitespace;
+ node->value.text.string = _mxml_vstrdupf(format, ap);
+
+ va_end(ap);
+ }
+
+ return (node);
+}
+
+
+/*
+ * 'mxmlRemove()' - Remove a node from its parent.
+ *
+ * Does not free memory used by the node - use mxmlDelete() for that.
+ * This function does nothing if the node has no parent.
+ */
+
+void
+mxmlRemove(mxml_node_t *node) /* I - Node to remove */
+{
+#ifdef DEBUG
+ fprintf(stderr, "mxmlRemove(node=%p)\n", node);
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || !node->parent)
+ return;
+
+ /*
+ * Remove from parent...
+ */
+
+#if DEBUG > 1
+ fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
+ if (node->parent)
+ {
+ fprintf(stderr, " BEFORE: node->parent->child=%p\n", node->parent->child);
+ fprintf(stderr, " BEFORE: node->parent->last_child=%p\n", node->parent->last_child);
+ }
+ fprintf(stderr, " BEFORE: node->child=%p\n", node->child);
+ fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child);
+ fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev);
+ fprintf(stderr, " BEFORE: node->next=%p\n", node->next);
+#endif /* DEBUG > 1 */
+
+ if (node->prev)
+ node->prev->next = node->next;
+ else
+ node->parent->child = node->next;
+
+ if (node->next)
+ node->next->prev = node->prev;
+ else
+ node->parent->last_child = node->prev;
+
+ node->parent = NULL;
+ node->prev = NULL;
+ node->next = NULL;
+
+#if DEBUG > 1
+ fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
+ if (node->parent)
+ {
+ fprintf(stderr, " AFTER: node->parent->child=%p\n", node->parent->child);
+ fprintf(stderr, " AFTER: node->parent->last_child=%p\n", node->parent->last_child);
+ }
+ fprintf(stderr, " AFTER: node->child=%p\n", node->child);
+ fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child);
+ fprintf(stderr, " AFTER: node->prev=%p\n", node->prev);
+ fprintf(stderr, " AFTER: node->next=%p\n", node->next);
+#endif /* DEBUG > 1 */
+}
+
+
+/*
+ * 'mxmlNewXML()' - Create a new XML document tree.
+ *
+ * The "version" argument specifies the version number to put in the
+ * ?xml element node. If NULL, version 1.0 is assumed.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+mxml_node_t * /* O - New ?xml node */
+mxmlNewXML(const char *version) /* I - Version number to use */
+{
+ char element[1024]; /* Element text */
+
+
+ snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?",
+ version ? version : "1.0");
+
+ return (mxmlNewElement(NULL, element));
+}
+
+
+/*
+ * 'mxmlRelease()' - Release a node.
+ *
+ * When the reference count reaches zero, the node (and any children)
+ * is deleted via mxmlDelete().
+ *
+ * @since Mini-XML 2.3@
+ */
+
+int /* O - New reference count */
+mxmlRelease(mxml_node_t *node) /* I - Node */
+{
+ if (node)
+ {
+ if ((-- node->ref_count) <= 0)
+ {
+ mxmlDelete(node);
+ return (0);
+ }
+ else
+ return (node->ref_count);
+ }
+ else
+ return (-1);
+}
+
+
+/*
+ * 'mxmlRetain()' - Retain a node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+int /* O - New reference count */
+mxmlRetain(mxml_node_t *node) /* I - Node */
+{
+ if (node)
+ return (++ node->ref_count);
+ else
+ return (-1);
+}
+
+
+/*
+ * 'mxml_new()' - Create a new node.
+ */
+
+static mxml_node_t * /* O - New node */
+mxml_new(mxml_node_t *parent, /* I - Parent node */
+ mxml_type_t type) /* I - Node type */
+{
+ mxml_node_t *node; /* New node */
+
+
+#if DEBUG > 1
+ fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type);
+#endif /* DEBUG > 1 */
+
+ /*
+ * Allocate memory for the node...
+ */
+
+ if ((node = calloc(1, sizeof(mxml_node_t))) == NULL)
+ {
+#if DEBUG > 1
+ fputs(" returning NULL\n", stderr);
+#endif /* DEBUG > 1 */
+
+ return (NULL);
+ }
+
+#if DEBUG > 1
+ fprintf(stderr, " returning %p\n", node);
+#endif /* DEBUG > 1 */
+
+ /*
+ * Set the node type...
+ */
+
+ node->type = type;
+ node->ref_count = 1;
+
+ /*
+ * Add to the parent if present...
+ */
+
+ if (parent)
+ mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
+
+ /*
+ * Return the new node...
+ */
+
+ return (node);
+}
+
+
+/*
+ * End of "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-private.c b/tools/gator/daemon/mxml/mxml-private.c
new file mode 100644
index 000000000000..72f3e2320c7c
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-private.c
@@ -0,0 +1,331 @@
+/*
+ * "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $"
+ *
+ * Private functions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxml_error() - Display an error message.
+ * mxml_integer_cb() - Default callback for integer values.
+ * mxml_opaque_cb() - Default callback for opaque values.
+ * mxml_real_cb() - Default callback for real number values.
+ * _mxml_global() - Get global data.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "mxml-private.h"
+
+
+/*
+ * Some crazy people think that unloading a shared object is a good or safe
+ * thing to do. Unfortunately, most objects are simply *not* safe to unload
+ * and bad things *will* happen.
+ *
+ * The following mess of conditional code allows us to provide a destructor
+ * function in Mini-XML for our thread-global storage so that it can possibly
+ * be unloaded safely, although since there is no standard way to do so I
+ * can't even provide any guarantees that you can do it safely on all platforms.
+ *
+ * This code currently supports AIX, HP-UX, Linux, Mac OS X, Solaris, and
+ * Windows. It might work on the BSDs and IRIX, but I haven't tested that.
+ */
+
+#if defined(__sun) || defined(_AIX)
+# pragma fini(_mxml_fini)
+# define _MXML_FINI _mxml_fini
+#elif defined(__hpux)
+# pragma FINI _mxml_fini
+# define _MXML_FINI _mxml_fini
+#elif defined(__GNUC__) /* Linux and Mac OS X */
+# define _MXML_FINI __attribute((destructor)) _mxml_fini
+#else
+# define _MXML_FINI _fini
+#endif /* __sun */
+
+
+/*
+ * 'mxml_error()' - Display an error message.
+ */
+
+void
+mxml_error(const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to arguments */
+ char s[1024]; /* Message string */
+ _mxml_global_t *global = _mxml_global();
+ /* Global data */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!format)
+ return;
+
+ /*
+ * Format the error message string...
+ */
+
+ va_start(ap, format);
+
+ vsnprintf(s, sizeof(s), format, ap);
+
+ va_end(ap);
+
+ /*
+ * And then display the error message...
+ */
+
+ if (global->error_cb)
+ (*global->error_cb)(s);
+ else
+ fprintf(stderr, "mxml: %s\n", s);
+}
+
+
+/*
+ * 'mxml_ignore_cb()' - Default callback for ignored values.
+ */
+
+mxml_type_t /* O - Node type */
+mxml_ignore_cb(mxml_node_t *node) /* I - Current node */
+{
+ (void)node;
+
+ return (MXML_IGNORE);
+}
+
+
+/*
+ * 'mxml_integer_cb()' - Default callback for integer values.
+ */
+
+mxml_type_t /* O - Node type */
+mxml_integer_cb(mxml_node_t *node) /* I - Current node */
+{
+ (void)node;
+
+ return (MXML_INTEGER);
+}
+
+
+/*
+ * 'mxml_opaque_cb()' - Default callback for opaque values.
+ */
+
+mxml_type_t /* O - Node type */
+mxml_opaque_cb(mxml_node_t *node) /* I - Current node */
+{
+ (void)node;
+
+ return (MXML_OPAQUE);
+}
+
+
+/*
+ * 'mxml_real_cb()' - Default callback for real number values.
+ */
+
+mxml_type_t /* O - Node type */
+mxml_real_cb(mxml_node_t *node) /* I - Current node */
+{
+ (void)node;
+
+ return (MXML_REAL);
+}
+
+
+#ifdef HAVE_PTHREAD_H /**** POSIX threading ****/
+# include <pthread.h>
+
+static pthread_key_t _mxml_key = -1; /* Thread local storage key */
+static pthread_once_t _mxml_key_once = PTHREAD_ONCE_INIT;
+ /* One-time initialization object */
+static void _mxml_init(void);
+static void _mxml_destructor(void *g);
+
+
+/*
+ * '_mxml_destructor()' - Free memory used for globals...
+ */
+
+static void
+_mxml_destructor(void *g) /* I - Global data */
+{
+ free(g);
+}
+
+
+/*
+ * '_mxml_fini()' - Clean up when unloaded.
+ */
+
+static void
+_MXML_FINI(void)
+{
+ _mxml_global_t *global; /* Global data */
+
+
+ if (_mxml_key != -1)
+ {
+ if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) != NULL)
+ _mxml_destructor(global);
+
+ pthread_key_delete(_mxml_key);
+ _mxml_key = -1;
+ }
+}
+
+
+/*
+ * '_mxml_global()' - Get global data.
+ */
+
+_mxml_global_t * /* O - Global data */
+_mxml_global(void)
+{
+ _mxml_global_t *global; /* Global data */
+
+
+ pthread_once(&_mxml_key_once, _mxml_init);
+
+ if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) == NULL)
+ {
+ global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t));
+ pthread_setspecific(_mxml_key, global);
+
+ global->num_entity_cbs = 1;
+ global->entity_cbs[0] = _mxml_entity_cb;
+ global->wrap = 72;
+ }
+
+ return (global);
+}
+
+
+/*
+ * '_mxml_init()' - Initialize global data...
+ */
+
+static void
+_mxml_init(void)
+{
+ pthread_key_create(&_mxml_key, _mxml_destructor);
+}
+
+
+#elif defined(WIN32) && defined(MXML1_EXPORTS) /**** WIN32 threading ****/
+# include <windows.h>
+
+static DWORD _mxml_tls_index; /* Index for global storage */
+
+
+/*
+ * 'DllMain()' - Main entry for library.
+ */
+
+BOOL WINAPI /* O - Success/failure */
+DllMain(HINSTANCE hinst, /* I - DLL module handle */
+ DWORD reason, /* I - Reason */
+ LPVOID reserved) /* I - Unused */
+{
+ _mxml_global_t *global; /* Global data */
+
+
+ (void)hinst;
+ (void)reserved;
+
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH : /* Called on library initialization */
+ if ((_mxml_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES)
+ return (FALSE);
+ break;
+
+ case DLL_THREAD_DETACH : /* Called when a thread terminates */
+ if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL)
+ free(global);
+ break;
+
+ case DLL_PROCESS_DETACH : /* Called when library is unloaded */
+ if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL)
+ free(global);
+
+ TlsFree(_mxml_tls_index);
+ break;
+
+ default:
+ break;
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * '_mxml_global()' - Get global data.
+ */
+
+_mxml_global_t * /* O - Global data */
+_mxml_global(void)
+{
+ _mxml_global_t *global; /* Global data */
+
+
+ if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) == NULL)
+ {
+ global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t));
+
+ global->num_entity_cbs = 1;
+ global->entity_cbs[0] = _mxml_entity_cb;
+ global->wrap = 72;
+
+ TlsSetValue(_mxml_tls_index, (LPVOID)global);
+ }
+
+ return (global);
+}
+
+
+#else /**** No threading ****/
+/*
+ * '_mxml_global()' - Get global data.
+ */
+
+_mxml_global_t * /* O - Global data */
+_mxml_global(void)
+{
+ static _mxml_global_t global = /* Global data */
+ {
+ NULL, /* error_cb */
+ 1, /* num_entity_cbs */
+ { _mxml_entity_cb }, /* entity_cbs */
+ 72, /* wrap */
+ NULL, /* custom_load_cb */
+ NULL /* custom_save_cb */
+ };
+
+
+ return (&global);
+}
+#endif /* HAVE_PTHREAD_H */
+
+
+/*
+ * End of "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-private.h b/tools/gator/daemon/mxml/mxml-private.h
new file mode 100644
index 000000000000..8789e6c52cbd
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-private.h
@@ -0,0 +1,50 @@
+/*
+ * "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $"
+ *
+ * Private definitions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * Global, per-thread data...
+ */
+
+typedef struct _mxml_global_s
+{
+ void (*error_cb)(const char *);
+ int num_entity_cbs;
+ int (*entity_cbs[100])(const char *name);
+ int wrap;
+ mxml_custom_load_cb_t custom_load_cb;
+ mxml_custom_save_cb_t custom_save_cb;
+} _mxml_global_t;
+
+
+/*
+ * Functions...
+ */
+
+extern _mxml_global_t *_mxml_global(void);
+extern int _mxml_entity_cb(const char *name);
+
+
+/*
+ * End of "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-search.c b/tools/gator/daemon/mxml/mxml-search.c
new file mode 100644
index 000000000000..f975af1543ca
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-search.c
@@ -0,0 +1,287 @@
+/*
+ * "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $"
+ *
+ * Search/navigation functions for Mini-XML, a small XML-like file
+ * parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlFindElement() - Find the named element.
+ * mxmlFindValue() - Find a value with the given path.
+ * mxmlWalkNext() - Walk to the next logical node in the tree.
+ * mxmlWalkPrev() - Walk to the previous logical node in the tree.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * 'mxmlFindElement()' - Find the named element.
+ *
+ * The search is constrained by the name, attribute name, and value; any
+ * NULL names or values are treated as wildcards, so different kinds of
+ * searches can be implemented by looking for all elements of a given name
+ * or all elements with a specific attribute. The descend argument determines
+ * whether the search descends into child nodes; normally you will use
+ * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find
+ * additional direct descendents of the node. The top node argument
+ * constrains the search to a particular node's children.
+ */
+
+mxml_node_t * /* O - Element node or NULL */
+mxmlFindElement(mxml_node_t *node, /* I - Current node */
+ mxml_node_t *top, /* I - Top node */
+ const char *name, /* I - Element name or NULL for any */
+ const char *attr, /* I - Attribute name, or NULL for none */
+ const char *value, /* I - Attribute value, or NULL for any */
+ int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
+{
+ const char *temp; /* Current attribute value */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!node || !top || (!attr && value))
+ return (NULL);
+
+ /*
+ * Start with the next node...
+ */
+
+ node = mxmlWalkNext(node, top, descend);
+
+ /*
+ * Loop until we find a matching element...
+ */
+
+ while (node != NULL)
+ {
+ /*
+ * See if this node matches...
+ */
+
+ if (node->type == MXML_ELEMENT &&
+ node->value.element.name &&
+ (!name || !strcmp(node->value.element.name, name)))
+ {
+ /*
+ * See if we need to check for an attribute...
+ */
+
+ if (!attr)
+ return (node); /* No attribute search, return it... */
+
+ /*
+ * Check for the attribute...
+ */
+
+ if ((temp = mxmlElementGetAttr(node, attr)) != NULL)
+ {
+ /*
+ * OK, we have the attribute, does it match?
+ */
+
+ if (!value || !strcmp(value, temp))
+ return (node); /* Yes, return it... */
+ }
+ }
+
+ /*
+ * No match, move on to the next node...
+ */
+
+ if (descend == MXML_DESCEND)
+ node = mxmlWalkNext(node, top, MXML_DESCEND);
+ else
+ node = node->next;
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlFindPath()' - Find a node with the given path.
+ *
+ * The "path" is a slash-separated list of element names. The name "*" is
+ * considered a wildcard for one or more levels of elements. For example,
+ * "foo/one/two", "bar/two/one", "*\/one", and so forth.
+ *
+ * The first child node of the found node is returned if the given node has
+ * children and the first child is a value node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+mxml_node_t * /* O - Found node or NULL */
+mxmlFindPath(mxml_node_t *top, /* I - Top node */
+ const char *path) /* I - Path to element */
+{
+ mxml_node_t *node; /* Current node */
+ char element[256]; /* Current element name */
+ const char *pathsep; /* Separator in path */
+ int descend; /* mxmlFindElement option */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (!top || !path || !*path)
+ return (NULL);
+
+ /*
+ * Search each element in the path...
+ */
+
+ node = top;
+ while (*path)
+ {
+ /*
+ * Handle wildcards...
+ */
+
+ if (!strncmp(path, "*/", 2))
+ {
+ path += 2;
+ descend = MXML_DESCEND;
+ }
+ else
+ descend = MXML_DESCEND_FIRST;
+
+ /*
+ * Get the next element in the path...
+ */
+
+ if ((pathsep = strchr(path, '/')) == NULL)
+ pathsep = path + strlen(path);
+
+ if (pathsep == path || (pathsep - path) >= sizeof(element))
+ return (NULL);
+
+ memcpy(element, path, pathsep - path);
+ element[pathsep - path] = '\0';
+
+ if (*pathsep)
+ path = pathsep + 1;
+ else
+ path = pathsep;
+
+ /*
+ * Search for the element...
+ */
+
+ if ((node = mxmlFindElement(node, node, element, NULL, NULL,
+ descend)) == NULL)
+ return (NULL);
+ }
+
+ /*
+ * If we get this far, return the node or its first child...
+ */
+
+ if (node->child && node->child->type != MXML_ELEMENT)
+ return (node->child);
+ else
+ return (node);
+}
+
+
+/*
+ * 'mxmlWalkNext()' - Walk to the next logical node in the tree.
+ *
+ * The descend argument controls whether the first child is considered
+ * to be the next node. The top node argument constrains the walk to
+ * the node's children.
+ */
+
+mxml_node_t * /* O - Next node or NULL */
+mxmlWalkNext(mxml_node_t *node, /* I - Current node */
+ mxml_node_t *top, /* I - Top node */
+ int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
+{
+ if (!node)
+ return (NULL);
+ else if (node->child && descend)
+ return (node->child);
+ else if (node == top)
+ return (NULL);
+ else if (node->next)
+ return (node->next);
+ else if (node->parent && node->parent != top)
+ {
+ node = node->parent;
+
+ while (!node->next)
+ if (node->parent == top || !node->parent)
+ return (NULL);
+ else
+ node = node->parent;
+
+ return (node->next);
+ }
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree.
+ *
+ * The descend argument controls whether the previous node's last child
+ * is considered to be the previous node. The top node argument constrains
+ * the walk to the node's children.
+ */
+
+mxml_node_t * /* O - Previous node or NULL */
+mxmlWalkPrev(mxml_node_t *node, /* I - Current node */
+ mxml_node_t *top, /* I - Top node */
+ int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
+{
+ if (!node || node == top)
+ return (NULL);
+ else if (node->prev)
+ {
+ if (node->prev->last_child && descend)
+ {
+ /*
+ * Find the last child under the previous node...
+ */
+
+ node = node->prev->last_child;
+
+ while (node->last_child)
+ node = node->last_child;
+
+ return (node);
+ }
+ else
+ return (node->prev);
+ }
+ else if (node->parent != top)
+ return (node->parent);
+ else
+ return (NULL);
+}
+
+
+/*
+ * End of "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-set.c b/tools/gator/daemon/mxml/mxml-set.c
new file mode 100644
index 000000000000..b0bd52790b2e
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-set.c
@@ -0,0 +1,349 @@
+/*
+ * "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $"
+ *
+ * Node set functions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * mxmlSetCDATA() - Set the element name of a CDATA node.
+ * mxmlSetCustom() - Set the data and destructor of a custom data node.
+ * mxmlSetElement() - Set the name of an element node.
+ * mxmlSetInteger() - Set the value of an integer node.
+ * mxmlSetOpaque() - Set the value of an opaque node.
+ * mxmlSetReal() - Set the value of a real number node.
+ * mxmlSetText() - Set the value of a text node.
+ * mxmlSetTextf() - Set the value of a text node to a formatted string.
+ * mxmlSetUserData() - Set the user data pointer for a node.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+
+
+/*
+ * 'mxmlSetCDATA()' - Set the element name of a CDATA node.
+ *
+ * The node is not changed if it (or its first child) is not a CDATA element node.
+ *
+ * @since Mini-XML 2.3@
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetCDATA(mxml_node_t *node, /* I - Node to set */
+ const char *data) /* I - New data string */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ strncmp(node->value.element.name, "![CDATA[", 8) &&
+ node->child && node->child->type == MXML_ELEMENT &&
+ !strncmp(node->child->value.element.name, "![CDATA[", 8))
+ node = node->child;
+
+ if (!node || node->type != MXML_ELEMENT || !data ||
+ strncmp(node->value.element.name, "![CDATA[", 8))
+ return (-1);
+
+ /*
+ * Free any old element value and set the new value...
+ */
+
+ if (node->value.element.name)
+ free(node->value.element.name);
+
+ node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetCustom()' - Set the data and destructor of a custom data node.
+ *
+ * The node is not changed if it (or its first child) is not a custom node.
+ *
+ * @since Mini-XML 2.1@
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetCustom(
+ mxml_node_t *node, /* I - Node to set */
+ void *data, /* I - New data pointer */
+ mxml_custom_destroy_cb_t destroy) /* I - New destructor function */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_CUSTOM)
+ node = node->child;
+
+ if (!node || node->type != MXML_CUSTOM)
+ return (-1);
+
+ /*
+ * Free any old element value and set the new value...
+ */
+
+ if (node->value.custom.data && node->value.custom.destroy)
+ (*(node->value.custom.destroy))(node->value.custom.data);
+
+ node->value.custom.data = data;
+ node->value.custom.destroy = destroy;
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetElement()' - Set the name of an element node.
+ *
+ * The node is not changed if it is not an element node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetElement(mxml_node_t *node, /* I - Node to set */
+ const char *name) /* I - New name string */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node || node->type != MXML_ELEMENT || !name)
+ return (-1);
+
+ /*
+ * Free any old element value and set the new value...
+ */
+
+ if (node->value.element.name)
+ free(node->value.element.name);
+
+ node->value.element.name = strdup(name);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetInteger()' - Set the value of an integer node.
+ *
+ * The node is not changed if it (or its first child) is not an integer node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetInteger(mxml_node_t *node, /* I - Node to set */
+ int integer) /* I - Integer value */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_INTEGER)
+ node = node->child;
+
+ if (!node || node->type != MXML_INTEGER)
+ return (-1);
+
+ /*
+ * Set the new value and return...
+ */
+
+ node->value.integer = integer;
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetOpaque()' - Set the value of an opaque node.
+ *
+ * The node is not changed if it (or its first child) is not an opaque node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetOpaque(mxml_node_t *node, /* I - Node to set */
+ const char *opaque) /* I - Opaque string */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_OPAQUE)
+ node = node->child;
+
+ if (!node || node->type != MXML_OPAQUE || !opaque)
+ return (-1);
+
+ /*
+ * Free any old opaque value and set the new value...
+ */
+
+ if (node->value.opaque)
+ free(node->value.opaque);
+
+ node->value.opaque = strdup(opaque);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetReal()' - Set the value of a real number node.
+ *
+ * The node is not changed if it (or its first child) is not a real number node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetReal(mxml_node_t *node, /* I - Node to set */
+ double real) /* I - Real number value */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_REAL)
+ node = node->child;
+
+ if (!node || node->type != MXML_REAL)
+ return (-1);
+
+ /*
+ * Set the new value and return...
+ */
+
+ node->value.real = real;
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetText()' - Set the value of a text node.
+ *
+ * The node is not changed if it (or its first child) is not a text node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetText(mxml_node_t *node, /* I - Node to set */
+ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
+ const char *string) /* I - String */
+{
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_TEXT)
+ node = node->child;
+
+ if (!node || node->type != MXML_TEXT || !string)
+ return (-1);
+
+ /*
+ * Free any old string value and set the new value...
+ */
+
+ if (node->value.text.string)
+ free(node->value.text.string);
+
+ node->value.text.whitespace = whitespace;
+ node->value.text.string = strdup(string);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetTextf()' - Set the value of a text node to a formatted string.
+ *
+ * The node is not changed if it (or its first child) is not a text node.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetTextf(mxml_node_t *node, /* I - Node to set */
+ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
+ const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to arguments */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (node && node->type == MXML_ELEMENT &&
+ node->child && node->child->type == MXML_TEXT)
+ node = node->child;
+
+ if (!node || node->type != MXML_TEXT || !format)
+ return (-1);
+
+ /*
+ * Free any old string value and set the new value...
+ */
+
+ if (node->value.text.string)
+ free(node->value.text.string);
+
+ va_start(ap, format);
+
+ node->value.text.whitespace = whitespace;
+ node->value.text.string = _mxml_strdupf(format, ap);
+
+ va_end(ap);
+
+ return (0);
+}
+
+
+/*
+ * 'mxmlSetUserData()' - Set the user data pointer for a node.
+ *
+ * @since Mini-XML 2.7@
+ */
+
+int /* O - 0 on success, -1 on failure */
+mxmlSetUserData(mxml_node_t *node, /* I - Node to set */
+ void *data) /* I - User data pointer */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!node)
+ return (-1);
+
+ /*
+ * Set the user data pointer and return...
+ */
+
+ node->user_data = data;
+ return (0);
+}
+
+
+/*
+ * End of "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml-string.c b/tools/gator/daemon/mxml/mxml-string.c
new file mode 100644
index 000000000000..6be42523f95c
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml-string.c
@@ -0,0 +1,476 @@
+/*
+ * "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $"
+ *
+ * String functions for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2010 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * _mxml_snprintf() - Format a string.
+ * _mxml_strdup() - Duplicate a string.
+ * _mxml_strdupf() - Format and duplicate a string.
+ * _mxml_vsnprintf() - Format a string into a fixed size buffer.
+ * _mxml_vstrdupf() - Format and duplicate a string.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+
+
+/*
+ * The va_copy macro is part of C99, but many compilers don't implement it.
+ * Provide a "direct assignment" implmentation when va_copy isn't defined...
+ */
+
+#ifndef va_copy
+# ifdef __va_copy
+# define va_copy(dst,src) __va_copy(dst,src)
+# else
+# define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list))
+# endif /* __va_copy */
+#endif /* va_copy */
+
+
+#ifndef HAVE_SNPRINTF
+/*
+ * '_mxml_snprintf()' - Format a string.
+ */
+
+int /* O - Number of bytes formatted */
+_mxml_snprintf(char *buffer, /* I - Output buffer */
+ size_t bufsize, /* I - Size of output buffer */
+ const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Argument list */
+ int bytes; /* Number of bytes formatted */
+
+
+ va_start(ap, format);
+ bytes = vsnprintf(buffer, bufsize, format, ap);
+ va_end(ap);
+
+ return (bytes);
+}
+#endif /* !HAVE_SNPRINTF */
+
+
+/*
+ * '_mxml_strdup()' - Duplicate a string.
+ */
+
+#ifndef HAVE_STRDUP
+char * /* O - New string pointer */
+_mxml_strdup(const char *s) /* I - String to duplicate */
+{
+ char *t; /* New string pointer */
+
+
+ if (s == NULL)
+ return (NULL);
+
+ if ((t = malloc(strlen(s) + 1)) == NULL)
+ return (NULL);
+
+ return (strcpy(t, s));
+}
+#endif /* !HAVE_STRDUP */
+
+
+/*
+ * '_mxml_strdupf()' - Format and duplicate a string.
+ */
+
+char * /* O - New string pointer */
+_mxml_strdupf(const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to additional arguments */
+ char *s; /* Pointer to formatted string */
+
+
+ /*
+ * Get a pointer to the additional arguments, format the string,
+ * and return it...
+ */
+
+ va_start(ap, format);
+ s = _mxml_vstrdupf(format, ap);
+ va_end(ap);
+
+ return (s);
+}
+
+
+#ifndef HAVE_VSNPRINTF
+/*
+ * '_mxml_vsnprintf()' - Format a string into a fixed size buffer.
+ */
+
+int /* O - Number of bytes formatted */
+_mxml_vsnprintf(char *buffer, /* O - Output buffer */
+ size_t bufsize, /* O - Size of output buffer */
+ const char *format, /* I - Printf-style format string */
+ va_list ap) /* I - Pointer to additional arguments */
+{
+ char *bufptr, /* Pointer to position in buffer */
+ *bufend, /* Pointer to end of buffer */
+ sign, /* Sign of format width */
+ size, /* Size character (h, l, L) */
+ type; /* Format type character */
+ int width, /* Width of field */
+ prec; /* Number of characters of precision */
+ char tformat[100], /* Temporary format string for sprintf() */
+ *tptr, /* Pointer into temporary format */
+ temp[1024]; /* Buffer for formatted numbers */
+ char *s; /* Pointer to string */
+ int slen; /* Length of string */
+ int bytes; /* Total number of bytes needed */
+
+
+ /*
+ * Loop through the format string, formatting as needed...
+ */
+
+ bufptr = buffer;
+ bufend = buffer + bufsize - 1;
+ bytes = 0;
+
+ while (*format)
+ {
+ if (*format == '%')
+ {
+ tptr = tformat;
+ *tptr++ = *format++;
+
+ if (*format == '%')
+ {
+ if (bufptr && bufptr < bufend) *bufptr++ = *format;
+ bytes ++;
+ format ++;
+ continue;
+ }
+ else if (strchr(" -+#\'", *format))
+ {
+ *tptr++ = *format;
+ sign = *format++;
+ }
+ else
+ sign = 0;
+
+ if (*format == '*')
+ {
+ /*
+ * Get width from argument...
+ */
+
+ format ++;
+ width = va_arg(ap, int);
+
+ snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
+ tptr += strlen(tptr);
+ }
+ else
+ {
+ width = 0;
+
+ while (isdigit(*format & 255))
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ width = width * 10 + *format++ - '0';
+ }
+ }
+
+ if (*format == '.')
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ format ++;
+
+ if (*format == '*')
+ {
+ /*
+ * Get precision from argument...
+ */
+
+ format ++;
+ prec = va_arg(ap, int);
+
+ snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
+ tptr += strlen(tptr);
+ }
+ else
+ {
+ prec = 0;
+
+ while (isdigit(*format & 255))
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ prec = prec * 10 + *format++ - '0';
+ }
+ }
+ }
+ else
+ prec = -1;
+
+ if (*format == 'l' && format[1] == 'l')
+ {
+ size = 'L';
+
+ if (tptr < (tformat + sizeof(tformat) - 2))
+ {
+ *tptr++ = 'l';
+ *tptr++ = 'l';
+ }
+
+ format += 2;
+ }
+ else if (*format == 'h' || *format == 'l' || *format == 'L')
+ {
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ size = *format++;
+ }
+
+ if (!*format)
+ break;
+
+ if (tptr < (tformat + sizeof(tformat) - 1))
+ *tptr++ = *format;
+
+ type = *format++;
+ *tptr = '\0';
+
+ switch (type)
+ {
+ case 'E' : /* Floating point formats */
+ case 'G' :
+ case 'e' :
+ case 'f' :
+ case 'g' :
+ if ((width + 2) > sizeof(temp))
+ break;
+
+ sprintf(temp, tformat, va_arg(ap, double));
+
+ bytes += strlen(temp);
+
+ if (bufptr)
+ {
+ if ((bufptr + strlen(temp)) > bufend)
+ {
+ strncpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr = bufend;
+ }
+ else
+ {
+ strcpy(bufptr, temp);
+ bufptr += strlen(temp);
+ }
+ }
+ break;
+
+ case 'B' : /* Integer formats */
+ case 'X' :
+ case 'b' :
+ case 'd' :
+ case 'i' :
+ case 'o' :
+ case 'u' :
+ case 'x' :
+ if ((width + 2) > sizeof(temp))
+ break;
+
+#ifdef HAVE_LONG_LONG
+ if (size == 'L')
+ sprintf(temp, tformat, va_arg(ap, long long));
+ else
+#endif /* HAVE_LONG_LONG */
+ sprintf(temp, tformat, va_arg(ap, int));
+
+ bytes += strlen(temp);
+
+ if (bufptr)
+ {
+ if ((bufptr + strlen(temp)) > bufend)
+ {
+ strncpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr = bufend;
+ }
+ else
+ {
+ strcpy(bufptr, temp);
+ bufptr += strlen(temp);
+ }
+ }
+ break;
+
+ case 'p' : /* Pointer value */
+ if ((width + 2) > sizeof(temp))
+ break;
+
+ sprintf(temp, tformat, va_arg(ap, void *));
+
+ bytes += strlen(temp);
+
+ if (bufptr)
+ {
+ if ((bufptr + strlen(temp)) > bufend)
+ {
+ strncpy(bufptr, temp, (size_t)(bufend - bufptr));
+ bufptr = bufend;
+ }
+ else
+ {
+ strcpy(bufptr, temp);
+ bufptr += strlen(temp);
+ }
+ }
+ break;
+
+ case 'c' : /* Character or character array */
+ bytes += width;
+
+ if (bufptr)
+ {
+ if (width <= 1)
+ *bufptr++ = va_arg(ap, int);
+ else
+ {
+ if ((bufptr + width) > bufend)
+ width = bufend - bufptr;
+
+ memcpy(bufptr, va_arg(ap, char *), (size_t)width);
+ bufptr += width;
+ }
+ }
+ break;
+
+ case 's' : /* String */
+ if ((s = va_arg(ap, char *)) == NULL)
+ s = "(null)";
+
+ slen = strlen(s);
+ if (slen > width && prec != width)
+ width = slen;
+
+ bytes += width;
+
+ if (bufptr)
+ {
+ if ((bufptr + width) > bufend)
+ width = bufend - bufptr;
+
+ if (slen > width)
+ slen = width;
+
+ if (sign == '-')
+ {
+ strncpy(bufptr, s, (size_t)slen);
+ memset(bufptr + slen, ' ', (size_t)(width - slen));
+ }
+ else
+ {
+ memset(bufptr, ' ', (size_t)(width - slen));
+ strncpy(bufptr + width - slen, s, (size_t)slen);
+ }
+
+ bufptr += width;
+ }
+ break;
+
+ case 'n' : /* Output number of chars so far */
+ *(va_arg(ap, int *)) = bytes;
+ break;
+ }
+ }
+ else
+ {
+ bytes ++;
+
+ if (bufptr && bufptr < bufend)
+ *bufptr++ = *format;
+
+ format ++;
+ }
+ }
+
+ /*
+ * Nul-terminate the string and return the number of characters needed.
+ */
+
+ *bufptr = '\0';
+
+ return (bytes);
+}
+#endif /* !HAVE_VSNPRINTF */
+
+
+/*
+ * '_mxml_vstrdupf()' - Format and duplicate a string.
+ */
+
+char * /* O - New string pointer */
+_mxml_vstrdupf(const char *format, /* I - Printf-style format string */
+ va_list ap) /* I - Pointer to additional arguments */
+{
+ int bytes; /* Number of bytes required */
+ char *buffer, /* String buffer */
+ temp[256]; /* Small buffer for first vsnprintf */
+ va_list apcopy; /* Copy of argument list */
+
+
+ /*
+ * First format with a tiny buffer; this will tell us how many bytes are
+ * needed...
+ */
+
+ va_copy(apcopy, ap);
+ bytes = vsnprintf(temp, sizeof(temp), format, apcopy);
+
+ if (bytes < sizeof(temp))
+ {
+ /*
+ * Hey, the formatted string fits in the tiny buffer, so just dup that...
+ */
+
+ return (strdup(temp));
+ }
+
+ /*
+ * Allocate memory for the whole thing and reformat to the new, larger
+ * buffer...
+ */
+
+ if ((buffer = calloc(1, bytes + 1)) != NULL)
+ vsnprintf(buffer, bytes + 1, format, ap);
+
+ /*
+ * Return the new string...
+ */
+
+ return (buffer);
+}
+
+
+/*
+ * End of "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $".
+ */
diff --git a/tools/gator/daemon/mxml/mxml.h b/tools/gator/daemon/mxml/mxml.h
new file mode 100644
index 000000000000..79c711f4c80f
--- /dev/null
+++ b/tools/gator/daemon/mxml/mxml.h
@@ -0,0 +1,329 @@
+/*
+ * "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $"
+ *
+ * Header file for Mini-XML, a small XML-like file parsing library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ */
+
+/*
+ * Prevent multiple inclusion...
+ */
+
+#ifndef _mxml_h_
+# define _mxml_h_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+# include <ctype.h>
+# include <errno.h>
+
+
+/*
+ * Constants...
+ */
+
+# define MXML_TAB 8 /* Tabs every N columns */
+
+# define MXML_NO_CALLBACK 0 /* Don't use a type callback */
+# define MXML_INTEGER_CALLBACK mxml_integer_cb
+ /* Treat all data as integers */
+# define MXML_OPAQUE_CALLBACK mxml_opaque_cb
+ /* Treat all data as opaque */
+# define MXML_REAL_CALLBACK mxml_real_cb
+ /* Treat all data as real numbers */
+# define MXML_TEXT_CALLBACK 0 /* Treat all data as text */
+# define MXML_IGNORE_CALLBACK mxml_ignore_cb
+ /* Ignore all non-element content */
+
+# define MXML_NO_PARENT 0 /* No parent for the node */
+
+# define MXML_DESCEND 1 /* Descend when finding/walking */
+# define MXML_NO_DESCEND 0 /* Don't descend when finding/walking */
+# define MXML_DESCEND_FIRST -1 /* Descend for first find */
+
+# define MXML_WS_BEFORE_OPEN 0 /* Callback for before open tag */
+# define MXML_WS_AFTER_OPEN 1 /* Callback for after open tag */
+# define MXML_WS_BEFORE_CLOSE 2 /* Callback for before close tag */
+# define MXML_WS_AFTER_CLOSE 3 /* Callback for after close tag */
+
+# define MXML_ADD_BEFORE 0 /* Add node before specified node */
+# define MXML_ADD_AFTER 1 /* Add node after specified node */
+# define MXML_ADD_TO_PARENT NULL /* Add node relative to parent */
+
+
+/*
+ * Data types...
+ */
+
+typedef enum mxml_sax_event_e /**** SAX event type. ****/
+{
+ MXML_SAX_CDATA, /* CDATA node */
+ MXML_SAX_COMMENT, /* Comment node */
+ MXML_SAX_DATA, /* Data node */
+ MXML_SAX_DIRECTIVE, /* Processing directive node */
+ MXML_SAX_ELEMENT_CLOSE, /* Element closed */
+ MXML_SAX_ELEMENT_OPEN /* Element opened */
+} mxml_sax_event_t;
+
+typedef enum mxml_type_e /**** The XML node type. ****/
+{
+ MXML_IGNORE = -1, /* Ignore/throw away node @since Mini-XML 2.3@ */
+ MXML_ELEMENT, /* XML element with attributes */
+ MXML_INTEGER, /* Integer value */
+ MXML_OPAQUE, /* Opaque string */
+ MXML_REAL, /* Real value */
+ MXML_TEXT, /* Text fragment */
+ MXML_CUSTOM /* Custom data @since Mini-XML 2.1@ */
+} mxml_type_t;
+
+typedef void (*mxml_custom_destroy_cb_t)(void *);
+ /**** Custom data destructor ****/
+
+typedef void (*mxml_error_cb_t)(const char *);
+ /**** Error callback function ****/
+
+typedef struct mxml_attr_s /**** An XML element attribute value. @private@ ****/
+{
+ char *name; /* Attribute name */
+ char *value; /* Attribute value */
+} mxml_attr_t;
+
+typedef struct mxml_element_s /**** An XML element value. @private@ ****/
+{
+ char *name; /* Name of element */
+ int num_attrs; /* Number of attributes */
+ mxml_attr_t *attrs; /* Attributes */
+} mxml_element_t;
+
+typedef struct mxml_text_s /**** An XML text value. @private@ ****/
+{
+ int whitespace; /* Leading whitespace? */
+ char *string; /* Fragment string */
+} mxml_text_t;
+
+typedef struct mxml_custom_s /**** An XML custom value. @private@ ****/
+{
+ void *data; /* Pointer to (allocated) custom data */
+ mxml_custom_destroy_cb_t destroy; /* Pointer to destructor function */
+} mxml_custom_t;
+
+typedef union mxml_value_u /**** An XML node value. @private@ ****/
+{
+ mxml_element_t element; /* Element */
+ int integer; /* Integer number */
+ char *opaque; /* Opaque string */
+ double real; /* Real number */
+ mxml_text_t text; /* Text fragment */
+ mxml_custom_t custom; /* Custom data @since Mini-XML 2.1@ */
+} mxml_value_t;
+
+struct mxml_node_s /**** An XML node. @private@ ****/
+{
+ mxml_type_t type; /* Node type */
+ struct mxml_node_s *next; /* Next node under same parent */
+ struct mxml_node_s *prev; /* Previous node under same parent */
+ struct mxml_node_s *parent; /* Parent node */
+ struct mxml_node_s *child; /* First child node */
+ struct mxml_node_s *last_child; /* Last child node */
+ mxml_value_t value; /* Node value */
+ int ref_count; /* Use count */
+ void *user_data; /* User data */
+};
+
+typedef struct mxml_node_s mxml_node_t; /**** An XML node. ****/
+
+struct mxml_index_s /**** An XML node index. @private@ ****/
+{
+ char *attr; /* Attribute used for indexing or NULL */
+ int num_nodes; /* Number of nodes in index */
+ int alloc_nodes; /* Allocated nodes in index */
+ int cur_node; /* Current node */
+ mxml_node_t **nodes; /* Node array */
+};
+
+typedef struct mxml_index_s mxml_index_t;
+ /**** An XML node index. ****/
+
+typedef int (*mxml_custom_load_cb_t)(mxml_node_t *, const char *);
+ /**** Custom data load callback function ****/
+
+typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *);
+ /**** Custom data save callback function ****/
+
+typedef int (*mxml_entity_cb_t)(const char *);
+ /**** Entity callback function */
+
+typedef mxml_type_t (*mxml_load_cb_t)(mxml_node_t *);
+ /**** Load callback function ****/
+
+typedef const char *(*mxml_save_cb_t)(mxml_node_t *, int);
+ /**** Save callback function ****/
+
+typedef void (*mxml_sax_cb_t)(mxml_node_t *, mxml_sax_event_t, void *);
+ /**** SAX callback function ****/
+
+
+/*
+ * C++ support...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+/*
+ * Prototypes...
+ */
+
+extern void mxmlAdd(mxml_node_t *parent, int where,
+ mxml_node_t *child, mxml_node_t *node);
+extern void mxmlDelete(mxml_node_t *node);
+extern void mxmlElementDeleteAttr(mxml_node_t *node,
+ const char *name);
+extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name);
+extern void mxmlElementSetAttr(mxml_node_t *node, const char *name,
+ const char *value);
+extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name,
+ const char *format, ...)
+# ifdef __GNUC__
+__attribute__ ((__format__ (__printf__, 3, 4)))
+# endif /* __GNUC__ */
+;
+extern int mxmlEntityAddCallback(mxml_entity_cb_t cb);
+extern const char *mxmlEntityGetName(int val);
+extern int mxmlEntityGetValue(const char *name);
+extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb);
+extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top,
+ const char *name, const char *attr,
+ const char *value, int descend);
+extern mxml_node_t *mxmlFindPath(mxml_node_t *node, const char *path);
+extern const char *mxmlGetCDATA(mxml_node_t *node);
+extern const void *mxmlGetCustom(mxml_node_t *node);
+extern const char *mxmlGetElement(mxml_node_t *node);
+extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node);
+extern int mxmlGetInteger(mxml_node_t *node);
+extern mxml_node_t *mxmlGetLastChild(mxml_node_t *node);
+extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node);
+extern const char *mxmlGetOpaque(mxml_node_t *node);
+extern mxml_node_t *mxmlGetParent(mxml_node_t *node);
+extern mxml_node_t *mxmlGetPrevSibling(mxml_node_t *node);
+extern double mxmlGetReal(mxml_node_t *node);
+extern int mxmlGetRefCount(mxml_node_t *node);
+extern const char *mxmlGetText(mxml_node_t *node, int *whitespace);
+extern mxml_type_t mxmlGetType(mxml_node_t *node);
+extern void *mxmlGetUserData(mxml_node_t *node);
+extern void mxmlIndexDelete(mxml_index_t *ind);
+extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind);
+extern mxml_node_t *mxmlIndexFind(mxml_index_t *ind,
+ const char *element,
+ const char *value);
+extern int mxmlIndexGetCount(mxml_index_t *ind);
+extern mxml_index_t *mxmlIndexNew(mxml_node_t *node, const char *element,
+ const char *attr);
+extern mxml_node_t *mxmlIndexReset(mxml_index_t *ind);
+extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, int fd,
+ mxml_type_t (*cb)(mxml_node_t *));
+extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp,
+ mxml_type_t (*cb)(mxml_node_t *));
+extern mxml_node_t *mxmlLoadString(mxml_node_t *top, const char *s,
+ mxml_type_t (*cb)(mxml_node_t *));
+extern mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string);
+extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data,
+ mxml_custom_destroy_cb_t destroy);
+extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name);
+extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, int integer);
+extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque);
+extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real);
+extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace,
+ const char *string);
+extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace,
+ const char *format, ...)
+# ifdef __GNUC__
+__attribute__ ((__format__ (__printf__, 3, 4)))
+# endif /* __GNUC__ */
+;
+extern mxml_node_t *mxmlNewXML(const char *version);
+extern int mxmlRelease(mxml_node_t *node);
+extern void mxmlRemove(mxml_node_t *node);
+extern int mxmlRetain(mxml_node_t *node);
+extern char *mxmlSaveAllocString(mxml_node_t *node,
+ mxml_save_cb_t cb);
+extern int mxmlSaveFd(mxml_node_t *node, int fd,
+ mxml_save_cb_t cb);
+extern int mxmlSaveFile(mxml_node_t *node, FILE *fp,
+ mxml_save_cb_t cb);
+extern int mxmlSaveString(mxml_node_t *node, char *buffer,
+ int bufsize, mxml_save_cb_t cb);
+extern mxml_node_t *mxmlSAXLoadFd(mxml_node_t *top, int fd,
+ mxml_type_t (*cb)(mxml_node_t *),
+ mxml_sax_cb_t sax, void *sax_data);
+extern mxml_node_t *mxmlSAXLoadFile(mxml_node_t *top, FILE *fp,
+ mxml_type_t (*cb)(mxml_node_t *),
+ mxml_sax_cb_t sax, void *sax_data);
+extern mxml_node_t *mxmlSAXLoadString(mxml_node_t *top, const char *s,
+ mxml_type_t (*cb)(mxml_node_t *),
+ mxml_sax_cb_t sax, void *sax_data);
+extern int mxmlSetCDATA(mxml_node_t *node, const char *data);
+extern int mxmlSetCustom(mxml_node_t *node, void *data,
+ mxml_custom_destroy_cb_t destroy);
+extern void mxmlSetCustomHandlers(mxml_custom_load_cb_t load,
+ mxml_custom_save_cb_t save);
+extern int mxmlSetElement(mxml_node_t *node, const char *name);
+extern void mxmlSetErrorCallback(mxml_error_cb_t cb);
+extern int mxmlSetInteger(mxml_node_t *node, int integer);
+extern int mxmlSetOpaque(mxml_node_t *node, const char *opaque);
+extern int mxmlSetReal(mxml_node_t *node, double real);
+extern int mxmlSetText(mxml_node_t *node, int whitespace,
+ const char *string);
+extern int mxmlSetTextf(mxml_node_t *node, int whitespace,
+ const char *format, ...)
+# ifdef __GNUC__
+__attribute__ ((__format__ (__printf__, 3, 4)))
+# endif /* __GNUC__ */
+;
+extern int mxmlSetUserData(mxml_node_t *node, void *data);
+extern void mxmlSetWrapMargin(int column);
+extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top,
+ int descend);
+extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top,
+ int descend);
+
+
+/*
+ * Semi-private functions...
+ */
+
+extern void mxml_error(const char *format, ...);
+extern mxml_type_t mxml_ignore_cb(mxml_node_t *node);
+extern mxml_type_t mxml_integer_cb(mxml_node_t *node);
+extern mxml_type_t mxml_opaque_cb(mxml_node_t *node);
+extern mxml_type_t mxml_real_cb(mxml_node_t *node);
+
+
+/*
+ * C++ support...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_mxml_h_ */
+
+
+/*
+ * End of "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $".
+ */
diff --git a/tools/gator/debian/changelog b/tools/gator/debian/changelog
new file mode 100644
index 000000000000..8cf4a16e9ec2
--- /dev/null
+++ b/tools/gator/debian/changelog
@@ -0,0 +1,172 @@
+gator (5.19.0) trusty; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Mon, 4 Aug 2014 11:37:16 +0100
+
+gator (5.18.1) saucy; urgency=low
+
+ * Fixes to get the Gator module working on Linux 3.15 following the
+ tracepoint API changes in that.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Thu, 24 Apr 2014 15:30:15 +0100
+
+gator (5.18.0) saucy; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Thu, 10 Apr 2014 09:26:42 +0100
+
+gator (5.17.0) saucy; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Wed, 18 Dec 2013 15:56:33 +0000
+
+gator (5.16.0) raring; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Tue, 10 Oct 2013 20:10:18 +0100
+
+gator (5.15.1) raring; urgency=low
+
+ * Fix gator-module-dkms install when kernel didn't already include
+ a version of gator. See issue https://cards.linaro.org/browse/ARM-44
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Tue, 30 Jul 2013 15:42:43 +0100
+
+gator (5.15.0) raring; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Tue, 11 Jun 2013 12:34:11 +0100
+
+gator (5.14.0) quantal; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Wed, 3 Apr 2013 14:34:18 +0100
+
+gator (5.13.0) quantal; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Mon, 14 Jan 2013 10:04:42 +0000
+
+gator (5.12.0) precise; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Wed, 24 Oct 2012 17:08:17 +0000
+
+gator (5.11.0) precise; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Fri, 10 Aug 2012 14:34:23 +0000
+
+gator (5.10.1) precise; urgency=low
+
+ * Add gator-module-dkms package.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Mon, 21 May 2012 15:59:10 +0000
+
+gator (5.10.0) precise; urgency=low
+
+ * New release from ARM.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Thu, 10 May 2012 16:47:32 +0000
+
+gator (5.9.7) precise; urgency=low
+
+ * New source and binary package layout with automated packaging scripts.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Wed, 2 May 2012 17:01:18 +0000
+
+gator-daemon (5.9-6) precise; urgency=low
+
+ * Remove Depends: gator-module* as Linaro kernels should
+ now all contain the required module.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Wed, 25 Apr 2012 18:06:28 +0000
+
+gator-daemon (5.9-5) precise; urgency=low
+
+ * Move to armhf and Precise
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Wed, 25 Apr 2012 10:16:00 +0000
+
+gator-daemon (5.9-4) oneiric; urgency=low
+
+ * Add alternative Depends on gator-module-in-tree.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Fri, 16 Mar 2012 18:24:27 +0000
+
+gator-daemon (5.9-3) oneiric; urgency=low
+
+ * Add missing build depends: 'quilt'.
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Thu, 23 Feb 2012 13:59:08 +0000
+
+gator-daemon (5.9-2) oneiric; urgency=low
+
+ [ Wade Cherry ]
+ * Fix counters initialisation
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Thu, 23 Feb 2012 12:06:33 +0000
+
+gator-daemon (5.9-1) oneiric; urgency=low
+
+ * New upstream release (DS-5.9)
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Mon, 20 Feb 2012 10:35:47 +0000
+
+gator-daemon (5.9~rc1-1) oneiric; urgency=low
+
+ * New upstream release (DS-5.9)
+
+ -- Jon Medhurst (Tixy) <jon.medhurst@linaro.org> Wed, 15 Feb 2012 14:38:26 +0000
+
+gator-daemon (5.8-1) oneiric; urgency=low
+
+ * New upstream release
+
+ -- Ricardo Salveti de Araujo <ricardo.salveti@linaro.org> Fri, 25 Nov 2011 12:41:57 -0200
+
+gator-daemon (5.8~rc2-1) oneiric; urgency=low
+
+ * New upstream version (DS-5.8~rc2)
+
+ -- Ricardo Salveti de Araujo <ricardo.salveti@linaro.org> Wed, 16 Nov 2011 15:30:07 -0200
+
+gator-daemon (5.7-2) natty; urgency=low
+
+ * Package now depends on the new gator-module meta package
+
+ -- Ricardo Salveti de Araujo <ricardo.salveti@linaro.org> Mon, 10 Oct 2011 23:39:34 -0300
+
+gator-daemon (5.7-1) natty; urgency=low
+
+ * Updated to DS-5.7 release
+ * Added sources (GPLv2)
+
+ -- Pawel Moll <pawel.moll@arm.com> Fri, 30 Sep 2011 16:02:05 +0100
+
+gator-daemon (5.6-2) natty; urgency=low
+
+ * Added dependency on 5.6 version of the gator-module[-dkms]
+
+ -- Pawel Moll <pawel.moll@arm.com> Tue, 13 Sep 2011 15:59:37 +0100
+
+gator-daemon (5.6-1) natty; urgency=low
+
+ * Updated to DS-5.6 release
+
+ -- Pawel Moll <pawel.moll@arm.com> Wed, 03 Aug 2011 11:54:40 +0100
+
+gator-daemon (5.5-1) natty; urgency=low
+
+ * Initial release
+
+ -- Pawel Moll <pawel.moll@arm.com> Mon, 06 Jun 2011 18:42:31 +0100
diff --git a/tools/gator/debian/compat b/tools/gator/debian/compat
new file mode 100644
index 000000000000..45a4fb75db86
--- /dev/null
+++ b/tools/gator/debian/compat
@@ -0,0 +1 @@
+8
diff --git a/tools/gator/debian/control b/tools/gator/debian/control
new file mode 100644
index 000000000000..f20a736e5e7e
--- /dev/null
+++ b/tools/gator/debian/control
@@ -0,0 +1,38 @@
+Source: gator
+Section: devel
+Priority: extra
+Maintainer: Pawel Moll <pawel.moll@arm.com>
+Build-Depends: debhelper (>= 7.0.5), vim-common
+Standards-Version: 3.9.2
+Homepage: http://www.arm.com/ds-5
+
+Package: gator
+Architecture: armhf
+Depends: ${misc:Depends}, gator-daemon (>= 5.9.0)
+Description: ARM Streamline gator
+ This meta-package depends on gator target packages required to use
+ ARM Streamline Performance Analyzer.
+
+Package: gator-daemon
+Architecture: armhf
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: ARM Streamline gator daemon
+ The target-side daemon gathering data for ARM Streamline
+ Performance Analyzer.
+
+Package: arm-ds5-target
+Architecture: armhf
+Depends: ${misc:Depends}, gdbserver (>= 6.8), openssh-server | ssh-server, gator (>= 5.9.0)
+Description: ARM DS-5 target dependencies
+ This meta-package depends on all components required to receive
+ full ARM DS-5 experience.
+
+Package: gator-module-dkms
+Architecture: armhf
+Section: kernel
+Depends: ${misc:Depends}, dkms (>= 1.95), make
+Description: ARM Streamline gator module - dkms version
+ The target-side kernel module gathering data for ARM Streamline
+ Performance Analyzer.
+ .
+ This package contains the source to be built with dkms.
diff --git a/tools/gator/debian/copyright b/tools/gator/debian/copyright
new file mode 100644
index 000000000000..3ed568c02e8a
--- /dev/null
+++ b/tools/gator/debian/copyright
@@ -0,0 +1,15 @@
+Files: *
+Copyright: 2010-2012 ARM Ltd.
+License: GPL-2
+ 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.
+ .
+ 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
+ with your system, in /usr/share/common-licenses/GPL-2. If not, see
+ <http://www.gnu.org/licenses/>.
diff --git a/tools/gator/debian/dkms.conf.in b/tools/gator/debian/dkms.conf.in
new file mode 100644
index 000000000000..5cb51e58a5ed
--- /dev/null
+++ b/tools/gator/debian/dkms.conf.in
@@ -0,0 +1,7 @@
+PACKAGE_NAME="gator-module"
+PACKAGE_VERSION="__VERSION__"
+MAKE[0]="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build"
+CLEAN="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean"
+BUILT_MODULE_NAME[0]="gator"
+DEST_MODULE_LOCATION[0]="/extra"
+AUTOINSTALL=yes
diff --git a/tools/gator/debian/gator-daemon.install b/tools/gator/debian/gator-daemon.install
new file mode 100644
index 000000000000..7b0d218c425d
--- /dev/null
+++ b/tools/gator/debian/gator-daemon.install
@@ -0,0 +1 @@
+daemon/gatord /usr/sbin/
diff --git a/tools/gator/debian/gator-daemon.postrm b/tools/gator/debian/gator-daemon.postrm
new file mode 100644
index 000000000000..97518f4e1a16
--- /dev/null
+++ b/tools/gator/debian/gator-daemon.postrm
@@ -0,0 +1,41 @@
+#!/bin/sh
+# postrm script for gator-daemon
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ purge)
+ rm -rf /var/lib/gatord/
+ ;;
+
+ remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/tools/gator/debian/gator-daemon.preinst b/tools/gator/debian/gator-daemon.preinst
new file mode 100644
index 000000000000..bb2dc7e5fe71
--- /dev/null
+++ b/tools/gator/debian/gator-daemon.preinst
@@ -0,0 +1,36 @@
+#!/bin/sh
+# preinst script for gator-daemon
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ install|upgrade)
+ mkdir -p /var/lib/gatord/
+ ;;
+
+ abort-upgrade)
+ ;;
+
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/tools/gator/debian/gator-daemon.upstart b/tools/gator/debian/gator-daemon.upstart
new file mode 100644
index 000000000000..1564efc53393
--- /dev/null
+++ b/tools/gator/debian/gator-daemon.upstart
@@ -0,0 +1,10 @@
+description "Load gator driver module and launch gator daemon"
+author "Pawel Moll <pawel.moll@arm.com>"
+
+start on filesystem
+
+exec /usr/sbin/gatord -c /var/lib/gatord/configuration.xml
+
+pre-start script
+ modprobe -q gator || true
+end script
diff --git a/tools/gator/debian/gator-module-dkms.install.in b/tools/gator/debian/gator-module-dkms.install.in
new file mode 100755
index 000000000000..4691ff785752
--- /dev/null
+++ b/tools/gator/debian/gator-module-dkms.install.in
@@ -0,0 +1,2 @@
+module/* /usr/src/gator-module-__VERSION__/
+debian/dkms.conf /usr/src/gator-module-__VERSION__/ \ No newline at end of file
diff --git a/tools/gator/debian/gator-module-dkms.postinst b/tools/gator/debian/gator-module-dkms.postinst
new file mode 100755
index 000000000000..b3de65e5b06c
--- /dev/null
+++ b/tools/gator/debian/gator-module-dkms.postinst
@@ -0,0 +1,49 @@
+#!/bin/sh
+# Copyright (C) 2002-2005 Flavio Stanchina
+# Copyright (C) 2005-2006 Aric Cyr
+# Copyright (C) 2007 Mario Limonciello
+# Copyright (C) 2009 Alberto Milone
+# Copyright (C) 2010 Ricardo Salveti de Araujo
+
+set -e
+
+NAME=gator-module
+PACKAGE_NAME=$NAME-dkms
+CVERSION=`dpkg-query -W -f='${Version}' $PACKAGE_NAME | awk -F "-" '{print $1}' | cut -d\: -f2`
+ARCH=`dpkg --print-architecture`
+
+dkms_configure () {
+ for POSTINST in /usr/lib/dkms/common.postinst "/usr/share/$PACKAGE_NAME/postinst"; do
+ if [ -f "$POSTINST" ]; then
+ "$POSTINST" "$NAME" "$CVERSION" "/usr/share/$PACKAGE_NAME" "$ARCH" "$2"
+ return $?
+ fi
+ echo "WARNING: $POSTINST does not exist." >&2
+ done
+ echo "ERROR: DKMS version is too old and $PACKAGE_NAME was not" >&2
+ echo "built with legacy DKMS support." >&2
+ echo "You must either rebuild $PACKAGE_NAME with legacy postinst" >&2
+ echo "support or upgrade DKMS to a more current version." >&2
+ return 1
+}
+
+case "$1" in
+ configure)
+ dkms_configure
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/tools/gator/debian/gator-module-dkms.prerm b/tools/gator/debian/gator-module-dkms.prerm
new file mode 100755
index 000000000000..cf0b5f99da76
--- /dev/null
+++ b/tools/gator/debian/gator-module-dkms.prerm
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+NAME=gator-module
+PACKAGE_NAME=$NAME-dkms
+VERSION=`dpkg-query -W -f='${Version}' $PACKAGE_NAME | awk -F "-" '{print $1}' | cut -d\: -f2`
+
+set -e
+
+case "$1" in
+ remove|upgrade|deconfigure)
+ if [ "`dkms status -m $NAME`" ]; then
+ dkms remove -m $NAME -v $VERSION --all
+ fi
+ ;;
+
+ failed-upgrade)
+ ;;
+
+ *)
+ echo "prerm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/tools/gator/debian/rules b/tools/gator/debian/rules
new file mode 100755
index 000000000000..c747748ff83c
--- /dev/null
+++ b/tools/gator/debian/rules
@@ -0,0 +1,15 @@
+#!/usr/bin/make -f
+
+DEB_HOST_GNU_TYPE = $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE = $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
+ CROSS_COMPILE ?= $(DEB_HOST_GNU_TYPE)-
+endif
+
+export CROSS_COMPILE
+
+export DH_OPTIONS
+
+%:
+ dh $@
diff --git a/tools/gator/debian/scripts/do-packaging b/tools/gator/debian/scripts/do-packaging
new file mode 100755
index 000000000000..1573d7e22f32
--- /dev/null
+++ b/tools/gator/debian/scripts/do-packaging
@@ -0,0 +1,104 @@
+#!/bin/bash
+
+set -e
+
+while [ $1 ]
+do
+ OPT=$1
+ shift
+ if [ "$OPT" == "--commit" ]
+ then
+ OPT_COMMIT=y
+ continue
+ fi
+ if [ "$OPT" == "--sign" ]
+ then
+ OPT_SIGN=y
+ continue
+ fi
+ if [ "$OPT" == "--no-git-clean" ]
+ then
+ OPT_NO_GIT_CLEAN=y
+ continue
+ fi
+ echo $0: Unknown option: "$OPT" 1>&2
+ exit 1
+done
+
+
+#
+# Clean working dirctory
+#
+
+if [ ! $OPT_NO_GIT_CLEAN ]
+then
+ git reset --hard HEAD
+ git clean -dfx ../..
+fi
+git checkout debian/changelog
+
+if [ ! -d module ]
+then
+ mkdir module
+ cp -a ../../drivers/gator/* module/
+fi
+
+#
+# Update changelog
+#
+
+if [ "$DEBEMAIL" == "" -o "$DEBFULLNAME" == "" ]
+then
+ export DEBEMAIL='jon.medhurst@linaro.org'
+ export DEBFULLNAME='Jon Medhurst (Tixy)'
+fi
+dch -i -c debian/changelog CHANGE_MSG
+dch -r -c debian/changelog ""
+
+TIMESTAMP="$(date -u +%Y%m%d%H%M%S)"
+sed -i -e "1 s/ubuntu1/-$TIMESTAMP/" debian/changelog
+
+GIT_COMMIT="$(git rev-parse HEAD)"
+GIT_REMOTE_AND_BRANCH="$(git show-ref | sed -n 's,'$GIT_COMMIT'.*refs/remotes/\(.*\),\1,p' | sed -n 1p)"
+GIT_REMOTE=$(echo $GIT_REMOTE_AND_BRANCH | awk -F/ '{print $1}')
+GIT_BRANCH=$(echo $GIT_REMOTE_AND_BRANCH | awk -F/ '{print $2}')
+GIT_URL="$(git remote -v show $GIT_REMOTE | sed -n 's/.*Fetch URL: \(.*\)/\1/p')"
+GIT_TAG="$(git describe --exact-match --tags $GIT_COMMIT 2>/dev/null || echo "")"
+
+CHANGE_MSG="Automatically packaged from git repository:\n"
+CHANGE_MSG="$CHANGE_MSG URL: $GIT_URL\n"
+CHANGE_MSG="$CHANGE_MSG Tag: $GIT_TAG\n"
+CHANGE_MSG="$CHANGE_MSG Branch: $GIT_BRANCH\n"
+CHANGE_MSG="$CHANGE_MSG Commit: $GIT_COMMIT"
+sed -i -e "s%CHANGE_MSG%$CHANGE_MSG%" debian/changelog
+
+echo $CHANGE_MSG
+
+FULL_VERSION="$(sed -n '1s/.*(\(.*\)).*/\1/p' debian/changelog)"
+VERSION="$(echo $FULL_VERSION | sed 's/\([0-9.]*\).*/\1/')"
+sed "s/__VERSION__/$VERSION/g" debian/dkms.conf.in > debian/dkms.conf
+sed "s/__VERSION__/$VERSION/g" debian/gator-module-dkms.install.in > debian/gator-module-dkms.install
+
+
+#
+# Commit changes
+#
+
+if [ $OPT_COMMIT ]
+then
+ git add debian/changelog
+ git commit -s -m "gator-$FULL_VERSION: Automated packaging"
+fi
+
+
+#
+# Build packages
+#
+
+if [ ! $OPT_SIGN ]
+then
+ DEBUILD_SIGN="-us -uc"
+fi
+
+debuild $DEBUILD_SIGN -aarmhf -b
+debuild --no-tgz-check $DEBUILD_SIGN -S
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 2240974b7745..395c4c8a014b 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -771,13 +771,13 @@ LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch])
# already
$(LIBAPIKFS): $(LIBAPIKFS_SOURCES)
ifeq ($(subdir),)
- $(QUIET_SUBDIR0)$(LIB_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libapikfs.a
+ $(QUIET_SUBDIR0)$(LIB_DIR) $(QUIET_SUBDIR1) CROSS_COMPILE=$(CROSS_COMPILE) O=$(OUTPUT) libapikfs.a
endif
$(LIBAPIKFS)-clean:
ifeq ($(subdir),)
$(call QUIET_CLEAN, libapikfs)
- @$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null
+ @$(MAKE) -C $(LIB_DIR) CROSS_COMPILE=$(CROSS_COMPILE) O=$(OUTPUT) clean >/dev/null
endif
help: