aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2013-05-08 15:55:50 -0700
committerJohn Stultz <john.stultz@linaro.org>2013-05-08 15:55:50 -0700
commit5cf418c983653d0cbb450ab10cad0ef9d5383adb (patch)
treee0ffee89d139e1464d039d3f4dacabaa44ef9540
parentc7de5f559828d44cbd0b7ff8035bfa0a0b7cf8f2 (diff)
parente0fd9affeb64088eff407dfc98bbd3a5c17ea479 (diff)
Merge remote-tracking branch 'linus/master' into linaro-android-3.10-experimental-merge
Conflicts: drivers/leds/Kconfig drivers/leds/Makefile fs/fuse/dev.c
-rw-r--r--Documentation/ABI/testing/sysfs-block-bcache156
-rw-r--r--Documentation/acpi/enumeration.txt32
-rw-r--r--Documentation/bcache.txt431
-rw-r--r--Documentation/block/cfq-iosched.txt47
-rw-r--r--Documentation/cgroups/memory.txt4
-rw-r--r--Documentation/coccinelle.txt11
-rw-r--r--Documentation/devicetree/bindings/arm/omap/l3-noc.txt1
-rw-r--r--Documentation/devicetree/bindings/arm/omap/timer.txt17
-rw-r--r--Documentation/devicetree/bindings/arm/primecell.txt19
-rw-r--r--Documentation/devicetree/bindings/arm/samsung/sysreg.txt7
-rw-r--r--Documentation/devicetree/bindings/ata/pata-arasan.txt22
-rw-r--r--Documentation/devicetree/bindings/clock/imx5-clock.txt14
-rw-r--r--Documentation/devicetree/bindings/clock/imx6q-clock.txt3
-rw-r--r--Documentation/devicetree/bindings/dma/fsl-mxs-dma.txt49
-rw-r--r--Documentation/devicetree/bindings/fb/mxsfb.txt36
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-grgpio.txt26
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt47
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-omap.txt15
-rw-r--r--Documentation/devicetree/bindings/gpu/samsung-g2d.txt20
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-mxs.txt12
-rw-r--r--Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.txt60
-rw-r--r--Documentation/devicetree/bindings/mmc/mxs-mmc.txt12
-rw-r--r--Documentation/devicetree/bindings/mtd/gpmi-nand.txt17
-rw-r--r--Documentation/devicetree/bindings/pinctrl/fsl,mxs-pinctrl.txt4
-rw-r--r--Documentation/devicetree/bindings/pwm/pwm-samsung.txt43
-rw-r--r--Documentation/devicetree/bindings/pwm/pwm-tiecap.txt12
-rw-r--r--Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt12
-rw-r--r--Documentation/devicetree/bindings/reset/fsl,imx-src.txt49
-rw-r--r--Documentation/devicetree/bindings/serial/pl011.txt17
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt8
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt7
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt8
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt8
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt8
-rw-r--r--Documentation/devicetree/bindings/spi/mxs-spi.txt12
-rw-r--r--Documentation/devicetree/bindings/spi/spi-davinci.txt51
-rw-r--r--Documentation/devicetree/bindings/spi/spi_pl022.txt36
-rw-r--r--Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt3
-rw-r--r--Documentation/devicetree/bindings/timer/arm,sp804.txt29
-rw-r--r--Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt16
-rw-r--r--Documentation/devicetree/bindings/usb/exynos-usb.txt10
-rw-r--r--Documentation/devicetree/bindings/usb/omap-usb.txt1
-rw-r--r--Documentation/devicetree/bindings/video/samsung-fimd.txt65
-rw-r--r--Documentation/filesystems/f2fs.txt4
-rw-r--r--Documentation/kbuild/kconfig.txt36
-rw-r--r--Documentation/kbuild/makefiles.txt7
-rw-r--r--Documentation/kernel-parameters.txt14
-rw-r--r--Documentation/leds/00-INDEX2
-rw-r--r--Documentation/leds/leds-lp5521.txt19
-rw-r--r--Documentation/leds/leds-lp5562.txt120
-rw-r--r--Documentation/leds/leds-lp55xx.txt46
-rw-r--r--MAINTAINERS19
-rw-r--r--Makefile2
-rw-r--r--arch/arm/Kconfig8
-rw-r--r--arch/arm/Kconfig.debug4
-rw-r--r--arch/arm/boot/dts/Makefile15
-rw-r--r--arch/arm/boot/dts/am335x-bone.dts10
-rw-r--r--arch/arm/boot/dts/am335x-evm.dts18
-rw-r--r--arch/arm/boot/dts/am335x-evmsk.dts18
-rw-r--r--arch/arm/boot/dts/am33xx.dtsi40
-rw-r--r--arch/arm/boot/dts/am3517-evm.dts2
-rw-r--r--arch/arm/boot/dts/am3517_mt_ventoux.dts2
-rw-r--r--arch/arm/boot/dts/armada-370-db.dts134
-rw-r--r--arch/arm/boot/dts/armada-370-mirabox.dts176
-rw-r--r--arch/arm/boot/dts/armada-370-rd.dts96
-rw-r--r--arch/arm/boot/dts/armada-370-xp.dtsi341
-rw-r--r--arch/arm/boot/dts/armada-370.dtsi330
-rw-r--r--arch/arm/boot/dts/armada-xp-db.dts202
-rw-r--r--arch/arm/boot/dts/armada-xp-gp.dts222
-rw-r--r--arch/arm/boot/dts/armada-xp-mv78230.dtsi284
-rw-r--r--arch/arm/boot/dts/armada-xp-mv78260.dtsi342
-rw-r--r--arch/arm/boot/dts/armada-xp-mv78460.dtsi484
-rw-r--r--arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts262
-rw-r--r--arch/arm/boot/dts/armada-xp.dtsi202
-rw-r--r--arch/arm/boot/dts/at91sam9g45.dtsi5
-rw-r--r--arch/arm/boot/dts/at91sam9n12.dtsi9
-rw-r--r--arch/arm/boot/dts/at91sam9x5.dtsi15
-rw-r--r--arch/arm/boot/dts/cros5250-common.dtsi54
-rw-r--r--arch/arm/boot/dts/da850-evm.dts40
-rw-r--r--arch/arm/boot/dts/da850.dtsi94
-rw-r--r--arch/arm/boot/dts/exynos4.dtsi40
-rw-r--r--arch/arm/boot/dts/exynos4210-origen.dts4
-rw-r--r--arch/arm/boot/dts/exynos4210-smdkv310.dts57
-rw-r--r--arch/arm/boot/dts/exynos4210-universal_c210.dts352
-rw-r--r--arch/arm/boot/dts/exynos4210.dtsi8
-rw-r--r--arch/arm/boot/dts/exynos4212.dtsi9
-rw-r--r--arch/arm/boot/dts/exynos4412-origen.dts21
-rw-r--r--arch/arm/boot/dts/exynos4412-smdk4412.dts8
-rw-r--r--arch/arm/boot/dts/exynos4412.dtsi17
-rw-r--r--arch/arm/boot/dts/exynos4x12.dtsi7
-rw-r--r--arch/arm/boot/dts/exynos5250-arndale.dts343
-rw-r--r--arch/arm/boot/dts/exynos5250-pinctrl.dtsi783
-rw-r--r--arch/arm/boot/dts/exynos5250-smdk5250.dts49
-rw-r--r--arch/arm/boot/dts/exynos5250-snow.dts149
-rw-r--r--arch/arm/boot/dts/exynos5250.dtsi348
-rw-r--r--arch/arm/boot/dts/exynos5440-sd5v1.dts39
-rw-r--r--arch/arm/boot/dts/exynos5440-ssdk5440.dts6
-rw-r--r--arch/arm/boot/dts/exynos5440.dtsi55
-rw-r--r--arch/arm/boot/dts/imx23-evk.dts36
-rw-r--r--arch/arm/boot/dts/imx23-olinuxino.dts1
-rw-r--r--arch/arm/boot/dts/imx23.dtsi42
-rw-r--r--arch/arm/boot/dts/imx28-apf28dev.dts25
-rw-r--r--arch/arm/boot/dts/imx28-apx4devkit.dts25
-rw-r--r--arch/arm/boot/dts/imx28-cfa10049.dts50
-rw-r--r--arch/arm/boot/dts/imx28-evk.dts36
-rw-r--r--arch/arm/boot/dts/imx28-m28evk.dts27
-rw-r--r--arch/arm/boot/dts/imx28.dtsi62
-rw-r--r--arch/arm/boot/dts/imx6qdl.dtsi8
-rw-r--r--arch/arm/boot/dts/integratorcp.dts6
-rw-r--r--arch/arm/boot/dts/omap2.dtsi39
-rw-r--r--arch/arm/boot/dts/omap2420-h4.dts46
-rw-r--r--arch/arm/boot/dts/omap2420.dtsi67
-rw-r--r--arch/arm/boot/dts/omap2430.dtsi87
-rw-r--r--arch/arm/boot/dts/omap3-beagle-xm.dts26
-rw-r--r--arch/arm/boot/dts/omap3-beagle.dts8
-rw-r--r--arch/arm/boot/dts/omap3-devkit8000.dts169
-rw-r--r--arch/arm/boot/dts/omap3-evm.dts14
-rw-r--r--arch/arm/boot/dts/omap3-igep.dtsi122
-rw-r--r--arch/arm/boot/dts/omap3-igep0020.dts56
-rw-r--r--arch/arm/boot/dts/omap3-igep0030.dts44
-rw-r--r--arch/arm/boot/dts/omap3-overo.dtsi31
-rw-r--r--arch/arm/boot/dts/omap3.dtsi142
-rw-r--r--arch/arm/boot/dts/omap3430-sdp.dts190
-rw-r--r--arch/arm/boot/dts/omap34xx.dtsi28
-rw-r--r--arch/arm/boot/dts/omap36xx.dtsi13
-rw-r--r--arch/arm/boot/dts/omap4-panda-a4.dts5
-rw-r--r--arch/arm/boot/dts/omap4-panda-common.dtsi251
-rw-r--r--arch/arm/boot/dts/omap4-panda-es.dts5
-rw-r--r--arch/arm/boot/dts/omap4-panda.dts201
-rw-r--r--arch/arm/boot/dts/omap4-sdp.dts74
-rw-r--r--arch/arm/boot/dts/omap4-var-som.dts2
-rw-r--r--arch/arm/boot/dts/omap4.dtsi135
-rw-r--r--arch/arm/boot/dts/omap443x.dtsi27
-rw-r--r--arch/arm/boot/dts/omap4460.dtsi32
-rw-r--r--arch/arm/boot/dts/omap5-evm.dts110
-rw-r--r--arch/arm/boot/dts/omap5.dtsi245
-rw-r--r--arch/arm/boot/dts/r8a73a4-ape6evm.dts52
-rw-r--r--arch/arm/boot/dts/r8a73a4.dtsi94
-rw-r--r--arch/arm/boot/dts/r8a7778-bockw.dts32
-rw-r--r--arch/arm/boot/dts/r8a7778.dtsi35
-rw-r--r--arch/arm/boot/dts/r8a7790-lager.dts31
-rw-r--r--arch/arm/boot/dts/r8a7790.dtsi63
-rw-r--r--arch/arm/boot/dts/sama5d3.dtsi19
-rw-r--r--arch/arm/boot/dts/sama5d34ek.dts2
-rw-r--r--arch/arm/boot/dts/sh73a0-kzm9g-reference.dts13
-rw-r--r--arch/arm/boot/dts/sh73a0.dtsi81
-rw-r--r--arch/arm/boot/dts/spear1340.dtsi3
-rw-r--r--arch/arm/boot/dts/spear13xx.dtsi25
-rw-r--r--arch/arm/boot/dts/tegra114-dalmore.dts891
-rw-r--r--arch/arm/boot/dts/tegra114.dtsi240
-rw-r--r--arch/arm/boot/dts/tegra20-colibri-512.dtsi12
-rw-r--r--arch/arm/boot/dts/tegra20-harmony.dts20
-rw-r--r--arch/arm/boot/dts/tegra20-medcom-wide.dts7
-rw-r--r--arch/arm/boot/dts/tegra20-paz00.dts10
-rw-r--r--arch/arm/boot/dts/tegra20-plutux.dts3
-rw-r--r--arch/arm/boot/dts/tegra20-seaboard.dts11
-rw-r--r--arch/arm/boot/dts/tegra20-tamonten.dtsi6
-rw-r--r--arch/arm/boot/dts/tegra20-tec.dts3
-rw-r--r--arch/arm/boot/dts/tegra20-trimslice.dts23
-rw-r--r--arch/arm/boot/dts/tegra20-ventana.dts22
-rw-r--r--arch/arm/boot/dts/tegra20-whistler.dts13
-rw-r--r--arch/arm/boot/dts/tegra20.dtsi53
-rw-r--r--arch/arm/boot/dts/tegra30-beaver.dts8
-rw-r--r--arch/arm/boot/dts/tegra30-cardhu-a02.dts1
-rw-r--r--arch/arm/boot/dts/tegra30-cardhu-a04.dts1
-rw-r--r--arch/arm/boot/dts/tegra30-cardhu.dtsi11
-rw-r--r--arch/arm/boot/dts/tegra30.dtsi1
-rw-r--r--arch/arm/boot/dts/twl4030.dtsi18
-rw-r--r--arch/arm/boot/dts/twl6030.dtsi12
-rw-r--r--arch/arm/boot/dts/versatile-ab.dts12
-rw-r--r--arch/arm/boot/dts/vexpress-v2p-ca9.dts1
-rw-r--r--arch/arm/boot/dts/xenvm-4.2.dts13
-rw-r--r--arch/arm/boot/dts/zynq-7000.dtsi7
-rw-r--r--arch/arm/common/timer-sp.c140
-rw-r--r--arch/arm/configs/bockw_defconfig94
-rw-r--r--arch/arm/configs/imx_v4_v5_defconfig1
-rw-r--r--arch/arm/configs/imx_v6_v7_defconfig1
-rw-r--r--arch/arm/include/asm/arch_timer.h13
-rw-r--r--arch/arm/include/asm/hardware/timer-sp.h16
-rw-r--r--arch/arm/include/asm/sched_clock.h2
-rw-r--r--arch/arm/include/asm/xen/hypercall.h1
-rw-r--r--arch/arm/include/debug/mvebu.S2
-rw-r--r--arch/arm/kernel/arch_timer.c29
-rw-r--r--arch/arm/kernel/sched_clock.c15
-rw-r--r--arch/arm/kernel/time.c7
-rw-r--r--arch/arm/mach-at91/cpuidle.c2
-rw-r--r--arch/arm/mach-at91/include/mach/cpu.h9
-rw-r--r--arch/arm/mach-at91/pm.c2
-rw-r--r--arch/arm/mach-at91/pm.h30
-rw-r--r--arch/arm/mach-at91/setup.c22
-rw-r--r--arch/arm/mach-davinci/da8xx-dt.c6
-rw-r--r--arch/arm/mach-dove/Makefile2
-rw-r--r--arch/arm/mach-dove/addr-map.c125
-rw-r--r--arch/arm/mach-dove/board-dt.c2
-rw-r--r--arch/arm/mach-dove/common.c39
-rw-r--r--arch/arm/mach-dove/common.h2
-rw-r--r--arch/arm/mach-dove/include/mach/dove.h5
-rw-r--r--arch/arm/mach-exynos/common.c88
-rw-r--r--arch/arm/mach-exynos/common.h7
-rw-r--r--arch/arm/mach-exynos/include/mach/map.h1
-rw-r--r--arch/arm/mach-highbank/highbank.c24
-rw-r--r--arch/arm/mach-imx/Kconfig10
-rw-r--r--arch/arm/mach-imx/Makefile1
-rw-r--r--arch/arm/mach-imx/anatop.c103
-rw-r--r--arch/arm/mach-imx/clk-imx51-imx53.c75
-rw-r--r--arch/arm/mach-imx/clk-imx6q.c126
-rw-r--r--arch/arm/mach-imx/clk.h17
-rw-r--r--arch/arm/mach-imx/common.h10
-rw-r--r--arch/arm/mach-imx/gpc.c23
-rw-r--r--arch/arm/mach-imx/mach-imx6q.c74
-rw-r--r--arch/arm/mach-imx/mm-imx1.c2
-rw-r--r--arch/arm/mach-imx/mxc.h11
-rw-r--r--arch/arm/mach-imx/platsmp.c4
-rw-r--r--arch/arm/mach-imx/pm-imx6q.c4
-rw-r--r--arch/arm/mach-imx/src.c66
-rw-r--r--arch/arm/mach-integrator/integrator_cp.c34
-rw-r--r--arch/arm/mach-kirkwood/Makefile2
-rw-r--r--arch/arm/mach-kirkwood/addr-map.c91
-rw-r--r--arch/arm/mach-kirkwood/board-dt.c2
-rw-r--r--arch/arm/mach-kirkwood/common.c38
-rw-r--r--arch/arm/mach-kirkwood/common.h2
-rw-r--r--arch/arm/mach-kirkwood/include/mach/kirkwood.h7
-rw-r--r--arch/arm/mach-kirkwood/pcie.c1
-rw-r--r--arch/arm/mach-msm/last_radio_log.c2
-rw-r--r--arch/arm/mach-mv78xx0/Makefile2
-rw-r--r--arch/arm/mach-mv78xx0/addr-map.c93
-rw-r--r--arch/arm/mach-mv78xx0/common.c10
-rw-r--r--arch/arm/mach-mv78xx0/include/mach/mv78xx0.h9
-rw-r--r--arch/arm/mach-mv78xx0/pcie.c21
-rw-r--r--arch/arm/mach-mvebu/Kconfig2
-rw-r--r--arch/arm/mach-mvebu/Makefile2
-rw-r--r--arch/arm/mach-mvebu/addr-map.c137
-rw-r--r--arch/arm/mach-mvebu/armada-370-xp.c27
-rw-r--r--arch/arm/mach-mvebu/armada-370-xp.h8
-rw-r--r--arch/arm/mach-mvebu/platsmp.c2
-rw-r--r--arch/arm/mach-mxs/mach-mxs.c170
-rw-r--r--arch/arm/mach-omap2/board-2430sdp.c43
-rw-r--r--arch/arm/mach-omap2/board-3430sdp.c53
-rw-r--r--arch/arm/mach-omap2/board-4430sdp.c2
-rw-r--r--arch/arm/mach-omap2/board-am3517evm.c74
-rw-r--r--arch/arm/mach-omap2/board-cm-t35.c60
-rw-r--r--arch/arm/mach-omap2/board-devkit8000.c27
-rw-r--r--arch/arm/mach-omap2/board-generic.c1
-rw-r--r--arch/arm/mach-omap2/board-ldp.c61
-rw-r--r--arch/arm/mach-omap2/board-omap3evm.c70
-rw-r--r--arch/arm/mach-omap2/board-omap3pandora.c7
-rw-r--r--arch/arm/mach-omap2/board-omap3stalker.c11
-rw-r--r--arch/arm/mach-omap2/board-omap4panda.c2
-rw-r--r--arch/arm/mach-omap2/board-overo.c58
-rw-r--r--arch/arm/mach-omap2/board-rx51-video.c26
-rw-r--r--arch/arm/mach-omap2/board-zoom-display.c38
-rw-r--r--arch/arm/mach-omap2/dss-common.c54
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.c48
-rw-r--r--arch/arm/mach-omap2/pmu.c14
-rw-r--r--arch/arm/mach-omap2/timer.c18
-rw-r--r--arch/arm/mach-orion5x/Makefile2
-rw-r--r--arch/arm/mach-orion5x/addr-map.c155
-rw-r--r--arch/arm/mach-orion5x/board-dt.c2
-rw-r--r--arch/arm/mach-orion5x/common.c49
-rw-r--r--arch/arm/mach-orion5x/common.h13
-rw-r--r--arch/arm/mach-orion5x/d2net-setup.c4
-rw-r--r--arch/arm/mach-orion5x/db88f5281-setup.c13
-rw-r--r--arch/arm/mach-orion5x/dns323-setup.c3
-rw-r--r--arch/arm/mach-orion5x/edmini_v2-setup.c4
-rw-r--r--arch/arm/mach-orion5x/include/mach/orion5x.h6
-rw-r--r--arch/arm/mach-orion5x/kurobox_pro-setup.c8
-rw-r--r--arch/arm/mach-orion5x/ls-chl-setup.c4
-rw-r--r--arch/arm/mach-orion5x/ls_hgl-setup.c4
-rw-r--r--arch/arm/mach-orion5x/lsmini-setup.c4
-rw-r--r--arch/arm/mach-orion5x/mss2-setup.c3
-rw-r--r--arch/arm/mach-orion5x/mv2120-setup.c3
-rw-r--r--arch/arm/mach-orion5x/net2big-setup.c4
-rw-r--r--arch/arm/mach-orion5x/pci.c7
-rw-r--r--arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c4
-rw-r--r--arch/arm/mach-orion5x/rd88f5181l-ge-setup.c4
-rw-r--r--arch/arm/mach-orion5x/rd88f5182-setup.c7
-rw-r--r--arch/arm/mach-orion5x/terastation_pro2-setup.c4
-rw-r--r--arch/arm/mach-orion5x/ts209-setup.c4
-rw-r--r--arch/arm/mach-orion5x/ts409-setup.c4
-rw-r--r--arch/arm/mach-orion5x/wnr854t-setup.c4
-rw-r--r--arch/arm/mach-orion5x/wrt350n-v2-setup.c4
-rw-r--r--arch/arm/mach-shmobile/Kconfig48
-rw-r--r--arch/arm/mach-shmobile/Makefile6
-rw-r--r--arch/arm/mach-shmobile/board-ape6evm.c94
-rw-r--r--arch/arm/mach-shmobile/board-armadillo800eva.c35
-rw-r--r--arch/arm/mach-shmobile/board-bockw.c81
-rw-r--r--arch/arm/mach-shmobile/board-kzm9d.c1
-rw-r--r--arch/arm/mach-shmobile/board-lager.c46
-rw-r--r--arch/arm/mach-shmobile/board-mackerel.c111
-rw-r--r--arch/arm/mach-shmobile/board-marzen.c32
-rw-r--r--arch/arm/mach-shmobile/clock-r8a73a4.c115
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7740.c114
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7778.c107
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7779.c219
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7790.c93
-rw-r--r--arch/arm/mach-shmobile/clock-sh7372.c50
-rw-r--r--arch/arm/mach-shmobile/clock-sh73a0.c115
-rw-r--r--arch/arm/mach-shmobile/clock.c13
-rw-r--r--arch/arm/mach-shmobile/include/mach/clock.h39
-rw-r--r--arch/arm/mach-shmobile/include/mach/common.h54
-rw-r--r--arch/arm/mach-shmobile/include/mach/r8a73a4.h8
-rw-r--r--arch/arm/mach-shmobile/include/mach/r8a7740.h9
-rw-r--r--arch/arm/mach-shmobile/include/mach/r8a7778.h32
-rw-r--r--arch/arm/mach-shmobile/include/mach/r8a7779.h332
-rw-r--r--arch/arm/mach-shmobile/include/mach/r8a7790.h9
-rw-r--r--arch/arm/mach-shmobile/include/mach/sh7372.h12
-rw-r--r--arch/arm/mach-shmobile/include/mach/sh73a0.h15
-rw-r--r--arch/arm/mach-shmobile/intc-r8a7740.c641
-rw-r--r--arch/arm/mach-shmobile/setup-emev2.c1
-rw-r--r--arch/arm/mach-shmobile/setup-r8a73a4.c202
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7740.c193
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7778.c244
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7779.c81
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7790.c150
-rw-r--r--arch/arm/mach-shmobile/setup-sh7372.c1
-rw-r--r--arch/arm/mach-shmobile/setup-sh73a0.c1
-rw-r--r--arch/arm/mach-shmobile/timer.c7
-rw-r--r--arch/arm/mach-spear/Makefile6
-rw-r--r--arch/arm/mach-spear/generic.h5
-rw-r--r--arch/arm/mach-spear/include/mach/spear.h2
-rw-r--r--arch/arm/mach-spear/spear1310.c30
-rw-r--r--arch/arm/mach-spear/spear1340.c32
-rw-r--r--arch/arm/mach-spear/spear13xx-dma.h128
-rw-r--r--arch/arm/mach-spear/spear13xx.c60
-rw-r--r--arch/arm/mach-versatile/core.c26
-rw-r--r--arch/arm/mach-versatile/versatile_dt.c1
-rw-r--r--arch/arm/mach-vexpress/v2m.c26
-rw-r--r--arch/arm/mach-virt/virt.c10
-rw-r--r--arch/arm/mach-zynq/Kconfig1
-rw-r--r--arch/arm/mach-zynq/Makefile6
-rw-r--r--arch/arm/mach-zynq/common.c67
-rw-r--r--arch/arm/mach-zynq/common.h20
-rw-r--r--arch/arm/mach-zynq/headsmp.S24
-rw-r--r--arch/arm/mach-zynq/hotplug.c104
-rw-r--r--arch/arm/mach-zynq/platsmp.c136
-rw-r--r--arch/arm/mach-zynq/slcr.c125
-rw-r--r--arch/arm/plat-omap/dmtimer.c241
-rw-r--r--arch/arm/plat-omap/include/plat/dmtimer.h1
-rw-r--r--arch/arm/plat-orion/Makefile6
-rw-r--r--arch/arm/plat-orion/addr-map.c178
-rw-r--r--arch/arm/plat-orion/gpio.c59
-rw-r--r--arch/arm/plat-samsung/Kconfig4
-rw-r--r--arch/arm/xen/enlighten.c74
-rw-r--r--arch/arm/xen/hypercall.S1
-rw-r--r--arch/arm64/Kconfig4
-rw-r--r--arch/arm64/boot/dts/foundation-v8.dts2
-rw-r--r--arch/arm64/include/asm/arch_timer.h5
-rw-r--r--arch/arm64/include/asm/system_misc.h2
-rw-r--r--arch/arm64/kernel/process.c8
-rw-r--r--arch/arm64/kernel/time.c6
-rw-r--r--arch/arm64/lib/bitops.S10
-rw-r--r--arch/arm64/mm/fault.c3
-rw-r--r--arch/cris/kernel/profile.c2
-rw-r--r--arch/hexagon/Kconfig2
-rw-r--r--arch/hexagon/kernel/vm_entry.S12
-rw-r--r--arch/parisc/Kconfig9
-rw-r--r--arch/parisc/Kconfig.debug11
-rw-r--r--arch/parisc/Makefile4
-rw-r--r--arch/parisc/include/asm/atomic.h23
-rw-r--r--arch/parisc/include/asm/dma-mapping.h3
-rw-r--r--arch/parisc/include/asm/hardirq.h32
-rw-r--r--arch/parisc/include/asm/processor.h20
-rw-r--r--arch/parisc/include/asm/thread_info.h2
-rw-r--r--arch/parisc/include/asm/tlbflush.h2
-rw-r--r--arch/parisc/kernel/cache.c6
-rw-r--r--arch/parisc/kernel/entry.S68
-rw-r--r--arch/parisc/kernel/hpmc.S4
-rw-r--r--arch/parisc/kernel/irq.c104
-rw-r--r--arch/parisc/kernel/pacache.S33
-rw-r--r--arch/parisc/kernel/setup.c2
-rw-r--r--arch/parisc/kernel/smp.c14
-rw-r--r--arch/parisc/kernel/syscall.S34
-rw-r--r--arch/parisc/kernel/traps.c24
-rw-r--r--arch/parisc/kernel/vmlinux.lds.S2
-rw-r--r--arch/parisc/mm/init.c2
-rw-r--r--arch/powerpc/include/asm/cputable.h2
-rw-r--r--arch/powerpc/include/asm/hw_irq.h16
-rw-r--r--arch/powerpc/include/asm/machdep.h3
-rw-r--r--arch/powerpc/include/asm/pci-bridge.h8
-rw-r--r--arch/powerpc/include/asm/ppc-opcode.h4
-rw-r--r--arch/powerpc/include/uapi/asm/cputable.h9
-rw-r--r--arch/powerpc/kernel/cputable.c14
-rw-r--r--arch/powerpc/kernel/pci-common.c101
-rw-r--r--arch/powerpc/kernel/pci_32.c2
-rw-r--r--arch/powerpc/kernel/pci_64.c2
-rw-r--r--arch/powerpc/kernel/sysfs.c2
-rw-r--r--arch/powerpc/kernel/traps.c10
-rw-r--r--arch/powerpc/mm/hash_utils_64.c1
-rw-r--r--arch/powerpc/platforms/cell/iommu.c2
-rw-r--r--arch/powerpc/platforms/cell/spu_base.c2
-rw-r--r--arch/powerpc/platforms/embedded6xx/mpc10x.h11
-rw-r--r--arch/powerpc/platforms/powermac/pci.c2
-rw-r--r--arch/powerpc/platforms/powernv/opal.c15
-rw-r--r--arch/powerpc/platforms/powernv/pci-ioda.c12
-rw-r--r--arch/powerpc/platforms/powernv/smp.c4
-rw-r--r--arch/powerpc/platforms/pseries/msi.c21
-rw-r--r--arch/powerpc/platforms/pseries/pci.c53
-rw-r--r--arch/powerpc/platforms/pseries/pseries.h4
-rw-r--r--arch/powerpc/platforms/pseries/setup.c2
-rw-r--r--arch/powerpc/platforms/wsp/wsp_pci.c2
-rw-r--r--arch/powerpc/sysdev/fsl_pci.c11
-rw-r--r--arch/powerpc/sysdev/mpic.c14
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.c15
-rw-r--r--arch/s390/hypfs/inode.c1
-rw-r--r--arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c2
-rw-r--r--arch/sparc/kernel/leon_smp.c15
-rw-r--r--arch/sparc/mm/init_32.c37
-rw-r--r--arch/sparc/mm/init_64.c28
-rw-r--r--arch/um/drivers/ubd_kern.c5
-rw-r--r--arch/x86/include/asm/irq_remapping.h9
-rw-r--r--arch/x86/include/asm/rwsem.h28
-rw-r--r--arch/x86/kernel/early-quirks.c20
-rw-r--r--arch/xtensa/platforms/iss/simdisk.c3
-rw-r--r--block/blk-cgroup.c4
-rw-r--r--block/blk-core.c265
-rw-r--r--block/cfq-iosched.c7
-rw-r--r--block/deadline-iosched.c2
-rw-r--r--block/elevator.c26
-rw-r--r--block/partitions/efi.c4
-rw-r--r--block/scsi_ioctl.c1
-rw-r--r--drivers/ata/pata_arasan_cf.c37
-rw-r--r--drivers/block/amiflop.c3
-rw-r--r--drivers/block/aoe/aoeblk.c6
-rw-r--r--drivers/block/aoe/aoecmd.c6
-rw-r--r--drivers/block/ataflop.c5
-rw-r--r--drivers/block/brd.c3
-rw-r--r--drivers/block/cciss.c27
-rw-r--r--drivers/block/cpqarray.c6
-rw-r--r--drivers/block/drbd/drbd_actlog.c246
-rw-r--r--drivers/block/drbd/drbd_bitmap.c13
-rw-r--r--drivers/block/drbd/drbd_int.h179
-rw-r--r--drivers/block/drbd/drbd_main.c256
-rw-r--r--drivers/block/drbd/drbd_nl.c200
-rw-r--r--drivers/block/drbd/drbd_proc.c10
-rw-r--r--drivers/block/drbd/drbd_receiver.c16
-rw-r--r--drivers/block/drbd/drbd_req.c192
-rw-r--r--drivers/block/drbd/drbd_req.h8
-rw-r--r--drivers/block/drbd/drbd_state.c28
-rw-r--r--drivers/block/drbd/drbd_strings.c1
-rw-r--r--drivers/block/drbd/drbd_worker.c24
-rw-r--r--drivers/block/floppy.c5
-rw-r--r--drivers/block/loop.c6
-rw-r--r--drivers/block/mg_disk.c2
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c79
-rw-r--r--drivers/block/mtip32xx/mtip32xx.h11
-rw-r--r--drivers/block/paride/pcd.c3
-rw-r--r--drivers/block/paride/pd.c4
-rw-r--r--drivers/block/paride/pf.c9
-rw-r--r--drivers/block/pktcdvd.c106
-rw-r--r--drivers/block/rbd.c6
-rw-r--r--drivers/block/swim.c4
-rw-r--r--drivers/block/swim3.c5
-rw-r--r--drivers/block/xen-blkfront.c3
-rw-r--r--drivers/block/xsysace.c3
-rw-r--r--drivers/block/z2ram.c6
-rw-r--r--drivers/bus/Kconfig7
-rw-r--r--drivers/bus/Makefile1
-rw-r--r--drivers/bus/mvebu-mbus.c870
-rw-r--r--drivers/cdrom/gdrom.c3
-rw-r--r--drivers/char/mem.c36
-rw-r--r--drivers/clk/samsung/clk-exynos4.c93
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c1
-rw-r--r--drivers/clk/samsung/clk-exynos5440.c1
-rw-r--r--drivers/clk/samsung/clk.h2
-rw-r--r--drivers/clocksource/Kconfig10
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/arm_arch_timer.c33
-rw-r--r--drivers/clocksource/exynos_mct.c22
-rw-r--r--drivers/clocksource/samsung_pwm_timer.c494
-rw-r--r--drivers/dma/mxs-dma.c109
-rw-r--r--drivers/gpio/Kconfig17
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpio-74x164.c8
-rw-r--r--drivers/gpio/gpio-adp5520.c4
-rw-r--r--drivers/gpio/gpio-em.c94
-rw-r--r--drivers/gpio/gpio-generic.c62
-rw-r--r--drivers/gpio/gpio-grgpio.c505
-rw-r--r--drivers/gpio/gpio-ich.c17
-rw-r--r--drivers/gpio/gpio-lpc32xx.c2
-rw-r--r--drivers/gpio/gpio-lynxpoint.c1
-rw-r--r--drivers/gpio/gpio-max7300.c4
-rw-r--r--drivers/gpio/gpio-max7301.c7
-rw-r--r--drivers/gpio/gpio-max732x.c5
-rw-r--r--drivers/gpio/gpio-mc33880.c19
-rw-r--r--drivers/gpio/gpio-mcp23s08.c137
-rw-r--r--drivers/gpio/gpio-mvebu.c85
-rw-r--r--drivers/gpio/gpio-omap.c116
-rw-r--r--drivers/gpio/gpio-pca953x.c3
-rw-r--r--drivers/gpio/gpio-pcf857x.c8
-rw-r--r--drivers/gpio/gpio-pxa.c4
-rw-r--r--drivers/gpio/gpio-rcar.c396
-rw-r--r--drivers/gpio/gpio-samsung.c1
-rw-r--r--drivers/gpio/gpio-sch.c111
-rw-r--r--drivers/gpio/gpio-stp-xway.c2
-rw-r--r--drivers/gpio/gpio-tc3589x.c8
-rw-r--r--drivers/gpio/gpio-tegra.c11
-rw-r--r--drivers/gpio/gpio-timberdale.c3
-rw-r--r--drivers/gpio/gpio-tps65910.c2
-rw-r--r--drivers/gpio/gpio-viperboard.c4
-rw-r--r--drivers/gpio/gpiolib-acpi.c217
-rw-r--r--drivers/gpio/gpiolib-of.c2
-rw-r--r--drivers/hwspinlock/Kconfig2
-rw-r--r--drivers/i2c/busses/i2c-mxs.c40
-rw-r--r--drivers/ide/ide-cd.c4
-rw-r--r--drivers/ide/ide-gd.c4
-rw-r--r--drivers/ide/ide-tape.c4
-rw-r--r--drivers/infiniband/core/iwcm.c2
-rw-r--r--drivers/infiniband/core/verbs.c3
-rw-r--r--drivers/infiniband/hw/cxgb3/cxio_resource.c4
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c2
-rw-r--r--drivers/infiniband/hw/cxgb4/id_table.c4
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c22
-rw-r--r--drivers/infiniband/hw/ipath/ipath_file_ops.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.c19
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c21
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c2
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c6
-rw-r--r--drivers/infiniband/hw/qib/qib_file_ops.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_sysfs.c6
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.c3
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c2
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c24
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h24
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c3
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c36
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c2
-rw-r--r--drivers/iommu/amd_iommu.c145
-rw-r--r--drivers/iommu/amd_iommu_init.c154
-rw-r--r--drivers/iommu/amd_iommu_types.h2
-rw-r--r--drivers/iommu/dmar.c23
-rw-r--r--drivers/iommu/exynos-iommu.c2
-rw-r--r--drivers/iommu/intel-iommu.c24
-rw-r--r--drivers/iommu/intel_irq_remapping.c10
-rw-r--r--drivers/iommu/iommu.c37
-rw-r--r--drivers/iommu/irq_remapping.c6
-rw-r--r--drivers/iommu/irq_remapping.h2
-rw-r--r--drivers/iommu/msm_iommu.c2
-rw-r--r--drivers/iommu/omap-iommu.c2
-rw-r--r--drivers/iommu/pci.h29
-rw-r--r--drivers/iommu/shmobile-iommu.c2
-rw-r--r--drivers/iommu/tegra-gart.c5
-rw-r--r--drivers/iommu/tegra-smmu.c5
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/exynos-combiner.c125
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c (renamed from arch/arm/mach-mvebu/irq-armada-370-xp.c)106
-rw-r--r--drivers/leds/Kconfig121
-rw-r--r--drivers/leds/Makefile13
-rw-r--r--drivers/leds/leds-asic3.c7
-rw-r--r--drivers/leds/leds-atmel-pwm.c4
-rw-r--r--drivers/leds/leds-bd2802.c14
-rw-r--r--drivers/leds/leds-lm355x.c2
-rw-r--r--drivers/leds/leds-lm3642.c2
-rw-r--r--drivers/leds/leds-lp5521.c22
-rw-r--r--drivers/leds/leds-lp5562.c599
-rw-r--r--drivers/leds/leds-lp55xx-common.c42
-rw-r--r--drivers/leds/leds-lp55xx-common.h4
-rw-r--r--drivers/leds/leds-lt3593.c5
-rw-r--r--drivers/leds/leds-ns2.c44
-rw-r--r--drivers/leds/leds-pwm.c50
-rw-r--r--drivers/leds/leds-renesas-tpu.c3
-rw-r--r--drivers/leds/leds-tca6507.c4
-rw-r--r--drivers/leds/leds-wm8350.c5
-rw-r--r--drivers/leds/trigger/Kconfig117
-rw-r--r--drivers/leds/trigger/Makefile12
-rw-r--r--drivers/leds/trigger/ledtrig-backlight.c (renamed from drivers/leds/ledtrig-backlight.c)2
-rw-r--r--drivers/leds/trigger/ledtrig-camera.c57
-rw-r--r--drivers/leds/trigger/ledtrig-cpu.c (renamed from drivers/leds/ledtrig-cpu.c)2
-rw-r--r--drivers/leds/trigger/ledtrig-default-on.c (renamed from drivers/leds/ledtrig-default-on.c)2
-rw-r--r--drivers/leds/trigger/ledtrig-gpio.c (renamed from drivers/leds/ledtrig-gpio.c)2
-rw-r--r--drivers/leds/trigger/ledtrig-heartbeat.c (renamed from drivers/leds/ledtrig-heartbeat.c)2
-rw-r--r--drivers/leds/trigger/ledtrig-ide-disk.c (renamed from drivers/leds/ledtrig-ide-disk.c)0
-rw-r--r--drivers/leds/trigger/ledtrig-oneshot.c (renamed from drivers/leds/ledtrig-oneshot.c)2
-rw-r--r--drivers/leds/trigger/ledtrig-sleep.c (renamed from drivers/leds/ledtrig-sleep.c)0
-rw-r--r--drivers/leds/trigger/ledtrig-timer.c (renamed from drivers/leds/ledtrig-timer.c)1
-rw-r--r--drivers/leds/trigger/ledtrig-transient.c (renamed from drivers/leds/ledtrig-transient.c)2
-rw-r--r--drivers/md/Kconfig2
-rw-r--r--drivers/md/Makefile1
-rw-r--r--drivers/md/bcache/Kconfig42
-rw-r--r--drivers/md/bcache/Makefile7
-rw-r--r--drivers/md/bcache/alloc.c599
-rw-r--r--drivers/md/bcache/bcache.h1259
-rw-r--r--drivers/md/bcache/bset.c1192
-rw-r--r--drivers/md/bcache/bset.h379
-rw-r--r--drivers/md/bcache/btree.c2503
-rw-r--r--drivers/md/bcache/btree.h405
-rw-r--r--drivers/md/bcache/closure.c345
-rw-r--r--drivers/md/bcache/closure.h672
-rw-r--r--drivers/md/bcache/debug.c565
-rw-r--r--drivers/md/bcache/debug.h54
-rw-r--r--drivers/md/bcache/io.c397
-rw-r--r--drivers/md/bcache/journal.c787
-rw-r--r--drivers/md/bcache/journal.h215
-rw-r--r--drivers/md/bcache/movinggc.c254
-rw-r--r--drivers/md/bcache/request.c1411
-rw-r--r--drivers/md/bcache/request.h62
-rw-r--r--drivers/md/bcache/stats.c246
-rw-r--r--drivers/md/bcache/stats.h58
-rw-r--r--drivers/md/bcache/super.c1987
-rw-r--r--drivers/md/bcache/sysfs.c817
-rw-r--r--drivers/md/bcache/sysfs.h110
-rw-r--r--drivers/md/bcache/trace.c26
-rw-r--r--drivers/md/bcache/util.c377
-rw-r--r--drivers/md/bcache/util.h589
-rw-r--r--drivers/md/bcache/writeback.c414
-rw-r--r--drivers/md/dm-crypt.c3
-rw-r--r--drivers/md/dm-raid1.c2
-rw-r--r--drivers/md/dm-stripe.c2
-rw-r--r--drivers/md/dm-verity.c4
-rw-r--r--drivers/md/dm.c4
-rw-r--r--drivers/md/faulty.c6
-rw-r--r--drivers/md/linear.c3
-rw-r--r--drivers/md/md.c21
-rw-r--r--drivers/md/raid0.c9
-rw-r--r--drivers/md/raid1.c133
-rw-r--r--drivers/md/raid10.c78
-rw-r--r--drivers/md/raid5.c49
-rw-r--r--drivers/memstick/core/mspro_block.c8
-rw-r--r--drivers/message/fusion/mptsas.c6
-rw-r--r--drivers/message/i2o/i2o_block.c8
-rw-r--r--drivers/mmc/card/block.c3
-rw-r--r--drivers/mmc/host/mxs-mmc.c48
-rw-r--r--drivers/mtd/mtd_blkdevs.c9
-rw-r--r--drivers/mtd/mtdblock.c4
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c51
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.h3
-rw-r--r--drivers/mtd/sm_ftl.c3
-rw-r--r--drivers/net/ethernet/3com/3c59x.c2
-rw-r--r--drivers/net/ethernet/broadcom/cnic.c4
-rw-r--r--drivers/net/ethernet/freescale/fec.h1
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c18
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c23
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c16
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/eq.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mcg.c120
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h79
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/srq.c15
-rw-r--r--drivers/net/hamradio/baycom_epp.c2
-rw-r--r--drivers/net/hamradio/hdlcdrv.c2
-rw-r--r--drivers/net/hamradio/yam.c2
-rw-r--r--drivers/net/team/team_mode_random.c2
-rw-r--r--drivers/net/usb/Kconfig11
-rw-r--r--drivers/net/usb/Makefile1
-rw-r--r--drivers/net/usb/cdc_ether.c10
-rw-r--r--drivers/net/usb/r8152.c1767
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/p2p.c2
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c4
-rw-r--r--drivers/parisc/sba_iommu.c19
-rw-r--r--drivers/pinctrl/sh-pfc/Kconfig5
-rw-r--r--drivers/pinctrl/sh-pfc/Makefile1
-rw-r--r--drivers/pinctrl/sh-pfc/core.c41
-rw-r--r--drivers/pinctrl/sh-pfc/core.h1
-rw-r--r--drivers/pinctrl/sh-pfc/gpio.c45
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a73a4.c2587
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7740.c64
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7779.c481
-rw-r--r--drivers/pinctrl/sh-pfc/pinctrl.c11
-rw-r--r--drivers/pwm/Kconfig8
-rw-r--r--drivers/pwm/pwm-ab8500.c10
-rw-r--r--drivers/pwm/pwm-atmel-tcb.c1
-rw-r--r--drivers/pwm/pwm-imx.c6
-rw-r--r--drivers/pwm/pwm-lpc32xx.c27
-rw-r--r--drivers/pwm/pwm-mxs.c4
-rw-r--r--drivers/pwm/pwm-puv3.c1
-rw-r--r--drivers/pwm/pwm-pxa.c23
-rw-r--r--drivers/pwm/pwm-samsung.c20
-rw-r--r--drivers/pwm/pwm-spear.c11
-rw-r--r--drivers/pwm/pwm-tegra.c2
-rw-r--r--drivers/pwm/pwm-tiecap.c6
-rw-r--r--drivers/pwm/pwm-tiehrpwm.c6
-rw-r--r--drivers/pwm/pwm-tipwmss.c2
-rw-r--r--drivers/pwm/pwm-tipwmss.h2
-rw-r--r--drivers/pwm/pwm-twl-led.c4
-rw-r--r--drivers/pwm/pwm-twl.c6
-rw-r--r--drivers/remoteproc/Kconfig27
-rw-r--r--drivers/remoteproc/Makefile1
-rw-r--r--drivers/remoteproc/da8xx_remoteproc.c324
-rw-r--r--drivers/remoteproc/remoteproc_core.c211
-rw-r--r--drivers/remoteproc/remoteproc_elf_loader.c100
-rw-r--r--drivers/remoteproc/remoteproc_internal.h15
-rw-r--r--drivers/remoteproc/remoteproc_virtio.c79
-rw-r--r--drivers/remoteproc/ste_modem_rproc.c45
-rw-r--r--drivers/rpmsg/Kconfig1
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c49
-rw-r--r--drivers/rtc/rtc-rs5c372.c5
-rw-r--r--drivers/s390/block/dasd.c18
-rw-r--r--drivers/s390/block/dcssblk.c15
-rw-r--r--drivers/s390/block/scm_blk.c3
-rw-r--r--drivers/scsi/libsas/sas_expander.c6
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_transport.c10
-rw-r--r--drivers/scsi/sd.c3
-rw-r--r--drivers/scsi/sg.c1
-rw-r--r--drivers/scsi/sr.c3
-rw-r--r--drivers/spi/spi-davinci.c4
-rw-r--r--drivers/spi/spi-mxs.c60
-rw-r--r--drivers/spi/spi-pl022.c43
-rw-r--r--drivers/staging/android/logger.c1
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-common.c12
-rw-r--r--drivers/tty/serial/amba-pl011.c64
-rw-r--r--drivers/tty/serial/mxs-auart.c52
-rw-r--r--drivers/usb/gadget/inode.c42
-rw-r--r--drivers/vhost/net.c100
-rw-r--r--drivers/vhost/scsi.c8
-rw-r--r--drivers/vhost/vhost.c4
-rw-r--r--drivers/vhost/vhost.h10
-rw-r--r--drivers/video/Kconfig2
-rw-r--r--drivers/video/backlight/pwm_bl.c7
-rw-r--r--drivers/video/mxsfb.c260
-rw-r--r--fs/9p/vfs_addr.c1
-rw-r--r--fs/afs/write.c1
-rw-r--r--fs/aio.c1578
-rw-r--r--fs/bio-integrity.c144
-rw-r--r--fs/bio.c367
-rw-r--r--fs/block_dev.c19
-rw-r--r--fs/btrfs/extent_io.c3
-rw-r--r--fs/btrfs/file.c1
-rw-r--r--fs/btrfs/inode.c1
-rw-r--r--fs/btrfs/volumes.c2
-rw-r--r--fs/buffer.c1
-rw-r--r--fs/ceph/file.c1
-rw-r--r--fs/compat.c1
-rw-r--r--fs/direct-io.c9
-rw-r--r--fs/ecryptfs/file.c1
-rw-r--r--fs/exofs/ore.c2
-rw-r--r--fs/exofs/ore_raid.c2
-rw-r--r--fs/ext2/inode.c1
-rw-r--r--fs/ext3/inode.c1
-rw-r--r--fs/ext3/super.c11
-rw-r--r--fs/ext4/file.c1
-rw-r--r--fs/ext4/indirect.c1
-rw-r--r--fs/ext4/inode.c1
-rw-r--r--fs/ext4/page-io.c1
-rw-r--r--fs/ext4/super.c11
-rw-r--r--fs/f2fs/checkpoint.c63
-rw-r--r--fs/f2fs/data.c203
-rw-r--r--fs/f2fs/debug.c10
-rw-r--r--fs/f2fs/dir.c110
-rw-r--r--fs/f2fs/f2fs.h93
-rw-r--r--fs/f2fs/file.c116
-rw-r--r--fs/f2fs/gc.c123
-rw-r--r--fs/f2fs/gc.h12
-rw-r--r--fs/f2fs/inode.c68
-rw-r--r--fs/f2fs/namei.c80
-rw-r--r--fs/f2fs/node.c411
-rw-r--r--fs/f2fs/node.h20
-rw-r--r--fs/f2fs/recovery.c83
-rw-r--r--fs/f2fs/segment.c137
-rw-r--r--fs/f2fs/segment.h41
-rw-r--r--fs/f2fs/super.c67
-rw-r--r--fs/f2fs/xattr.c28
-rw-r--r--fs/fat/inode.c1
-rw-r--r--fs/fs-writeback.c102
-rw-r--r--fs/fuse/cuse.c11
-rw-r--r--fs/fuse/dev.c81
-rw-r--r--fs/fuse/dir.c17
-rw-r--r--fs/fuse/file.c273
-rw-r--r--fs/fuse/fuse_i.h36
-rw-r--r--fs/fuse/inode.c12
-rw-r--r--fs/gfs2/aops.c1
-rw-r--r--fs/gfs2/file.c1
-rw-r--r--fs/gfs2/lops.c2
-rw-r--r--fs/hfs/dir.c2
-rw-r--r--fs/hfs/inode.c1
-rw-r--r--fs/hfsplus/inode.c1
-rw-r--r--fs/hugetlbfs/inode.c24
-rw-r--r--fs/jfs/inode.c1
-rw-r--r--fs/jfs/jfs_logmgr.c2
-rw-r--r--fs/logfs/dev_bdev.c5
-rw-r--r--fs/nfs/blocklayout/blocklayout.h2
-rw-r--r--fs/nfs/blocklayout/blocklayoutdev.c4
-rw-r--r--fs/nfs/blocklayout/blocklayoutdm.c8
-rw-r--r--fs/nilfs2/inode.c2
-rw-r--r--fs/ntfs/file.c1
-rw-r--r--fs/ntfs/inode.c1
-rw-r--r--fs/ocfs2/aops.h2
-rw-r--r--fs/ocfs2/dlmglue.c2
-rw-r--r--fs/ocfs2/inode.h2
-rw-r--r--fs/pipe.c1
-rw-r--r--fs/proc/stat.c2
-rw-r--r--fs/read_write.c35
-rw-r--r--fs/reiserfs/inode.c1
-rw-r--r--fs/reiserfs/journal.c16
-rw-r--r--fs/ubifs/file.c1
-rw-r--r--fs/udf/inode.c1
-rw-r--r--fs/xfs/xfs_aops.c1
-rw-r--r--fs/xfs/xfs_file.c1
-rw-r--r--include/clocksource/arm_arch_timer.h6
-rw-r--r--include/clocksource/samsung_pwm.h36
-rw-r--r--include/linux/acpi_gpio.h19
-rw-r--r--include/linux/aio.h178
-rw-r--r--include/linux/backing-dev.h16
-rw-r--r--include/linux/basic_mmio_gpio.h1
-rw-r--r--include/linux/bio.h115
-rw-r--r--include/linux/blk_types.h5
-rw-r--r--include/linux/blkdev.h31
-rw-r--r--include/linux/cgroup.h1
-rw-r--r--include/linux/cgroup_subsys.h6
-rw-r--r--include/linux/drbd.h5
-rw-r--r--include/linux/drbd_limits.h11
-rw-r--r--include/linux/errno.h1
-rw-r--r--include/linux/f2fs_fs.h17
-rw-r--r--include/linux/fs.h2
-rw-r--r--include/linux/hugetlb.h19
-rw-r--r--include/linux/idr.h10
-rw-r--r--include/linux/iommu.h18
-rw-r--r--include/linux/kmalloc_sizes.h45
-rw-r--r--include/linux/kref.h9
-rw-r--r--include/linux/leds.h33
-rw-r--r--include/linux/lru_cache.h1
-rw-r--r--include/linux/mbus.h27
-rw-r--r--include/linux/mlx4/device.h104
-rw-r--r--include/linux/mlx4/srq.h2
-rw-r--r--include/linux/mm.h20
-rw-r--r--include/linux/mmc/host.h2
-rw-r--r--include/linux/mtd/blktrans.h2
-rw-r--r--include/linux/mxsfb.h52
-rw-r--r--include/linux/netdevice.h1
-rw-r--r--include/linux/of.h5
-rw-r--r--include/linux/pata_arasan_cf_data.h2
-rw-r--r--include/linux/pid_namespace.h1
-rw-r--r--include/linux/platform_data/gpio-rcar.h26
-rw-r--r--include/linux/platform_data/leds-lp55xx.h21
-rw-r--r--include/linux/random.h7
-rw-r--r--include/linux/remoteproc.h13
-rw-r--r--include/linux/rwsem.h10
-rw-r--r--include/linux/sched.h6
-rw-r--r--include/linux/slab.h231
-rw-r--r--include/linux/slab_def.h54
-rw-r--r--include/linux/slub_def.h136
-rw-r--r--include/linux/spi/mxs-spi.h4
-rw-r--r--include/linux/wait.h86
-rw-r--r--include/linux/writeback.h1
-rw-r--r--include/net/inet_frag.h5
-rw-r--r--include/trace/events/bcache.h271
-rw-r--r--include/trace/events/block.h12
-rw-r--r--include/trace/events/f2fs.h682
-rw-r--r--include/trace/events/writeback.h5
-rw-r--r--include/uapi/linux/fuse.h7
-rw-r--r--include/uapi/linux/virtio_net.h2
-rw-r--r--ipc/shm.c6
-rw-r--r--kernel/fork.c5
-rw-r--r--kernel/irq/irqdomain.c20
-rw-r--r--kernel/lockdep.c1
-rw-r--r--kernel/printk.c1
-rw-r--r--kernel/ptrace.c1
-rw-r--r--kernel/relay.c2
-rw-r--r--kernel/rwsem.c16
-rw-r--r--kernel/trace/blktrace.c1
-rw-r--r--lib/kobject.c2
-rw-r--r--lib/lru_cache.c56
-rw-r--r--lib/rwsem-spinlock.c38
-rw-r--r--lib/rwsem.c242
-rw-r--r--mm/backing-dev.c259
-rw-r--r--mm/bounce.c75
-rw-r--r--mm/memcontrol.c36
-rw-r--r--mm/mmap.c7
-rw-r--r--mm/mmu_context.c3
-rw-r--r--mm/page_io.c2
-rw-r--r--mm/shmem.c1
-rw-r--r--mm/slab.c790
-rw-r--r--mm/slab.h43
-rw-r--r--mm/slab_common.c174
-rw-r--r--mm/slub.c221
-rw-r--r--mm/swap.c1
-rw-r--r--mm/vmalloc.c2
-rw-r--r--net/core/net-sysfs.c12
-rw-r--r--net/core/netpoll.c2
-rw-r--r--net/ipv4/fib_trie.c13
-rw-r--r--net/ipv4/inet_fragment.c1
-rw-r--r--net/ipv4/tcp_metrics.c15
-rw-r--r--net/tipc/link.c11
-rw-r--r--scripts/Kbuild.include2
-rw-r--r--scripts/Makefile.headersinst4
-rwxr-xr-xscripts/coccicheck31
-rw-r--r--scripts/headers_install.pl63
-rw-r--r--scripts/headers_install.sh43
-rw-r--r--scripts/kconfig/Makefile4
-rw-r--r--scripts/kconfig/conf.c12
-rw-r--r--scripts/kconfig/confdata.c59
-rw-r--r--scripts/kconfig/list.h40
-rw-r--r--scripts/kconfig/lxdialog/check-lxdialog.sh6
-rw-r--r--scripts/kconfig/lxdialog/dialog.h7
-rw-r--r--scripts/kconfig/lxdialog/util.c45
-rw-r--r--scripts/kconfig/mconf.c74
-rw-r--r--scripts/kconfig/menu.c16
-rwxr-xr-xscripts/kconfig/merge_config.sh10
-rw-r--r--scripts/mod/Makefile2
-rw-r--r--scripts/package/Makefile39
-rw-r--r--scripts/package/buildtar2
-rwxr-xr-xscripts/tags.sh4
-rw-r--r--security/keys/internal.h2
-rw-r--r--security/keys/keyctl.c1
-rw-r--r--sound/core/pcm_native.c2
895 files changed, 47078 insertions, 13311 deletions
diff --git a/Documentation/ABI/testing/sysfs-block-bcache b/Documentation/ABI/testing/sysfs-block-bcache
new file mode 100644
index 000000000000..9e4bbc5d51fd
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-block-bcache
@@ -0,0 +1,156 @@
+What: /sys/block/<disk>/bcache/unregister
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ A write to this file causes the backing device or cache to be
+ unregistered. If a backing device had dirty data in the cache,
+ writeback mode is automatically disabled and all dirty data is
+ flushed before the device is unregistered. Caches unregister
+ all associated backing devices before unregistering themselves.
+
+What: /sys/block/<disk>/bcache/clear_stats
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ Writing to this file resets all the statistics for the device.
+
+What: /sys/block/<disk>/bcache/cache
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For a backing device that has cache, a symlink to
+ the bcache/ dir of that cache.
+
+What: /sys/block/<disk>/bcache/cache_hits
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For backing devices: integer number of full cache hits,
+ counted per bio. A partial cache hit counts as a miss.
+
+What: /sys/block/<disk>/bcache/cache_misses
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For backing devices: integer number of cache misses.
+
+What: /sys/block/<disk>/bcache/cache_hit_ratio
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For backing devices: cache hits as a percentage.
+
+What: /sys/block/<disk>/bcache/sequential_cutoff
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For backing devices: Threshold past which sequential IO will
+ skip the cache. Read and written as bytes in human readable
+ units (i.e. echo 10M > sequntial_cutoff).
+
+What: /sys/block/<disk>/bcache/bypassed
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ Sum of all reads and writes that have bypassed the cache (due
+ to the sequential cutoff). Expressed as bytes in human
+ readable units.
+
+What: /sys/block/<disk>/bcache/writeback
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For backing devices: When on, writeback caching is enabled and
+ writes will be buffered in the cache. When off, caching is in
+ writethrough mode; reads and writes will be added to the
+ cache but no write buffering will take place.
+
+What: /sys/block/<disk>/bcache/writeback_running
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For backing devices: when off, dirty data will not be written
+ from the cache to the backing device. The cache will still be
+ used to buffer writes until it is mostly full, at which point
+ writes transparently revert to writethrough mode. Intended only
+ for benchmarking/testing.
+
+What: /sys/block/<disk>/bcache/writeback_delay
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For backing devices: In writeback mode, when dirty data is
+ written to the cache and the cache held no dirty data for that
+ backing device, writeback from cache to backing device starts
+ after this delay, expressed as an integer number of seconds.
+
+What: /sys/block/<disk>/bcache/writeback_percent
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For backing devices: If nonzero, writeback from cache to
+ backing device only takes place when more than this percentage
+ of the cache is used, allowing more write coalescing to take
+ place and reducing total number of writes sent to the backing
+ device. Integer between 0 and 40.
+
+What: /sys/block/<disk>/bcache/synchronous
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For a cache, a boolean that allows synchronous mode to be
+ switched on and off. In synchronous mode all writes are ordered
+ such that the cache can reliably recover from unclean shutdown;
+ if disabled bcache will not generally wait for writes to
+ complete but if the cache is not shut down cleanly all data
+ will be discarded from the cache. Should not be turned off with
+ writeback caching enabled.
+
+What: /sys/block/<disk>/bcache/discard
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For a cache, a boolean allowing discard/TRIM to be turned off
+ or back on if the device supports it.
+
+What: /sys/block/<disk>/bcache/bucket_size
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For a cache, bucket size in human readable units, as set at
+ cache creation time; should match the erase block size of the
+ SSD for optimal performance.
+
+What: /sys/block/<disk>/bcache/nbuckets
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For a cache, the number of usable buckets.
+
+What: /sys/block/<disk>/bcache/tree_depth
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For a cache, height of the btree excluding leaf nodes (i.e. a
+ one node tree will have a depth of 0).
+
+What: /sys/block/<disk>/bcache/btree_cache_size
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ Number of btree buckets/nodes that are currently cached in
+ memory; cache dynamically grows and shrinks in response to
+ memory pressure from the rest of the system.
+
+What: /sys/block/<disk>/bcache/written
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For a cache, total amount of data in human readable units
+ written to the cache, excluding all metadata.
+
+What: /sys/block/<disk>/bcache/btree_written
+Date: November 2010
+Contact: Kent Overstreet <kent.overstreet@gmail.com>
+Description:
+ For a cache, sum of all btree writes in human readable units.
diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt
index 94a656131885..b0d541042ac6 100644
--- a/Documentation/acpi/enumeration.txt
+++ b/Documentation/acpi/enumeration.txt
@@ -199,6 +199,8 @@ the device to the driver. For example:
{
Name (SBUF, ResourceTemplate()
{
+ ...
+ // Used to power on/off the device
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000,
IoRestrictionOutputOnly, "\\_SB.PCI0.GPI0",
0x00, ResourceConsumer,,)
@@ -206,10 +208,20 @@ the device to the driver. For example:
// Pin List
0x0055
}
+
+ // Interrupt for the device
+ GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone,
+ 0x0000, "\\_SB.PCI0.GPI0", 0x00, ResourceConsumer,,)
+ {
+ // Pin list
+ 0x0058
+ }
+
...
- Return (SBUF)
}
+
+ Return (SBUF)
}
These GPIO numbers are controller relative and path "\\_SB.PCI0.GPI0"
@@ -220,6 +232,24 @@ The driver can do this by including <linux/acpi_gpio.h> and then calling
acpi_get_gpio(path, gpio). This will return the Linux GPIO number or
negative errno if there was no translation found.
+In a simple case of just getting the Linux GPIO number from device
+resources one can use acpi_get_gpio_by_index() helper function. It takes
+pointer to the device and index of the GpioIo/GpioInt descriptor in the
+device resources list. For example:
+
+ int gpio_irq, gpio_power;
+ int ret;
+
+ gpio_irq = acpi_get_gpio_by_index(dev, 1, NULL);
+ if (gpio_irq < 0)
+ /* handle error */
+
+ gpio_power = acpi_get_gpio_by_index(dev, 0, NULL);
+ if (gpio_power < 0)
+ /* handle error */
+
+ /* Now we can use the GPIO numbers */
+
Other GpioIo parameters must be converted first by the driver to be
suitable to the gpiolib before passing them.
diff --git a/Documentation/bcache.txt b/Documentation/bcache.txt
new file mode 100644
index 000000000000..77db8809bd96
--- /dev/null
+++ b/Documentation/bcache.txt
@@ -0,0 +1,431 @@
+Say you've got a big slow raid 6, and an X-25E or three. Wouldn't it be
+nice if you could use them as cache... Hence bcache.
+
+Wiki and git repositories are at:
+ http://bcache.evilpiepirate.org
+ http://evilpiepirate.org/git/linux-bcache.git
+ http://evilpiepirate.org/git/bcache-tools.git
+
+It's designed around the performance characteristics of SSDs - it only allocates
+in erase block sized buckets, and it uses a hybrid btree/log to track cached
+extants (which can be anywhere from a single sector to the bucket size). It's
+designed to avoid random writes at all costs; it fills up an erase block
+sequentially, then issues a discard before reusing it.
+
+Both writethrough and writeback caching are supported. Writeback defaults to
+off, but can be switched on and off arbitrarily at runtime. Bcache goes to
+great lengths to protect your data - it reliably handles unclean shutdown. (It
+doesn't even have a notion of a clean shutdown; bcache simply doesn't return
+writes as completed until they're on stable storage).
+
+Writeback caching can use most of the cache for buffering writes - writing
+dirty data to the backing device is always done sequentially, scanning from the
+start to the end of the index.
+
+Since random IO is what SSDs excel at, there generally won't be much benefit
+to caching large sequential IO. Bcache detects sequential IO and skips it;
+it also keeps a rolling average of the IO sizes per task, and as long as the
+average is above the cutoff it will skip all IO from that task - instead of
+caching the first 512k after every seek. Backups and large file copies should
+thus entirely bypass the cache.
+
+In the event of a data IO error on the flash it will try to recover by reading
+from disk or invalidating cache entries. For unrecoverable errors (meta data
+or dirty data), caching is automatically disabled; if dirty data was present
+in the cache it first disables writeback caching and waits for all dirty data
+to be flushed.
+
+Getting started:
+You'll need make-bcache from the bcache-tools repository. Both the cache device
+and backing device must be formatted before use.
+ make-bcache -B /dev/sdb
+ make-bcache -C /dev/sdc
+
+make-bcache has the ability to format multiple devices at the same time - if
+you format your backing devices and cache device at the same time, you won't
+have to manually attach:
+ make-bcache -B /dev/sda /dev/sdb -C /dev/sdc
+
+To make bcache devices known to the kernel, echo them to /sys/fs/bcache/register:
+
+ echo /dev/sdb > /sys/fs/bcache/register
+ echo /dev/sdc > /sys/fs/bcache/register
+
+To register your bcache devices automatically, you could add something like
+this to an init script:
+
+ echo /dev/sd* > /sys/fs/bcache/register_quiet
+
+It'll look for bcache superblocks and ignore everything that doesn't have one.
+
+Registering the backing device makes the bcache show up in /dev; you can now
+format it and use it as normal. But the first time using a new bcache device,
+it'll be running in passthrough mode until you attach it to a cache. See the
+section on attaching.
+
+The devices show up at /dev/bcacheN, and can be controlled via sysfs from
+/sys/block/bcacheN/bcache:
+
+ mkfs.ext4 /dev/bcache0
+ mount /dev/bcache0 /mnt
+
+Cache devices are managed as sets; multiple caches per set isn't supported yet
+but will allow for mirroring of metadata and dirty data in the future. Your new
+cache set shows up as /sys/fs/bcache/<UUID>
+
+ATTACHING:
+
+After your cache device and backing device are registered, the backing device
+must be attached to your cache set to enable caching. Attaching a backing
+device to a cache set is done thusly, with the UUID of the cache set in
+/sys/fs/bcache:
+
+ echo <UUID> > /sys/block/bcache0/bcache/attach
+
+This only has to be done once. The next time you reboot, just reregister all
+your bcache devices. If a backing device has data in a cache somewhere, the
+/dev/bcache# device won't be created until the cache shows up - particularly
+important if you have writeback caching turned on.
+
+If you're booting up and your cache device is gone and never coming back, you
+can force run the backing device:
+
+ echo 1 > /sys/block/sdb/bcache/running
+
+(You need to use /sys/block/sdb (or whatever your backing device is called), not
+/sys/block/bcache0, because bcache0 doesn't exist yet. If you're using a
+partition, the bcache directory would be at /sys/block/sdb/sdb2/bcache)
+
+The backing device will still use that cache set if it shows up in the future,
+but all the cached data will be invalidated. If there was dirty data in the
+cache, don't expect the filesystem to be recoverable - you will have massive
+filesystem corruption, though ext4's fsck does work miracles.
+
+ERROR HANDLING:
+
+Bcache tries to transparently handle IO errors to/from the cache device without
+affecting normal operation; if it sees too many errors (the threshold is
+configurable, and defaults to 0) it shuts down the cache device and switches all
+the backing devices to passthrough mode.
+
+ - For reads from the cache, if they error we just retry the read from the
+ backing device.
+
+ - For writethrough writes, if the write to the cache errors we just switch to
+ invalidating the data at that lba in the cache (i.e. the same thing we do for
+ a write that bypasses the cache)
+
+ - For writeback writes, we currently pass that error back up to the
+ filesystem/userspace. This could be improved - we could retry it as a write
+ that skips the cache so we don't have to error the write.
+
+ - When we detach, we first try to flush any dirty data (if we were running in
+ writeback mode). It currently doesn't do anything intelligent if it fails to
+ read some of the dirty data, though.
+
+TROUBLESHOOTING PERFORMANCE:
+
+Bcache has a bunch of config options and tunables. The defaults are intended to
+be reasonable for typical desktop and server workloads, but they're not what you
+want for getting the best possible numbers when benchmarking.
+
+ - Bad write performance
+
+ If write performance is not what you expected, you probably wanted to be
+ running in writeback mode, which isn't the default (not due to a lack of
+ maturity, but simply because in writeback mode you'll lose data if something
+ happens to your SSD)
+
+ # echo writeback > /sys/block/bcache0/cache_mode
+
+ - Bad performance, or traffic not going to the SSD that you'd expect
+
+ By default, bcache doesn't cache everything. It tries to skip sequential IO -
+ because you really want to be caching the random IO, and if you copy a 10
+ gigabyte file you probably don't want that pushing 10 gigabytes of randomly
+ accessed data out of your cache.
+
+ But if you want to benchmark reads from cache, and you start out with fio
+ writing an 8 gigabyte test file - so you want to disable that.
+
+ # echo 0 > /sys/block/bcache0/bcache/sequential_cutoff
+
+ To set it back to the default (4 mb), do
+
+ # echo 4M > /sys/block/bcache0/bcache/sequential_cutoff
+
+ - Traffic's still going to the spindle/still getting cache misses
+
+ In the real world, SSDs don't always keep up with disks - particularly with
+ slower SSDs, many disks being cached by one SSD, or mostly sequential IO. So
+ you want to avoid being bottlenecked by the SSD and having it slow everything
+ down.
+
+ To avoid that bcache tracks latency to the cache device, and gradually
+ throttles traffic if the latency exceeds a threshold (it does this by
+ cranking down the sequential bypass).
+
+ You can disable this if you need to by setting the thresholds to 0:
+
+ # echo 0 > /sys/fs/bcache/<cache set>/congested_read_threshold_us
+ # echo 0 > /sys/fs/bcache/<cache set>/congested_write_threshold_us
+
+ The default is 2000 us (2 milliseconds) for reads, and 20000 for writes.
+
+ - Still getting cache misses, of the same data
+
+ One last issue that sometimes trips people up is actually an old bug, due to
+ the way cache coherency is handled for cache misses. If a btree node is full,
+ a cache miss won't be able to insert a key for the new data and the data
+ won't be written to the cache.
+
+ In practice this isn't an issue because as soon as a write comes along it'll
+ cause the btree node to be split, and you need almost no write traffic for
+ this to not show up enough to be noticable (especially since bcache's btree
+ nodes are huge and index large regions of the device). But when you're
+ benchmarking, if you're trying to warm the cache by reading a bunch of data
+ and there's no other traffic - that can be a problem.
+
+ Solution: warm the cache by doing writes, or use the testing branch (there's
+ a fix for the issue there).
+
+SYSFS - BACKING DEVICE:
+
+attach
+ Echo the UUID of a cache set to this file to enable caching.
+
+cache_mode
+ Can be one of either writethrough, writeback, writearound or none.
+
+clear_stats
+ Writing to this file resets the running total stats (not the day/hour/5 minute
+ decaying versions).
+
+detach
+ Write to this file to detach from a cache set. If there is dirty data in the
+ cache, it will be flushed first.
+
+dirty_data
+ Amount of dirty data for this backing device in the cache. Continuously
+ updated unlike the cache set's version, but may be slightly off.
+
+label
+ Name of underlying device.
+
+readahead
+ Size of readahead that should be performed. Defaults to 0. If set to e.g.
+ 1M, it will round cache miss reads up to that size, but without overlapping
+ existing cache entries.
+
+running
+ 1 if bcache is running (i.e. whether the /dev/bcache device exists, whether
+ it's in passthrough mode or caching).
+
+sequential_cutoff
+ A sequential IO will bypass the cache once it passes this threshhold; the
+ most recent 128 IOs are tracked so sequential IO can be detected even when
+ it isn't all done at once.
+
+sequential_merge
+ If non zero, bcache keeps a list of the last 128 requests submitted to compare
+ against all new requests to determine which new requests are sequential
+ continuations of previous requests for the purpose of determining sequential
+ cutoff. This is necessary if the sequential cutoff value is greater than the
+ maximum acceptable sequential size for any single request.
+
+state
+ The backing device can be in one of four different states:
+
+ no cache: Has never been attached to a cache set.
+
+ clean: Part of a cache set, and there is no cached dirty data.
+
+ dirty: Part of a cache set, and there is cached dirty data.
+
+ inconsistent: The backing device was forcibly run by the user when there was
+ dirty data cached but the cache set was unavailable; whatever data was on the
+ backing device has likely been corrupted.
+
+stop
+ Write to this file to shut down the bcache device and close the backing
+ device.
+
+writeback_delay
+ When dirty data is written to the cache and it previously did not contain
+ any, waits some number of seconds before initiating writeback. Defaults to
+ 30.
+
+writeback_percent
+ If nonzero, bcache tries to keep around this percentage of the cache dirty by
+ throttling background writeback and using a PD controller to smoothly adjust
+ the rate.
+
+writeback_rate
+ Rate in sectors per second - if writeback_percent is nonzero, background
+ writeback is throttled to this rate. Continuously adjusted by bcache but may
+ also be set by the user.
+
+writeback_running
+ If off, writeback of dirty data will not take place at all. Dirty data will
+ still be added to the cache until it is mostly full; only meant for
+ benchmarking. Defaults to on.
+
+SYSFS - BACKING DEVICE STATS:
+
+There are directories with these numbers for a running total, as well as
+versions that decay over the past day, hour and 5 minutes; they're also
+aggregated in the cache set directory as well.
+
+bypassed
+ Amount of IO (both reads and writes) that has bypassed the cache
+
+cache_hits
+cache_misses
+cache_hit_ratio
+ Hits and misses are counted per individual IO as bcache sees them; a
+ partial hit is counted as a miss.
+
+cache_bypass_hits
+cache_bypass_misses
+ Hits and misses for IO that is intended to skip the cache are still counted,
+ but broken out here.
+
+cache_miss_collisions
+ Counts instances where data was going to be inserted into the cache from a
+ cache miss, but raced with a write and data was already present (usually 0
+ since the synchronization for cache misses was rewritten)
+
+cache_readaheads
+ Count of times readahead occured.
+
+SYSFS - CACHE SET:
+
+average_key_size
+ Average data per key in the btree.
+
+bdev<0..n>
+ Symlink to each of the attached backing devices.
+
+block_size
+ Block size of the cache devices.
+
+btree_cache_size
+ Amount of memory currently used by the btree cache
+
+bucket_size
+ Size of buckets
+
+cache<0..n>
+ Symlink to each of the cache devices comprising this cache set.
+
+cache_available_percent
+ Percentage of cache device free.
+
+clear_stats
+ Clears the statistics associated with this cache
+
+dirty_data
+ Amount of dirty data is in the cache (updated when garbage collection runs).
+
+flash_vol_create
+ Echoing a size to this file (in human readable units, k/M/G) creates a thinly
+ provisioned volume backed by the cache set.
+
+io_error_halflife
+io_error_limit
+ These determines how many errors we accept before disabling the cache.
+ Each error is decayed by the half life (in # ios). If the decaying count
+ reaches io_error_limit dirty data is written out and the cache is disabled.
+
+journal_delay_ms
+ Journal writes will delay for up to this many milliseconds, unless a cache
+ flush happens sooner. Defaults to 100.
+
+root_usage_percent
+ Percentage of the root btree node in use. If this gets too high the node
+ will split, increasing the tree depth.
+
+stop
+ Write to this file to shut down the cache set - waits until all attached
+ backing devices have been shut down.
+
+tree_depth
+ Depth of the btree (A single node btree has depth 0).
+
+unregister
+ Detaches all backing devices and closes the cache devices; if dirty data is
+ present it will disable writeback caching and wait for it to be flushed.
+
+SYSFS - CACHE SET INTERNAL:
+
+This directory also exposes timings for a number of internal operations, with
+separate files for average duration, average frequency, last occurence and max
+duration: garbage collection, btree read, btree node sorts and btree splits.
+
+active_journal_entries
+ Number of journal entries that are newer than the index.
+
+btree_nodes
+ Total nodes in the btree.
+
+btree_used_percent
+ Average fraction of btree in use.
+
+bset_tree_stats
+ Statistics about the auxiliary search trees
+
+btree_cache_max_chain
+ Longest chain in the btree node cache's hash table
+
+cache_read_races
+ Counts instances where while data was being read from the cache, the bucket
+ was reused and invalidated - i.e. where the pointer was stale after the read
+ completed. When this occurs the data is reread from the backing device.
+
+trigger_gc
+ Writing to this file forces garbage collection to run.
+
+SYSFS - CACHE DEVICE:
+
+block_size
+ Minimum granularity of writes - should match hardware sector size.
+
+btree_written
+ Sum of all btree writes, in (kilo/mega/giga) bytes
+
+bucket_size
+ Size of buckets
+
+cache_replacement_policy
+ One of either lru, fifo or random.
+
+discard
+ Boolean; if on a discard/TRIM will be issued to each bucket before it is
+ reused. Defaults to off, since SATA TRIM is an unqueued command (and thus
+ slow).
+
+freelist_percent
+ Size of the freelist as a percentage of nbuckets. Can be written to to
+ increase the number of buckets kept on the freelist, which lets you
+ artificially reduce the size of the cache at runtime. Mostly for testing
+ purposes (i.e. testing how different size caches affect your hit rate), but
+ since buckets are discarded when they move on to the freelist will also make
+ the SSD's garbage collection easier by effectively giving it more reserved
+ space.
+
+io_errors
+ Number of errors that have occured, decayed by io_error_halflife.
+
+metadata_written
+ Sum of all non data writes (btree writes and all other metadata).
+
+nbuckets
+ Total buckets in this cache
+
+priority_stats
+ Statistics about how recently data in the cache has been accessed. This can
+ reveal your working set size.
+
+written
+ Sum of all data that has been written to the cache; comparison with
+ btree_written gives the amount of write inflation in bcache.
diff --git a/Documentation/block/cfq-iosched.txt b/Documentation/block/cfq-iosched.txt
index a5eb7d19a65d..9887f0414c16 100644
--- a/Documentation/block/cfq-iosched.txt
+++ b/Documentation/block/cfq-iosched.txt
@@ -5,7 +5,7 @@ The main aim of CFQ scheduler is to provide a fair allocation of the disk
I/O bandwidth for all the processes which requests an I/O operation.
CFQ maintains the per process queue for the processes which request I/O
-operation(syncronous requests). In case of asynchronous requests, all the
+operation(synchronous requests). In case of asynchronous requests, all the
requests from all the processes are batched together according to their
process's I/O priority.
@@ -66,6 +66,47 @@ This parameter is used to set the timeout of synchronous requests. Default
value of this is 124ms. In case to favor synchronous requests over asynchronous
one, this value should be decreased relative to fifo_expire_async.
+group_idle
+-----------
+This parameter forces idling at the CFQ group level instead of CFQ
+queue level. This was introduced after after a bottleneck was observed
+in higher end storage due to idle on sequential queue and allow dispatch
+from a single queue. The idea with this parameter is that it can be run with
+slice_idle=0 and group_idle=8, so that idling does not happen on individual
+queues in the group but happens overall on the group and thus still keeps the
+IO controller working.
+Not idling on individual queues in the group will dispatch requests from
+multiple queues in the group at the same time and achieve higher throughput
+on higher end storage.
+
+Default value for this parameter is 8ms.
+
+latency
+-------
+This parameter is used to enable/disable the latency mode of the CFQ
+scheduler. If latency mode (called low_latency) is enabled, CFQ tries
+to recompute the slice time for each process based on the target_latency set
+for the system. This favors fairness over throughput. Disabling low
+latency (setting it to 0) ignores target latency, allowing each process in the
+system to get a full time slice.
+
+By default low latency mode is enabled.
+
+target_latency
+--------------
+This parameter is used to calculate the time slice for a process if cfq's
+latency mode is enabled. It will ensure that sync requests have an estimated
+latency. But if sequential workload is higher(e.g. sequential read),
+then to meet the latency constraints, throughput may decrease because of less
+time for each process to issue I/O request before the cfq queue is switched.
+
+Though this can be overcome by disabling the latency_mode, it may increase
+the read latency for some applications. This parameter allows for changing
+target_latency through the sysfs interface which can provide the balanced
+throughput and read latency.
+
+Default value for target_latency is 300ms.
+
slice_async
-----------
This parameter is same as of slice_sync but for asynchronous queue. The
@@ -98,8 +139,8 @@ in the device exceeds this parameter. This parameter is used for synchronous
request.
In case of storage with several disk, this setting can limit the parallel
-processing of request. Therefore, increasing the value can imporve the
-performace although this can cause the latency of some I/O to increase due
+processing of request. Therefore, increasing the value can improve the
+performance although this can cause the latency of some I/O to increase due
to more number of requests.
CFQ Group scheduling
diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt
index 09027a9fece5..ddf4f93967a9 100644
--- a/Documentation/cgroups/memory.txt
+++ b/Documentation/cgroups/memory.txt
@@ -480,7 +480,9 @@ memory.stat file includes following statistics
# per-memory cgroup local status
cache - # of bytes of page cache memory.
-rss - # of bytes of anonymous and swap cache memory.
+rss - # of bytes of anonymous and swap cache memory (includes
+ transparent hugepages).
+rss_huge - # of bytes of anonymous transparent hugepages.
mapped_file - # of bytes of mapped file (includes tmpfs/shmem)
pgpgin - # of charging events to the memory cgroup. The charging
event happens each time a page is accounted as either mapped
diff --git a/Documentation/coccinelle.txt b/Documentation/coccinelle.txt
index dffa2d620d6d..18de78599dd4 100644
--- a/Documentation/coccinelle.txt
+++ b/Documentation/coccinelle.txt
@@ -114,7 +114,7 @@ To apply Coccinelle to a specific directory, M= can be used.
For example, to check drivers/net/wireless/ one may write:
make coccicheck M=drivers/net/wireless/
-
+
To apply Coccinelle on a file basis, instead of a directory basis, the
following command may be used:
@@ -134,6 +134,15 @@ MODE variable explained above.
In this mode, there is no information about semantic patches
displayed, and no commit message proposed.
+ Additional flags
+~~~~~~~~~~~~~~~~~~
+
+Additional flags can be passed to spatch through the SPFLAGS
+variable.
+
+ make SPFLAGS=--use_glimpse coccicheck
+
+See spatch --help to learn more about spatch options.
Proposing new semantic patches
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/devicetree/bindings/arm/omap/l3-noc.txt b/Documentation/devicetree/bindings/arm/omap/l3-noc.txt
index 6888a5efc860..c0105de55cbd 100644
--- a/Documentation/devicetree/bindings/arm/omap/l3-noc.txt
+++ b/Documentation/devicetree/bindings/arm/omap/l3-noc.txt
@@ -6,6 +6,7 @@ provided by Arteris.
Required properties:
- compatible : Should be "ti,omap3-l3-smx" for OMAP3 family
Should be "ti,omap4-l3-noc" for OMAP4 family
+- reg: Contains L3 register address range for each noc domain.
- ti,hwmods: "l3_main_1", ... One hwmod for each noc domain.
Examples:
diff --git a/Documentation/devicetree/bindings/arm/omap/timer.txt b/Documentation/devicetree/bindings/arm/omap/timer.txt
index 8732d4d41f8b..d02e27c764ec 100644
--- a/Documentation/devicetree/bindings/arm/omap/timer.txt
+++ b/Documentation/devicetree/bindings/arm/omap/timer.txt
@@ -1,7 +1,20 @@
OMAP Timer bindings
Required properties:
-- compatible: Must be "ti,omap2-timer" for OMAP2+ controllers.
+- compatible: Should be set to one of the below. Please note that
+ OMAP44xx devices have timer instances that are 100%
+ register compatible with OMAP3xxx devices as well as
+ newer timers that are not 100% register compatible.
+ So for OMAP44xx devices timer instances may use
+ different compatible strings.
+
+ ti,omap2420-timer (applicable to OMAP24xx devices)
+ ti,omap3430-timer (applicable to OMAP3xxx/44xx devices)
+ ti,omap4430-timer (applicable to OMAP44xx devices)
+ ti,omap5430-timer (applicable to OMAP543x devices)
+ ti,am335x-timer (applicable to AM335x devices)
+ ti,am335x-timer-1ms (applicable to AM335x devices)
+
- reg: Contains timer register address range (base address and
length).
- interrupts: Contains the interrupt information for the timer. The
@@ -22,7 +35,7 @@ Optional properties:
Example:
timer12: timer@48304000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x48304000 0x400>;
interrupts = <95>;
ti,hwmods = "timer12"
diff --git a/Documentation/devicetree/bindings/arm/primecell.txt b/Documentation/devicetree/bindings/arm/primecell.txt
index 64fc82bc8928..0df6acacfaea 100644
--- a/Documentation/devicetree/bindings/arm/primecell.txt
+++ b/Documentation/devicetree/bindings/arm/primecell.txt
@@ -16,14 +16,31 @@ Optional properties:
- clocks : From common clock binding. First clock is phandle to clock for apb
pclk. Additional clocks are optional and specific to those peripherals.
- clock-names : From common clock binding. Shall be "apb_pclk" for first clock.
+- dmas : From common DMA binding. If present, refers to one or more dma channels.
+- dma-names : From common DMA binding, needs to match the 'dmas' property.
+ Devices with exactly one receive and transmit channel shall name
+ these "rx" and "tx", respectively.
+- pinctrl-<n> : Pinctrl states as described in bindings/pinctrl/pinctrl-bindings.txt
+- pinctrl-names : Names corresponding to the numbered pinctrl states
+- interrupts : one or more interrupt specifiers
+- interrupt-names : names corresponding to the interrupts properties
Example:
serial@fff36000 {
compatible = "arm,pl011", "arm,primecell";
arm,primecell-periphid = <0x00341011>;
+
clocks = <&pclk>;
clock-names = "apb_pclk";
-
+
+ dmas = <&dma-controller 4>, <&dma-controller 5>;
+ dma-names = "rx", "tx";
+
+ pinctrl-0 = <&uart0_default_mux>, <&uart0_default_mode>;
+ pinctrl-1 = <&uart0_sleep_mode>;
+ pinctrl-names = "default","sleep";
+
+ interrupts = <0 11 0x4>;
};
diff --git a/Documentation/devicetree/bindings/arm/samsung/sysreg.txt b/Documentation/devicetree/bindings/arm/samsung/sysreg.txt
new file mode 100644
index 000000000000..5039c0a12f55
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/samsung/sysreg.txt
@@ -0,0 +1,7 @@
+SAMSUNG S5P/Exynos SoC series System Registers (SYSREG)
+
+Properties:
+ - name : should be 'sysreg';
+ - compatible : should contain "samsung,<chip name>-sysreg", "syscon";
+ For Exynos4 SoC series it should be "samsung,exynos4-sysreg", "syscon";
+ - reg : offset and length of the register set.
diff --git a/Documentation/devicetree/bindings/ata/pata-arasan.txt b/Documentation/devicetree/bindings/ata/pata-arasan.txt
index 95ec7f825ede..2aff154be84e 100644
--- a/Documentation/devicetree/bindings/ata/pata-arasan.txt
+++ b/Documentation/devicetree/bindings/ata/pata-arasan.txt
@@ -6,6 +6,26 @@ Required properties:
- interrupt-parent: Should be the phandle for the interrupt controller
that services interrupts for this device
- interrupt: Should contain the CF interrupt number
+- clock-frequency: Interface clock rate, in Hz, one of
+ 25000000
+ 33000000
+ 40000000
+ 50000000
+ 66000000
+ 75000000
+ 100000000
+ 125000000
+ 150000000
+ 166000000
+ 200000000
+
+Optional properties:
+- arasan,broken-udma: if present, UDMA mode is unusable
+- arasan,broken-mwdma: if present, MWDMA mode is unusable
+- arasan,broken-pio: if present, PIO mode is unusable
+- dmas: one DMA channel, as described in bindings/dma/dma.txt
+ required unless both UDMA and MWDMA mode are broken
+- dma-names: the corresponding channel name, must be "data"
Example:
@@ -14,4 +34,6 @@ Example:
reg = <0xfc000000 0x1000>;
interrupt-parent = <&vic1>;
interrupts = <12>;
+ dmas = <&dma-controller 23>;
+ dma-names = "data";
};
diff --git a/Documentation/devicetree/bindings/clock/imx5-clock.txt b/Documentation/devicetree/bindings/clock/imx5-clock.txt
index 2a0c904c46ae..d71b4b2c077d 100644
--- a/Documentation/devicetree/bindings/clock/imx5-clock.txt
+++ b/Documentation/devicetree/bindings/clock/imx5-clock.txt
@@ -38,7 +38,6 @@ clocks and IDs.
usb_phy_podf 23
cpu_podf 24
di_pred 25
- tve_di 26
tve_s 27
uart1_ipg_gate 28
uart1_per_gate 29
@@ -172,6 +171,19 @@ clocks and IDs.
can1_serial_gate 157
can1_ipg_gate 158
owire_gate 159
+ gpu3d_s 160
+ gpu2d_s 161
+ gpu3d_gate 162
+ gpu2d_gate 163
+ garb_gate 164
+ cko1_sel 165
+ cko1_podf 166
+ cko1 167
+ cko2_sel 168
+ cko2_podf 169
+ cko2 170
+ srtc_gate 171
+ pata_gate 172
Examples (for mx53):
diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt
index 969b38e06ad3..6deb6fd1c7cd 100644
--- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt
+++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt
@@ -205,6 +205,9 @@ clocks and IDs.
enet_ref 190
usbphy1_gate 191
usbphy2_gate 192
+ pll4_post_div 193
+ pll5_post_div 194
+ pll5_video_div 195
Examples:
diff --git a/Documentation/devicetree/bindings/dma/fsl-mxs-dma.txt b/Documentation/devicetree/bindings/dma/fsl-mxs-dma.txt
index ded0398d3bdc..a4873e5e3e36 100644
--- a/Documentation/devicetree/bindings/dma/fsl-mxs-dma.txt
+++ b/Documentation/devicetree/bindings/dma/fsl-mxs-dma.txt
@@ -3,17 +3,58 @@
Required properties:
- compatible : Should be "fsl,<chip>-dma-apbh" or "fsl,<chip>-dma-apbx"
- reg : Should contain registers location and length
+- interrupts : Should contain the interrupt numbers of DMA channels.
+ If a channel is empty/reserved, 0 should be filled in place.
+- #dma-cells : Must be <1>. The number cell specifies the channel ID.
+- dma-channels : Number of channels supported by the DMA controller
+
+Optional properties:
+- interrupt-names : Name of DMA channel interrupts
Supported chips:
imx23, imx28.
Examples:
-dma-apbh@80004000 {
+
+dma_apbh: dma-apbh@80004000 {
compatible = "fsl,imx28-dma-apbh";
- reg = <0x80004000 2000>;
+ reg = <0x80004000 0x2000>;
+ interrupts = <82 83 84 85
+ 88 88 88 88
+ 88 88 88 88
+ 87 86 0 0>;
+ interrupt-names = "ssp0", "ssp1", "ssp2", "ssp3",
+ "gpmi0", "gmpi1", "gpmi2", "gmpi3",
+ "gpmi4", "gmpi5", "gpmi6", "gmpi7",
+ "hsadc", "lcdif", "empty", "empty";
+ #dma-cells = <1>;
+ dma-channels = <16>;
};
-dma-apbx@80024000 {
+dma_apbx: dma-apbx@80024000 {
compatible = "fsl,imx28-dma-apbx";
- reg = <0x80024000 2000>;
+ reg = <0x80024000 0x2000>;
+ interrupts = <78 79 66 0
+ 80 81 68 69
+ 70 71 72 73
+ 74 75 76 77>;
+ interrupt-names = "auart4-rx", "aurat4-tx", "spdif-tx", "empty",
+ "saif0", "saif1", "i2c0", "i2c1",
+ "auart0-rx", "auart0-tx", "auart1-rx", "auart1-tx",
+ "auart2-rx", "auart2-tx", "auart3-rx", "auart3-tx";
+ #dma-cells = <1>;
+ dma-channels = <16>;
+};
+
+DMA clients connected to the MXS DMA controller must use the format
+described in the dma.txt file.
+
+Examples:
+
+auart0: serial@8006a000 {
+ compatible = "fsl,imx28-auart", "fsl,imx23-auart";
+ reg = <0x8006a000 0x2000>;
+ interrupts = <112>;
+ dmas = <&dma_apbx 8>, <&dma_apbx 9>;
+ dma-names = "rx", "tx";
};
diff --git a/Documentation/devicetree/bindings/fb/mxsfb.txt b/Documentation/devicetree/bindings/fb/mxsfb.txt
index b41e5e52a676..96ec5179c8a0 100644
--- a/Documentation/devicetree/bindings/fb/mxsfb.txt
+++ b/Documentation/devicetree/bindings/fb/mxsfb.txt
@@ -5,9 +5,16 @@ Required properties:
imx23 and imx28.
- reg: Address and length of the register set for lcdif
- interrupts: Should contain lcdif interrupts
+- display : phandle to display node (see below for details)
-Optional properties:
-- panel-enable-gpios : Should specify the gpio for panel enable
+* display node
+
+Required properties:
+- bits-per-pixel : <16> for RGB565, <32> for RGB888/666.
+- bus-width : number of data lines. Could be <8>, <16>, <18> or <24>.
+
+Required sub-node:
+- display-timings : Refer to binding doc display-timing.txt for details.
Examples:
@@ -15,5 +22,28 @@ lcdif@80030000 {
compatible = "fsl,imx28-lcdif";
reg = <0x80030000 2000>;
interrupts = <38 86>;
- panel-enable-gpios = <&gpio3 30 0>;
+
+ display: display {
+ bits-per-pixel = <32>;
+ bus-width = <24>;
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <33500000>;
+ hactive = <800>;
+ vactive = <480>;
+ hfront-porch = <164>;
+ hback-porch = <89>;
+ hsync-len = <10>;
+ vback-porch = <23>;
+ vfront-porch = <10>;
+ vsync-len = <10>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+ };
};
diff --git a/Documentation/devicetree/bindings/gpio/gpio-grgpio.txt b/Documentation/devicetree/bindings/gpio/gpio-grgpio.txt
new file mode 100644
index 000000000000..e466598105fc
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-grgpio.txt
@@ -0,0 +1,26 @@
+Aeroflex Gaisler GRGPIO General Purpose I/O cores.
+
+The GRGPIO GPIO core is available in the GRLIB VHDL IP core library.
+
+Note: In the ordinary environment for the GRGPIO core, a Leon SPARC system,
+these properties are built from information in the AMBA plug&play.
+
+Required properties:
+
+- name : Should be "GAISLER_GPIO" or "01_01a"
+
+- reg : Address and length of the register set for the device
+
+- interrupts : Interrupt numbers for this device
+
+Optional properties:
+
+- nbits : The number of gpio lines. If not present driver assumes 32 lines.
+
+- irqmap : An array with an index for each gpio line. An index is either a valid
+ index into the interrupts property array, or 0xffffffff that indicates
+ no irq for that line. Driver provides no interrupt support if not
+ present.
+
+For further information look in the documentation for the GLIB IP core library:
+http://www.gaisler.com/products/grlib/grip.pdf
diff --git a/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt b/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt
new file mode 100644
index 000000000000..629d0ef17308
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt
@@ -0,0 +1,47 @@
+Microchip MCP2308/MCP23S08/MCP23017/MCP23S17 driver for
+8-/16-bit I/O expander with serial interface (I2C/SPI)
+
+Required properties:
+- compatible : Should be
+ - "mcp,mcp23s08" for 8 GPIO SPI version
+ - "mcp,mcp23s17" for 16 GPIO SPI version
+ - "mcp,mcp23008" for 8 GPIO I2C version or
+ - "mcp,mcp23017" for 16 GPIO I2C version of the chip
+- #gpio-cells : Should be two.
+ - first cell is the pin number
+ - second cell is used to specify flags. Flags are currently unused.
+- gpio-controller : Marks the device node as a GPIO controller.
+- reg : For an address on its bus. I2C uses this a the I2C address of the chip.
+ SPI uses this to specify the chipselect line which the chip is
+ connected to. The driver and the SPI variant of the chip support
+ multiple chips on the same chipselect. Have a look at
+ mcp,spi-present-mask below.
+
+Required device specific properties (only for SPI chips):
+- mcp,spi-present-mask : This is a present flag, that makes only sense for SPI
+ chips - as the name suggests. Multiple SPI chips can share the same
+ SPI chipselect. Set a bit in bit0-7 in this mask to 1 if there is a
+ chip connected with the corresponding spi address set. For example if
+ you have a chip with address 3 connected, you have to set bit3 to 1,
+ which is 0x08. mcp23s08 chip variant only supports bits 0-3. It is not
+ possible to mix mcp23s08 and mcp23s17 on the same chipselect. Set at
+ least one bit to 1 for SPI chips.
+- spi-max-frequency = The maximum frequency this chip is able to handle
+
+Example I2C:
+gpiom1: gpio@20 {
+ compatible = "mcp,mcp23017";
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x20>;
+};
+
+Example SPI:
+gpiom1: gpio@0 {
+ compatible = "mcp,mcp23s17";
+ gpio-controller;
+ #gpio-cells = <2>;
+ spi-present-mask = <0x01>;
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+};
diff --git a/Documentation/devicetree/bindings/gpio/gpio-omap.txt b/Documentation/devicetree/bindings/gpio/gpio-omap.txt
index bff51a2fee1e..8d950522e7fa 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-omap.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-omap.txt
@@ -5,12 +5,12 @@ Required properties:
- "ti,omap2-gpio" for OMAP2 controllers
- "ti,omap3-gpio" for OMAP3 controllers
- "ti,omap4-gpio" for OMAP4 controllers
+- gpio-controller : Marks the device node as a GPIO controller.
- #gpio-cells : Should be two.
- first cell is the pin number
- second cell is used to specify optional parameters (unused)
-- gpio-controller : Marks the device node as a GPIO controller.
+- interrupt-controller: Mark the device node as an interrupt controller.
- #interrupt-cells : Should be 2.
-- interrupt-controller: Mark the device node as an interrupt controller
The first cell is the GPIO number.
The second cell is used to specify flags:
bits[3:0] trigger type and level flags:
@@ -20,8 +20,11 @@ Required properties:
8 = active low level-sensitive.
OMAP specific properties:
-- ti,hwmods: Name of the hwmod associated to the GPIO:
- "gpio<X>", <X> being the 1-based instance number from the HW spec
+- ti,hwmods: Name of the hwmod associated to the GPIO:
+ "gpio<X>", <X> being the 1-based instance number
+ from the HW spec.
+- ti,gpio-always-on: Indicates if a GPIO bank is always powered and
+ so will never lose its logic state.
Example:
@@ -29,8 +32,8 @@ Example:
gpio4: gpio4 {
compatible = "ti,omap4-gpio";
ti,hwmods = "gpio4";
- #gpio-cells = <2>;
gpio-controller;
- #interrupt-cells = <2>;
+ #gpio-cells = <2>;
interrupt-controller;
+ #interrupt-cells = <2>;
};
diff --git a/Documentation/devicetree/bindings/gpu/samsung-g2d.txt b/Documentation/devicetree/bindings/gpu/samsung-g2d.txt
new file mode 100644
index 000000000000..2b14a940eb75
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/samsung-g2d.txt
@@ -0,0 +1,20 @@
+* Samsung 2D Graphics Accelerator
+
+Required properties:
+ - compatible : value should be one among the following:
+ (a) "samsung,s5pv210-g2d" for G2D IP present in S5PV210 & Exynos4210 SoC
+ (b) "samsung,exynos4212-g2d" for G2D IP present in Exynos4x12 SoCs
+ (c) "samsung,exynos5250-g2d" for G2D IP present in Exynos5250 SoC
+
+ - reg : Physical base address of the IP registers and length of memory
+ mapped region.
+
+ - interrupts : G2D interrupt number to the CPU.
+
+Example:
+ g2d@12800000 {
+ compatible = "samsung,s5pv210-g2d";
+ reg = <0x12800000 0x1000>;
+ interrupts = <0 89 0>;
+ status = "disabled";
+ };
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mxs.txt b/Documentation/devicetree/bindings/i2c/i2c-mxs.txt
index 7a3fe9e5f4cb..4e1c8ac01eba 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-mxs.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-mxs.txt
@@ -3,10 +3,13 @@
Required properties:
- compatible: Should be "fsl,<chip>-i2c"
- reg: Should contain registers location and length
-- interrupts: Should contain ERROR and DMA interrupts
+- interrupts: Should contain ERROR interrupt number
- clock-frequency: Desired I2C bus clock frequency in Hz.
Only 100000Hz and 400000Hz modes are supported.
-- fsl,i2c-dma-channel: APBX DMA channel for the I2C
+- dmas: DMA specifier, consisting of a phandle to DMA controller node
+ and I2C DMA channel ID.
+ Refer to dma.txt and fsl-mxs-dma.txt for details.
+- dma-names: Must be "rx-tx".
Examples:
@@ -15,7 +18,8 @@ i2c0: i2c@80058000 {
#size-cells = <0>;
compatible = "fsl,imx28-i2c";
reg = <0x80058000 2000>;
- interrupts = <111 68>;
+ interrupts = <111>;
clock-frequency = <100000>;
- fsl,i2c-dma-channel = <6>;
+ dmas = <&dma_apbx 6>;
+ dma-names = "rx-tx";
};
diff --git a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.txt b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.txt
new file mode 100644
index 000000000000..ef77cc7a0e46
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.txt
@@ -0,0 +1,60 @@
+NVIDIA Tegra20/Tegra30/Tegra114 I2C controller driver.
+
+Required properties:
+- compatible : should be:
+ "nvidia,tegra114-i2c"
+ "nvidia,tegra30-i2c"
+ "nvidia,tegra20-i2c"
+ "nvidia,tegra20-i2c-dvc"
+ Details of compatible are as follows:
+ nvidia,tegra20-i2c-dvc: Tegra20 has specific I2C controller called as DVC I2C
+ controller. This only support master mode of I2C communication. Register
+ interface/offset and interrupts handling are different than generic I2C
+ controller. Driver of DVC I2C controller is only compatible with
+ "nvidia,tegra20-i2c-dvc".
+ nvidia,tegra20-i2c: Tegra20 has 4 generic I2C controller. This can support
+ master and slave mode of I2C communication. The i2c-tegra driver only
+ support master mode of I2C communication. Driver of I2C controller is
+ only compatible with "nvidia,tegra20-i2c".
+ nvidia,tegra30-i2c: Tegra30 has 5 generic I2C controller. This controller is
+ very much similar to Tegra20 I2C controller with additional feature:
+ Continue Transfer Support. This feature helps to implement M_NO_START
+ as per I2C core API transfer flags. Driver of I2C controller is
+ compatible with "nvidia,tegra30-i2c" to enable the continue transfer
+ support. This is also compatible with "nvidia,tegra20-i2c" without
+ continue transfer support.
+ nvidia,tegra114-i2c: Tegra114 has 5 generic I2C controller. This controller is
+ very much similar to Tegra30 I2C controller with some hardware
+ modification:
+ - Tegra30/Tegra20 I2C controller has 2 clock source called div-clk and
+ fast-clk. Tegra114 has only one clock source called as div-clk and
+ hence clock mechanism is changed in I2C controller.
+ - Tegra30/Tegra20 I2C controller has enabled per packet transfer by
+ default and there is no way to disable it. Tegra114 has this
+ interrupt disable by default and SW need to enable explicitly.
+ Due to above changes, Tegra114 I2C driver makes incompatible with
+ previous hardware driver. Hence, tegra114 I2C controller is compatible
+ with "nvidia,tegra114-i2c".
+- reg: Should contain I2C controller registers physical address and length.
+- interrupts: Should contain I2C controller interrupts.
+- address-cells: Address cells for I2C device address.
+- size-cells: Size of the I2C device address.
+- clocks: Clock ID as per
+ Documentation/devicetree/bindings/clock/tegra<chip-id>.txt
+ for I2C controller.
+- clock-names: Name of the clock:
+ Tegra20/Tegra30 I2C controller: "div-clk and "fast-clk".
+ Tegra114 I2C controller: "div-clk".
+
+Example:
+
+ i2c@7000c000 {
+ compatible = "nvidia,tegra20-i2c";
+ reg = <0x7000c000 0x100>;
+ interrupts = <0 38 0x04>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 12>, <&tegra_car 124>;
+ clock-names = "div-clk", "fast-clk";
+ status = "disabled";
+ };
diff --git a/Documentation/devicetree/bindings/mmc/mxs-mmc.txt b/Documentation/devicetree/bindings/mmc/mxs-mmc.txt
index 54949f6faede..515addc20070 100644
--- a/Documentation/devicetree/bindings/mmc/mxs-mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/mxs-mmc.txt
@@ -9,15 +9,19 @@ and the properties used by the mxsmmc driver.
Required properties:
- compatible: Should be "fsl,<chip>-mmc". The supported chips include
imx23 and imx28.
-- interrupts: Should contain ERROR and DMA interrupts
-- fsl,ssp-dma-channel: APBH DMA channel for the SSP
+- interrupts: Should contain ERROR interrupt number
+- dmas: DMA specifier, consisting of a phandle to DMA controller node
+ and SSP DMA channel ID.
+ Refer to dma.txt and fsl-mxs-dma.txt for details.
+- dma-names: Must be "rx-tx".
Examples:
ssp0: ssp@80010000 {
compatible = "fsl,imx28-mmc";
reg = <0x80010000 2000>;
- interrupts = <96 82>;
- fsl,ssp-dma-channel = <0>;
+ interrupts = <96>;
+ dmas = <&dma_apbh 0>;
+ dma-names = "rx-tx";
bus-width = <8>;
};
diff --git a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt
index 3fb3f9015365..551b2a179d01 100644
--- a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt
@@ -7,10 +7,12 @@ Required properties:
- compatible : should be "fsl,<chip>-gpmi-nand"
- reg : should contain registers location and length for gpmi and bch.
- reg-names: Should contain the reg names "gpmi-nand" and "bch"
- - interrupts : The first is the DMA interrupt number for GPMI.
- The second is the BCH interrupt number.
- - interrupt-names : The interrupt names "gpmi-dma", "bch";
- - fsl,gpmi-dma-channel : Should contain the dma channel it uses.
+ - interrupts : BCH interrupt number.
+ - interrupt-names : Should be "bch".
+ - dmas: DMA specifier, consisting of a phandle to DMA controller node
+ and GPMI DMA channel ID.
+ Refer to dma.txt and fsl-mxs-dma.txt for details.
+ - dma-names: Must be "rx-tx".
Optional properties:
- nand-on-flash-bbt: boolean to enable on flash bbt option if not
@@ -27,9 +29,10 @@ gpmi-nand@8000c000 {
#size-cells = <1>;
reg = <0x8000c000 2000>, <0x8000a000 2000>;
reg-names = "gpmi-nand", "bch";
- interrupts = <88>, <41>;
- interrupt-names = "gpmi-dma", "bch";
- fsl,gpmi-dma-channel = <4>;
+ interrupts = <41>;
+ interrupt-names = "bch";
+ dmas = <&dma_apbh 4>;
+ dma-names = "rx-tx";
partition@0 {
...
diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,mxs-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,mxs-pinctrl.txt
index f7e8e8f4d9a3..3077370c89af 100644
--- a/Documentation/devicetree/bindings/pinctrl/fsl,mxs-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/fsl,mxs-pinctrl.txt
@@ -70,6 +70,10 @@ Optional subnode-properties:
0: Disable the internal pull-up
1: Enable the internal pull-up
+Note that when enabling the pull-up, the internal pad keeper gets disabled.
+Also, some pins doesn't have a pull up, in that case, setting the fsl,pull-up
+will only disable the internal pad keeper.
+
Examples:
pinctrl@80018000 {
diff --git a/Documentation/devicetree/bindings/pwm/pwm-samsung.txt b/Documentation/devicetree/bindings/pwm/pwm-samsung.txt
new file mode 100644
index 000000000000..ac67c687a327
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-samsung.txt
@@ -0,0 +1,43 @@
+* Samsung PWM timers
+
+Samsung SoCs contain PWM timer blocks which can be used for system clock source
+and clock event timers, as well as to drive SoC outputs with PWM signal. Each
+PWM timer block provides 5 PWM channels (not all of them can drive physical
+outputs - see SoC and board manual).
+
+Be aware that the clocksource driver supports only uniprocessor systems.
+
+Required properties:
+- compatible : should be one of following:
+ samsung,s3c2410-pwm - for 16-bit timers present on S3C24xx SoCs
+ samsung,s3c6400-pwm - for 32-bit timers present on S3C64xx SoCs
+ samsung,s5p6440-pwm - for 32-bit timers present on S5P64x0 SoCs
+ samsung,s5pc100-pwm - for 32-bit timers present on S5PC100, S5PV210,
+ Exynos4210 rev0 SoCs
+ samsung,exynos4210-pwm - for 32-bit timers present on Exynos4210,
+ Exynos4x12 and Exynos5250 SoCs
+- reg: base address and size of register area
+- interrupts: list of timer interrupts (one interrupt per timer, starting at
+ timer 0)
+- #pwm-cells: number of cells used for PWM specifier - must be 3
+ the specifier format is as follows:
+ - phandle to PWM controller node
+ - index of PWM channel (from 0 to 4)
+ - PWM signal period in nanoseconds
+ - bitmask of optional PWM flags:
+ 0x1 - invert PWM signal
+
+Optional properties:
+- samsung,pwm-outputs: list of PWM channels used as PWM outputs on particular
+ platform - an array of up to 5 elements being indices of PWM channels
+ (from 0 to 4), the order does not matter.
+
+Example:
+ pwm@7f006000 {
+ compatible = "samsung,s3c6400-pwm";
+ reg = <0x7f006000 0x1000>;
+ interrupt-parent = <&vic0>;
+ interrupts = <23>, <24>, <25>, <27>, <28>;
+ samsung,pwm-outputs = <0>, <1>;
+ #pwm-cells = <3>;
+ }
diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt
index 131e8c11d26f..681afad73778 100644
--- a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt
@@ -1,7 +1,9 @@
TI SOC ECAP based APWM controller
Required properties:
-- compatible: Must be "ti,am33xx-ecap"
+- compatible: Must be "ti,<soc>-ecap".
+ for am33xx - compatible = "ti,am33xx-ecap";
+ for da850 - compatible = "ti,da850-ecap", "ti,am33xx-ecap";
- #pwm-cells: Should be 3. Number of cells being used to specify PWM property.
First cell specifies the per-chip index of the PWM to use, the second
cell is the period in nanoseconds and bit 0 in the third cell is used to
@@ -15,9 +17,15 @@ Optional properties:
Example:
-ecap0: ecap@0 {
+ecap0: ecap@0 { /* ECAP on am33xx */
compatible = "ti,am33xx-ecap";
#pwm-cells = <3>;
reg = <0x48300100 0x80>;
ti,hwmods = "ecap0";
};
+
+ecap0: ecap@0 { /* ECAP on da850 */
+ compatible = "ti,da850-ecap", "ti,am33xx-ecap";
+ #pwm-cells = <3>;
+ reg = <0x306000 0x80>;
+};
diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt
index 4fc7079d822e..337c6fc65d3f 100644
--- a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt
@@ -1,7 +1,9 @@
TI SOC EHRPWM based PWM controller
Required properties:
-- compatible : Must be "ti,am33xx-ehrpwm"
+- compatible: Must be "ti,<soc>-ehrpwm".
+ for am33xx - compatible = "ti,am33xx-ehrpwm";
+ for da850 - compatible = "ti,da850-ehrpwm", "ti,am33xx-ehrpwm";
- #pwm-cells: Should be 3. Number of cells being used to specify PWM property.
First cell specifies the per-chip index of the PWM to use, the second
cell is the period in nanoseconds and bit 0 in the third cell is used to
@@ -15,9 +17,15 @@ Optional properties:
Example:
-ehrpwm0: ehrpwm@0 {
+ehrpwm0: ehrpwm@0 { /* EHRPWM on am33xx */
compatible = "ti,am33xx-ehrpwm";
#pwm-cells = <3>;
reg = <0x48300200 0x100>;
ti,hwmods = "ehrpwm0";
};
+
+ehrpwm0: ehrpwm@0 { /* EHRPWM on da850 */
+ compatible = "ti,da850-ehrpwm", "ti,am33xx-ehrpwm";
+ #pwm-cells = <3>;
+ reg = <0x300000 0x2000>;
+};
diff --git a/Documentation/devicetree/bindings/reset/fsl,imx-src.txt b/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
new file mode 100644
index 000000000000..13301777e11c
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
@@ -0,0 +1,49 @@
+Freescale i.MX System Reset Controller
+======================================
+
+Please also refer to reset.txt in this directory for common reset
+controller binding usage.
+
+Required properties:
+- compatible: Should be "fsl,<chip>-src"
+- reg: should be register base and length as documented in the
+ datasheet
+- interrupts: Should contain SRC interrupt and CPU WDOG interrupt,
+ in this order.
+- #reset-cells: 1, see below
+
+example:
+
+src: src@020d8000 {
+ compatible = "fsl,imx6q-src";
+ reg = <0x020d8000 0x4000>;
+ interrupts = <0 91 0x04 0 96 0x04>;
+ #reset-cells = <1>;
+};
+
+Specifying reset lines connected to IP modules
+==============================================
+
+The system reset controller can be used to reset the GPU, VPU,
+IPU, and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device
+nodes should specify the reset line on the SRC in their resets
+property, containing a phandle to the SRC device node and a
+RESET_INDEX specifying which module to reset, as described in
+reset.txt
+
+example:
+
+ ipu1: ipu@02400000 {
+ resets = <&src 2>;
+ };
+ ipu2: ipu@02800000 {
+ resets = <&src 4>;
+ };
+
+The following RESET_INDEX values are valid for i.MX5:
+GPU_RESET 0
+VPU_RESET 1
+IPU1_RESET 2
+OPEN_VG_RESET 3
+The following additional RESET_INDEX value is valid for i.MX6:
+IPU2_RESET 4
diff --git a/Documentation/devicetree/bindings/serial/pl011.txt b/Documentation/devicetree/bindings/serial/pl011.txt
new file mode 100644
index 000000000000..5d2e840ae65c
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/pl011.txt
@@ -0,0 +1,17 @@
+* ARM AMBA Primecell PL011 serial UART
+
+Required properties:
+- compatible: must be "arm,primecell", "arm,pl011"
+- reg: exactly one register range with length 0x1000
+- interrupts: exactly one interrupt specifier
+
+Optional properties:
+- pinctrl: When present, must have one state named "sleep"
+ and one state named "default"
+- clocks: When present, must refer to exactly one clock named
+ "apb_pclk"
+- dmas: When present, may have one or two dma channels.
+ The first one must be named "rx", the second one
+ must be named "tx".
+
+See also bindings/arm/primecell.txt
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt
index b77a97c9101e..05ffecb57103 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt
@@ -2,6 +2,11 @@ NVIDIA Tegra audio complex
Required properties:
- compatible : "nvidia,tegra-audio-alc5632"
+- clocks : Must contain an entry for each entry in clock-names.
+- clock-names : Must include the following entries:
+ "pll_a" (The Tegra clock of that name),
+ "pll_a_out0" (The Tegra clock of that name),
+ "mclk" (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk)
- nvidia,model : The user-visible name of this sound complex.
- nvidia,audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
@@ -56,4 +61,7 @@ sound {
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,audio-codec = <&alc5632>;
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt
index 04b14cfb1f16..ef1fe7358279 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-trimslice.txt
@@ -2,6 +2,11 @@ NVIDIA Tegra audio complex for TrimSlice
Required properties:
- compatible : "nvidia,tegra-audio-trimslice"
+- clocks : Must contain an entry for each entry in clock-names.
+- clock-names : Must include the following entries:
+ "pll_a" (The Tegra clock of that name),
+ "pll_a_out0" (The Tegra clock of that name),
+ "mclk" (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk)
- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller
- nvidia,audio-codec : The phandle of the WM8903 audio codec
@@ -11,4 +16,6 @@ sound {
compatible = "nvidia,tegra-audio-trimslice";
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,audio-codec = <&codec>;
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt
index c4dd39ce6165..d14510613a7f 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt
@@ -2,6 +2,11 @@ NVIDIA Tegra audio complex
Required properties:
- compatible : "nvidia,tegra-audio-wm8753"
+- clocks : Must contain an entry for each entry in clock-names.
+- clock-names : Must include the following entries:
+ "pll_a" (The Tegra clock of that name),
+ "pll_a_out0" (The Tegra clock of that name),
+ "mclk" (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk)
- nvidia,model : The user-visible name of this sound complex.
- nvidia,audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
@@ -50,5 +55,8 @@ sound {
nvidia,i2s-controller = <&i2s1>;
nvidia,audio-codec = <&wm8753>;
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt
index d5b0da8bf1d8..3bf722deb722 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt
@@ -2,6 +2,11 @@ NVIDIA Tegra audio complex
Required properties:
- compatible : "nvidia,tegra-audio-wm8903"
+- clocks : Must contain an entry for each entry in clock-names.
+- clock-names : Must include the following entries:
+ "pll_a" (The Tegra clock of that name),
+ "pll_a_out0" (The Tegra clock of that name),
+ "mclk" (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk)
- nvidia,model : The user-visible name of this sound complex.
- nvidia,audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
@@ -67,5 +72,8 @@ sound {
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
nvidia,int-mic-en-gpios = <&gpio 184 0>; /*gpio PX0 */
nvidia,ext-mic-en-gpios = <&gpio 185 0>; /* gpio PX1 */
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
index be35d34e8b26..ad589b163639 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
@@ -2,6 +2,11 @@ NVIDIA Tegra audio complex
Required properties:
- compatible : "nvidia,tegra-audio-wm9712"
+- clocks : Must contain an entry for each entry in clock-names.
+- clock-names : Must include the following entries:
+ "pll_a" (The Tegra clock of that name),
+ "pll_a_out0" (The Tegra clock of that name),
+ "mclk" (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk)
- nvidia,model : The user-visible name of this sound complex.
- nvidia,audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
@@ -48,4 +53,7 @@ sound {
"Mic", "MIC1";
nvidia,ac97-controller = <&ac97>;
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 93>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
diff --git a/Documentation/devicetree/bindings/spi/mxs-spi.txt b/Documentation/devicetree/bindings/spi/mxs-spi.txt
index e2e13957c2a4..3499b73293c2 100644
--- a/Documentation/devicetree/bindings/spi/mxs-spi.txt
+++ b/Documentation/devicetree/bindings/spi/mxs-spi.txt
@@ -3,8 +3,11 @@
Required properties:
- compatible: Should be "fsl,<soc>-spi", where soc is "imx23" or "imx28"
- reg: Offset and length of the register set for the device
-- interrupts: Should contain SSP interrupts (error irq first, dma irq second)
-- fsl,ssp-dma-channel: APBX DMA channel for the SSP
+- interrupts: Should contain SSP ERROR interrupt
+- dmas: DMA specifier, consisting of a phandle to DMA controller node
+ and SSP DMA channel ID.
+ Refer to dma.txt and fsl-mxs-dma.txt for details.
+- dma-names: Must be "rx-tx".
Optional properties:
- clock-frequency : Input clock frequency to the SPI block in Hz.
@@ -17,6 +20,7 @@ ssp0: ssp@80010000 {
#size-cells = <0>;
compatible = "fsl,imx28-spi";
reg = <0x80010000 0x2000>;
- interrupts = <96 82>;
- fsl,ssp-dma-channel = <0>;
+ interrupts = <96>;
+ dmas = <&dma_apbh 0>;
+ dma-names = "rx-tx";
};
diff --git a/Documentation/devicetree/bindings/spi/spi-davinci.txt b/Documentation/devicetree/bindings/spi/spi-davinci.txt
new file mode 100644
index 000000000000..6d0ac8d0ad9b
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-davinci.txt
@@ -0,0 +1,51 @@
+Davinci SPI controller device bindings
+
+Required properties:
+- #address-cells: number of cells required to define a chip select
+ address on the SPI bus. Should be set to 1.
+- #size-cells: should be zero.
+- compatible:
+ - "ti,dm6441-spi" for SPI used similar to that on DM644x SoC family
+ - "ti,da830-spi" for SPI used similar to that on DA8xx SoC family
+- reg: Offset and length of SPI controller register space
+- num-cs: Number of chip selects
+- ti,davinci-spi-intr-line: interrupt line used to connect the SPI
+ IP to the interrupt controller within the SoC. Possible values
+ are 0 and 1. Manual says one of the two possible interrupt
+ lines can be tied to the interrupt controller. Set this
+ based on a specifc SoC configuration.
+- interrupts: interrupt number mapped to CPU.
+- clocks: spi clk phandle
+
+Example of a NOR flash slave device (n25q032) connected to DaVinci
+SPI controller device over the SPI bus.
+
+spi0:spi@20BF0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "ti,dm6446-spi";
+ reg = <0x20BF0000 0x1000>;
+ num-cs = <4>;
+ ti,davinci-spi-intr-line = <0>;
+ interrupts = <338>;
+ clocks = <&clkspi>;
+
+ flash: n25q032@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,m25p32";
+ spi-max-frequency = <25000000>;
+ reg = <0>;
+
+ partition@0 {
+ label = "u-boot-spl";
+ reg = <0x0 0x80000>;
+ read-only;
+ };
+
+ partition@1 {
+ label = "test";
+ reg = <0x80000 0x380000>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/spi/spi_pl022.txt b/Documentation/devicetree/bindings/spi/spi_pl022.txt
index f158fd31cfda..22ed6797216d 100644
--- a/Documentation/devicetree/bindings/spi/spi_pl022.txt
+++ b/Documentation/devicetree/bindings/spi/spi_pl022.txt
@@ -16,6 +16,11 @@ Optional properties:
device will be suspended immediately
- pl022,rt : indicates the controller should run the message pump with realtime
priority to minimise the transfer latency on the bus (boolean)
+- dmas : Two or more DMA channel specifiers following the convention outlined
+ in bindings/dma/dma.txt
+- dma-names: Names for the dma channels, if present. There must be at
+ least one channel named "tx" for transmit and named "rx" for
+ receive.
SPI slave nodes must be children of the SPI master node and can
@@ -32,3 +37,34 @@ contain the following properties.
- pl022,wait-state : Microwire interface: Wait state
- pl022,duplex : Microwire interface: Full/Half duplex
+
+Example:
+
+ spi@e0100000 {
+ compatible = "arm,pl022", "arm,primecell";
+ reg = <0xe0100000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0 31 0x4>;
+ dmas = <&dma-controller 23 1>,
+ <&dma-controller 24 0>;
+ dma-names = "rx", "tx";
+
+ m25p80@1 {
+ compatible = "st,m25p80";
+ reg = <1>;
+ spi-max-frequency = <12000000>;
+ spi-cpol;
+ spi-cpha;
+ pl022,hierarchy = <0>;
+ pl022,interface = <0>;
+ pl022,slave-tx-disable;
+ pl022,com-mode = <0x2>;
+ pl022,rx-level-trig = <0>;
+ pl022,tx-level-trig = <0>;
+ pl022,ctrl-len = <0x11>;
+ pl022,wait-state = <0>;
+ pl022,duplex = <0>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt b/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt
index 8071ac20d4b3..b876d4925a57 100644
--- a/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt
+++ b/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt
@@ -8,6 +8,8 @@ Required properties:
- interrupts: Should contain sync interrupt and error interrupt,
in this order.
- #crtc-cells: 1, See below
+- resets: phandle pointing to the system reset controller and
+ reset line index, see reset/fsl,imx-src.txt for details
example:
@@ -16,6 +18,7 @@ ipu: ipu@18000000 {
compatible = "fsl,imx53-ipu";
reg = <0x18000000 0x080000000>;
interrupts = <11 10>;
+ resets = <&src 2>;
};
Parallel display support
diff --git a/Documentation/devicetree/bindings/timer/arm,sp804.txt b/Documentation/devicetree/bindings/timer/arm,sp804.txt
new file mode 100644
index 000000000000..5cd8eee74af1
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/arm,sp804.txt
@@ -0,0 +1,29 @@
+ARM sp804 Dual Timers
+---------------------------------------
+
+Required properties:
+- compatible: Should be "arm,sp804" & "arm,primecell"
+- interrupts: Should contain the list of Dual Timer interrupts. This is the
+ interrupt for timer 1 and timer 2. In the case of a single entry, it is
+ the combined interrupt or if "arm,sp804-has-irq" is present that
+ specifies which timer interrupt is connected.
+- reg: Should contain location and length for dual timer register.
+- clocks: clocks driving the dual timer hardware. This list should be 1 or 3
+ clocks. With 3 clocks, the order is timer0 clock, timer1 clock,
+ apb_pclk. A single clock can also be specified if the same clock is
+ used for all clock inputs.
+
+Optional properties:
+- arm,sp804-has-irq = <#>: In the case of only 1 timer irq line connected, this
+ specifies if the irq connection is for timer 1 or timer 2. A value of 1
+ or 2 should be used.
+
+Example:
+
+ timer0: timer@fc800000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0xfc800000 0x1000>;
+ interrupts = <0 0 4>, <0 1 4>;
+ clocks = <&timclk1 &timclk2 &pclk>;
+ clock-names = "timer1", "timer2", "apb_pclk";
+ };
diff --git a/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt b/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt
index 273a8d5b3300..2c00ec64628e 100644
--- a/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt
+++ b/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt
@@ -5,20 +5,18 @@ Required properties:
imx23 and imx28.
- reg : Address and length of the register set for the device
- interrupts : Should contain the auart interrupt numbers
-
-Optional properties:
-- fsl,auart-dma-channel : The DMA channels, the first is for RX, the other
- is for TX. If you add this property, it also means that you
- will enable the DMA support for the auart.
- Note: due to the hardware bug in imx23(see errata : 2836),
- only the imx28 can enable the DMA support for the auart.
+- dmas: DMA specifier, consisting of a phandle to DMA controller node
+ and AUART DMA channel ID.
+ Refer to dma.txt and fsl-mxs-dma.txt for details.
+- dma-names: "rx" for RX channel, "tx" for TX channel.
Example:
auart0: serial@8006a000 {
compatible = "fsl,imx28-auart", "fsl,imx23-auart";
reg = <0x8006a000 0x2000>;
- interrupts = <112 70 71>;
- fsl,auart-dma-channel = <8 9>;
+ interrupts = <112>;
+ dmas = <&dma_apbx 8>, <&dma_apbx 9>;
+ dma-names = "rx", "tx";
};
Note: Each auart port should have an alias correctly numbered in "aliases"
diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
index f66fcddba46f..b3abde736017 100644
--- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
+++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
@@ -10,6 +10,8 @@ Required properties:
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
+ - clocks: from common clock binding: handle to usb clock.
+ - clock-names: from common clock binding: Shall be "usbhost".
Optional properties:
- samsung,vbus-gpio: if present, specifies the GPIO that
@@ -22,6 +24,9 @@ Example:
reg = <0x12110000 0x100>;
interrupts = <0 71 0>;
samsung,vbus-gpio = <&gpx2 6 1 3 3>;
+
+ clocks = <&clock 285>;
+ clock-names = "usbhost";
};
OHCI
@@ -31,10 +36,15 @@ Required properties:
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
+ - clocks: from common clock binding: handle to usb clock.
+ - clock-names: from common clock binding: Shall be "usbhost".
Example:
usb@12120000 {
compatible = "samsung,exynos4210-ohci";
reg = <0x12120000 0x100>;
interrupts = <0 71 0>;
+
+ clocks = <&clock 285>;
+ clock-names = "usbhost";
};
diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt
index 662f0f1d2315..d4769f343d6c 100644
--- a/Documentation/devicetree/bindings/usb/omap-usb.txt
+++ b/Documentation/devicetree/bindings/usb/omap-usb.txt
@@ -18,6 +18,7 @@ OMAP MUSB GLUE
represents PERIPHERAL.
- power : Should be "50". This signifies the controller can supply upto
100mA when operating in host mode.
+ - usb-phy : the phandle for the PHY device
Optional properties:
- ctrl-module : phandle of the control module this glue uses to write to
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt
new file mode 100644
index 000000000000..778838a0336a
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt
@@ -0,0 +1,65 @@
+Device-Tree bindings for Samsung SoC display controller (FIMD)
+
+FIMD (Fully Interactive Mobile Display) is the Display Controller for the
+Samsung series of SoCs which transfers the image data from a video memory
+buffer to an external LCD interface.
+
+Required properties:
+- compatible: value should be one of the following
+ "samsung,s3c2443-fimd"; /* for S3C24XX SoCs */
+ "samsung,s3c6400-fimd"; /* for S3C64XX SoCs */
+ "samsung,s5p6440-fimd"; /* for S5P64X0 SoCs */
+ "samsung,s5pc100-fimd"; /* for S5PC100 SoC */
+ "samsung,s5pv210-fimd"; /* for S5PV210 SoC */
+ "samsung,exynos4210-fimd"; /* for Exynos4 SoCs */
+ "samsung,exynos5250-fimd"; /* for Exynos5 SoCs */
+
+- reg: physical base address and length of the FIMD registers set.
+
+- interrupt-parent: should be the phandle of the fimd controller's
+ parent interrupt controller.
+
+- interrupts: should contain a list of all FIMD IP block interrupts in the
+ order: FIFO Level, VSYNC, LCD_SYSTEM. The interrupt specifier
+ format depends on the interrupt controller used.
+
+- interrupt-names: should contain the interrupt names: "fifo", "vsync",
+ "lcd_sys", in the same order as they were listed in the interrupts
+ property.
+
+- pinctrl-0: pin control group to be used for this controller.
+
+- pinctrl-names: must contain a "default" entry.
+
+- clocks: must include clock specifiers corresponding to entries in the
+ clock-names property.
+
+- clock-names: list of clock names sorted in the same order as the clocks
+ property. Must contain "sclk_fimd" and "fimd".
+
+Optional Properties:
+- samsung,power-domain: a phandle to FIMD power domain node.
+
+Example:
+
+SoC specific DT entry:
+
+ fimd@11c00000 {
+ compatible = "samsung,exynos4210-fimd";
+ interrupt-parent = <&combiner>;
+ reg = <0x11c00000 0x20000>;
+ interrupt-names = "fifo", "vsync", "lcd_sys";
+ interrupts = <11 0>, <11 1>, <11 2>;
+ clocks = <&clock 140>, <&clock 283>;
+ clock-names = "sclk_fimd", "fimd";
+ samsung,power-domain = <&pd_lcd0>;
+ status = "disabled";
+ };
+
+Board specific DT entry:
+
+ fimd@11c00000 {
+ pinctrl-0 = <&lcd_clk &lcd_data24 &pwm1_out>;
+ pinctrl-names = "default";
+ status = "okay";
+ };
diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
index dcf338e62b71..bd3c56c67380 100644
--- a/Documentation/filesystems/f2fs.txt
+++ b/Documentation/filesystems/f2fs.txt
@@ -146,7 +146,7 @@ USAGE
Format options
--------------
--l [label] : Give a volume label, up to 256 unicode name.
+-l [label] : Give a volume label, up to 512 unicode name.
-a [0 or 1] : Split start location of each area for heap-based allocation.
1 is set by default, which performs this.
-o [int] : Set overprovision ratio in percent over volume size.
@@ -156,6 +156,8 @@ Format options
-z [int] : Set the number of sections per zone.
1 is set by default.
-e [str] : Set basic extension list. e.g. "mp3,gif,mov"
+-t [0 or 1] : Disable discard command or not.
+ 1 is set by default, which conducts discard.
================================================================================
DESIGN
diff --git a/Documentation/kbuild/kconfig.txt b/Documentation/kbuild/kconfig.txt
index b8b77bbc784f..3f429ed8b3b8 100644
--- a/Documentation/kbuild/kconfig.txt
+++ b/Documentation/kbuild/kconfig.txt
@@ -90,6 +90,42 @@ disable the options that are explicitly listed in the specified
mini-config files.
______________________________________________________________________
+Environment variables for 'randconfig'
+
+KCONFIG_SEED
+--------------------------------------------------
+You can set this to the integer value used to seed the RNG, if you want
+to somehow debug the behaviour of the kconfig parser/frontends.
+If not set, the current time will be used.
+
+KCONFIG_PROBABILITY
+--------------------------------------------------
+This variable can be used to skew the probabilities. This variable can
+be unset or empty, or set to three different formats:
+ KCONFIG_PROBABILITY y:n split y:m:n split
+ -----------------------------------------------------------------
+ unset or empty 50 : 50 33 : 33 : 34
+ N N : 100-N N/2 : N/2 : 100-N
+ [1] N:M N+M : 100-(N+M) N : M : 100-(N+M)
+ [2] N:M:L N : 100-N M : L : 100-(M+L)
+
+where N, M and L are integers (in base 10) in the range [0,100], and so
+that:
+ [1] N+M is in the range [0,100]
+ [2] M+L is in the range [0,100]
+
+Examples:
+ KCONFIG_PROBABILITY=10
+ 10% of booleans will be set to 'y', 90% to 'n'
+ 5% of tristates will be set to 'y', 5% to 'm', 90% to 'n'
+ KCONFIG_PROBABILITY=15:25
+ 40% of booleans will be set to 'y', 60% to 'n'
+ 15% of tristates will be set to 'y', 25% to 'm', 60% to 'n'
+ KCONFIG_PROBABILITY=10:15:15
+ 10% of booleans will be set to 'y', 90% to 'n'
+ 15% of tristates will be set to 'y', 15% to 'm', 70% to 'n'
+
+______________________________________________________________________
Environment variables for 'silentoldconfig'
KCONFIG_NOSILENTUPDATE
diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt
index 5198b742fde1..d567a7cc552b 100644
--- a/Documentation/kbuild/makefiles.txt
+++ b/Documentation/kbuild/makefiles.txt
@@ -593,7 +593,7 @@ more details, with real examples.
Example:
#Makefile
- LDFLAGS_vmlinux += $(call really-ld-option, -X)
+ LDFLAGS_vmlinux += $(call ld-option, -X)
=== 4 Host Program support
@@ -921,8 +921,9 @@ When kbuild executes, the following steps are followed (roughly):
Often, the KBUILD_CFLAGS variable depends on the configuration.
Example:
- #arch/x86/Makefile
- cflags-$(CONFIG_M386) += -march=i386
+ #arch/x86/boot/compressed/Makefile
+ cflags-$(CONFIG_X86_32) := -march=i386
+ cflags-$(CONFIG_X86_64) := -mcmodel=small
KBUILD_CFLAGS += $(cflags-y)
Many arch Makefiles dynamically run the target C compiler to
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 8920f9f5fa9e..c3bfacb92910 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1277,6 +1277,20 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
iucv= [HW,NET]
+ ivrs_ioapic [HW,X86_64]
+ Provide an override to the IOAPIC-ID<->DEVICE-ID
+ mapping provided in the IVRS ACPI table. For
+ example, to map IOAPIC-ID decimal 10 to
+ PCI device 00:14.0 write the parameter as:
+ ivrs_ioapic[10]=00:14.0
+
+ ivrs_hpet [HW,X86_64]
+ Provide an override to the HPET-ID<->DEVICE-ID
+ mapping provided in the IVRS ACPI table. For
+ example, to map HPET-ID decimal 0 to
+ PCI device 00:14.0 write the parameter as:
+ ivrs_hpet[0]=00:14.0
+
js= [HW,JOY] Analog joystick
See Documentation/input/joystick.txt.
diff --git a/Documentation/leds/00-INDEX b/Documentation/leds/00-INDEX
index 5246090ef15c..1ecd1596633e 100644
--- a/Documentation/leds/00-INDEX
+++ b/Documentation/leds/00-INDEX
@@ -6,6 +6,8 @@ leds-lp5521.txt
- notes on how to use the leds-lp5521 driver.
leds-lp5523.txt
- notes on how to use the leds-lp5523 driver.
+leds-lp5562.txt
+ - notes on how to use the leds-lp5562 driver.
leds-lp55xx.txt
- description about lp55xx common driver.
leds-lm3556.txt
diff --git a/Documentation/leds/leds-lp5521.txt b/Documentation/leds/leds-lp5521.txt
index 270f57196339..79e4c2e6e5e8 100644
--- a/Documentation/leds/leds-lp5521.txt
+++ b/Documentation/leds/leds-lp5521.txt
@@ -81,22 +81,3 @@ static struct lp55xx_platform_data lp5521_platform_data = {
If the current is set to 0 in the platform data, that channel is
disabled and it is not visible in the sysfs.
-
-The 'update_config' : CONFIG register (ADDR 08h)
-This value is platform-specific data.
-If update_config is not defined, the CONFIG register is set with
-'LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT'.
-(Enable auto-powersave, set charge pump to auto, red to battery)
-
-example of update_config :
-
-#define LP5521_CONFIGS (LP5521_PWM_HF | LP5521_PWRSAVE_EN | \
- LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT | \
- LP5521_CLK_INT)
-
-static struct lp55xx_platform_data lp5521_pdata = {
- .led_config = lp5521_led_config,
- .num_channels = ARRAY_SIZE(lp5521_led_config),
- .clock_mode = LP55XX_CLOCK_INT,
- .update_config = LP5521_CONFIGS,
-};
diff --git a/Documentation/leds/leds-lp5562.txt b/Documentation/leds/leds-lp5562.txt
new file mode 100644
index 000000000000..5a823ff6b393
--- /dev/null
+++ b/Documentation/leds/leds-lp5562.txt
@@ -0,0 +1,120 @@
+Kernel driver for LP5562
+========================
+
+* TI LP5562 LED Driver
+
+Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+
+Description
+
+ LP5562 can drive up to 4 channels. R/G/B and White.
+ LEDs can be controlled directly via the led class control interface.
+
+ All four channels can be also controlled using the engine micro programs.
+ LP5562 has the internal program memory for running various LED patterns.
+ For the details, please refer to 'firmware' section in leds-lp55xx.txt
+
+Device attribute: engine_mux
+
+ 3 Engines are allocated in LP5562, but the number of channel is 4.
+ Therefore each channel should be mapped to the engine number.
+ Value : RGB or W
+
+ This attribute is used for programming LED data with the firmware interface.
+ Unlike the LP5521/LP5523/55231, LP5562 has unique feature for the engine mux,
+ so additional sysfs is required.
+
+ LED Map
+ Red ... Engine 1 (fixed)
+ Green ... Engine 2 (fixed)
+ Blue ... Engine 3 (fixed)
+ White ... Engine 1 or 2 or 3 (selective)
+
+How to load the program data using engine_mux
+
+ Before loading the LP5562 program data, engine_mux should be written between
+ the engine selection and loading the firmware.
+ Engine mux has two different mode, RGB and W.
+ RGB is used for loading RGB program data, W is used for W program data.
+
+ For example, run blinking green channel pattern,
+ echo 2 > /sys/bus/i2c/devices/xxxx/select_engine # 2 is for green channel
+ echo "RGB" > /sys/bus/i2c/devices/xxxx/engine_mux # engine mux for RGB
+ echo 1 > /sys/class/firmware/lp5562/loading
+ echo "4000600040FF6000" > /sys/class/firmware/lp5562/data
+ echo 0 > /sys/class/firmware/lp5562/loading
+ echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
+
+ To run a blinking white pattern,
+ echo 1 or 2 or 3 > /sys/bus/i2c/devices/xxxx/select_engine
+ echo "W" > /sys/bus/i2c/devices/xxxx/engine_mux
+ echo 1 > /sys/class/firmware/lp5562/loading
+ echo "4000600040FF6000" > /sys/class/firmware/lp5562/data
+ echo 0 > /sys/class/firmware/lp5562/loading
+ echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
+
+How to load the predefined patterns
+
+ Please refer to 'leds-lp55xx.txt"
+
+Setting Current of Each Channel
+
+ Like LP5521 and LP5523/55231, LP5562 provides LED current settings.
+ The 'led_current' and 'max_current' are used.
+
+(Example of Platform data)
+
+To configure the platform specific data, lp55xx_platform_data structure is used.
+
+static struct lp55xx_led_config lp5562_led_config[] = {
+ {
+ .name = "R",
+ .chan_nr = 0,
+ .led_current = 20,
+ .max_current = 40,
+ },
+ {
+ .name = "G",
+ .chan_nr = 1,
+ .led_current = 20,
+ .max_current = 40,
+ },
+ {
+ .name = "B",
+ .chan_nr = 2,
+ .led_current = 20,
+ .max_current = 40,
+ },
+ {
+ .name = "W",
+ .chan_nr = 3,
+ .led_current = 20,
+ .max_current = 40,
+ },
+};
+
+static int lp5562_setup(void)
+{
+ /* setup HW resources */
+}
+
+static void lp5562_release(void)
+{
+ /* Release HW resources */
+}
+
+static void lp5562_enable(bool state)
+{
+ /* Control of chip enable signal */
+}
+
+static struct lp55xx_platform_data lp5562_platform_data = {
+ .led_config = lp5562_led_config,
+ .num_channels = ARRAY_SIZE(lp5562_led_config),
+ .setup_resources = lp5562_setup,
+ .release_resources = lp5562_release,
+ .enable = lp5562_enable,
+};
+
+If the current is set to 0 in the platform data, that channel is
+disabled and it is not visible in the sysfs.
diff --git a/Documentation/leds/leds-lp55xx.txt b/Documentation/leds/leds-lp55xx.txt
index ced41868d2d1..eec8fa2ffe4e 100644
--- a/Documentation/leds/leds-lp55xx.txt
+++ b/Documentation/leds/leds-lp55xx.txt
@@ -5,7 +5,7 @@ Authors: Milo(Woogyom) Kim <milo.kim@ti.com>
Description
-----------
-LP5521, LP5523/55231 have common features as below.
+LP5521, LP5523/55231 and LP5562 have common features as below.
Register access via the I2C
Device initialization/deinitialization
@@ -116,3 +116,47 @@ To support this, 'run_engine' and 'firmware_cb' are configurable in each driver.
run_engine : Control the selected engine
firmware_cb : The callback function after loading the firmware is done.
Chip specific commands for loading and updating program memory.
+
+( Predefined pattern data )
+
+Without the firmware interface, LP55xx driver provides another method for
+loading a LED pattern. That is 'predefined' pattern.
+A predefined pattern is defined in the platform data and load it(or them)
+via the sysfs if needed.
+To use the predefined pattern concept, 'patterns' and 'num_patterns' should be
+configured.
+
+ Example of predefined pattern data:
+
+ /* mode_1: blinking data */
+ static const u8 mode_1[] = {
+ 0x40, 0x00, 0x60, 0x00, 0x40, 0xFF, 0x60, 0x00,
+ };
+
+ /* mode_2: always on */
+ static const u8 mode_2[] = { 0x40, 0xFF, };
+
+ struct lp55xx_predef_pattern board_led_patterns[] = {
+ {
+ .r = mode_1,
+ .size_r = ARRAY_SIZE(mode_1),
+ },
+ {
+ .b = mode_2,
+ .size_b = ARRAY_SIZE(mode_2),
+ },
+ }
+
+ struct lp55xx_platform_data lp5562_pdata = {
+ ...
+ .patterns = board_led_patterns,
+ .num_patterns = ARRAY_SIZE(board_led_patterns),
+ };
+
+Then, mode_1 and mode_2 can be run via through the sysfs.
+
+ echo 1 > /sys/bus/i2c/devices/xxxx/led_pattern # red blinking LED pattern
+ echo 2 > /sys/bus/i2c/devices/xxxx/led_pattern # blue LED always on
+
+To stop running pattern,
+ echo 0 > /sys/bus/i2c/devices/xxxx/led_pattern
diff --git a/MAINTAINERS b/MAINTAINERS
index e1f5fac1838e..5f5c895e6621 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1620,6 +1620,13 @@ W: http://www.baycom.org/~tom/ham/ham.html
S: Maintained
F: drivers/net/hamradio/baycom*
+BCACHE (BLOCK LAYER CACHE)
+M: Kent Overstreet <koverstreet@google.com>
+L: linux-bcache@vger.kernel.org
+W: http://bcache.evilpiepirate.org
+S: Maintained:
+F: drivers/md/bcache/
+
BEFS FILE SYSTEM
S: Orphan
F: Documentation/filesystems/befs.txt
@@ -6716,6 +6723,14 @@ F: drivers/remoteproc/
F: Documentation/remoteproc.txt
F: include/linux/remoteproc.h
+REMOTE PROCESSOR MESSAGING (RPMSG) SUBSYSTEM
+M: Ohad Ben-Cohen <ohad@wizery.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/ohad/rpmsg.git
+S: Maintained
+F: drivers/rpmsg/
+F: Documentation/rpmsg.txt
+F: include/linux/rpmsg.h
+
RFKILL
M: Johannes Berg <johannes@sipsolutions.net>
L: linux-wireless@vger.kernel.org
@@ -7140,9 +7155,9 @@ F: drivers/misc/phantom.c
F: include/uapi/linux/phantom.h
SERIAL ATA (SATA) SUBSYSTEM
-M: Jeff Garzik <jgarzik@pobox.com>
+M: Tejun Heo <tj@kernel.org>
L: linux-ide@vger.kernel.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata.git
S: Supported
F: drivers/ata/
F: include/linux/ata.h
diff --git a/Makefile b/Makefile
index 878d7aa3d2dd..a3a834b11a97 100644
--- a/Makefile
+++ b/Makefile
@@ -757,6 +757,8 @@ export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
export LDFLAGS_vmlinux
+# used by scripts/pacmage/Makefile
+export KBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(vmlinux-alldirs)) arch Documentation include samples scripts tools virt)
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index c3a7845f6228..2a79c1513f5d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -498,6 +498,7 @@ config ARCH_DOVE
select PINCTRL_DOVE
select PLAT_ORION_LEGACY
select USB_ARCH_HAS_EHCI
+ select MVEBU_MBUS
help
Support for the Marvell Dove SoC 88AP510
@@ -511,6 +512,7 @@ config ARCH_KIRKWOOD
select PINCTRL
select PINCTRL_KIRKWOOD
select PLAT_ORION_LEGACY
+ select MVEBU_MBUS
help
Support for the following Marvell Kirkwood series SoCs:
88F6180, 88F6192 and 88F6281.
@@ -522,6 +524,7 @@ config ARCH_MV78XX0
select GENERIC_CLOCKEVENTS
select PCI
select PLAT_ORION_LEGACY
+ select MVEBU_MBUS
help
Support for the following Marvell MV78xx0 series SoCs:
MV781x0, MV782x0.
@@ -534,6 +537,7 @@ config ARCH_ORION5X
select GENERIC_CLOCKEVENTS
select PCI
select PLAT_ORION_LEGACY
+ select MVEBU_MBUS
help
Support for the following Marvell Orion 5x series SoCs:
Orion-1 (5181), Orion-VoIP (5181L), Orion-NAS (5182),
@@ -643,7 +647,7 @@ config ARCH_SHMOBILE
select MULTI_IRQ_HANDLER
select NEED_MACH_MEMORY_H
select NO_IOPORT
- select PINCTRL
+ select PINCTRL if ARCH_WANT_OPTIONAL_GPIOLIB
select PM_GENERIC_DOMAINS if PM
select SPARSE_IRQ
help
@@ -1055,6 +1059,7 @@ config PLAT_VERSATILE
config ARM_TIMER_SP804
bool
select CLKSRC_MMIO
+ select CLKSRC_OF if OF
source arch/arm/mm/Kconfig
@@ -1789,6 +1794,7 @@ config XEN
depends on ARM && AEABI && OF
depends on CPU_V7 && !CPU_V6
depends on !GENERIC_ATOMIC64
+ select ARM_PSCI
help
Say Y if you want to run Linux in a Virtual Machine on Xen on ARM.
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index bee20d384b13..21cc8a765988 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -266,11 +266,11 @@ choice
on i.MX53.
config DEBUG_IMX6Q_UART
- bool "i.MX6Q Debug UART"
+ bool "i.MX6Q/DL Debug UART"
depends on SOC_IMX6Q
help
Say Y here if you want kernel low-level debugging support
- on i.MX6Q.
+ on i.MX6Q/DL.
config DEBUG_MMP_UART2
bool "Kernel low-level debugging message via MMP UART2"
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 853e199ea89f..b9f7121e6ecf 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -49,10 +49,12 @@ dtb-$(CONFIG_ARCH_DOVE) += dove-cm-a510.dtb \
dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \
exynos4210-smdkv310.dtb \
exynos4210-trats.dtb \
+ exynos4210-universal_c210.dtb \
exynos4412-odroidx.dtb \
exynos4412-smdk4412.dtb \
exynos4412-origen.dtb \
exynos5250-arndale.dtb \
+ exynos5440-sd5v1.dtb \
exynos5250-smdk5250.dtb \
exynos5250-snow.dtb \
exynos5440-ssdk5440.dtb
@@ -134,10 +136,14 @@ dtb-$(CONFIG_ARCH_MXS) += imx23-evk.dtb \
imx28-tx28.dtb
dtb-$(CONFIG_ARCH_NOMADIK) += ste-nomadik-s8815.dtb
dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
+ omap3430-sdp.dtb \
omap3-beagle.dtb \
+ omap3-devkit8000.dtb \
omap3-beagle-xm.dtb \
omap3-evm.dtb \
omap3-tobi.dtb \
+ omap3-igep0020.dtb \
+ omap3-igep0030.dtb \
omap4-panda.dtb \
omap4-panda-a4.dtb \
omap4-panda-es.dtb \
@@ -155,9 +161,12 @@ dtb-$(CONFIG_ARCH_U8500) += snowball.dtb \
ccu9540.dtb
dtb-$(CONFIG_ARCH_SHMOBILE) += emev2-kzm9d.dtb \
r8a7740-armadillo800eva.dtb \
+ r8a7778-bockw.dtb \
r8a7779-marzen-reference.dtb \
+ r8a7790-lager.dtb \
sh73a0-kzm9g.dtb \
sh73a0-kzm9g-reference.dtb \
+ r8a73a4-ape6evm.dtb \
sh7372-mackerel.dtb
dtb-$(CONFIG_ARCH_SOCFPGA) += socfpga_cyclone5.dtb \
socfpga_vt.dtb
@@ -186,11 +195,13 @@ dtb-$(CONFIG_ARCH_TEGRA) += tegra20-harmony.dtb \
tegra30-cardhu-a04.dtb \
tegra114-dalmore.dtb \
tegra114-pluto.dtb
+dtb-$(CONFIG_ARCH_VERSATILE) += versatile-ab.dtb \
+ versatile-pb.dtb
dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
vexpress-v2p-ca9.dtb \
vexpress-v2p-ca15-tc1.dtb \
- vexpress-v2p-ca15_a7.dtb \
- xenvm-4.2.dtb
+ vexpress-v2p-ca15_a7.dtb
+dtb-$(CONFIG_ARCH_VIRT) += xenvm-4.2.dtb
dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
wm8505-ref.dtb \
wm8650-mid.dtb \
diff --git a/arch/arm/boot/dts/am335x-bone.dts b/arch/arm/boot/dts/am335x-bone.dts
index 11b240c5d323..5302f79c05b7 100644
--- a/arch/arm/boot/dts/am335x-bone.dts
+++ b/arch/arm/boot/dts/am335x-bone.dts
@@ -43,7 +43,7 @@
status = "okay";
};
- i2c1: i2c@44e0b000 {
+ i2c0: i2c@44e0b000 {
status = "okay";
clock-frequency = <400000>;
@@ -59,27 +59,27 @@
led@2 {
label = "beaglebone:green:heartbeat";
- gpios = <&gpio2 21 0>;
+ gpios = <&gpio1 21 0>;
linux,default-trigger = "heartbeat";
default-state = "off";
};
led@3 {
label = "beaglebone:green:mmc0";
- gpios = <&gpio2 22 0>;
+ gpios = <&gpio1 22 0>;
linux,default-trigger = "mmc0";
default-state = "off";
};
led@4 {
label = "beaglebone:green:usr2";
- gpios = <&gpio2 23 0>;
+ gpios = <&gpio1 23 0>;
default-state = "off";
};
led@5 {
label = "beaglebone:green:usr3";
- gpios = <&gpio2 24 0>;
+ gpios = <&gpio1 24 0>;
default-state = "off";
};
};
diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts
index d6496440fcea..0423298a26fe 100644
--- a/arch/arm/boot/dts/am335x-evm.dts
+++ b/arch/arm/boot/dts/am335x-evm.dts
@@ -51,7 +51,7 @@
status = "okay";
};
- i2c1: i2c@44e0b000 {
+ i2c0: i2c@44e0b000 {
status = "okay";
clock-frequency = <400000>;
@@ -60,7 +60,7 @@
};
};
- i2c2: i2c@4802a000 {
+ i2c1: i2c@4802a000 {
status = "okay";
clock-frequency = <100000>;
@@ -123,12 +123,12 @@
debounce-delay-ms = <5>;
col-scan-delay-us = <2>;
- row-gpios = <&gpio2 25 0 /* Bank1, pin25 */
- &gpio2 26 0 /* Bank1, pin26 */
- &gpio2 27 0>; /* Bank1, pin27 */
+ row-gpios = <&gpio1 25 0 /* Bank1, pin25 */
+ &gpio1 26 0 /* Bank1, pin26 */
+ &gpio1 27 0>; /* Bank1, pin27 */
- col-gpios = <&gpio2 21 0 /* Bank1, pin21 */
- &gpio2 22 0>; /* Bank1, pin22 */
+ col-gpios = <&gpio1 21 0 /* Bank1, pin21 */
+ &gpio1 22 0>; /* Bank1, pin22 */
linux,keymap = <0x0000008b /* MENU */
0x0100009e /* BACK */
@@ -147,14 +147,14 @@
switch@9 {
label = "volume-up";
linux,code = <115>;
- gpios = <&gpio1 2 1>;
+ gpios = <&gpio0 2 1>;
gpio-key,wakeup;
};
switch@10 {
label = "volume-down";
linux,code = <114>;
- gpios = <&gpio1 3 1>;
+ gpios = <&gpio0 3 1>;
gpio-key,wakeup;
};
};
diff --git a/arch/arm/boot/dts/am335x-evmsk.dts b/arch/arm/boot/dts/am335x-evmsk.dts
index f5a6162a4ff2..f67c360844f4 100644
--- a/arch/arm/boot/dts/am335x-evmsk.dts
+++ b/arch/arm/boot/dts/am335x-evmsk.dts
@@ -58,7 +58,7 @@
status = "okay";
};
- i2c1: i2c@44e0b000 {
+ i2c0: i2c@44e0b000 {
status = "okay";
clock-frequency = <400000>;
@@ -115,26 +115,26 @@
led@1 {
label = "evmsk:green:usr0";
- gpios = <&gpio2 4 0>;
+ gpios = <&gpio1 4 0>;
default-state = "off";
};
led@2 {
label = "evmsk:green:usr1";
- gpios = <&gpio2 5 0>;
+ gpios = <&gpio1 5 0>;
default-state = "off";
};
led@3 {
label = "evmsk:green:mmc0";
- gpios = <&gpio2 6 0>;
+ gpios = <&gpio1 6 0>;
linux,default-trigger = "mmc0";
default-state = "off";
};
led@4 {
label = "evmsk:green:heartbeat";
- gpios = <&gpio2 7 0>;
+ gpios = <&gpio1 7 0>;
linux,default-trigger = "heartbeat";
default-state = "off";
};
@@ -148,26 +148,26 @@
switch@1 {
label = "button0";
linux,code = <0x100>;
- gpios = <&gpio3 3 0>;
+ gpios = <&gpio2 3 0>;
};
switch@2 {
label = "button1";
linux,code = <0x101>;
- gpios = <&gpio3 2 0>;
+ gpios = <&gpio2 2 0>;
};
switch@3 {
label = "button2";
linux,code = <0x102>;
- gpios = <&gpio1 30 0>;
+ gpios = <&gpio0 30 0>;
gpio-key,wakeup;
};
switch@4 {
label = "button3";
linux,code = <0x103>;
- gpios = <&gpio3 5 0>;
+ gpios = <&gpio2 5 0>;
};
};
};
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 91fe4f148f80..d1101103aa51 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -21,6 +21,8 @@
serial3 = &uart4;
serial4 = &uart5;
serial5 = &uart6;
+ d_can0 = &dcan0;
+ d_can1 = &dcan1;
};
cpus {
@@ -87,7 +89,7 @@
reg = <0x48200000 0x1000>;
};
- gpio1: gpio@44e07000 {
+ gpio0: gpio@44e07000 {
compatible = "ti,omap4-gpio";
ti,hwmods = "gpio1";
gpio-controller;
@@ -98,7 +100,7 @@
interrupts = <96>;
};
- gpio2: gpio@4804c000 {
+ gpio1: gpio@4804c000 {
compatible = "ti,omap4-gpio";
ti,hwmods = "gpio2";
gpio-controller;
@@ -109,7 +111,7 @@
interrupts = <98>;
};
- gpio3: gpio@481ac000 {
+ gpio2: gpio@481ac000 {
compatible = "ti,omap4-gpio";
ti,hwmods = "gpio3";
gpio-controller;
@@ -120,7 +122,7 @@
interrupts = <32>;
};
- gpio4: gpio@481ae000 {
+ gpio3: gpio@481ae000 {
compatible = "ti,omap4-gpio";
ti,hwmods = "gpio4";
gpio-controller;
@@ -185,7 +187,7 @@
status = "disabled";
};
- i2c1: i2c@44e0b000 {
+ i2c0: i2c@44e0b000 {
compatible = "ti,omap4-i2c";
#address-cells = <1>;
#size-cells = <0>;
@@ -195,7 +197,7 @@
status = "disabled";
};
- i2c2: i2c@4802a000 {
+ i2c1: i2c@4802a000 {
compatible = "ti,omap4-i2c";
#address-cells = <1>;
#size-cells = <0>;
@@ -205,7 +207,7 @@
status = "disabled";
};
- i2c3: i2c@4819c000 {
+ i2c2: i2c@4819c000 {
compatible = "ti,omap4-i2c";
#address-cells = <1>;
#size-cells = <0>;
@@ -225,7 +227,8 @@
dcan0: d_can@481cc000 {
compatible = "bosch,d_can";
ti,hwmods = "d_can0";
- reg = <0x481cc000 0x2000>;
+ reg = <0x481cc000 0x2000
+ 0x44e10644 0x4>;
interrupts = <52>;
status = "disabled";
};
@@ -233,13 +236,14 @@
dcan1: d_can@481d0000 {
compatible = "bosch,d_can";
ti,hwmods = "d_can1";
- reg = <0x481d0000 0x2000>;
+ reg = <0x481d0000 0x2000
+ 0x44e10644 0x4>;
interrupts = <55>;
status = "disabled";
};
timer1: timer@44e31000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,am335x-timer-1ms";
reg = <0x44e31000 0x400>;
interrupts = <67>;
ti,hwmods = "timer1";
@@ -247,21 +251,21 @@
};
timer2: timer@48040000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,am335x-timer";
reg = <0x48040000 0x400>;
interrupts = <68>;
ti,hwmods = "timer2";
};
timer3: timer@48042000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,am335x-timer";
reg = <0x48042000 0x400>;
interrupts = <69>;
ti,hwmods = "timer3";
};
timer4: timer@48044000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,am335x-timer";
reg = <0x48044000 0x400>;
interrupts = <92>;
ti,hwmods = "timer4";
@@ -269,7 +273,7 @@
};
timer5: timer@48046000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,am335x-timer";
reg = <0x48046000 0x400>;
interrupts = <93>;
ti,hwmods = "timer5";
@@ -277,7 +281,7 @@
};
timer6: timer@48048000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,am335x-timer";
reg = <0x48048000 0x400>;
interrupts = <94>;
ti,hwmods = "timer6";
@@ -285,7 +289,7 @@
};
timer7: timer@4804a000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,am335x-timer";
reg = <0x4804a000 0x400>;
interrupts = <95>;
ti,hwmods = "timer7";
@@ -305,7 +309,7 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x48030000 0x400>;
- interrupt = <65>;
+ interrupts = <65>;
ti,spi-num-cs = <2>;
ti,hwmods = "spi0";
status = "disabled";
@@ -316,7 +320,7 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x481a0000 0x400>;
- interrupt = <125>;
+ interrupts = <125>;
ti,spi-num-cs = <2>;
ti,hwmods = "spi1";
status = "disabled";
diff --git a/arch/arm/boot/dts/am3517-evm.dts b/arch/arm/boot/dts/am3517-evm.dts
index 474f760ecadf..e9b5bdae4908 100644
--- a/arch/arm/boot/dts/am3517-evm.dts
+++ b/arch/arm/boot/dts/am3517-evm.dts
@@ -7,7 +7,7 @@
*/
/dts-v1/;
-/include/ "omap3.dtsi"
+/include/ "omap34xx.dtsi"
/ {
model = "TI AM3517 EVM (AM3517/05)";
diff --git a/arch/arm/boot/dts/am3517_mt_ventoux.dts b/arch/arm/boot/dts/am3517_mt_ventoux.dts
index 5eb26d7d9b4e..556868388a23 100644
--- a/arch/arm/boot/dts/am3517_mt_ventoux.dts
+++ b/arch/arm/boot/dts/am3517_mt_ventoux.dts
@@ -7,7 +7,7 @@
*/
/dts-v1/;
-/include/ "omap3.dtsi"
+/include/ "omap34xx.dtsi"
/ {
model = "TeeJet Mt.Ventoux";
diff --git a/arch/arm/boot/dts/armada-370-db.dts b/arch/arm/boot/dts/armada-370-db.dts
index 6403acdbb75f..2353b1f13704 100644
--- a/arch/arm/boot/dts/armada-370-db.dts
+++ b/arch/arm/boot/dts/armada-370-db.dts
@@ -30,85 +30,87 @@
};
soc {
- serial@d0012000 {
- clock-frequency = <200000000>;
- status = "okay";
- };
- sata@d00a0000 {
- nr-ports = <2>;
- status = "okay";
- };
-
- mdio {
- phy0: ethernet-phy@0 {
- reg = <0>;
+ internal-regs {
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "okay";
};
-
- phy1: ethernet-phy@1 {
- reg = <1>;
+ sata@a0000 {
+ nr-ports = <2>;
+ status = "okay";
};
- };
- ethernet@d0070000 {
- status = "okay";
- phy = <&phy0>;
- phy-mode = "rgmii-id";
- };
- ethernet@d0074000 {
- status = "okay";
- phy = <&phy1>;
- phy-mode = "rgmii-id";
- };
+ mdio {
+ phy0: ethernet-phy@0 {
+ reg = <0>;
+ };
- mvsdio@d00d4000 {
- pinctrl-0 = <&sdio_pins1>;
- pinctrl-names = "default";
- /*
- * This device is disabled by default, because
- * using the SD card connector requires
- * changing the default CON40 connector
- * "DB-88F6710_MPP_2xRGMII_DEVICE_Jumper" to a
- * different connector
- * "DB-88F6710_MPP_RGMII_SD_Jumper".
- */
- status = "disabled";
- /* No CD or WP GPIOs */
- };
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
- usb@d0050000 {
- status = "okay";
- };
+ ethernet@70000 {
+ status = "okay";
+ phy = <&phy0>;
+ phy-mode = "rgmii-id";
+ };
+ ethernet@74000 {
+ status = "okay";
+ phy = <&phy1>;
+ phy-mode = "rgmii-id";
+ };
- usb@d0051000 {
- status = "okay";
- };
+ mvsdio@d4000 {
+ pinctrl-0 = <&sdio_pins1>;
+ pinctrl-names = "default";
+ /*
+ * This device is disabled by default, because
+ * using the SD card connector requires
+ * changing the default CON40 connector
+ * "DB-88F6710_MPP_2xRGMII_DEVICE_Jumper" to a
+ * different connector
+ * "DB-88F6710_MPP_RGMII_SD_Jumper".
+ */
+ status = "disabled";
+ /* No CD or WP GPIOs */
+ };
- spi0: spi@d0010600 {
- status = "okay";
+ usb@50000 {
+ status = "okay";
+ };
- spi-flash@0 {
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "mx25l25635e";
- reg = <0>; /* Chip select 0 */
- spi-max-frequency = <50000000>;
+ usb@51000 {
+ status = "okay";
};
- };
- pcie-controller {
- status = "okay";
- /*
- * The two PCIe units are accessible through
- * both standard PCIe slots and mini-PCIe
- * slots on the board.
- */
- pcie@1,0 {
- /* Port 0, Lane 0 */
+ spi0: spi@10600 {
status = "okay";
+
+ spi-flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "mx25l25635e";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <50000000>;
+ };
};
- pcie@2,0 {
- /* Port 1, Lane 0 */
+
+ pcie-controller {
status = "okay";
+ /*
+ * The two PCIe units are accessible through
+ * both standard PCIe slots and mini-PCIe
+ * slots on the board.
+ */
+ pcie@1,0 {
+ /* Port 0, Lane 0 */
+ status = "okay";
+ };
+ pcie@2,0 {
+ /* Port 1, Lane 0 */
+ status = "okay";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/armada-370-mirabox.dts b/arch/arm/boot/dts/armada-370-mirabox.dts
index 58ee79372206..14e36e19d515 100644
--- a/arch/arm/boot/dts/armada-370-mirabox.dts
+++ b/arch/arm/boot/dts/armada-370-mirabox.dts
@@ -25,113 +25,115 @@
};
soc {
- serial@d0012000 {
- clock-frequency = <200000000>;
- status = "okay";
- };
- timer@d0020300 {
- clock-frequency = <600000000>;
- status = "okay";
- };
-
- pinctrl {
- pwr_led_pin: pwr-led-pin {
- marvell,pins = "mpp63";
- marvell,function = "gpo";
+ internal-regs {
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "okay";
};
-
- stat_led_pins: stat-led-pins {
- marvell,pins = "mpp64", "mpp65";
- marvell,function = "gpio";
+ timer@20300 {
+ clock-frequency = <600000000>;
+ status = "okay";
};
- };
- gpio_leds {
- compatible = "gpio-leds";
- pinctrl-names = "default";
- pinctrl-0 = <&pwr_led_pin &stat_led_pins>;
+ pinctrl {
+ pwr_led_pin: pwr-led-pin {
+ marvell,pins = "mpp63";
+ marvell,function = "gpo";
+ };
- green_pwr_led {
- label = "mirabox:green:pwr";
- gpios = <&gpio1 31 1>;
- linux,default-trigger = "heartbeat";
+ stat_led_pins: stat-led-pins {
+ marvell,pins = "mpp64", "mpp65";
+ marvell,function = "gpio";
+ };
};
- blue_stat_led {
- label = "mirabox:blue:stat";
- gpios = <&gpio2 0 1>;
- linux,default-trigger = "cpu0";
+ gpio_leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwr_led_pin &stat_led_pins>;
+
+ green_pwr_led {
+ label = "mirabox:green:pwr";
+ gpios = <&gpio1 31 1>;
+ linux,default-trigger = "heartbeat";
+ };
+
+ blue_stat_led {
+ label = "mirabox:blue:stat";
+ gpios = <&gpio2 0 1>;
+ linux,default-trigger = "cpu0";
+ };
+
+ green_stat_led {
+ label = "mirabox:green:stat";
+ gpios = <&gpio2 1 1>;
+ default-state = "off";
+ };
};
- green_stat_led {
- label = "mirabox:green:stat";
- gpios = <&gpio2 1 1>;
- default-state = "off";
- };
- };
+ mdio {
+ phy0: ethernet-phy@0 {
+ reg = <0>;
+ };
- mdio {
- phy0: ethernet-phy@0 {
- reg = <0>;
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
};
-
- phy1: ethernet-phy@1 {
- reg = <1>;
+ ethernet@70000 {
+ status = "okay";
+ phy = <&phy0>;
+ phy-mode = "rgmii-id";
+ };
+ ethernet@74000 {
+ status = "okay";
+ phy = <&phy1>;
+ phy-mode = "rgmii-id";
};
- };
- ethernet@d0070000 {
- status = "okay";
- phy = <&phy0>;
- phy-mode = "rgmii-id";
- };
- ethernet@d0074000 {
- status = "okay";
- phy = <&phy1>;
- phy-mode = "rgmii-id";
- };
-
- mvsdio@d00d4000 {
- pinctrl-0 = <&sdio_pins3>;
- pinctrl-names = "default";
- status = "okay";
- /*
- * No CD or WP GPIOs: SDIO interface used for
- * Wifi/Bluetooth chip
- */
- };
-
- usb@d0050000 {
- status = "okay";
- };
- usb@d0051000 {
- status = "okay";
- };
+ mvsdio@d4000 {
+ pinctrl-0 = <&sdio_pins3>;
+ pinctrl-names = "default";
+ status = "okay";
+ /*
+ * No CD or WP GPIOs: SDIO interface used for
+ * Wifi/Bluetooth chip
+ */
+ };
- i2c@d0011000 {
- status = "okay";
- clock-frequency = <100000>;
- pca9505: pca9505@25 {
- compatible = "nxp,pca9505";
- gpio-controller;
- #gpio-cells = <2>;
- reg = <0x25>;
+ usb@50000 {
+ status = "okay";
};
- };
- pcie-controller {
- status = "okay";
+ usb@51000 {
+ status = "okay";
+ };
- /* Internal mini-PCIe connector */
- pcie@1,0 {
- /* Port 0, Lane 0 */
+ i2c@11000 {
status = "okay";
+ clock-frequency = <100000>;
+ pca9505: pca9505@25 {
+ compatible = "nxp,pca9505";
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x25>;
+ };
};
- /* Connected on the PCB to a USB 3.0 XHCI controller */
- pcie@2,0 {
- /* Port 1, Lane 0 */
+ pcie-controller {
status = "okay";
+
+ /* Internal mini-PCIe connector */
+ pcie@1,0 {
+ /* Port 0, Lane 0 */
+ status = "okay";
+ };
+
+ /* Connected on the PCB to a USB 3.0 XHCI controller */
+ pcie@2,0 {
+ /* Port 1, Lane 0 */
+ status = "okay";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/armada-370-rd.dts b/arch/arm/boot/dts/armada-370-rd.dts
index 516dec31b469..130f8390a7e4 100644
--- a/arch/arm/boot/dts/armada-370-rd.dts
+++ b/arch/arm/boot/dts/armada-370-rd.dts
@@ -28,60 +28,62 @@
};
soc {
- serial@d0012000 {
- clock-frequency = <200000000>;
- status = "okay";
- };
- sata@d00a0000 {
- nr-ports = <2>;
- status = "okay";
- };
-
- mdio {
- phy0: ethernet-phy@0 {
- reg = <0>;
+ internal-regs {
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "okay";
};
+ sata@a0000 {
+ nr-ports = <2>;
+ status = "okay";
+ };
+
+ mdio {
+ phy0: ethernet-phy@0 {
+ reg = <0>;
+ };
- phy1: ethernet-phy@1 {
- reg = <1>;
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
};
- };
- ethernet@d0070000 {
- status = "okay";
- phy = <&phy0>;
- phy-mode = "sgmii";
- };
- ethernet@d0074000 {
- status = "okay";
- phy = <&phy1>;
- phy-mode = "rgmii-id";
- };
+ ethernet@70000 {
+ status = "okay";
+ phy = <&phy0>;
+ phy-mode = "sgmii";
+ };
+ ethernet@74000 {
+ status = "okay";
+ phy = <&phy1>;
+ phy-mode = "rgmii-id";
+ };
- mvsdio@d00d4000 {
- pinctrl-0 = <&sdio_pins1>;
- pinctrl-names = "default";
- status = "okay";
- /* No CD or WP GPIOs */
- };
+ mvsdio@d4000 {
+ pinctrl-0 = <&sdio_pins1>;
+ pinctrl-names = "default";
+ status = "okay";
+ /* No CD or WP GPIOs */
+ };
- usb@d0050000 {
- status = "okay";
- };
+ usb@50000 {
+ status = "okay";
+ };
- usb@d0051000 {
- status = "okay";
- };
- };
+ usb@51000 {
+ status = "okay";
+ };
- gpio-keys {
- compatible = "gpio-keys";
- #address-cells = <1>;
- #size-cells = <0>;
- button@1 {
- label = "Software Button";
- linux,code = <116>;
- gpios = <&gpio0 6 1>;
+ gpio-keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button@1 {
+ label = "Software Button";
+ linux,code = <116>;
+ gpios = <&gpio0 6 1>;
+ };
+ };
};
};
-};
+ };
diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi
index 758c4ea90344..272bbc65fab0 100644
--- a/arch/arm/boot/dts/armada-370-xp.dtsi
+++ b/arch/arm/boot/dts/armada-370-xp.dtsi
@@ -16,7 +16,7 @@
* 370 and Armada XP SoC.
*/
-/include/ "skeleton.dtsi"
+/include/ "skeleton64.dtsi"
/ {
model = "Marvell Armada 370 and XP SoC";
@@ -28,204 +28,203 @@
};
};
- mpic: interrupt-controller@d0020000 {
- compatible = "marvell,mpic";
- #interrupt-cells = <1>;
- #size-cells = <1>;
- interrupt-controller;
- };
-
- coherency-fabric@d0020200 {
- compatible = "marvell,coherency-fabric";
- reg = <0xd0020200 0xb0>,
- <0xd0021810 0x1c>;
- };
-
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
interrupt-parent = <&mpic>;
- ranges;
+ ranges = <0 0 0xd0000000 0x100000>;
+
+ internal-regs {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ mpic: interrupt-controller@20000 {
+ compatible = "marvell,mpic";
+ #interrupt-cells = <1>;
+ #size-cells = <1>;
+ interrupt-controller;
+ };
+
+ coherency-fabric@20200 {
+ compatible = "marvell,coherency-fabric";
+ reg = <0x20200 0xb0>, <0x21810 0x1c>;
+ };
- serial@d0012000 {
+ serial@12000 {
compatible = "snps,dw-apb-uart";
- reg = <0xd0012000 0x100>;
+ reg = <0x12000 0x100>;
reg-shift = <2>;
interrupts = <41>;
reg-io-width = <1>;
status = "disabled";
- };
- serial@d0012100 {
+ };
+ serial@12100 {
compatible = "snps,dw-apb-uart";
- reg = <0xd0012100 0x100>;
+ reg = <0x12100 0x100>;
reg-shift = <2>;
interrupts = <42>;
reg-io-width = <1>;
status = "disabled";
- };
-
- timer@d0020300 {
- compatible = "marvell,armada-370-xp-timer";
- reg = <0xd0020300 0x30>,
- <0xd0021040 0x30>;
- interrupts = <37>, <38>, <39>, <40>, <5>, <6>;
- clocks = <&coreclk 2>;
- };
-
- addr-decoding@d0020000 {
- compatible = "marvell,armada-addr-decoding-controller";
- reg = <0xd0020000 0x258>;
- };
-
- sata@d00a0000 {
- compatible = "marvell,orion-sata";
- reg = <0xd00a0000 0x2400>;
- interrupts = <55>;
- clocks = <&gateclk 15>, <&gateclk 30>;
- clock-names = "0", "1";
- status = "disabled";
- };
+ };
+
+ timer@20300 {
+ compatible = "marvell,armada-370-xp-timer";
+ reg = <0x20300 0x30>, <0x21040 0x30>;
+ interrupts = <37>, <38>, <39>, <40>, <5>, <6>;
+ clocks = <&coreclk 2>;
+ };
+
+ sata@a0000 {
+ compatible = "marvell,orion-sata";
+ reg = <0xa0000 0x2400>;
+ interrupts = <55>;
+ clocks = <&gateclk 15>, <&gateclk 30>;
+ clock-names = "0", "1";
+ status = "disabled";
+ };
- mdio {
- #address-cells = <1>;
- #size-cells = <0>;
- compatible = "marvell,orion-mdio";
- reg = <0xd0072004 0x4>;
- };
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "marvell,orion-mdio";
+ reg = <0x72004 0x4>;
+ };
- ethernet@d0070000 {
+ ethernet@70000 {
compatible = "marvell,armada-370-neta";
- reg = <0xd0070000 0x2500>;
+ reg = <0x70000 0x2500>;
interrupts = <8>;
clocks = <&gateclk 4>;
status = "disabled";
- };
+ };
- ethernet@d0074000 {
+ ethernet@74000 {
compatible = "marvell,armada-370-neta";
- reg = <0xd0074000 0x2500>;
+ reg = <0x74000 0x2500>;
interrupts = <10>;
clocks = <&gateclk 3>;
status = "disabled";
- };
-
- i2c0: i2c@d0011000 {
- compatible = "marvell,mv64xxx-i2c";
- reg = <0xd0011000 0x20>;
- #address-cells = <1>;
- #size-cells = <0>;
- interrupts = <31>;
- timeout-ms = <1000>;
- clocks = <&coreclk 0>;
- status = "disabled";
- };
-
- i2c1: i2c@d0011100 {
- compatible = "marvell,mv64xxx-i2c";
- reg = <0xd0011100 0x20>;
- #address-cells = <1>;
- #size-cells = <0>;
- interrupts = <32>;
- timeout-ms = <1000>;
- clocks = <&coreclk 0>;
- status = "disabled";
- };
-
- rtc@10300 {
- compatible = "marvell,orion-rtc";
- reg = <0xd0010300 0x20>;
- interrupts = <50>;
- };
-
- mvsdio@d00d4000 {
- compatible = "marvell,orion-sdio";
- reg = <0xd00d4000 0x200>;
- interrupts = <54>;
- clocks = <&gateclk 17>;
- status = "disabled";
- };
-
- usb@d0050000 {
- compatible = "marvell,orion-ehci";
- reg = <0xd0050000 0x500>;
- interrupts = <45>;
- status = "disabled";
- };
-
- usb@d0051000 {
- compatible = "marvell,orion-ehci";
- reg = <0xd0051000 0x500>;
- interrupts = <46>;
- status = "disabled";
- };
-
- spi0: spi@d0010600 {
- compatible = "marvell,orion-spi";
- reg = <0xd0010600 0x28>;
- #address-cells = <1>;
- #size-cells = <0>;
- cell-index = <0>;
- interrupts = <30>;
- clocks = <&coreclk 0>;
- status = "disabled";
- };
-
- spi1: spi@d0010680 {
- compatible = "marvell,orion-spi";
- reg = <0xd0010680 0x28>;
- #address-cells = <1>;
- #size-cells = <0>;
- cell-index = <1>;
- interrupts = <92>;
- clocks = <&coreclk 0>;
- status = "disabled";
- };
-
- devbus-bootcs@d0010400 {
- compatible = "marvell,mvebu-devbus";
- reg = <0xd0010400 0x8>;
- #address-cells = <1>;
- #size-cells = <1>;
- clocks = <&coreclk 0>;
- status = "disabled";
- };
-
- devbus-cs0@d0010408 {
- compatible = "marvell,mvebu-devbus";
- reg = <0xd0010408 0x8>;
- #address-cells = <1>;
- #size-cells = <1>;
- clocks = <&coreclk 0>;
- status = "disabled";
- };
-
- devbus-cs1@d0010410 {
- compatible = "marvell,mvebu-devbus";
- reg = <0xd0010410 0x8>;
- #address-cells = <1>;
- #size-cells = <1>;
- clocks = <&coreclk 0>;
- status = "disabled";
- };
+ };
+
+ i2c0: i2c@11000 {
+ compatible = "marvell,mv64xxx-i2c";
+ reg = <0x11000 0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <31>;
+ timeout-ms = <1000>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@11100 {
+ compatible = "marvell,mv64xxx-i2c";
+ reg = <0x11100 0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <32>;
+ timeout-ms = <1000>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ rtc@10300 {
+ compatible = "marvell,orion-rtc";
+ reg = <0x10300 0x20>;
+ interrupts = <50>;
+ };
+
+ mvsdio@d4000 {
+ compatible = "marvell,orion-sdio";
+ reg = <0xd4000 0x200>;
+ interrupts = <54>;
+ clocks = <&gateclk 17>;
+ status = "disabled";
+ };
- devbus-cs2@d0010418 {
- compatible = "marvell,mvebu-devbus";
- reg = <0xd0010418 0x8>;
- #address-cells = <1>;
- #size-cells = <1>;
- clocks = <&coreclk 0>;
- status = "disabled";
- };
+ usb@50000 {
+ compatible = "marvell,orion-ehci";
+ reg = <0x50000 0x500>;
+ interrupts = <45>;
+ status = "disabled";
+ };
- devbus-cs3@d0010420 {
- compatible = "marvell,mvebu-devbus";
- reg = <0xd0010420 0x8>;
- #address-cells = <1>;
- #size-cells = <1>;
- clocks = <&coreclk 0>;
- status = "disabled";
+ usb@51000 {
+ compatible = "marvell,orion-ehci";
+ reg = <0x51000 0x500>;
+ interrupts = <46>;
+ status = "disabled";
+ };
+
+ spi0: spi@10600 {
+ compatible = "marvell,orion-spi";
+ reg = <0x10600 0x28>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <0>;
+ interrupts = <30>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ spi1: spi@10680 {
+ compatible = "marvell,orion-spi";
+ reg = <0x10680 0x28>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <1>;
+ interrupts = <92>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-bootcs@10400 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <0x10400 0x8>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs0@10408 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <0x10408 0x8>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs1@10410 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <0x10410 0x8>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs2@10418 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <0x10418 0x8>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs3@10420 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <0x10420 0x8>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
};
};
-};
-
+ };
diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi
index 18f6eb47cc50..b2c1b5af9749 100644
--- a/arch/arm/boot/dts/armada-370.dtsi
+++ b/arch/arm/boot/dts/armada-370.dtsi
@@ -16,16 +16,11 @@
*/
/include/ "armada-370-xp.dtsi"
+/include/ "skeleton.dtsi"
/ {
model = "Marvell Armada 370 family SoC";
compatible = "marvell,armada370", "marvell,armada-370-xp";
- L2: l2-cache {
- compatible = "marvell,aurora-outer-cache";
- reg = <0xd0008000 0x1000>;
- cache-id-part = <0x100>;
- wt-override;
- };
aliases {
gpio0 = &gpio0;
@@ -33,188 +28,197 @@
gpio2 = &gpio2;
};
- mpic: interrupt-controller@d0020000 {
- reg = <0xd0020a00 0x1d0>,
- <0xd0021870 0x58>;
- };
-
soc {
- system-controller@d0018200 {
+ ranges = <0 0xd0000000 0x100000>;
+ internal-regs {
+ system-controller@18200 {
compatible = "marvell,armada-370-xp-system-controller";
- reg = <0xd0018200 0x100>;
- };
-
- pinctrl {
- compatible = "marvell,mv88f6710-pinctrl";
- reg = <0xd0018000 0x38>;
-
- sdio_pins1: sdio-pins1 {
- marvell,pins = "mpp9", "mpp11", "mpp12",
- "mpp13", "mpp14", "mpp15";
- marvell,function = "sd0";
+ reg = <0x18200 0x100>;
};
- sdio_pins2: sdio-pins2 {
- marvell,pins = "mpp47", "mpp48", "mpp49",
- "mpp50", "mpp51", "mpp52";
- marvell,function = "sd0";
+ L2: l2-cache {
+ compatible = "marvell,aurora-outer-cache";
+ reg = <0xd0008000 0x1000>;
+ cache-id-part = <0x100>;
+ wt-override;
};
- sdio_pins3: sdio-pins3 {
- marvell,pins = "mpp48", "mpp49", "mpp50",
- "mpp51", "mpp52", "mpp53";
- marvell,function = "sd0";
+ mpic: interrupt-controller@20000 {
+ reg = <0x20a00 0x1d0>, <0x21870 0x58>;
};
- };
-
- gpio0: gpio@d0018100 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018100 0x40>;
- ngpios = <32>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <82>, <83>, <84>, <85>;
- };
-
- gpio1: gpio@d0018140 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018140 0x40>;
- ngpios = <32>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <87>, <88>, <89>, <90>;
- };
-
- gpio2: gpio@d0018180 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018180 0x40>;
- ngpios = <2>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <91>;
- };
- coreclk: mvebu-sar@d0018230 {
- compatible = "marvell,armada-370-core-clock";
- reg = <0xd0018230 0x08>;
- #clock-cells = <1>;
- };
-
- gateclk: clock-gating-control@d0018220 {
- compatible = "marvell,armada-370-gating-clock";
- reg = <0xd0018220 0x4>;
- clocks = <&coreclk 0>;
- #clock-cells = <1>;
- };
-
- xor@d0060800 {
- compatible = "marvell,orion-xor";
- reg = <0xd0060800 0x100
- 0xd0060A00 0x100>;
- status = "okay";
-
- xor00 {
- interrupts = <51>;
- dmacap,memcpy;
- dmacap,xor;
+ pinctrl {
+ compatible = "marvell,mv88f6710-pinctrl";
+ reg = <0x18000 0x38>;
+
+ sdio_pins1: sdio-pins1 {
+ marvell,pins = "mpp9", "mpp11", "mpp12",
+ "mpp13", "mpp14", "mpp15";
+ marvell,function = "sd0";
+ };
+
+ sdio_pins2: sdio-pins2 {
+ marvell,pins = "mpp47", "mpp48", "mpp49",
+ "mpp50", "mpp51", "mpp52";
+ marvell,function = "sd0";
+ };
+
+ sdio_pins3: sdio-pins3 {
+ marvell,pins = "mpp48", "mpp49", "mpp50",
+ "mpp51", "mpp52", "mpp53";
+ marvell,function = "sd0";
+ };
};
- xor01 {
- interrupts = <52>;
- dmacap,memcpy;
- dmacap,xor;
- dmacap,memset;
- };
- };
-
- xor@d0060900 {
- compatible = "marvell,orion-xor";
- reg = <0xd0060900 0x100
- 0xd0060b00 0x100>;
- status = "okay";
- xor10 {
- interrupts = <94>;
- dmacap,memcpy;
- dmacap,xor;
+ gpio0: gpio@18100 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18100 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <82>, <83>, <84>, <85>;
};
- xor11 {
- interrupts = <95>;
- dmacap,memcpy;
- dmacap,xor;
- dmacap,memset;
- };
- };
- usb@d0050000 {
- clocks = <&coreclk 0>;
- };
-
- usb@d0051000 {
- clocks = <&coreclk 0>;
- };
+ gpio1: gpio@18140 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18140 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <87>, <88>, <89>, <90>;
+ };
- thermal@d0018300 {
- compatible = "marvell,armada370-thermal";
- reg = <0xd0018300 0x4
- 0xd0018304 0x4>;
- status = "okay";
- };
+ gpio2: gpio@18180 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18180 0x40>;
+ ngpios = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <91>;
+ };
- pcie-controller {
- compatible = "marvell,armada-370-pcie";
- status = "disabled";
- device_type = "pci";
+ coreclk: mvebu-sar@18230 {
+ compatible = "marvell,armada-370-core-clock";
+ reg = <0x18230 0x08>;
+ #clock-cells = <1>;
+ };
- #address-cells = <3>;
- #size-cells = <2>;
+ gateclk: clock-gating-control@18220 {
+ compatible = "marvell,armada-370-gating-clock";
+ reg = <0x18220 0x4>;
+ clocks = <&coreclk 0>;
+ #clock-cells = <1>;
+ };
- bus-range = <0x00 0xff>;
+ xor@60800 {
+ compatible = "marvell,orion-xor";
+ reg = <0x60800 0x100
+ 0x60A00 0x100>;
+ status = "okay";
+
+ xor00 {
+ interrupts = <51>;
+ dmacap,memcpy;
+ dmacap,xor;
+ };
+ xor01 {
+ interrupts = <52>;
+ dmacap,memcpy;
+ dmacap,xor;
+ dmacap,memset;
+ };
+ };
- reg = <0xd0040000 0x2000>, <0xd0080000 0x2000>;
+ xor@60900 {
+ compatible = "marvell,orion-xor";
+ reg = <0x60900 0x100
+ 0x60b00 0x100>;
+ status = "okay";
+
+ xor10 {
+ interrupts = <94>;
+ dmacap,memcpy;
+ dmacap,xor;
+ };
+ xor11 {
+ interrupts = <95>;
+ dmacap,memcpy;
+ dmacap,xor;
+ dmacap,memset;
+ };
+ };
- reg-names = "pcie0.0", "pcie1.0";
+ usb@50000 {
+ clocks = <&coreclk 0>;
+ };
- ranges = <0x82000000 0 0xd0040000 0xd0040000 0 0x00002000 /* Port 0.0 registers */
- 0x82000000 0 0xd0080000 0xd0080000 0 0x00002000 /* Port 1.0 registers */
- 0x82000000 0 0xe0000000 0xe0000000 0 0x08000000 /* non-prefetchable memory */
- 0x81000000 0 0 0xe8000000 0 0x00100000>; /* downstream I/O */
+ usb@51000 {
+ clocks = <&coreclk 0>;
+ };
- pcie@1,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0040000 0 0x2000>;
- reg = <0x0800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 58>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 5>;
- status = "disabled";
+ thermal@18300 {
+ compatible = "marvell,armada370-thermal";
+ reg = <0x18300 0x4
+ 0x18304 0x4>;
+ status = "okay";
};
- pcie@2,0 {
+ pcie-controller {
+ compatible = "marvell,armada-370-pcie";
+ status = "disabled";
device_type = "pci";
- assigned-addresses = <0x82002800 0 0xd0080000 0 0x2000>;
- reg = <0x1000 0 0 0 0>;
+
#address-cells = <3>;
#size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 62>;
- marvell,pcie-port = <1>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 9>;
- status = "disabled";
+
+ bus-range = <0x00 0xff>;
+
+ reg = <0x40000 0x2000>, <0x80000 0x2000>;
+
+ reg-names = "pcie0.0", "pcie1.0";
+
+ ranges = <0x82000000 0 0x40000 0x40000 0 0x00002000 /* Port 0.0 registers */
+ 0x82000000 0 0x80000 0x80000 0 0x00002000 /* Port 1.0 registers */
+ 0x82000000 0 0xe0000000 0xe0000000 0 0x08000000 /* non-prefetchable memory */
+ 0x81000000 0 0 0xe8000000 0 0x00100000>; /* downstream I/O */
+
+ pcie@1,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 58>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
+ };
+
+ pcie@2,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82002800 0 0x80000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 62>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 9>;
+ status = "disabled";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/armada-xp-db.dts b/arch/arm/boot/dts/armada-xp-db.dts
index 54cc5bb705fb..d6cc8bf8272e 100644
--- a/arch/arm/boot/dts/armada-xp-db.dts
+++ b/arch/arm/boot/dts/armada-xp-db.dts
@@ -26,132 +26,134 @@
memory {
device_type = "memory";
- reg = <0x00000000 0x80000000>; /* 2 GB */
+ reg = <0 0x00000000 0 0x80000000>; /* 2 GB */
};
soc {
- serial@d0012000 {
- clock-frequency = <250000000>;
- status = "okay";
- };
- serial@d0012100 {
- clock-frequency = <250000000>;
- status = "okay";
- };
- serial@d0012200 {
- clock-frequency = <250000000>;
- status = "okay";
- };
- serial@d0012300 {
- clock-frequency = <250000000>;
- status = "okay";
- };
-
- sata@d00a0000 {
- nr-ports = <2>;
- status = "okay";
- };
-
- mdio {
- phy0: ethernet-phy@0 {
- reg = <0>;
+ internal-regs {
+ serial@12000 {
+ clock-frequency = <250000000>;
+ status = "okay";
};
-
- phy1: ethernet-phy@1 {
- reg = <1>;
+ serial@12100 {
+ clock-frequency = <250000000>;
+ status = "okay";
};
-
- phy2: ethernet-phy@2 {
- reg = <25>;
+ serial@12200 {
+ clock-frequency = <250000000>;
+ status = "okay";
};
-
- phy3: ethernet-phy@3 {
- reg = <27>;
+ serial@12300 {
+ clock-frequency = <250000000>;
+ status = "okay";
};
- };
-
- ethernet@d0070000 {
- status = "okay";
- phy = <&phy0>;
- phy-mode = "rgmii-id";
- };
- ethernet@d0074000 {
- status = "okay";
- phy = <&phy1>;
- phy-mode = "rgmii-id";
- };
- ethernet@d0030000 {
- status = "okay";
- phy = <&phy2>;
- phy-mode = "sgmii";
- };
- ethernet@d0034000 {
- status = "okay";
- phy = <&phy3>;
- phy-mode = "sgmii";
- };
-
- mvsdio@d00d4000 {
- pinctrl-0 = <&sdio_pins>;
- pinctrl-names = "default";
- status = "okay";
- /* No CD or WP GPIOs */
- };
- usb@d0050000 {
- status = "okay";
- };
+ sata@a0000 {
+ nr-ports = <2>;
+ status = "okay";
+ };
- usb@d0051000 {
- status = "okay";
- };
+ mdio {
+ phy0: ethernet-phy@0 {
+ reg = <0>;
+ };
- usb@d0052000 {
- status = "okay";
- };
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
- spi0: spi@d0010600 {
- status = "okay";
+ phy2: ethernet-phy@2 {
+ reg = <25>;
+ };
- spi-flash@0 {
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "m25p64";
- reg = <0>; /* Chip select 0 */
- spi-max-frequency = <20000000>;
+ phy3: ethernet-phy@3 {
+ reg = <27>;
+ };
};
- };
- pcie-controller {
- status = "okay";
+ ethernet@70000 {
+ status = "okay";
+ phy = <&phy0>;
+ phy-mode = "rgmii-id";
+ };
+ ethernet@74000 {
+ status = "okay";
+ phy = <&phy1>;
+ phy-mode = "rgmii-id";
+ };
+ ethernet@30000 {
+ status = "okay";
+ phy = <&phy2>;
+ phy-mode = "sgmii";
+ };
+ ethernet@34000 {
+ status = "okay";
+ phy = <&phy3>;
+ phy-mode = "sgmii";
+ };
- /*
- * All 6 slots are physically present as
- * standard PCIe slots on the board.
- */
- pcie@1,0 {
- /* Port 0, Lane 0 */
+ mvsdio@d4000 {
+ pinctrl-0 = <&sdio_pins>;
+ pinctrl-names = "default";
status = "okay";
+ /* No CD or WP GPIOs */
};
- pcie@2,0 {
- /* Port 0, Lane 1 */
+
+ usb@50000 {
status = "okay";
};
- pcie@3,0 {
- /* Port 0, Lane 2 */
+
+ usb@51000 {
status = "okay";
};
- pcie@4,0 {
- /* Port 0, Lane 3 */
+
+ usb@52000 {
status = "okay";
};
- pcie@9,0 {
- /* Port 2, Lane 0 */
+
+ spi0: spi@10600 {
status = "okay";
+
+ spi-flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "m25p64";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <20000000>;
+ };
};
- pcie@10,0 {
- /* Port 3, Lane 0 */
+
+ pcie-controller {
status = "okay";
+
+ /*
+ * All 6 slots are physically present as
+ * standard PCIe slots on the board.
+ */
+ pcie@1,0 {
+ /* Port 0, Lane 0 */
+ status = "okay";
+ };
+ pcie@2,0 {
+ /* Port 0, Lane 1 */
+ status = "okay";
+ };
+ pcie@3,0 {
+ /* Port 0, Lane 2 */
+ status = "okay";
+ };
+ pcie@4,0 {
+ /* Port 0, Lane 3 */
+ status = "okay";
+ };
+ pcie@9,0 {
+ /* Port 2, Lane 0 */
+ status = "okay";
+ };
+ pcie@10,0 {
+ /* Port 3, Lane 0 */
+ status = "okay";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/armada-xp-gp.dts b/arch/arm/boot/dts/armada-xp-gp.dts
index 04f28a712b98..26ad06fc147e 100644
--- a/arch/arm/boot/dts/armada-xp-gp.dts
+++ b/arch/arm/boot/dts/armada-xp-gp.dts
@@ -26,137 +26,141 @@
memory {
device_type = "memory";
-
/*
- * 4 GB of plug-in RAM modules by default but only 3GB
- * are visible, the amount of memory available can be
- * changed by the bootloader according the size of the
- * module actually plugged
+ * 8 GB of plug-in RAM modules by default.The amount
+ * of memory available can be changed by the
+ * bootloader according the size of the module
+ * actually plugged. Only 7GB are usable because
+ * addresses from 0xC0000000 to 0xffffffff are used by
+ * the internal registers of the SoC.
*/
- reg = <0x00000000 0xC0000000>;
+ reg = <0x00000000 0x00000000 0x00000000 0xC0000000>,
+ <0x00000001 0x00000000 0x00000001 0x00000000>;
};
soc {
- serial@d0012000 {
- clock-frequency = <250000000>;
- status = "okay";
- };
- serial@d0012100 {
- clock-frequency = <250000000>;
- status = "okay";
- };
- serial@d0012200 {
- clock-frequency = <250000000>;
- status = "okay";
- };
- serial@d0012300 {
- clock-frequency = <250000000>;
- status = "okay";
- };
-
- sata@d00a0000 {
- nr-ports = <2>;
- status = "okay";
- };
-
- mdio {
- phy0: ethernet-phy@0 {
- reg = <16>;
+ internal-regs {
+ serial@12000 {
+ clock-frequency = <250000000>;
+ status = "okay";
};
-
- phy1: ethernet-phy@1 {
- reg = <17>;
+ serial@12100 {
+ clock-frequency = <250000000>;
+ status = "okay";
};
-
- phy2: ethernet-phy@2 {
- reg = <18>;
+ serial@12200 {
+ clock-frequency = <250000000>;
+ status = "okay";
+ };
+ serial@12300 {
+ clock-frequency = <250000000>;
+ status = "okay";
};
- phy3: ethernet-phy@3 {
- reg = <19>;
+ sata@a0000 {
+ nr-ports = <2>;
+ status = "okay";
};
- };
- ethernet@d0070000 {
- status = "okay";
- phy = <&phy0>;
- phy-mode = "rgmii-id";
- };
- ethernet@d0074000 {
- status = "okay";
- phy = <&phy1>;
- phy-mode = "rgmii-id";
- };
- ethernet@d0030000 {
- status = "okay";
- phy = <&phy2>;
- phy-mode = "rgmii-id";
- };
- ethernet@d0034000 {
- status = "okay";
- phy = <&phy3>;
- phy-mode = "rgmii-id";
- };
+ mdio {
+ phy0: ethernet-phy@0 {
+ reg = <16>;
+ };
- spi0: spi@d0010600 {
- status = "okay";
+ phy1: ethernet-phy@1 {
+ reg = <17>;
+ };
- spi-flash@0 {
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "n25q128a13";
- reg = <0>; /* Chip select 0 */
- spi-max-frequency = <108000000>;
- };
- };
+ phy2: ethernet-phy@2 {
+ reg = <18>;
+ };
- devbus-bootcs@d0010400 {
- status = "okay";
- ranges = <0 0xf0000000 0x1000000>; /* @addr 0xf000000, size 0x1000000 */
-
- /* Device Bus parameters are required */
-
- /* Read parameters */
- devbus,bus-width = <8>;
- devbus,turn-off-ps = <60000>;
- devbus,badr-skew-ps = <0>;
- devbus,acc-first-ps = <124000>;
- devbus,acc-next-ps = <248000>;
- devbus,rd-setup-ps = <0>;
- devbus,rd-hold-ps = <0>;
-
- /* Write parameters */
- devbus,sync-enable = <0>;
- devbus,wr-high-ps = <60000>;
- devbus,wr-low-ps = <60000>;
- devbus,ale-wr-ps = <60000>;
-
- /* NOR 16 MiB */
- nor@0 {
- compatible = "cfi-flash";
- reg = <0 0x1000000>;
- bank-width = <2>;
+ phy3: ethernet-phy@3 {
+ reg = <19>;
+ };
};
- };
- pcie-controller {
- status = "okay";
+ ethernet@70000 {
+ status = "okay";
+ phy = <&phy0>;
+ phy-mode = "rgmii-id";
+ };
+ ethernet@74000 {
+ status = "okay";
+ phy = <&phy1>;
+ phy-mode = "rgmii-id";
+ };
+ ethernet@30000 {
+ status = "okay";
+ phy = <&phy2>;
+ phy-mode = "rgmii-id";
+ };
+ ethernet@34000 {
+ status = "okay";
+ phy = <&phy3>;
+ phy-mode = "rgmii-id";
+ };
- /*
- * The 3 slots are physically present as
- * standard PCIe slots on the board.
- */
- pcie@1,0 {
- /* Port 0, Lane 0 */
+ spi0: spi@10600 {
status = "okay";
+
+ spi-flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "n25q128a13";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <108000000>;
+ };
};
- pcie@9,0 {
- /* Port 2, Lane 0 */
+
+ devbus-bootcs@10400 {
status = "okay";
+ ranges = <0 0xf0000000 0x1000000>; /* @addr 0xf000000, size 0x1000000 */
+
+ /* Device Bus parameters are required */
+
+ /* Read parameters */
+ devbus,bus-width = <8>;
+ devbus,turn-off-ps = <60000>;
+ devbus,badr-skew-ps = <0>;
+ devbus,acc-first-ps = <124000>;
+ devbus,acc-next-ps = <248000>;
+ devbus,rd-setup-ps = <0>;
+ devbus,rd-hold-ps = <0>;
+
+ /* Write parameters */
+ devbus,sync-enable = <0>;
+ devbus,wr-high-ps = <60000>;
+ devbus,wr-low-ps = <60000>;
+ devbus,ale-wr-ps = <60000>;
+
+ /* NOR 16 MiB */
+ nor@0 {
+ compatible = "cfi-flash";
+ reg = <0 0x1000000>;
+ bank-width = <2>;
+ };
};
- pcie@10,0 {
- /* Port 3, Lane 0 */
+
+ pcie-controller {
status = "okay";
+
+ /*
+ * The 3 slots are physically present as
+ * standard PCIe slots on the board.
+ */
+ pcie@1,0 {
+ /* Port 0, Lane 0 */
+ status = "okay";
+ };
+ pcie@9,0 {
+ /* Port 2, Lane 0 */
+ status = "okay";
+ };
+ pcie@10,0 {
+ /* Port 3, Lane 0 */
+ status = "okay";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/armada-xp-mv78230.dtsi b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
index c2c78459a4d4..f8eaa383e07f 100644
--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi
+++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
@@ -25,159 +25,161 @@
};
cpus {
- #address-cells = <1>;
- #size-cells = <0>;
-
- cpu@0 {
- device_type = "cpu";
- compatible = "marvell,sheeva-v7";
- reg = <0>;
- clocks = <&cpuclk 0>;
- };
-
- cpu@1 {
- device_type = "cpu";
- compatible = "marvell,sheeva-v7";
- reg = <1>;
- clocks = <&cpuclk 1>;
- };
- };
-
- soc {
- pinctrl {
- compatible = "marvell,mv78230-pinctrl";
- reg = <0xd0018000 0x38>;
-
- sdio_pins: sdio-pins {
- marvell,pins = "mpp30", "mpp31", "mpp32",
- "mpp33", "mpp34", "mpp35";
- marvell,function = "sd0";
- };
- };
-
- gpio0: gpio@d0018100 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018100 0x40>;
- ngpios = <32>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <82>, <83>, <84>, <85>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <0>;
+ clocks = <&cpuclk 0>;
};
- gpio1: gpio@d0018140 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018140 0x40>;
- ngpios = <17>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <87>, <88>, <89>;
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <1>;
+ clocks = <&cpuclk 1>;
};
+ };
- /*
- * MV78230 has 2 PCIe units Gen2.0: One unit can be
- * configured as x4 or quad x1 lanes. One unit is
- * x4/x1.
- */
- pcie-controller {
- compatible = "marvell,armada-xp-pcie";
- status = "disabled";
- device_type = "pci";
-
- #address-cells = <3>;
- #size-cells = <2>;
-
- bus-range = <0x00 0xff>;
-
- ranges = <0x82000000 0 0xd0040000 0xd0040000 0 0x00002000 /* Port 0.0 registers */
- 0x82000000 0 0xd0042000 0xd0042000 0 0x00002000 /* Port 2.0 registers */
- 0x82000000 0 0xd0044000 0xd0044000 0 0x00002000 /* Port 0.1 registers */
- 0x82000000 0 0xd0048000 0xd0048000 0 0x00002000 /* Port 0.2 registers */
- 0x82000000 0 0xd004c000 0xd004c000 0 0x00002000 /* Port 0.3 registers */
- 0x82000000 0 0xe0000000 0xe0000000 0 0x08000000 /* non-prefetchable memory */
- 0x81000000 0 0 0xe8000000 0 0x00100000>; /* downstream I/O */
-
- pcie@1,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0040000 0 0x2000>;
- reg = <0x0800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 58>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 5>;
- status = "disabled";
+ soc {
+ internal-regs {
+ pinctrl {
+ compatible = "marvell,mv78230-pinctrl";
+ reg = <0x18000 0x38>;
+
+ sdio_pins: sdio-pins {
+ marvell,pins = "mpp30", "mpp31", "mpp32",
+ "mpp33", "mpp34", "mpp35";
+ marvell,function = "sd0";
+ };
};
- pcie@2,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0044000 0 0x2000>;
- reg = <0x1000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 59>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <1>;
- clocks = <&gateclk 6>;
- status = "disabled";
+ gpio0: gpio@18100 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18100 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <82>, <83>, <84>, <85>;
};
- pcie@3,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0048000 0 0x2000>;
- reg = <0x1800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 60>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <2>;
- clocks = <&gateclk 7>;
- status = "disabled";
+ gpio1: gpio@18140 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18140 0x40>;
+ ngpios = <17>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <87>, <88>, <89>;
};
- pcie@4,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd004c000 0 0x2000>;
- reg = <0x2000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 61>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <3>;
- clocks = <&gateclk 8>;
+ /*
+ * MV78230 has 2 PCIe units Gen2.0: One unit can be
+ * configured as x4 or quad x1 lanes. One unit is
+ * x4/x1.
+ */
+ pcie-controller {
+ compatible = "marvell,armada-xp-pcie";
status = "disabled";
- };
-
- pcie@9,0 {
device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0042000 0 0x2000>;
- reg = <0x4800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 99>;
- marvell,pcie-port = <2>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 26>;
- status = "disabled";
+
+#address-cells = <3>;
+#size-cells = <2>;
+
+ bus-range = <0x00 0xff>;
+
+ ranges = <0x82000000 0 0x40000 0x40000 0 0x00002000 /* Port 0.0 registers */
+ 0x82000000 0 0x42000 0x42000 0 0x00002000 /* Port 2.0 registers */
+ 0x82000000 0 0x44000 0x44000 0 0x00002000 /* Port 0.1 registers */
+ 0x82000000 0 0x48000 0x48000 0 0x00002000 /* Port 0.2 registers */
+ 0x82000000 0 0x4c000 0x4c000 0 0x00002000 /* Port 0.3 registers */
+ 0x82000000 0 0xe0000000 0xe0000000 0 0x08000000 /* non-prefetchable memory */
+ 0x81000000 0 0 0xe8000000 0 0x00100000>; /* downstream I/O */
+
+ pcie@1,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 58>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
+ };
+
+ pcie@2,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 59>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
+ };
+
+ pcie@3,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x48000 0 0x2000>;
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 60>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <2>;
+ clocks = <&gateclk 7>;
+ status = "disabled";
+ };
+
+ pcie@4,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x4c000 0 0x2000>;
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 61>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <3>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
+ };
+
+ pcie@9,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x42000 0 0x2000>;
+ reg = <0x4800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 99>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 26>;
+ status = "disabled";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/armada-xp-mv78260.dtsi b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
index 885bf229eef7..f4029f015aff 100644
--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi
+++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
@@ -26,196 +26,198 @@
};
cpus {
- #address-cells = <1>;
- #size-cells = <0>;
-
- cpu@0 {
- device_type = "cpu";
- compatible = "marvell,sheeva-v7";
- reg = <0>;
- clocks = <&cpuclk 0>;
- };
-
- cpu@1 {
- device_type = "cpu";
- compatible = "marvell,sheeva-v7";
- reg = <1>;
- clocks = <&cpuclk 1>;
- };
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <0>;
+ clocks = <&cpuclk 0>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <1>;
+ clocks = <&cpuclk 1>;
+ };
};
soc {
- pinctrl {
- compatible = "marvell,mv78260-pinctrl";
- reg = <0xd0018000 0x38>;
-
- sdio_pins: sdio-pins {
- marvell,pins = "mpp30", "mpp31", "mpp32",
- "mpp33", "mpp34", "mpp35";
- marvell,function = "sd0";
+ internal-regs {
+ pinctrl {
+ compatible = "marvell,mv78260-pinctrl";
+ reg = <0x18000 0x38>;
+
+ sdio_pins: sdio-pins {
+ marvell,pins = "mpp30", "mpp31", "mpp32",
+ "mpp33", "mpp34", "mpp35";
+ marvell,function = "sd0";
+ };
};
- };
- gpio0: gpio@d0018100 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018100 0x40>;
- ngpios = <32>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <82>, <83>, <84>, <85>;
- };
+ gpio0: gpio@18100 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18100 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <82>, <83>, <84>, <85>;
+ };
- gpio1: gpio@d0018140 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018140 0x40>;
- ngpios = <32>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <87>, <88>, <89>, <90>;
- };
+ gpio1: gpio@18140 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18140 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <87>, <88>, <89>, <90>;
+ };
- gpio2: gpio@d0018180 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018180 0x40>;
- ngpios = <3>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <91>;
- };
+ gpio2: gpio@18180 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18180 0x40>;
+ ngpios = <3>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <91>;
+ };
- ethernet@d0034000 {
+ ethernet@34000 {
compatible = "marvell,armada-370-neta";
- reg = <0xd0034000 0x2500>;
+ reg = <0x34000 0x2500>;
interrupts = <14>;
clocks = <&gateclk 1>;
status = "disabled";
- };
-
- /*
- * MV78260 has 3 PCIe units Gen2.0: Two units can be
- * configured as x4 or quad x1 lanes. One unit is
- * x4/x1.
- */
- pcie-controller {
- compatible = "marvell,armada-xp-pcie";
- status = "disabled";
- device_type = "pci";
-
- #address-cells = <3>;
- #size-cells = <2>;
-
- bus-range = <0x00 0xff>;
-
- ranges = <0x82000000 0 0xd0040000 0xd0040000 0 0x00002000 /* Port 0.0 registers */
- 0x82000000 0 0xd0042000 0xd0042000 0 0x00002000 /* Port 2.0 registers */
- 0x82000000 0 0xd0044000 0xd0044000 0 0x00002000 /* Port 0.1 registers */
- 0x82000000 0 0xd0048000 0xd0048000 0 0x00002000 /* Port 0.2 registers */
- 0x82000000 0 0xd004c000 0xd004c000 0 0x00002000 /* Port 0.3 registers */
- 0x82000000 0 0xd0080000 0xd0080000 0 0x00002000 /* Port 1.0 registers */
- 0x82000000 0 0xd0082000 0xd0082000 0 0x00002000 /* Port 3.0 registers */
- 0x82000000 0 0xe0000000 0xe0000000 0 0x08000000 /* non-prefetchable memory */
- 0x81000000 0 0 0xe8000000 0 0x00100000>; /* downstream I/O */
-
- pcie@1,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0040000 0 0x2000>;
- reg = <0x0800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 58>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 5>;
- status = "disabled";
- };
-
- pcie@2,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0044000 0 0x2000>;
- reg = <0x1000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 59>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <1>;
- clocks = <&gateclk 6>;
- status = "disabled";
};
- pcie@3,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0048000 0 0x2000>;
- reg = <0x1800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 60>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <2>;
- clocks = <&gateclk 7>;
+ /*
+ * MV78260 has 3 PCIe units Gen2.0: Two units can be
+ * configured as x4 or quad x1 lanes. One unit is
+ * x4/x1.
+ */
+ pcie-controller {
+ compatible = "marvell,armada-xp-pcie";
status = "disabled";
- };
-
- pcie@4,0 {
device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd004c000 0 0x2000>;
- reg = <0x2000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 61>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <3>;
- clocks = <&gateclk 8>;
- status = "disabled";
- };
- pcie@9,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0042000 0 0x2000>;
- reg = <0x4800 0 0 0 0>;
#address-cells = <3>;
#size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 99>;
- marvell,pcie-port = <2>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 26>;
- status = "disabled";
- };
- pcie@10,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0082000 0 0x2000>;
- reg = <0x5000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 103>;
- marvell,pcie-port = <3>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 27>;
- status = "disabled";
+ bus-range = <0x00 0xff>;
+
+ ranges = <0x82000000 0 0x40000 0x40000 0 0x00002000 /* Port 0.0 registers */
+ 0x82000000 0 0x42000 0x42000 0 0x00002000 /* Port 2.0 registers */
+ 0x82000000 0 0x44000 0x44000 0 0x00002000 /* Port 0.1 registers */
+ 0x82000000 0 0x48000 0x48000 0 0x00002000 /* Port 0.2 registers */
+ 0x82000000 0 0x4c000 0x4c000 0 0x00002000 /* Port 0.3 registers */
+ 0x82000000 0 0x80000 0x80000 0 0x00002000 /* Port 1.0 registers */
+ 0x82000000 0 0x82000 0x82000 0 0x00002000 /* Port 3.0 registers */
+ 0x82000000 0 0xe0000000 0xe0000000 0 0x08000000 /* non-prefetchable memory */
+ 0x81000000 0 0 0xe8000000 0 0x00100000>; /* downstream I/O */
+
+ pcie@1,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 58>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
+ };
+
+ pcie@2,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 59>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
+ };
+
+ pcie@3,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x48000 0 0x2000>;
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 60>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <2>;
+ clocks = <&gateclk 7>;
+ status = "disabled";
+ };
+
+ pcie@4,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x4c000 0 0x2000>;
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 61>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <3>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
+ };
+
+ pcie@9,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x42000 0 0x2000>;
+ reg = <0x4800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 99>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 26>;
+ status = "disabled";
+ };
+
+ pcie@10,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x82000 0 0x2000>;
+ reg = <0x5000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 103>;
+ marvell,pcie-port = <3>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 27>;
+ status = "disabled";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/armada-xp-mv78460.dtsi b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
index 23a5ac4490a8..6ab56bd35de9 100644
--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi
+++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
@@ -27,277 +27,279 @@
cpus {
- #address-cells = <1>;
- #size-cells = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
- cpu@0 {
- device_type = "cpu";
- compatible = "marvell,sheeva-v7";
- reg = <0>;
- clocks = <&cpuclk 0>;
- };
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <0>;
+ clocks = <&cpuclk 0>;
+ };
- cpu@1 {
- device_type = "cpu";
- compatible = "marvell,sheeva-v7";
- reg = <1>;
- clocks = <&cpuclk 1>;
- };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <1>;
+ clocks = <&cpuclk 1>;
+ };
- cpu@2 {
- device_type = "cpu";
- compatible = "marvell,sheeva-v7";
- reg = <2>;
- clocks = <&cpuclk 2>;
- };
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <2>;
+ clocks = <&cpuclk 2>;
+ };
- cpu@3 {
- device_type = "cpu";
- compatible = "marvell,sheeva-v7";
- reg = <3>;
- clocks = <&cpuclk 3>;
- };
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <3>;
+ clocks = <&cpuclk 3>;
+ };
};
soc {
- pinctrl {
- compatible = "marvell,mv78460-pinctrl";
- reg = <0xd0018000 0x38>;
+ internal-regs {
+ pinctrl {
+ compatible = "marvell,mv78460-pinctrl";
+ reg = <0x18000 0x38>;
- sdio_pins: sdio-pins {
- marvell,pins = "mpp30", "mpp31", "mpp32",
- "mpp33", "mpp34", "mpp35";
- marvell,function = "sd0";
+ sdio_pins: sdio-pins {
+ marvell,pins = "mpp30", "mpp31", "mpp32",
+ "mpp33", "mpp34", "mpp35";
+ marvell,function = "sd0";
+ };
};
- };
- gpio0: gpio@d0018100 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018100 0x40>;
- ngpios = <32>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <82>, <83>, <84>, <85>;
- };
+ gpio0: gpio@18100 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18100 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <82>, <83>, <84>, <85>;
+ };
- gpio1: gpio@d0018140 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018140 0x40>;
- ngpios = <32>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <87>, <88>, <89>, <90>;
- };
+ gpio1: gpio@18140 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18140 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <87>, <88>, <89>, <90>;
+ };
- gpio2: gpio@d0018180 {
- compatible = "marvell,orion-gpio";
- reg = <0xd0018180 0x40>;
- ngpios = <3>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupts-cells = <2>;
- interrupts = <91>;
- };
+ gpio2: gpio@18180 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18180 0x40>;
+ ngpios = <3>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupts-cells = <2>;
+ interrupts = <91>;
+ };
- ethernet@d0034000 {
+ ethernet@34000 {
compatible = "marvell,armada-370-neta";
- reg = <0xd0034000 0x2500>;
+ reg = <0x34000 0x2500>;
interrupts = <14>;
clocks = <&gateclk 1>;
status = "disabled";
- };
+ };
- /*
- * MV78460 has 4 PCIe units Gen2.0: Two units can be
- * configured as x4 or quad x1 lanes. Two units are
- * x4/x1.
- */
- pcie-controller {
- compatible = "marvell,armada-xp-pcie";
- status = "disabled";
- device_type = "pci";
+ /*
+ * MV78460 has 4 PCIe units Gen2.0: Two units can be
+ * configured as x4 or quad x1 lanes. Two units are
+ * x4/x1.
+ */
+ pcie-controller {
+ compatible = "marvell,armada-xp-pcie";
+ status = "disabled";
+ device_type = "pci";
- #address-cells = <3>;
- #size-cells = <2>;
+ #address-cells = <3>;
+ #size-cells = <2>;
- bus-range = <0x00 0xff>;
+ bus-range = <0x00 0xff>;
- ranges = <0x82000000 0 0xd0040000 0xd0040000 0 0x00002000 /* Port 0.0 registers */
- 0x82000000 0 0xd0042000 0xd0042000 0 0x00002000 /* Port 2.0 registers */
- 0x82000000 0 0xd0044000 0xd0044000 0 0x00002000 /* Port 0.1 registers */
- 0x82000000 0 0xd0048000 0xd0048000 0 0x00002000 /* Port 0.2 registers */
- 0x82000000 0 0xd004c000 0xd004c000 0 0x00002000 /* Port 0.3 registers */
- 0x82000000 0 0xd0080000 0xd0080000 0 0x00002000 /* Port 1.0 registers */
- 0x82000000 0 0xd0082000 0xd0082000 0 0x00002000 /* Port 3.0 registers */
- 0x82000000 0 0xd0084000 0xd0084000 0 0x00002000 /* Port 1.1 registers */
- 0x82000000 0 0xd0088000 0xd0088000 0 0x00002000 /* Port 1.2 registers */
- 0x82000000 0 0xd008c000 0xd008c000 0 0x00002000 /* Port 1.3 registers */
- 0x82000000 0 0xe0000000 0xe0000000 0 0x08000000 /* non-prefetchable memory */
- 0x81000000 0 0 0xe8000000 0 0x00100000>; /* downstream I/O */
+ ranges = <0x82000000 0 0x40000 0x40000 0 0x00002000 /* Port 0.0 registers */
+ 0x82000000 0 0x42000 0x42000 0 0x00002000 /* Port 2.0 registers */
+ 0x82000000 0 0x44000 0x44000 0 0x00002000 /* Port 0.1 registers */
+ 0x82000000 0 0x48000 0x48000 0 0x00002000 /* Port 0.2 registers */
+ 0x82000000 0 0x4c000 0x4c000 0 0x00002000 /* Port 0.3 registers */
+ 0x82000000 0 0x80000 0x80000 0 0x00002000 /* Port 1.0 registers */
+ 0x82000000 0 0x82000 0x82000 0 0x00002000 /* Port 3.0 registers */
+ 0x82000000 0 0x84000 0x84000 0 0x00002000 /* Port 1.1 registers */
+ 0x82000000 0 0x88000 0x88000 0 0x00002000 /* Port 1.2 registers */
+ 0x82000000 0 0x8c000 0x8c000 0 0x00002000 /* Port 1.3 registers */
+ 0x82000000 0 0xe0000000 0xe0000000 0 0x08000000 /* non-prefetchable memory */
+ 0x81000000 0 0 0xe8000000 0 0x00100000>; /* downstream I/O */
- pcie@1,0 {
- device_type = "pci";
- assigned-addresses = <0x82000800 0 0xd0040000 0 0x2000>;
- reg = <0x0800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 58>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 5>;
- status = "disabled";
- };
+ pcie@1,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 58>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
+ };
- pcie@2,0 {
- device_type = "pci";
- assigned-addresses = <0x82001000 0 0xd0044000 0 0x2000>;
- reg = <0x1000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 59>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <1>;
- clocks = <&gateclk 6>;
- status = "disabled";
- };
+ pcie@2,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82001000 0 0x44000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 59>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
+ };
- pcie@3,0 {
- device_type = "pci";
- assigned-addresses = <0x82001800 0 0xd0048000 0 0x2000>;
- reg = <0x1800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 60>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <2>;
- clocks = <&gateclk 7>;
- status = "disabled";
- };
+ pcie@3,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82001800 0 0x48000 0 0x2000>;
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 60>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <2>;
+ clocks = <&gateclk 7>;
+ status = "disabled";
+ };
- pcie@4,0 {
- device_type = "pci";
- assigned-addresses = <0x82002000 0 0xd004c000 0 0x2000>;
- reg = <0x2000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 61>;
- marvell,pcie-port = <0>;
- marvell,pcie-lane = <3>;
- clocks = <&gateclk 8>;
- status = "disabled";
- };
+ pcie@4,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82002000 0 0x4c000 0 0x2000>;
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 61>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <3>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
+ };
- pcie@5,0 {
- device_type = "pci";
- assigned-addresses = <0x82002800 0 0xd0080000 0 0x2000>;
- reg = <0x2800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 62>;
- marvell,pcie-port = <1>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 9>;
- status = "disabled";
- };
+ pcie@5,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82002800 0 0x80000 0 0x2000>;
+ reg = <0x2800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 62>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 9>;
+ status = "disabled";
+ };
- pcie@6,0 {
- device_type = "pci";
- assigned-addresses = <0x82003000 0 0xd0084000 0 0x2000>;
- reg = <0x3000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 63>;
- marvell,pcie-port = <1>;
- marvell,pcie-lane = <1>;
- clocks = <&gateclk 10>;
- status = "disabled";
- };
+ pcie@6,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82003000 0 0x84000 0 0x2000>;
+ reg = <0x3000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 63>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 10>;
+ status = "disabled";
+ };
- pcie@7,0 {
- device_type = "pci";
- assigned-addresses = <0x82003800 0 0xd0088000 0 0x2000>;
- reg = <0x3800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 64>;
- marvell,pcie-port = <1>;
- marvell,pcie-lane = <2>;
- clocks = <&gateclk 11>;
- status = "disabled";
- };
+ pcie@7,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82003800 0 0x88000 0 0x2000>;
+ reg = <0x3800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 64>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <2>;
+ clocks = <&gateclk 11>;
+ status = "disabled";
+ };
- pcie@8,0 {
- device_type = "pci";
- assigned-addresses = <0x82004000 0 0xd008c000 0 0x2000>;
- reg = <0x4000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 65>;
- marvell,pcie-port = <1>;
- marvell,pcie-lane = <3>;
- clocks = <&gateclk 12>;
- status = "disabled";
- };
- pcie@9,0 {
- device_type = "pci";
- assigned-addresses = <0x82004800 0 0xd0042000 0 0x2000>;
- reg = <0x4800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 99>;
- marvell,pcie-port = <2>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 26>;
- status = "disabled";
- };
+ pcie@8,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82004000 0 0x8c000 0 0x2000>;
+ reg = <0x4000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 65>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <3>;
+ clocks = <&gateclk 12>;
+ status = "disabled";
+ };
+ pcie@9,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82004800 0 0x42000 0 0x2000>;
+ reg = <0x4800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 99>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 26>;
+ status = "disabled";
+ };
- pcie@10,0 {
- device_type = "pci";
- assigned-addresses = <0x82005000 0 0xd0082000 0 0x2000>;
- reg = <0x5000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- ranges;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &mpic 103>;
- marvell,pcie-port = <3>;
- marvell,pcie-lane = <0>;
- clocks = <&gateclk 27>;
- status = "disabled";
+ pcie@10,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82005000 0 0x82000 0 0x2000>;
+ reg = <0x5000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 103>;
+ marvell,pcie-port = <3>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 27>;
+ status = "disabled";
+ };
};
};
};
- };
+};
diff --git a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
index 9d04f04d4e39..f14d36c46159 100644
--- a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
+++ b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
@@ -23,158 +23,160 @@
memory {
device_type = "memory";
- reg = <0x00000000 0xC0000000>; /* 3 GB */
+ reg = <0 0x00000000 0 0xC0000000>; /* 3 GB */
};
soc {
- serial@d0012000 {
- clock-frequency = <250000000>;
- status = "okay";
- };
- serial@d0012100 {
- clock-frequency = <250000000>;
- status = "okay";
- };
- pinctrl {
- led_pins: led-pins-0 {
- marvell,pins = "mpp49", "mpp51", "mpp53";
- marvell,function = "gpio";
+ internal-regs {
+ serial@12000 {
+ clock-frequency = <250000000>;
+ status = "okay";
};
- };
- leds {
- compatible = "gpio-leds";
- pinctrl-names = "default";
- pinctrl-0 = <&led_pins>;
-
- red_led {
- label = "red_led";
- gpios = <&gpio1 17 1>;
- default-state = "off";
+ serial@12100 {
+ clock-frequency = <250000000>;
+ status = "okay";
};
-
- yellow_led {
- label = "yellow_led";
- gpios = <&gpio1 19 1>;
- default-state = "off";
+ pinctrl {
+ led_pins: led-pins-0 {
+ marvell,pins = "mpp49", "mpp51", "mpp53";
+ marvell,function = "gpio";
+ };
};
-
- green_led {
- label = "green_led";
- gpios = <&gpio1 21 1>;
- default-state = "off";
- linux,default-trigger = "heartbeat";
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins>;
+
+ red_led {
+ label = "red_led";
+ gpios = <&gpio1 17 1>;
+ default-state = "off";
+ };
+
+ yellow_led {
+ label = "yellow_led";
+ gpios = <&gpio1 19 1>;
+ default-state = "off";
+ };
+
+ green_led {
+ label = "green_led";
+ gpios = <&gpio1 21 1>;
+ default-state = "off";
+ linux,default-trigger = "heartbeat";
+ };
};
- };
- gpio_keys {
- compatible = "gpio-keys";
- #address-cells = <1>;
- #size-cells = <0>;
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
- button@1 {
- label = "Init Button";
- linux,code = <116>;
- gpios = <&gpio1 28 0>;
+ button@1 {
+ label = "Init Button";
+ linux,code = <116>;
+ gpios = <&gpio1 28 0>;
+ };
};
- };
- mdio {
- phy0: ethernet-phy@0 {
- reg = <0>;
- };
+ mdio {
+ phy0: ethernet-phy@0 {
+ reg = <0>;
+ };
- phy1: ethernet-phy@1 {
- reg = <1>;
- };
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
- phy2: ethernet-phy@2 {
- reg = <2>;
- };
+ phy2: ethernet-phy@2 {
+ reg = <2>;
+ };
- phy3: ethernet-phy@3 {
- reg = <3>;
+ phy3: ethernet-phy@3 {
+ reg = <3>;
+ };
};
- };
- ethernet@d0070000 {
- status = "okay";
- phy = <&phy0>;
- phy-mode = "sgmii";
- };
- ethernet@d0074000 {
- status = "okay";
- phy = <&phy1>;
- phy-mode = "sgmii";
- };
- ethernet@d0030000 {
- status = "okay";
- phy = <&phy2>;
- phy-mode = "sgmii";
- };
- ethernet@d0034000 {
- status = "okay";
- phy = <&phy3>;
- phy-mode = "sgmii";
- };
- i2c@d0011000 {
- status = "okay";
- clock-frequency = <400000>;
- };
- i2c@d0011100 {
- status = "okay";
- clock-frequency = <400000>;
+ ethernet@70000 {
+ status = "okay";
+ phy = <&phy0>;
+ phy-mode = "sgmii";
+ };
+ ethernet@74000 {
+ status = "okay";
+ phy = <&phy1>;
+ phy-mode = "sgmii";
+ };
+ ethernet@30000 {
+ status = "okay";
+ phy = <&phy2>;
+ phy-mode = "sgmii";
+ };
+ ethernet@34000 {
+ status = "okay";
+ phy = <&phy3>;
+ phy-mode = "sgmii";
+ };
+ i2c@11000 {
+ status = "okay";
+ clock-frequency = <400000>;
+ };
+ i2c@11100 {
+ status = "okay";
+ clock-frequency = <400000>;
- s35390a: s35390a@30 {
- compatible = "s35390a";
- reg = <0x30>;
+ s35390a: s35390a@30 {
+ compatible = "s35390a";
+ reg = <0x30>;
+ };
+ };
+ sata@a0000 {
+ nr-ports = <2>;
+ status = "okay";
+ };
+ usb@50000 {
+ status = "okay";
+ };
+ usb@51000 {
+ status = "okay";
};
- };
- sata@d00a0000 {
- nr-ports = <2>;
- status = "okay";
- };
- usb@d0050000 {
- status = "okay";
- };
- usb@d0051000 {
- status = "okay";
- };
- devbus-bootcs@d0010400 {
- status = "okay";
- ranges = <0 0xf0000000 0x8000000>; /* @addr 0xf000000, size 0x8000000 */
-
- /* Device Bus parameters are required */
-
- /* Read parameters */
- devbus,bus-width = <8>;
- devbus,turn-off-ps = <60000>;
- devbus,badr-skew-ps = <0>;
- devbus,acc-first-ps = <124000>;
- devbus,acc-next-ps = <248000>;
- devbus,rd-setup-ps = <0>;
- devbus,rd-hold-ps = <0>;
-
- /* Write parameters */
- devbus,sync-enable = <0>;
- devbus,wr-high-ps = <60000>;
- devbus,wr-low-ps = <60000>;
- devbus,ale-wr-ps = <60000>;
-
- /* NOR 128 MiB */
- nor@0 {
- compatible = "cfi-flash";
- reg = <0 0x8000000>;
- bank-width = <2>;
+ devbus-bootcs@10400 {
+ status = "okay";
+ ranges = <0 0xf0000000 0x8000000>; /* @addr 0xf000000, size 0x8000000 */
+
+ /* Device Bus parameters are required */
+
+ /* Read parameters */
+ devbus,bus-width = <8>;
+ devbus,turn-off-ps = <60000>;
+ devbus,badr-skew-ps = <0>;
+ devbus,acc-first-ps = <124000>;
+ devbus,acc-next-ps = <248000>;
+ devbus,rd-setup-ps = <0>;
+ devbus,rd-hold-ps = <0>;
+
+ /* Write parameters */
+ devbus,sync-enable = <0>;
+ devbus,wr-high-ps = <60000>;
+ devbus,wr-low-ps = <60000>;
+ devbus,ale-wr-ps = <60000>;
+
+ /* NOR 128 MiB */
+ nor@0 {
+ compatible = "cfi-flash";
+ reg = <0 0x8000000>;
+ bank-width = <2>;
+ };
};
- };
- pcie-controller {
- status = "okay";
- /* Internal mini-PCIe connector */
- pcie@1,0 {
- /* Port 0, Lane 0 */
+ pcie-controller {
status = "okay";
+ /* Internal mini-PCIe connector */
+ pcie@1,0 {
+ /* Port 0, Lane 0 */
+ status = "okay";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi
index 29dfeb6d4a26..bacab11c10dc 100644
--- a/arch/arm/boot/dts/armada-xp.dtsi
+++ b/arch/arm/boot/dts/armada-xp.dtsi
@@ -22,140 +22,140 @@
model = "Marvell Armada XP family SoC";
compatible = "marvell,armadaxp", "marvell,armada-370-xp";
- L2: l2-cache {
- compatible = "marvell,aurora-system-cache";
- reg = <0xd0008000 0x1000>;
- cache-id-part = <0x100>;
- wt-override;
- };
+ soc {
+ internal-regs {
+ L2: l2-cache {
+ compatible = "marvell,aurora-system-cache";
+ reg = <0x08000 0x1000>;
+ cache-id-part = <0x100>;
+ wt-override;
+ };
- mpic: interrupt-controller@d0020000 {
- reg = <0xd0020a00 0x2d0>,
- <0xd0021070 0x58>;
- };
+ mpic: interrupt-controller@20000 {
+ reg = <0x20a00 0x2d0>, <0x21070 0x58>;
+ };
- armada-370-xp-pmsu@d0022000 {
- compatible = "marvell,armada-370-xp-pmsu";
- reg = <0xd0022100 0x430>,
- <0xd0020800 0x20>;
- };
+ armada-370-xp-pmsu@22000 {
+ compatible = "marvell,armada-370-xp-pmsu";
+ reg = <0x22100 0x430>, <0x20800 0x20>;
+ };
- soc {
- serial@d0012200 {
+ serial@12200 {
compatible = "snps,dw-apb-uart";
- reg = <0xd0012200 0x100>;
+ reg = <0x12200 0x100>;
reg-shift = <2>;
interrupts = <43>;
reg-io-width = <1>;
status = "disabled";
- };
- serial@d0012300 {
+ };
+ serial@12300 {
compatible = "snps,dw-apb-uart";
- reg = <0xd0012300 0x100>;
+ reg = <0x12300 0x100>;
reg-shift = <2>;
interrupts = <44>;
reg-io-width = <1>;
status = "disabled";
- };
+ };
- timer@d0020300 {
+ timer@20300 {
marvell,timer-25Mhz;
- };
+ };
- coreclk: mvebu-sar@d0018230 {
- compatible = "marvell,armada-xp-core-clock";
- reg = <0xd0018230 0x08>;
- #clock-cells = <1>;
- };
+ coreclk: mvebu-sar@18230 {
+ compatible = "marvell,armada-xp-core-clock";
+ reg = <0x18230 0x08>;
+ #clock-cells = <1>;
+ };
- cpuclk: clock-complex@d0018700 {
- #clock-cells = <1>;
- compatible = "marvell,armada-xp-cpu-clock";
- reg = <0xd0018700 0xA0>;
- clocks = <&coreclk 1>;
- };
+ cpuclk: clock-complex@18700 {
+ #clock-cells = <1>;
+ compatible = "marvell,armada-xp-cpu-clock";
+ reg = <0x18700 0xA0>;
+ clocks = <&coreclk 1>;
+ };
- gateclk: clock-gating-control@d0018220 {
- compatible = "marvell,armada-xp-gating-clock";
- reg = <0xd0018220 0x4>;
- clocks = <&coreclk 0>;
- #clock-cells = <1>;
- };
+ gateclk: clock-gating-control@18220 {
+ compatible = "marvell,armada-xp-gating-clock";
+ reg = <0x18220 0x4>;
+ clocks = <&coreclk 0>;
+ #clock-cells = <1>;
+ };
- system-controller@d0018200 {
+ system-controller@18200 {
compatible = "marvell,armada-370-xp-system-controller";
- reg = <0xd0018200 0x500>;
- };
+ reg = <0x18200 0x500>;
+ };
- ethernet@d0030000 {
+ ethernet@30000 {
compatible = "marvell,armada-370-neta";
- reg = <0xd0030000 0x2500>;
+ reg = <0x30000 0x2500>;
interrupts = <12>;
clocks = <&gateclk 2>;
status = "disabled";
- };
-
- xor@d0060900 {
- compatible = "marvell,orion-xor";
- reg = <0xd0060900 0x100
- 0xd0060b00 0x100>;
- clocks = <&gateclk 22>;
- status = "okay";
-
- xor10 {
- interrupts = <51>;
- dmacap,memcpy;
- dmacap,xor;
- };
- xor11 {
- interrupts = <52>;
- dmacap,memcpy;
- dmacap,xor;
- dmacap,memset;
};
- };
- xor@d00f0900 {
- compatible = "marvell,orion-xor";
- reg = <0xd00F0900 0x100
- 0xd00F0B00 0x100>;
- clocks = <&gateclk 28>;
- status = "okay";
-
- xor00 {
- interrupts = <94>;
- dmacap,memcpy;
- dmacap,xor;
+ xor@60900 {
+ compatible = "marvell,orion-xor";
+ reg = <0x60900 0x100
+ 0x60b00 0x100>;
+ clocks = <&gateclk 22>;
+ status = "okay";
+
+ xor10 {
+ interrupts = <51>;
+ dmacap,memcpy;
+ dmacap,xor;
+ };
+ xor11 {
+ interrupts = <52>;
+ dmacap,memcpy;
+ dmacap,xor;
+ dmacap,memset;
+ };
};
- xor01 {
- interrupts = <95>;
- dmacap,memcpy;
- dmacap,xor;
- dmacap,memset;
+
+ xor@f0900 {
+ compatible = "marvell,orion-xor";
+ reg = <0xF0900 0x100
+ 0xF0B00 0x100>;
+ clocks = <&gateclk 28>;
+ status = "okay";
+
+ xor00 {
+ interrupts = <94>;
+ dmacap,memcpy;
+ dmacap,xor;
+ };
+ xor01 {
+ interrupts = <95>;
+ dmacap,memcpy;
+ dmacap,xor;
+ dmacap,memset;
+ };
};
- };
- usb@d0050000 {
- clocks = <&gateclk 18>;
- };
+ usb@50000 {
+ clocks = <&gateclk 18>;
+ };
- usb@d0051000 {
- clocks = <&gateclk 19>;
- };
+ usb@51000 {
+ clocks = <&gateclk 19>;
+ };
- usb@d0052000 {
- compatible = "marvell,orion-ehci";
- reg = <0xd0052000 0x500>;
- interrupts = <47>;
- clocks = <&gateclk 20>;
- status = "disabled";
- };
+ usb@52000 {
+ compatible = "marvell,orion-ehci";
+ reg = <0x52000 0x500>;
+ interrupts = <47>;
+ clocks = <&gateclk 20>;
+ status = "disabled";
+ };
- thermal@d00182b0 {
- compatible = "marvell,armadaxp-thermal";
- reg = <0xd00182b0 0x4
- 0xd00184d0 0x4>;
- status = "okay";
+ thermal@182b0 {
+ compatible = "marvell,armadaxp-thermal";
+ reg = <0x182b0 0x4
+ 0x184d0 0x4>;
+ status = "okay";
+ };
};
};
};
diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi
index f8f7370e8669..bf18a735c37d 100644
--- a/arch/arm/boot/dts/at91sam9g45.dtsi
+++ b/arch/arm/boot/dts/at91sam9g45.dtsi
@@ -108,6 +108,7 @@
compatible = "atmel,at91sam9g45-dma";
reg = <0xffffec00 0x200>;
interrupts = <21 4 0>;
+ #dma-cells = <2>;
};
pinctrl@fffff200 {
@@ -533,6 +534,8 @@
compatible = "atmel,hsmci";
reg = <0xfff80000 0x600>;
interrupts = <11 4 0>;
+ dmas = <&dma 1 0>;
+ dma-names = "rxtx";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -542,6 +545,8 @@
compatible = "atmel,hsmci";
reg = <0xfffd0000 0x600>;
interrupts = <29 4 0>;
+ dmas = <&dma 1 13>;
+ dma-names = "rxtx";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi
index b2961f1ea51b..3de8e6dfbcb1 100644
--- a/arch/arm/boot/dts/at91sam9n12.dtsi
+++ b/arch/arm/boot/dts/at91sam9n12.dtsi
@@ -89,6 +89,8 @@
compatible = "atmel,hsmci";
reg = <0xf0008000 0x600>;
interrupts = <12 4 0>;
+ dmas = <&dma 1 0>;
+ dma-names = "rxtx";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -110,6 +112,7 @@
compatible = "atmel,at91sam9g45-dma";
reg = <0xffffec00 0x200>;
interrupts = <20 4 0>;
+ #dma-cells = <2>;
};
pinctrl@fffff400 {
@@ -378,6 +381,9 @@
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf8010000 0x100>;
interrupts = <9 4 6>;
+ dmas = <&dma 1 13>,
+ <&dma 1 14>;
+ dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -387,6 +393,9 @@
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf8014000 0x100>;
interrupts = <10 4 6>;
+ dmas = <&dma 1 15>,
+ <&dma 1 16>;
+ dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi
index 640b3bbbb706..1145ac330fb7 100644
--- a/arch/arm/boot/dts/at91sam9x5.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5.dtsi
@@ -104,12 +104,14 @@
compatible = "atmel,at91sam9g45-dma";
reg = <0xffffec00 0x200>;
interrupts = <20 4 0>;
+ #dma-cells = <2>;
};
dma1: dma-controller@ffffee00 {
compatible = "atmel,at91sam9g45-dma";
reg = <0xffffee00 0x200>;
interrupts = <21 4 0>;
+ #dma-cells = <2>;
};
pinctrl@fffff400 {
@@ -465,6 +467,8 @@
compatible = "atmel,hsmci";
reg = <0xf0008000 0x600>;
interrupts = <12 4 0>;
+ dmas = <&dma0 1 0>;
+ dma-names = "rxtx";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -474,6 +478,8 @@
compatible = "atmel,hsmci";
reg = <0xf000c000 0x600>;
interrupts = <26 4 0>;
+ dmas = <&dma1 1 0>;
+ dma-names = "rxtx";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -535,6 +541,9 @@
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf8010000 0x100>;
interrupts = <9 4 6>;
+ dmas = <&dma0 1 7>,
+ <&dma0 1 8>;
+ dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
@@ -546,6 +555,9 @@
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf8014000 0x100>;
interrupts = <10 4 6>;
+ dmas = <&dma1 1 5>,
+ <&dma1 1 6>;
+ dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
@@ -557,6 +569,9 @@
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf8018000 0x100>;
interrupts = <11 4 6>;
+ dmas = <&dma0 1 9>,
+ <&dma0 1 10>;
+ dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/cros5250-common.dtsi b/arch/arm/boot/dts/cros5250-common.dtsi
index 62eceb4f0d3f..0a61bbb9102f 100644
--- a/arch/arm/boot/dts/cros5250-common.dtsi
+++ b/arch/arm/boot/dts/cros5250-common.dtsi
@@ -19,11 +19,19 @@
chosen {
};
+ pinctrl@11400000 {
+ /*
+ * Disabled pullups since external part has its own pullups and
+ * double-pulling gets us out of spec in some cases.
+ */
+ i2c2_bus: i2c2-bus {
+ samsung,pin-pud = <0>;
+ };
+ };
+
i2c@12C60000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <378000>;
- gpios = <&gpb3 0 2 3 0>,
- <&gpb3 1 2 3 0>;
max77686@09 {
compatible = "maxim,max77686";
@@ -167,21 +175,12 @@
i2c@12C70000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <378000>;
- gpios = <&gpb3 2 2 3 0>,
- <&gpb3 3 2 3 0>;
};
i2c@12C80000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <66000>;
- /*
- * Disabled pullups since external part has its own pullups and
- * double-pulling gets us out of spec in some cases.
- */
- gpios = <&gpa0 6 3 0 0>,
- <&gpa0 7 3 0 0>;
-
hdmiddc@50 {
compatible = "samsung,exynos5-hdmiddc";
reg = <0x50>;
@@ -191,19 +190,16 @@
i2c@12C90000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <66000>;
- gpios = <&gpa1 2 3 3 0>,
- <&gpa1 3 3 3 0>;
};
i2c@12CA0000 {
- status = "disabled";
+ samsung,i2c-sda-delay = <100>;
+ samsung,i2c-max-bus-freq = <66000>;
};
i2c@12CB0000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <66000>;
- gpios = <&gpa2 2 3 3 0>,
- <&gpa2 3 3 3 0>;
};
i2c@12CC0000 {
@@ -213,8 +209,6 @@
i2c@12CD0000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <66000>;
- gpios = <&gpb2 2 3 3 0>,
- <&gpb2 3 3 3 0>;
};
i2c@12CE0000 {
@@ -236,15 +230,12 @@
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_cd &sd0_bus4 &sd0_bus8>;
slot@0 {
reg = <0>;
bus-width = <8>;
- gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
- <&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
- <&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
- <&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
- <&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
};
};
@@ -260,15 +251,13 @@
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
slot@0 {
reg = <0>;
bus-width = <4>;
- samsung,cd-pinmux-gpio = <&gpc3 2 2 3 3>;
- wp-gpios = <&gpc2 1 0 0 3>;
- gpios = <&gpc3 0 2 0 3>, <&gpc3 1 2 0 3>,
- <&gpc3 3 2 3 3>, <&gpc3 4 2 3 3>,
- <&gpc3 5 2 3 3>, <&gpc3 6 2 3 3>;
+ wp-gpios = <&gpc2 1 0>;
};
};
@@ -281,11 +270,11 @@
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
+ /* See board-specific dts files for pin setup */
slot@0 {
reg = <0>;
bus-width = <4>;
- /* See board-specific dts files for GPIOs */
};
};
@@ -294,9 +283,6 @@
};
spi_1: spi@12d30000 {
- gpios = <&gpa2 4 2 3 0>,
- <&gpa2 6 2 3 0>,
- <&gpa2 7 2 3 0>;
samsung,spi-src-clk = <0>;
num-cs = <1>;
};
@@ -306,7 +292,7 @@
};
hdmi {
- hpd-gpio = <&gpx3 7 0xf 1 3>;
+ hpd-gpio = <&gpx3 7 0>;
};
gpio-keys {
@@ -314,7 +300,7 @@
power {
label = "Power";
- gpios = <&gpx1 3 0 0x10000 0>;
+ gpios = <&gpx1 3 1>;
linux,code = <116>; /* KEY_POWER */
gpio-key,wakeup;
};
diff --git a/arch/arm/boot/dts/da850-evm.dts b/arch/arm/boot/dts/da850-evm.dts
index c5834a6c5bf4..c914357c0d89 100644
--- a/arch/arm/boot/dts/da850-evm.dts
+++ b/arch/arm/boot/dts/da850-evm.dts
@@ -50,6 +50,46 @@
pinctrl-names = "default";
pinctrl-0 = <&mmc0_pins>;
};
+ spi1: spi@1f0e000 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi1_pins &spi1_cs0_pin>;
+ flash: m25p80@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "m25p64";
+ spi-max-frequency = <30000000>;
+ reg = <0>;
+ partition@0 {
+ label = "U-Boot-SPL";
+ reg = <0x00000000 0x00010000>;
+ read-only;
+ };
+ partition@1 {
+ label = "U-Boot";
+ reg = <0x00010000 0x00080000>;
+ read-only;
+ };
+ partition@2 {
+ label = "U-Boot-Env";
+ reg = <0x00090000 0x00010000>;
+ read-only;
+ };
+ partition@3 {
+ label = "Kernel";
+ reg = <0x000a0000 0x00280000>;
+ };
+ partition@4 {
+ label = "Filesystem";
+ reg = <0x00320000 0x00400000>;
+ };
+ partition@5 {
+ label = "MAC-Address";
+ reg = <0x007f0000 0x00010000>;
+ read-only;
+ };
+ };
+ };
};
nand_cs3@62000000 {
status = "okay";
diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
index 3ade343f13cc..2c88313d2c7a 100644
--- a/arch/arm/boot/dts/da850.dtsi
+++ b/arch/arm/boot/dts/da850.dtsi
@@ -71,6 +71,60 @@
0x28 0x00222222 0x00ffffff
>;
};
+ ehrpwm0a_pins: pinmux_ehrpwm0a_pins {
+ pinctrl-single,bits = <
+ /* EPWM0A */
+ 0xc 0x00000002 0x0000000f
+ >;
+ };
+ ehrpwm0b_pins: pinmux_ehrpwm0b_pins {
+ pinctrl-single,bits = <
+ /* EPWM0B */
+ 0xc 0x00000020 0x000000f0
+ >;
+ };
+ ehrpwm1a_pins: pinmux_ehrpwm1a_pins {
+ pinctrl-single,bits = <
+ /* EPWM1A */
+ 0x14 0x00000002 0x0000000f
+ >;
+ };
+ ehrpwm1b_pins: pinmux_ehrpwm1b_pins {
+ pinctrl-single,bits = <
+ /* EPWM1B */
+ 0x14 0x00000020 0x000000f0
+ >;
+ };
+ ecap0_pins: pinmux_ecap0_pins {
+ pinctrl-single,bits = <
+ /* ECAP0_APWM0 */
+ 0x8 0x20000000 0xf0000000
+ >;
+ };
+ ecap1_pins: pinmux_ecap1_pins {
+ pinctrl-single,bits = <
+ /* ECAP1_APWM1 */
+ 0x4 0x40000000 0xf0000000
+ >;
+ };
+ ecap2_pins: pinmux_ecap2_pins {
+ pinctrl-single,bits = <
+ /* ECAP2_APWM2 */
+ 0x4 0x00000004 0x0000000f
+ >;
+ };
+ spi1_pins: pinmux_spi_pins {
+ pinctrl-single,bits = <
+ /* SIMO, SOMI, CLK */
+ 0x14 0x00110100 0x00ff0f00
+ >;
+ };
+ spi1_cs0_pin: pinmux_spi1_cs0 {
+ pinctrl-single,bits = <
+ /* CS0 */
+ 0x14 0x00000010 0x000000f0
+ >;
+ };
};
serial0: serial@1c42000 {
compatible = "ns16550a";
@@ -122,6 +176,46 @@
interrupts = <16>;
status = "disabled";
};
+ ehrpwm0: ehrpwm@01f00000 {
+ compatible = "ti,da850-ehrpwm", "ti,am33xx-ehrpwm";
+ #pwm-cells = <3>;
+ reg = <0x300000 0x2000>;
+ status = "disabled";
+ };
+ ehrpwm1: ehrpwm@01f02000 {
+ compatible = "ti,da850-ehrpwm", "ti,am33xx-ehrpwm";
+ #pwm-cells = <3>;
+ reg = <0x302000 0x2000>;
+ status = "disabled";
+ };
+ ecap0: ecap@01f06000 {
+ compatible = "ti,da850-ecap", "ti,am33xx-ecap";
+ #pwm-cells = <3>;
+ reg = <0x306000 0x80>;
+ status = "disabled";
+ };
+ ecap1: ecap@01f07000 {
+ compatible = "ti,da850-ecap", "ti,am33xx-ecap";
+ #pwm-cells = <3>;
+ reg = <0x307000 0x80>;
+ status = "disabled";
+ };
+ ecap2: ecap@01f08000 {
+ compatible = "ti,da850-ecap", "ti,am33xx-ecap";
+ #pwm-cells = <3>;
+ reg = <0x308000 0x80>;
+ status = "disabled";
+ };
+ spi1: spi@1f0e000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "ti,da830-spi";
+ reg = <0x30e000 0x1000>;
+ num-cs = <4>;
+ ti,davinci-spi-intr-line = <1>;
+ interrupts = <56>;
+ status = "disabled";
+ };
};
nand_cs3@62000000 {
compatible = "ti,davinci-nand";
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 9ac47d51c407..359694c78918 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -38,6 +38,11 @@
i2c7 = &i2c_7;
};
+ chipid@10000000 {
+ compatible = "samsung,exynos4210-chipid";
+ reg = <0x10000000 0x100>;
+ };
+
pd_mfc: mfc-power-domain@10023C40 {
compatible = "samsung,exynos4210-pd";
reg = <0x10023C40 0x20>;
@@ -82,6 +87,11 @@
reg = <0x10440000 0x1000>;
};
+ sys_reg: sysreg {
+ compatible = "samsung,exynos4-sysreg", "syscon";
+ reg = <0x10010000 0x400>;
+ };
+
watchdog@10060000 {
compatible = "samsung,s3c2410-wdt";
reg = <0x10060000 0x100>;
@@ -197,6 +207,8 @@
interrupts = <0 58 0>;
clocks = <&clock 317>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_bus>;
status = "disabled";
};
@@ -208,6 +220,8 @@
interrupts = <0 59 0>;
clocks = <&clock 318>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_bus>;
status = "disabled";
};
@@ -287,6 +301,8 @@
#size-cells = <0>;
clocks = <&clock 327>, <&clock 159>;
clock-names = "spi", "spi_busclk0";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi0_bus>;
status = "disabled";
};
@@ -300,6 +316,8 @@
#size-cells = <0>;
clocks = <&clock 328>, <&clock 160>;
clock-names = "spi", "spi_busclk0";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi1_bus>;
status = "disabled";
};
@@ -313,6 +331,16 @@
#size-cells = <0>;
clocks = <&clock 329>, <&clock 161>;
clock-names = "spi", "spi_busclk0";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi2_bus>;
+ status = "disabled";
+ };
+
+ pwm@139D0000 {
+ compatible = "samsung,exynos4210-pwm";
+ reg = <0x139D0000 0x1000>;
+ interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
+ #pwm-cells = <2>;
status = "disabled";
};
@@ -356,4 +384,16 @@
#dma-requests = <1>;
};
};
+
+ fimd: fimd@11c00000 {
+ compatible = "samsung,exynos4210-fimd";
+ interrupt-parent = <&combiner>;
+ reg = <0x11c00000 0x20000>;
+ interrupt-names = "fifo", "vsync", "lcd_sys";
+ interrupts = <11 0>, <11 1>, <11 2>;
+ clocks = <&clock 140>, <&clock 283>;
+ clock-names = "sclk_fimd", "fimd";
+ samsung,power-domain = <&pd_lcd0>;
+ status = "disabled";
+ };
};
diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts
index 1b30bc8e2654..524b90846df5 100644
--- a/arch/arm/boot/dts/exynos4210-origen.dts
+++ b/arch/arm/boot/dts/exynos4210-origen.dts
@@ -57,6 +57,10 @@
status = "okay";
};
+ g2d@12800000 {
+ status = "okay";
+ };
+
codec@13400000 {
samsung,mfc-r = <0x43000000 0x800000>;
samsung,mfc-l = <0x51000000 0x800000>;
diff --git a/arch/arm/boot/dts/exynos4210-smdkv310.dts b/arch/arm/boot/dts/exynos4210-smdkv310.dts
index f52c86e2d424..91332b72acf5 100644
--- a/arch/arm/boot/dts/exynos4210-smdkv310.dts
+++ b/arch/arm/boot/dts/exynos4210-smdkv310.dts
@@ -30,16 +30,13 @@
};
sdhci@12530000 {
- samsung,sdhci-bus-width = <4>;
- linux,mmc_cap_4_bit_data;
- samsung,sdhci-cd-internal;
- gpio-cd = <&gpk2 2 2 3 3>;
- gpios = <&gpk2 0 2 0 3>,
- <&gpk2 1 2 0 3>,
- <&gpk2 3 2 3 3>,
- <&gpk2 4 2 3 3>,
- <&gpk2 5 2 3 3>,
- <&gpk2 6 2 3 3>;
+ bus-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
+ status = "okay";
+ };
+
+ g2d@12800000 {
status = "okay";
};
@@ -65,25 +62,32 @@
status = "okay";
};
+ pinctrl@11000000 {
+ keypad_rows: keypad-rows {
+ samsung,pins = "gpx2-0", "gpx2-1";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ keypad_cols: keypad-cols {
+ samsung,pins = "gpx1-0", "gpx1-1", "gpx1-2", "gpx1-3",
+ "gpx1-4", "gpx1-5", "gpx1-6", "gpx1-7";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+ };
+
keypad@100A0000 {
samsung,keypad-num-rows = <2>;
samsung,keypad-num-columns = <8>;
linux,keypad-no-autorepeat;
linux,keypad-wakeup;
+ pinctrl-names = "default";
+ pinctrl-0 = <&keypad_rows &keypad_cols>;
status = "okay";
- row-gpios = <&gpx2 0 3 3 0>,
- <&gpx2 1 3 3 0>;
-
- col-gpios = <&gpx1 0 3 0 0>,
- <&gpx1 1 3 0 0>,
- <&gpx1 2 3 0 0>,
- <&gpx1 3 3 0 0>,
- <&gpx1 4 3 0 0>,
- <&gpx1 5 3 0 0>,
- <&gpx1 6 3 0 0>,
- <&gpx1 7 3 0 0>;
-
key_1 {
keypad,row = <0>;
keypad,column = <3>;
@@ -149,9 +153,7 @@
#address-cells = <1>;
#size-cells = <0>;
samsung,i2c-sda-delay = <100>;
- samsung,i2c-max-bus-freq = <20000>;
- gpios = <&gpd1 0 2 3 0>,
- <&gpd1 1 2 3 0>;
+ samsung,i2c-max-bus-freq = <100000>;
status = "okay";
eeprom@50 {
@@ -166,9 +168,6 @@
};
spi_2: spi@13940000 {
- gpios = <&gpc1 1 5 3 0>,
- <&gpc1 3 5 3 0>,
- <&gpc1 4 5 3 0>;
status = "okay";
w25x80@0 {
@@ -179,7 +178,7 @@
spi-max-frequency = <1000000>;
controller-data {
- cs-gpio = <&gpc1 2 1 0 3>;
+ cs-gpio = <&gpc1 2 0>;
samsung,spi-feedback-delay = <0>;
};
diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts
new file mode 100644
index 000000000000..345cdb51dcb7
--- /dev/null
+++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts
@@ -0,0 +1,352 @@
+/*
+ * Samsung's Exynos4210 based Universal C210 board device tree source
+ *
+ * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Device tree source file for Samsung's Universal C210 board which is based on
+ * Samsung's Exynos4210 rev0 SoC.
+ *
+ * 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.
+*/
+
+/dts-v1/;
+/include/ "exynos4210.dtsi"
+
+/ {
+ model = "Samsung Universal C210 based on Exynos4210 rev0";
+ compatible = "samsung,universal_c210", "samsung,exynos4210";
+
+ memory {
+ reg = <0x40000000 0x10000000
+ 0x50000000 0x10000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1";
+ };
+
+ mct@10050000 {
+ compatible = "none";
+ };
+
+ fixed-rate-clocks {
+ xxti {
+ compatible = "samsung,clock-xxti";
+ clock-frequency = <0>;
+ };
+
+ xusbxti {
+ compatible = "samsung,clock-xusbxti";
+ clock-frequency = <24000000>;
+ };
+ };
+
+ vemmc_reg: voltage-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "VMEM_VDD_2_8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ gpio = <&gpe1 3 0>;
+ enable-active-high;
+ };
+
+ sdhci_emmc: sdhci@12510000 {
+ bus-width = <8>;
+ non-removable;
+ pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus8>;
+ pinctrl-names = "default";
+ vmmc-supply = <&vemmc_reg>;
+ status = "okay";
+ };
+
+ serial@13800000 {
+ status = "okay";
+ };
+
+ serial@13810000 {
+ status = "okay";
+ };
+
+ serial@13820000 {
+ status = "okay";
+ };
+
+ serial@13830000 {
+ status = "okay";
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ vol-up-key {
+ gpios = <&gpx2 0 1>;
+ linux,code = <115>;
+ label = "volume up";
+ debounce-interval = <1>;
+ };
+
+ vol-down-key {
+ gpios = <&gpx2 1 1>;
+ linux,code = <114>;
+ label = "volume down";
+ debounce-interval = <1>;
+ };
+
+ config-key {
+ gpios = <&gpx2 2 1>;
+ linux,code = <171>;
+ label = "config";
+ debounce-interval = <1>;
+ gpio-key,wakeup;
+ };
+
+ camera-key {
+ gpios = <&gpx2 3 1>;
+ linux,code = <212>;
+ label = "camera";
+ debounce-interval = <1>;
+ };
+
+ power-key {
+ gpios = <&gpx2 7 1>;
+ linux,code = <116>;
+ label = "power";
+ debounce-interval = <1>;
+ gpio-key,wakeup;
+ };
+
+ ok-key {
+ gpios = <&gpx3 5 1>;
+ linux,code = <352>;
+ label = "ok";
+ debounce-interval = <1>;
+ };
+ };
+
+ tsp_reg: voltage-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "TSP_2_8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ gpio = <&gpe2 3 0>;
+ enable-active-high;
+ };
+
+ i2c@13890000 {
+ samsung,i2c-sda-delay = <100>;
+ samsung,i2c-slave-addr = <0x10>;
+ samsung,i2c-max-bus-freq = <100000>;
+ pinctrl-0 = <&i2c3_bus>;
+ pinctrl-names = "default";
+ status = "okay";
+
+ tsp@4a {
+ /* TBD: Atmel maXtouch touchscreen */
+ reg = <0x4a>;
+ };
+ };
+
+ i2c@138B0000 {
+ samsung,i2c-sda-delay = <100>;
+ samsung,i2c-slave-addr = <0x10>;
+ samsung,i2c-max-bus-freq = <100000>;
+ pinctrl-0 = <&i2c5_bus>;
+ pinctrl-names = "default";
+ status = "okay";
+
+ vdd_arm_reg: pmic@60 {
+ compatible = "maxim,max8952";
+ reg = <0x60>;
+
+ max8952,vid-gpios = <&gpx0 3 0>, <&gpx0 4 0>;
+ max8952,default-mode = <0>;
+ max8952,dvs-mode-microvolt = <1250000>, <1200000>,
+ <1050000>, <950000>;
+ max8952,sync-freq = <0>;
+ max8952,ramp-speed = <0>;
+
+ regulator-name = "vdd_arm";
+ regulator-min-microvolt = <770000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ pmic@66 {
+ compatible = "national,lp3974";
+ reg = <0x66>;
+
+ max8998,pmic-buck1-default-dvs-idx = <0>;
+ max8998,pmic-buck1-dvs-gpios = <&gpx0 5 0>,
+ <&gpx0 6 0>;
+ max8998,pmic-buck1-dvs-voltage = <1100000>, <1000000>,
+ <1100000>, <1000000>;
+
+ max8998,pmic-buck2-default-dvs-idx = <0>;
+ max8998,pmic-buck2-dvs-gpio = <&gpe2 0 0>;
+ max8998,pmic-buck2-dvs-voltage = <1200000>, <1100000>;
+
+ regulators {
+ ldo2_reg: LDO2 {
+ regulator-name = "VALIVE_1.2V";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ };
+
+ ldo3_reg: LDO3 {
+ regulator-name = "VUSB+MIPI_1.1V";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ };
+
+ ldo4_reg: LDO4 {
+ regulator-name = "VADC_3.3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ ldo5_reg: LDO5 {
+ regulator-name = "VTF_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ };
+
+ ldo6_reg: LDO6 {
+ regulator-name = "LDO6";
+ regulator-min-microvolt = <2000000>;
+ regulator-max-microvolt = <2000000>;
+ };
+
+ ldo7_reg: LDO7 {
+ regulator-name = "VLCD+VMIPI_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo8_reg: LDO8 {
+ regulator-name = "VUSB+VDAC_3.3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ ldo9_reg: LDO9 {
+ regulator-name = "VCC_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+
+ ldo10_reg: LDO10 {
+ regulator-name = "VPLL_1.1V";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ ldo11_reg: LDO11 {
+ regulator-name = "CAM_AF_3.3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ ldo12_reg: LDO12 {
+ regulator-name = "PS_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ };
+
+ ldo13_reg: LDO13 {
+ regulator-name = "VHIC_1.2V";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
+
+ ldo14_reg: LDO14 {
+ regulator-name = "CAM_I_HOST_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo15_reg: LDO15 {
+ regulator-name = "CAM_S_DIG+FM33_CORE_1.2V";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
+
+ ldo16_reg: LDO16 {
+ regulator-name = "CAM_S_ANA_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ };
+
+ ldo17_reg: LDO17 {
+ regulator-name = "VCC_3.0V_LCD";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "VINT_1.1V";
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ buck2_reg: BUCK2 {
+ regulator-name = "VG3D_1.1V";
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-boot-on;
+ };
+
+ buck3_reg: BUCK3 {
+ regulator-name = "VCC_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ buck4_reg: BUCK4 {
+ regulator-name = "VMEM_1.2V";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ };
+
+ ap32khz_reg: EN32KHz-AP {
+ regulator-name = "32KHz AP";
+ regulator-always-on;
+ };
+
+ cp32khz_reg: EN32KHz-CP {
+ regulator-name = "32KHz CP";
+ };
+
+ vichg_reg: ENVICHG {
+ regulator-name = "VICHG";
+ };
+
+ safeout1_reg: ESAFEOUT1 {
+ regulator-name = "SAFEOUT1";
+ regulator-always-on;
+ };
+
+ safeout2_reg: ESAFEOUT2 {
+ regulator-name = "SAFEOUT2";
+ regulator-boot-on;
+ };
+ };
+ };
+ };
+
+ pwm@139D0000 {
+ compatible = "samsung,s5p6440-pwm";
+ status = "okay";
+ };
+};
diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi
index 15143bdbafb8..54710de82908 100644
--- a/arch/arm/boot/dts/exynos4210.dtsi
+++ b/arch/arm/boot/dts/exynos4210.dtsi
@@ -41,6 +41,7 @@
};
combiner:interrupt-controller@10440000 {
+ samsung,combiner-nr = <16>;
interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
<0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
<0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
@@ -112,4 +113,11 @@
reg = <0x100C0000 0x100>;
interrupts = <2 4>;
};
+
+ g2d@12800000 {
+ compatible = "samsung,s5pv210-g2d";
+ reg = <0x12800000 0x1000>;
+ interrupts = <0 89 0>;
+ status = "disabled";
+ };
};
diff --git a/arch/arm/boot/dts/exynos4212.dtsi b/arch/arm/boot/dts/exynos4212.dtsi
index 36d4299789ef..c0f60f49cea6 100644
--- a/arch/arm/boot/dts/exynos4212.dtsi
+++ b/arch/arm/boot/dts/exynos4212.dtsi
@@ -26,6 +26,15 @@
cpu-offset = <0x8000>;
};
+ interrupt-controller@10440000 {
+ samsung,combiner-nr = <18>;
+ interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
+ <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
+ <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
+ <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
+ <0 107 0>, <0 108 0>;
+ };
+
mct@10050000 {
compatible = "samsung,exynos4412-mct";
reg = <0x10050000 0x800>;
diff --git a/arch/arm/boot/dts/exynos4412-origen.dts b/arch/arm/boot/dts/exynos4412-origen.dts
index 1fecf7666dc0..1c21bad32ca9 100644
--- a/arch/arm/boot/dts/exynos4412-origen.dts
+++ b/arch/arm/boot/dts/exynos4412-origen.dts
@@ -72,6 +72,27 @@
status = "okay";
};
+ fimd@11c00000 {
+ pinctrl-0 = <&lcd_clk &lcd_data24 &pwm1_out>;
+ pinctrl-names = "default";
+ status = "okay";
+ };
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing {
+ clock-frequency = <50000>;
+ hactive = <1024>;
+ vactive = <600>;
+ hfront-porch = <64>;
+ hback-porch = <16>;
+ hsync-len = <48>;
+ vback-porch = <64>;
+ vfront-porch = <16>;
+ vsync-len = <3>;
+ };
+ };
+
serial@13800000 {
status = "okay";
};
diff --git a/arch/arm/boot/dts/exynos4412-smdk4412.dts b/arch/arm/boot/dts/exynos4412-smdk4412.dts
index 874beeaef99d..dd564310d4a5 100644
--- a/arch/arm/boot/dts/exynos4412-smdk4412.dts
+++ b/arch/arm/boot/dts/exynos4412-smdk4412.dts
@@ -27,6 +27,14 @@
bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
};
+ g2d@10800000 {
+ status = "okay";
+ };
+
+ g2d@10800000 {
+ status = "okay";
+ };
+
sdhci@12530000 {
bus-width = <4>;
pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_bus4 &sd2_cd>;
diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi
index d75c047e80a9..270b389e0a1b 100644
--- a/arch/arm/boot/dts/exynos4412.dtsi
+++ b/arch/arm/boot/dts/exynos4412.dtsi
@@ -26,6 +26,15 @@
cpu-offset = <0x4000>;
};
+ interrupt-controller@10440000 {
+ samsung,combiner-nr = <20>;
+ interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
+ <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
+ <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
+ <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
+ <0 107 0>, <0 108 0>, <0 48 0>, <0 42 0>;
+ };
+
mct@10050000 {
compatible = "samsung,exynos4412-mct";
reg = <0x10050000 0x800>;
@@ -51,4 +60,12 @@
<0x7 0 &gic 1 12 0>;
};
};
+
+ mshc@12550000 {
+ compatible = "samsung,exynos4412-dw-mshc";
+ reg = <0x12550000 0x1000>;
+ interrupts = <0 77 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
};
diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi
index 7496b8d633ea..e3380a7a285c 100644
--- a/arch/arm/boot/dts/exynos4x12.dtsi
+++ b/arch/arm/boot/dts/exynos4x12.dtsi
@@ -72,4 +72,11 @@
reg = <0x106E0000 0x1000>;
interrupts = <0 72 0>;
};
+
+ g2d@10800000 {
+ compatible = "samsung,exynos4212-g2d";
+ reg = <0x10800000 0x1000>;
+ interrupts = <0 89 0>;
+ status = "disabled";
+ };
};
diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts
index 5de019cb0e58..02cfc76d002f 100644
--- a/arch/arm/boot/dts/exynos5250-arndale.dts
+++ b/arch/arm/boot/dts/exynos5250-arndale.dts
@@ -24,8 +24,266 @@
bootargs = "console=ttySAC2,115200";
};
+ codec@11000000 {
+ samsung,mfc-r = <0x43000000 0x800000>;
+ samsung,mfc-l = <0x51000000 0x800000>;
+ };
+
i2c@12C60000 {
- status = "disabled";
+ samsung,i2c-sda-delay = <100>;
+ samsung,i2c-max-bus-freq = <20000>;
+ samsung,i2c-slave-addr = <0x66>;
+
+ s5m8767_pmic@66 {
+ compatible = "samsung,s5m8767-pmic";
+ reg = <0x66>;
+
+ s5m8767,pmic-buck2-dvs-voltage = <1300000>;
+ s5m8767,pmic-buck3-dvs-voltage = <1100000>;
+ s5m8767,pmic-buck4-dvs-voltage = <1200000>;
+ s5m8767,pmic-buck-dvs-gpios = <&gpd1 0 0>,
+ <&gpd1 1 0>,
+ <&gpd1 2 0>;
+ s5m8767,pmic-buck-ds-gpios = <&gpx2 3 0>,
+ <&gpx2 4 0>,
+ <&gpx2 5 0>;
+ regulators {
+ ldo1_reg: LDO1 {
+ regulator-name = "VDD_ALIVE_1.0V";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo2_reg: LDO2 {
+ regulator-name = "VDD_28IO_DP_1.35V";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo3_reg: LDO3 {
+ regulator-name = "VDD_COMMON1_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo4_reg: LDO4 {
+ regulator-name = "VDD_IOPERI_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ op_mode = <1>;
+ };
+
+ ldo5_reg: LDO5 {
+ regulator-name = "VDD_EXT_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo6_reg: LDO6 {
+ regulator-name = "VDD_MPLL_1.1V";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo7_reg: LDO7 {
+ regulator-name = "VDD_XPLL_1.1V";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo8_reg: LDO8 {
+ regulator-name = "VDD_COMMON2_1.0V";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo9_reg: LDO9 {
+ regulator-name = "VDD_33ON_3.0V";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ op_mode = <1>;
+ };
+
+ ldo10_reg: LDO10 {
+ regulator-name = "VDD_COMMON3_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo11_reg: LDO11 {
+ regulator-name = "VDD_ABB2_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo12_reg: LDO12 {
+ regulator-name = "VDD_USB_3.0V";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo13_reg: LDO13 {
+ regulator-name = "VDDQ_C2C_W_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo14_reg: LDO14 {
+ regulator-name = "VDD18_ABB0_3_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo15_reg: LDO15 {
+ regulator-name = "VDD10_COMMON4_1.0V";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo16_reg: LDO16 {
+ regulator-name = "VDD18_HSIC_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo17_reg: LDO17 {
+ regulator-name = "VDDQ_MMC2_3_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ ldo18_reg: LDO18 {
+ regulator-name = "VDD_33ON_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ op_mode = <1>;
+ };
+
+ ldo22_reg: LDO22 {
+ regulator-name = "EXT_33_OFF";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ op_mode = <1>;
+ };
+
+ ldo23_reg: LDO23 {
+ regulator-name = "EXT_28_OFF";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ op_mode = <1>;
+ };
+
+ ldo25_reg: LDO25 {
+ regulator-name = "PVDD_LDO25";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ op_mode = <1>;
+ };
+
+ ldo26_reg: LDO26 {
+ regulator-name = "EXT_18_OFF";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ op_mode = <1>;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "vdd_mif";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ buck2_reg: BUCK2 {
+ regulator-name = "vdd_arm";
+ regulator-min-microvolt = <925000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ buck3_reg: BUCK3 {
+ regulator-name = "vdd_int";
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ buck4_reg: BUCK4 {
+ regulator-name = "vdd_g3d";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ buck5_reg: BUCK5 {
+ regulator-name = "VDD_MEM_1.35V";
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <1355000>;
+ regulator-always-on;
+ regulator-boot-on;
+ op_mode = <1>;
+ };
+
+ buck9_reg: BUCK9 {
+ regulator-name = "VDD_33_OFF_EXT1";
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <3000000>;
+ op_mode = <1>;
+ };
+ };
+ };
};
i2c@12C70000 {
@@ -69,15 +327,13 @@
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
+ vmmc-supply = <&mmc_reg>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4 &sd0_bus8>;
slot@0 {
reg = <0>;
bus-width = <8>;
- gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
- <&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
- <&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>,
- <&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
- <&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>;
};
};
@@ -93,14 +349,14 @@
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
+ vmmc-supply = <&mmc_reg>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
slot@0 {
reg = <0>;
bus-width = <4>;
- samsung,cd-pinmux-gpio = <&gpc3 2 2 3 3>;
- gpios = <&gpc3 0 2 0 3>, <&gpc3 1 2 0 3>,
- <&gpc3 3 2 3 3>, <&gpc3 4 2 3 3>,
- <&gpc3 5 2 3 3>, <&gpc3 6 2 3 3>;
+ disable-wp;
};
};
@@ -120,6 +376,73 @@
status = "disabled";
};
+ gpio_keys {
+ compatible = "gpio-keys";
+
+ menu {
+ label = "SW-TACT2";
+ gpios = <&gpx1 4 1>;
+ linux,code = <139>;
+ gpio-key,wakeup;
+ };
+
+ home {
+ label = "SW-TACT3";
+ gpios = <&gpx1 5 1>;
+ linux,code = <102>;
+ gpio-key,wakeup;
+ };
+
+ up {
+ label = "SW-TACT4";
+ gpios = <&gpx1 6 1>;
+ linux,code = <103>;
+ gpio-key,wakeup;
+ };
+
+ down {
+ label = "SW-TACT5";
+ gpios = <&gpx1 7 1>;
+ linux,code = <108>;
+ gpio-key,wakeup;
+ };
+
+ back {
+ label = "SW-TACT6";
+ gpios = <&gpx2 0 1>;
+ linux,code = <158>;
+ gpio-key,wakeup;
+ };
+
+ wakeup {
+ label = "SW-TACT7";
+ gpios = <&gpx2 1 1>;
+ linux,code = <143>;
+ gpio-key,wakeup;
+ };
+ };
+
+ hdmi {
+ hpd-gpio = <&gpx3 7 2>;
+ vdd_osc-supply = <&ldo10_reg>;
+ vdd_pll-supply = <&ldo8_reg>;
+ vdd-supply = <&ldo8_reg>;
+ };
+
+ mmc_reg: voltage-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "VDD_33ON_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ gpio = <&gpx1 1 1>;
+ enable-active-high;
+ };
+
+ reg_hdmi_en: fixedregulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "hdmi-en";
+ };
+
fixed-rate-clocks {
xxti {
compatible = "samsung,clock-xxti";
diff --git a/arch/arm/boot/dts/exynos5250-pinctrl.dtsi b/arch/arm/boot/dts/exynos5250-pinctrl.dtsi
new file mode 100644
index 000000000000..d1650fb34c0a
--- /dev/null
+++ b/arch/arm/boot/dts/exynos5250-pinctrl.dtsi
@@ -0,0 +1,783 @@
+/*
+ * Samsung's Exynos5250 SoC pin-mux and pin-config device tree source
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung's Exynos5250 SoC pin-mux and pin-config optiosn are listed as device
+ * tree nodes are listed in this file.
+ *
+ * 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.
+*/
+
+/ {
+ pinctrl@11400000 {
+ gpa0: gpa0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpa1: gpa1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpa2: gpa2 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpb0: gpb0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpb1: gpb1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpb2: gpb2 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpb3: gpb3 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpc0: gpc0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpc1: gpc1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpc2: gpc2 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpc3: gpc3 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpd0: gpd0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpd1: gpd1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpy0: gpy0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpy1: gpy1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpy2: gpy2 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpy3: gpy3 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpy4: gpy4 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpy5: gpy5 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpy6: gpy6 {
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpc4: gpc4 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpx0: gpx0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ interrupt-parent = <&combiner>;
+ #interrupt-cells = <2>;
+ interrupts = <23 0>, <24 0>, <25 0>, <25 1>,
+ <26 0>, <26 1>, <27 0>, <27 1>;
+ };
+
+ gpx1: gpx1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ interrupt-parent = <&combiner>;
+ #interrupt-cells = <2>;
+ interrupts = <28 0>, <28 1>, <29 0>, <29 1>,
+ <30 0>, <30 1>, <31 0>, <31 1>;
+ };
+
+ gpx2: gpx2 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpx3: gpx3 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ uart0_data: uart0-data {
+ samsung,pins = "gpa0-0", "gpa0-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart0_fctl: uart0-fctl {
+ samsung,pins = "gpa0-2", "gpa0-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+
+ i2c2_bus: i2c2-bus {
+ samsung,pins = "gpa0-6", "gpa0-7";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ i2c2_hs_bus: i2c2-hs-bus {
+ samsung,pins = "gpa0-6", "gpa0-7";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ uart2_data: uart2-data {
+ samsung,pins = "gpa1-0", "gpa1-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart2_fctl: uart2-fctl {
+ samsung,pins = "gpa1-2", "gpa1-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+
+ i2c3_bus: i2c3-bus {
+ samsung,pins = "gpa1-2", "gpa1-3";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ i2c3_hs_bus: i2c3-hs-bus {
+ samsung,pins = "gpa1-2", "gpa1-3";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ uart3_data: uart3-data {
+ samsung,pins = "gpa1-4", "gpa1-4";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ spi0_bus: spi0-bus {
+ samsung,pins = "gpa2-0", "gpa2-2", "gpa2-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c4_bus: i2c4-bus {
+ samsung,pins = "gpa2-0", "gpa2-1";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ i2c5_bus: i2c5-bus {
+ samsung,pins = "gpa2-2", "gpa2-3";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ spi1_bus: spi1-bus {
+ samsung,pins = "gpa2-4", "gpa2-6", "gpa2-7";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2s1_bus: i2s1-bus {
+ samsung,pins = "gpb0-0", "gpb0-1", "gpb0-2", "gpb0-3",
+ "gpb0-4";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ pcm1_bus: pcm1-bus {
+ samsung,pins = "gpb0-0", "gpb0-1", "gpb0-2", "gpb0-3",
+ "gpb0-4";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ ac97_bus: ac97-bus {
+ samsung,pins = "gpb0-0", "gpb0-1", "gpb0-2", "gpb0-3",
+ "gpb0-4";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2s2_bus: i2s2-bus {
+ samsung,pins = "gpb1-0", "gpb1-1", "gpb1-2", "gpb1-3",
+ "gpb1-4";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ pcm2_bus: pcm2-bus {
+ samsung,pins = "gpb1-0", "gpb1-1", "gpb1-2", "gpb1-3",
+ "gpb1-4";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ spdif_bus: spdif-bus {
+ samsung,pins = "gpb1-0", "gpb1-1";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ spi2_bus: spi2-bus {
+ samsung,pins = "gpb1-1", "gpb1-3", "gpb1-4";
+ samsung,pin-function = <5>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c6_bus: i2c6-bus {
+ samsung,pins = "gpb1-3", "gpb1-4";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c7_bus: i2c7-bus {
+ samsung,pins = "gpb2-2", "gpb2-3";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c0_bus: i2c0-bus {
+ samsung,pins = "gpb3-0", "gpb3-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c1_bus: i2c1-bus {
+ samsung,pins = "gpb3-2", "gpb3-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c0_hs_bus: i2c0-hs-bus {
+ samsung,pins = "gpb3-0", "gpb3-1";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ i2c1_hs_bus: i2c1-hs-bus {
+ samsung,pins = "gpb3-2", "gpb3-3";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ sd0_clk: sd0-clk {
+ samsung,pins = "gpc0-0";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd0_cmd: sd0-cmd {
+ samsung,pins = "gpc0-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd0_cd: sd0-cd {
+ samsung,pins = "gpc0-2";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd0_bus1: sd0-bus-width1 {
+ samsung,pins = "gpc0-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd0_bus4: sd0-bus-width4 {
+ samsung,pins = "gpc0-3", "gpc0-4", "gpc0-5", "gpc0-6";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd0_bus8: sd0-bus-width8 {
+ samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd1_clk: sd1-clk {
+ samsung,pins = "gpc2-0";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd1_cmd: sd1-cmd {
+ samsung,pins = "gpc2-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd1_cd: sd1-cd {
+ samsung,pins = "gpc2-2";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd1_bus1: sd1-bus-width1 {
+ samsung,pins = "gpc2-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd1_bus4: sd1-bus-width4 {
+ samsung,pins = "gpc2-3", "gpc2-4", "gpc2-5", "gpc2-6";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd2_clk: sd2-clk {
+ samsung,pins = "gpc3-0";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd2_cmd: sd2-cmd {
+ samsung,pins = "gpc3-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd2_cd: sd2-cd {
+ samsung,pins = "gpc3-2";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd2_bus1: sd2-bus-width1 {
+ samsung,pins = "gpc3-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd2_bus4: sd2-bus-width4 {
+ samsung,pins = "gpc3-3", "gpc3-4", "gpc3-5", "gpc3-6";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd2_bus8: sd2-bus-width8 {
+ samsung,pins = "gpc4-3", "gpc4-4", "gpc4-5", "gpc4-6";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd3_clk: sd3-clk {
+ samsung,pins = "gpc4-0";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd3_cmd: sd3-cmd {
+ samsung,pins = "gpc4-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd3_cd: sd3-cd {
+ samsung,pins = "gpc4-2";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd3_bus1: sd3-bus-width1 {
+ samsung,pins = "gpc4-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ sd3_bus4: sd3-bus-width4 {
+ samsung,pins = "gpc4-3", "gpc4-4", "gpc4-5", "gpc4-6";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <3>;
+ };
+
+ uart1_data: uart1-data {
+ samsung,pins = "gpd0-0", "gpd0-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart1_fctl: uart1-fctl {
+ samsung,pins = "gpd0-2", "gpd0-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+ };
+
+ pinctrl@13400000 {
+ gpe0: gpe0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpe1: gpe1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpf0: gpf0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpf1: gpf1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpg0: gpg0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpg1: gpg1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpg2: gpg2 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gph0: gph0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gph1: gph1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ cam_gpio_a: cam-gpio-a {
+ samsung,pins = "gpe0-0", "gpe0-1", "gpe0-2", "gpe0-3",
+ "gpe0-4", "gpe0-5", "gpe0-6", "gpe0-7",
+ "gpe1-0", "gpe1-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ cam_gpio_b: cam-gpio-b {
+ samsung,pins = "gpf0-0", "gpf0-1", "gpf0-2", "gpf0-3",
+ "gpf1-0", "gpf1-1", "gpf1-2", "gpf1-3";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+
+ cam_i2c2_bus: cam-i2c2-bus {
+ samsung,pins = "gpe0-6", "gpe1-0";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ cam_spi1_bus: cam-spi1-bus {
+ samsung,pins = "gpe0-4", "gpe0-5", "gpf0-2", "gpf0-3";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+
+ cam_i2c1_bus: cam-i2c1-bus {
+ samsung,pins = "gpf0-2", "gpf0-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ cam_i2c0_bus: cam-i2c0-bus {
+ samsung,pins = "gpf0-0", "gpf0-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samaung,pin-drv = <0>;
+ };
+
+ cam_spi0_bus: cam-spi0-bus {
+ samsung,pins = "gpf1-0", "gpf1-1", "gpf1-2", "gpf1-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+
+ cam_bayrgb_bus: cam-bayrgb-bus {
+ samsung,pins = "gpg0-0", "gpg0-1", "gpg0-2", "gpg0-3",
+ "gpg0-4", "gpg0-5", "gpg0-6", "gpg0-7",
+ "gpg1-0", "gpg1-1", "gpg1-2", "gpg1-3",
+ "gpg1-4", "gpg1-5", "gpg1-6", "gpg1-7",
+ "gpg2-0", "gpg2-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+
+ cam_port_a: cam-port-a {
+ samsung,pins = "gph0-0", "gph0-1", "gph0-2", "gph0-3",
+ "gph1-0", "gph1-1", "gph1-2", "gph1-3",
+ "gph1-4", "gph1-5", "gph1-6", "gph1-7";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+ };
+
+ pinctrl@10d10000 {
+ gpv0: gpv0 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpv1: gpv1 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpv2: gpv2 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpv3: gpv3 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpv4: gpv4 {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ c2c_rxd: c2c-rxd {
+ samsung,pins = "gpv0-0", "gpv0-1", "gpv0-2", "gpv0-3",
+ "gpv0-4", "gpv0-5", "gpv0-6", "gpv0-7",
+ "gpv1-0", "gpv1-1", "gpv1-2", "gpv1-3",
+ "gpv1-4", "gpv1-5", "gpv1-6", "gpv1-7";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+
+ c2c_txd: c2c-txd {
+ samsung,pins = "gpv2-0", "gpv2-1", "gpv2-2", "gpv2-3",
+ "gpv2-4", "gpv2-5", "gpv2-6", "gpv2-7",
+ "gpv3-0", "gpv3-1", "gpv3-2", "gpv3-3",
+ "gpv3-4", "gpv3-5", "gpv3-6", "gpv3-7";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samaung,pin-drv = <0>;
+ };
+ };
+
+ pinctrl@03680000 {
+ gpz: gpz {
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ i2s0_bus: i2s0-bus {
+ samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3",
+ "gpz-4", "gpz-5", "gpz-6";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts
index 872ae1f93c75..26d856ba50a1 100644
--- a/arch/arm/boot/dts/exynos5250-smdk5250.dts
+++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts
@@ -30,8 +30,6 @@
i2c@12C60000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <20000>;
- gpios = <&gpb3 0 2 3 0>,
- <&gpb3 1 2 3 0>;
eeprom@50 {
compatible = "samsung,s524ad0xd1";
@@ -42,8 +40,6 @@
i2c@12C70000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <20000>;
- gpios = <&gpb3 2 2 3 0>,
- <&gpb3 3 2 3 0>;
eeprom@51 {
compatible = "samsung,s524ad0xd1";
@@ -74,8 +70,6 @@
i2c@12C80000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <66000>;
- gpios = <&gpa0 6 3 3 0>,
- <&gpa0 7 3 3 0>;
hdmiddc@50 {
compatible = "samsung,exynos5-hdmiddc";
@@ -122,15 +116,12 @@
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4 &sd0_bus8>;
slot@0 {
reg = <0>;
bus-width = <8>;
- gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
- <&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
- <&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
- <&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
- <&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
};
};
@@ -146,17 +137,13 @@
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
slot@0 {
reg = <0>;
bus-width = <4>;
- samsung,cd-pinmux-gpio = <&gpc3 2 2 3 3>;
disable-wp;
- gpios = <&gpc3 0 2 0 3>, <&gpc3 1 2 0 3>,
- <&gpc3 3 2 3 3>, <&gpc3 4 2 3 3>,
- <&gpc3 5 2 3 3>, <&gpc3 6 2 3 3>,
- <&gpc4 3 3 3 3>, <&gpc4 3 3 3 3>,
- <&gpc4 5 3 3 3>, <&gpc4 6 3 3 3>;
};
};
@@ -169,10 +156,6 @@
};
spi_1: spi@12d30000 {
- gpios = <&gpa2 4 2 3 0>,
- <&gpa2 6 2 3 0>,
- <&gpa2 7 2 3 0>;
-
w25q80bw@0 {
#address-cells = <1>;
#size-cells = <1>;
@@ -181,7 +164,7 @@
spi-max-frequency = <1000000>;
controller-data {
- cs-gpio = <&gpa2 5 1 0 3>;
+ cs-gpio = <&gpa2 5 0>;
samsung,spi-feedback-delay = <0>;
};
@@ -203,7 +186,7 @@
};
hdmi {
- hpd-gpio = <&gpx3 7 0xf 1 3>;
+ hpd-gpio = <&gpx3 7 0>;
};
codec@11000000 {
@@ -212,9 +195,7 @@
};
i2s0: i2s@03830000 {
- gpios = <&gpz 0 2 0 0>, <&gpz 1 2 0 0>, <&gpz 2 2 0 0>,
- <&gpz 3 2 0 0>, <&gpz 4 2 0 0>, <&gpz 5 2 0 0>,
- <&gpz 6 2 0 0>;
+ status = "okay";
};
i2s1: i2s@12D60000 {
@@ -245,6 +226,22 @@
samsung,lane-count = <4>;
};
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing@0 {
+ /* 1280x800 */
+ clock-frequency = <50000>;
+ hactive = <1280>;
+ vactive = <800>;
+ hfront-porch = <4>;
+ hback-porch = <4>;
+ hsync-len = <4>;
+ vback-porch = <4>;
+ vfront-porch = <4>;
+ vsync-len = <4>;
+ };
+ };
+
fixed-rate-clocks {
xxti {
compatible = "samsung,clock-xxti";
diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index babd9f9b1bf9..bf4744bab445 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -16,12 +16,31 @@
model = "Google Snow";
compatible = "google,snow", "samsung,exynos5250";
+ aliases {
+ i2c104 = &i2c_104;
+ };
+
+ pinctrl@11400000 {
+ sd3_clk: sd3-clk {
+ samsung,pin-drv = <0>;
+ };
+
+ sd3_cmd: sd3-cmd {
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd3_bus4: sd3-bus-width4 {
+ samsung,pin-drv = <0>;
+ };
+ };
+
gpio-keys {
compatible = "gpio-keys";
lid-switch {
label = "Lid";
- gpios = <&gpx3 5 0 0x10000 0>;
+ gpios = <&gpx3 5 1>;
linux,input-type = <5>; /* EV_SW */
linux,code = <0>; /* SW_LID */
debounce-interval = <1>;
@@ -29,15 +48,137 @@
};
};
+ i2c-arbitrator {
+ compatible = "i2c-arb-gpio-challenge";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c-parent = <&{/i2c@12CA0000}>;
+
+ our-claim-gpio = <&gpf0 3 1>;
+ their-claim-gpios = <&gpe0 4 1>;
+ slew-delay-us = <10>;
+ wait-retry-us = <3000>;
+ wait-free-us = <50000>;
+
+ /* Use ID 104 as a hint that we're on physical bus 4 */
+ i2c_104: i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ battery: sbs-battery@b {
+ compatible = "sbs,sbs-battery";
+ reg = <0xb>;
+ sbs,poll-retry-count = <1>;
+ };
+
+ ec: embedded-controller {
+ compatible = "google,cros-ec-i2c";
+ reg = <0x1e>;
+ interrupts = <6 0>;
+ interrupt-parent = <&gpx1>;
+ wakeup-source;
+
+ keyboard-controller {
+ compatible = "google,cros-ec-keyb";
+ keypad,num-rows = <8>;
+ keypad,num-columns = <13>;
+ google,needs-ghost-filter;
+ linux,keymap = <0x0001003a /* CAPSLK */
+ 0x0002003b /* F1 */
+ 0x00030030 /* B */
+ 0x00040044 /* F10 */
+ 0x00060031 /* N */
+ 0x0008000d /* = */
+ 0x000a0064 /* R_ALT */
+
+ 0x01010001 /* ESC */
+ 0x0102003e /* F4 */
+ 0x01030022 /* G */
+ 0x01040041 /* F7 */
+ 0x01060023 /* H */
+ 0x01080028 /* ' */
+ 0x01090043 /* F9 */
+ 0x010b000e /* BKSPACE */
+
+ 0x0200001d /* L_CTRL */
+ 0x0201000f /* TAB */
+ 0x0202003d /* F3 */
+ 0x02030014 /* T */
+ 0x02040040 /* F6 */
+ 0x0205001b /* ] */
+ 0x02060015 /* Y */
+ 0x02070056 /* 102ND */
+ 0x0208001a /* [ */
+ 0x02090042 /* F8 */
+
+ 0x03010029 /* GRAVE */
+ 0x0302003c /* F2 */
+ 0x03030006 /* 5 */
+ 0x0304003f /* F5 */
+ 0x03060007 /* 6 */
+ 0x0308000c /* - */
+ 0x030b002b /* \ */
+
+ 0x04000061 /* R_CTRL */
+ 0x0401001e /* A */
+ 0x04020020 /* D */
+ 0x04030021 /* F */
+ 0x0404001f /* S */
+ 0x04050025 /* K */
+ 0x04060024 /* J */
+ 0x04080027 /* ; */
+ 0x04090026 /* L */
+ 0x040b001c /* ENTER */
+
+ 0x0501002c /* Z */
+ 0x0502002e /* C */
+ 0x0503002f /* V */
+ 0x0504002d /* X */
+ 0x05050033 /* , */
+ 0x05060032 /* M */
+ 0x0507002a /* L_SHIFT */
+ 0x05080035 /* / */
+ 0x05090034 /* . */
+ 0x050B0039 /* SPACE */
+
+ 0x06010002 /* 1 */
+ 0x06020004 /* 3 */
+ 0x06030005 /* 4 */
+ 0x06040003 /* 2 */
+ 0x06050009 /* 8 */
+ 0x06060008 /* 7 */
+ 0x0608000b /* 0 */
+ 0x0609000a /* 9 */
+ 0x060a0038 /* L_ALT */
+ 0x060b006c /* DOWN */
+ 0x060c006a /* RIGHT */
+
+ 0x07010010 /* Q */
+ 0x07020012 /* E */
+ 0x07030013 /* R */
+ 0x07040011 /* W */
+ 0x07050017 /* I */
+ 0x07060016 /* U */
+ 0x07070036 /* R_SHIFT */
+ 0x07080019 /* P */
+ 0x07090018 /* O */
+ 0x070b0067 /* UP */
+ 0x070c0069>; /* LEFT */
+ };
+ };
+ };
+ };
+
/*
* On Snow we've got SIP WiFi and so can keep drive strengths low to
* reduce EMI.
*/
dwmmc3@12230000 {
slot@0 {
- gpios = <&gpc4 0 2 0 0>, <&gpc4 1 2 3 0>,
- <&gpc4 3 2 3 0>, <&gpc4 4 2 3 0>,
- <&gpc4 5 2 3 0>, <&gpc4 6 2 3 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd3_clk &sd3_cmd &sd3_bus4>;
};
};
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index 28758e5dd15c..98dfc3ea5c0b 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -18,6 +18,7 @@
*/
/include/ "skeleton.dtsi"
+/include/ "exynos5250-pinctrl.dtsi"
/ {
compatible = "samsung,exynos5250";
@@ -44,6 +45,15 @@
i2c6 = &i2c_6;
i2c7 = &i2c_7;
i2c8 = &i2c_8;
+ pinctrl0 = &pinctrl_0;
+ pinctrl1 = &pinctrl_1;
+ pinctrl2 = &pinctrl_2;
+ pinctrl3 = &pinctrl_3;
+ };
+
+ chipid@10000000 {
+ compatible = "samsung,exynos4210-chipid";
+ reg = <0x10000000 0x100>;
};
pd_gsc: gsc-power-domain@0x10044000 {
@@ -63,10 +73,22 @@
};
gic:interrupt-controller@10481000 {
- compatible = "arm,cortex-a9-gic";
+ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
- reg = <0x10481000 0x1000>, <0x10482000 0x2000>;
+ reg = <0x10481000 0x1000>,
+ <0x10482000 0x1000>,
+ <0x10484000 0x2000>,
+ <0x10486000 0x2000>;
+ interrupts = <1 9 0xf04>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
};
combiner:interrupt-controller@10440000 {
@@ -115,6 +137,36 @@
interrupts = <1 2>, <22 4>;
};
+ pinctrl_0: pinctrl@11400000 {
+ compatible = "samsung,exynos5250-pinctrl";
+ reg = <0x11400000 0x1000>;
+ interrupts = <0 46 0>;
+
+ wakup_eint: wakeup-interrupt-controller {
+ compatible = "samsung,exynos4210-wakeup-eint";
+ interrupt-parent = <&gic>;
+ interrupts = <0 32 0>;
+ };
+ };
+
+ pinctrl_1: pinctrl@13400000 {
+ compatible = "samsung,exynos5250-pinctrl";
+ reg = <0x13400000 0x1000>;
+ interrupts = <0 45 0>;
+ };
+
+ pinctrl_2: pinctrl@10d10000 {
+ compatible = "samsung,exynos5250-pinctrl";
+ reg = <0x10d10000 0x1000>;
+ interrupts = <0 50 0>;
+ };
+
+ pinctrl_3: pinctrl@03680000 {
+ compatible = "samsung,exynos5250-pinctrl";
+ reg = <0x0368000 0x1000>;
+ interrupts = <0 47 0>;
+ };
+
watchdog {
compatible = "samsung,s3c2410-wdt";
reg = <0x101D0000 0x100>;
@@ -200,6 +252,8 @@
#size-cells = <0>;
clocks = <&clock 294>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_bus>;
};
i2c_1: i2c@12C70000 {
@@ -210,6 +264,8 @@
#size-cells = <0>;
clocks = <&clock 295>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_bus>;
};
i2c_2: i2c@12C80000 {
@@ -220,6 +276,8 @@
#size-cells = <0>;
clocks = <&clock 296>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_bus>;
};
i2c_3: i2c@12C90000 {
@@ -230,6 +288,8 @@
#size-cells = <0>;
clocks = <&clock 297>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_bus>;
};
i2c_4: i2c@12CA0000 {
@@ -240,6 +300,8 @@
#size-cells = <0>;
clocks = <&clock 298>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c4_bus>;
};
i2c_5: i2c@12CB0000 {
@@ -250,6 +312,8 @@
#size-cells = <0>;
clocks = <&clock 299>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c5_bus>;
};
i2c_6: i2c@12CC0000 {
@@ -260,6 +324,8 @@
#size-cells = <0>;
clocks = <&clock 300>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c6_bus>;
};
i2c_7: i2c@12CD0000 {
@@ -270,6 +336,8 @@
#size-cells = <0>;
clocks = <&clock 301>;
clock-names = "i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c7_bus>;
};
i2c_8: i2c@12CE0000 {
@@ -302,6 +370,8 @@
#size-cells = <0>;
clocks = <&clock 304>, <&clock 154>;
clock-names = "spi", "spi_busclk0";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi0_bus>;
};
spi_1: spi@12d30000 {
@@ -315,6 +385,8 @@
#size-cells = <0>;
clocks = <&clock 305>, <&clock 155>;
clock-names = "spi", "spi_busclk0";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi1_bus>;
};
spi_2: spi@12d40000 {
@@ -328,6 +400,8 @@
#size-cells = <0>;
clocks = <&clock 306>, <&clock 156>;
clock-names = "spi", "spi_busclk0";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi2_bus>;
};
dwmmc_0: dwmmc0@12200000 {
@@ -381,6 +455,8 @@
samsung,supports-rstclr;
samsung,supports-secdai;
samsung,idma-addr = <0x03000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s0_bus>;
};
i2s1: i2s@12D60000 {
@@ -389,6 +465,8 @@
dmas = <&pdma1 12
&pdma1 11>;
dma-names = "tx", "rx";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s1_bus>;
};
i2s2: i2s@12D70000 {
@@ -397,18 +475,26 @@
dmas = <&pdma0 12
&pdma0 11>;
dma-names = "tx", "rx";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s2_bus>;
};
usb@12110000 {
compatible = "samsung,exynos4210-ehci";
reg = <0x12110000 0x100>;
interrupts = <0 71 0>;
+
+ clocks = <&clock 285>;
+ clock-names = "usbhost";
};
usb@12120000 {
compatible = "samsung,exynos4210-ohci";
reg = <0x12120000 0x100>;
interrupts = <0 71 0>;
+
+ clocks = <&clock 285>;
+ clock-names = "usbhost";
};
amba {
@@ -463,254 +549,6 @@
};
};
- gpio-controllers {
- #address-cells = <1>;
- #size-cells = <1>;
- gpio-controller;
- ranges;
-
- gpa0: gpio-controller@11400000 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400000 0x20>;
- #gpio-cells = <4>;
- };
-
- gpa1: gpio-controller@11400020 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400020 0x20>;
- #gpio-cells = <4>;
- };
-
- gpa2: gpio-controller@11400040 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400040 0x20>;
- #gpio-cells = <4>;
- };
-
- gpb0: gpio-controller@11400060 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400060 0x20>;
- #gpio-cells = <4>;
- };
-
- gpb1: gpio-controller@11400080 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400080 0x20>;
- #gpio-cells = <4>;
- };
-
- gpb2: gpio-controller@114000A0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x114000A0 0x20>;
- #gpio-cells = <4>;
- };
-
- gpb3: gpio-controller@114000C0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x114000C0 0x20>;
- #gpio-cells = <4>;
- };
-
- gpc0: gpio-controller@114000E0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x114000E0 0x20>;
- #gpio-cells = <4>;
- };
-
- gpc1: gpio-controller@11400100 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400100 0x20>;
- #gpio-cells = <4>;
- };
-
- gpc2: gpio-controller@11400120 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400120 0x20>;
- #gpio-cells = <4>;
- };
-
- gpc3: gpio-controller@11400140 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400140 0x20>;
- #gpio-cells = <4>;
- };
-
- gpc4: gpio-controller@114002E0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x114002E0 0x20>;
- #gpio-cells = <4>;
- };
-
- gpd0: gpio-controller@11400160 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400160 0x20>;
- #gpio-cells = <4>;
- };
-
- gpd1: gpio-controller@11400180 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400180 0x20>;
- #gpio-cells = <4>;
- };
-
- gpy0: gpio-controller@114001A0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x114001A0 0x20>;
- #gpio-cells = <4>;
- };
-
- gpy1: gpio-controller@114001C0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x114001C0 0x20>;
- #gpio-cells = <4>;
- };
-
- gpy2: gpio-controller@114001E0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x114001E0 0x20>;
- #gpio-cells = <4>;
- };
-
- gpy3: gpio-controller@11400200 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400200 0x20>;
- #gpio-cells = <4>;
- };
-
- gpy4: gpio-controller@11400220 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400220 0x20>;
- #gpio-cells = <4>;
- };
-
- gpy5: gpio-controller@11400240 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400240 0x20>;
- #gpio-cells = <4>;
- };
-
- gpy6: gpio-controller@11400260 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400260 0x20>;
- #gpio-cells = <4>;
- };
-
- gpx0: gpio-controller@11400C00 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400C00 0x20>;
- #gpio-cells = <4>;
- };
-
- gpx1: gpio-controller@11400C20 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400C20 0x20>;
- #gpio-cells = <4>;
- };
-
- gpx2: gpio-controller@11400C40 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400C40 0x20>;
- #gpio-cells = <4>;
- };
-
- gpx3: gpio-controller@11400C60 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x11400C60 0x20>;
- #gpio-cells = <4>;
- };
-
- gpe0: gpio-controller@13400000 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x13400000 0x20>;
- #gpio-cells = <4>;
- };
-
- gpe1: gpio-controller@13400020 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x13400020 0x20>;
- #gpio-cells = <4>;
- };
-
- gpf0: gpio-controller@13400040 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x13400040 0x20>;
- #gpio-cells = <4>;
- };
-
- gpf1: gpio-controller@13400060 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x13400060 0x20>;
- #gpio-cells = <4>;
- };
-
- gpg0: gpio-controller@13400080 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x13400080 0x20>;
- #gpio-cells = <4>;
- };
-
- gpg1: gpio-controller@134000A0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x134000A0 0x20>;
- #gpio-cells = <4>;
- };
-
- gpg2: gpio-controller@134000C0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x134000C0 0x20>;
- #gpio-cells = <4>;
- };
-
- gph0: gpio-controller@134000E0 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x134000E0 0x20>;
- #gpio-cells = <4>;
- };
-
- gph1: gpio-controller@13400100 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x13400100 0x20>;
- #gpio-cells = <4>;
- };
-
- gpv0: gpio-controller@10D10000 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x10D10000 0x20>;
- #gpio-cells = <4>;
- };
-
- gpv1: gpio-controller@10D10020 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x10D10020 0x20>;
- #gpio-cells = <4>;
- };
-
- gpv2: gpio-controller@10D10040 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x10D10060 0x20>;
- #gpio-cells = <4>;
- };
-
- gpv3: gpio-controller@10D10060 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x10D10080 0x20>;
- #gpio-cells = <4>;
- };
-
- gpv4: gpio-controller@10D10080 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x10D100C0 0x20>;
- #gpio-cells = <4>;
- };
-
- gpz: gpio-controller@03860000 {
- compatible = "samsung,exynos4-gpio";
- reg = <0x03860000 0x20>;
- #gpio-cells = <4>;
- };
- };
-
-
gsc_0: gsc@0x13e00000 {
compatible = "samsung,exynos5-gsc";
reg = <0x13e00000 0x1000>;
@@ -776,4 +614,14 @@
samsung,enable-mask = <1>;
};
};
+
+ fimd {
+ compatible = "samsung,exynos5250-fimd";
+ interrupt-parent = <&combiner>;
+ reg = <0x14400000 0x40000>;
+ interrupt-names = "fifo", "vsync", "lcd_sys";
+ interrupts = <18 4>, <18 5>, <18 6>;
+ clocks = <&clock 133>, <&clock 339>;
+ clock-names = "sclk_fimd", "fimd";
+ };
};
diff --git a/arch/arm/boot/dts/exynos5440-sd5v1.dts b/arch/arm/boot/dts/exynos5440-sd5v1.dts
new file mode 100644
index 000000000000..ef747b52b674
--- /dev/null
+++ b/arch/arm/boot/dts/exynos5440-sd5v1.dts
@@ -0,0 +1,39 @@
+/*
+ * SAMSUNG SD5v1 board device tree source
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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.
+*/
+
+/dts-v1/;
+/include/ "exynos5440.dtsi"
+
+/ {
+ model = "SAMSUNG SD5v1 board based on EXYNOS5440";
+ compatible = "samsung,sd5v1", "samsung,exynos5440";
+
+ chosen {
+ bootargs = "root=/dev/sda2 rw rootwait ignore_loglevel early_printk no_console_suspend mem=2048M@0x80000000 console=ttySAC0,115200";
+ };
+
+ fixed-rate-clocks {
+ xtal {
+ compatible = "samsung,clock-xtal";
+ clock-frequency = <50000000>;
+ };
+ };
+
+ gmac: ethernet@00230000 {
+ fixed_phy;
+ phy_addr = <1>;
+ };
+
+ spi {
+ status = "disabled";
+ };
+
+};
diff --git a/arch/arm/boot/dts/exynos5440-ssdk5440.dts b/arch/arm/boot/dts/exynos5440-ssdk5440.dts
index a21eb4cbe893..d55042beb5c5 100644
--- a/arch/arm/boot/dts/exynos5440-ssdk5440.dts
+++ b/arch/arm/boot/dts/exynos5440-ssdk5440.dts
@@ -16,12 +16,8 @@
model = "SAMSUNG SSDK5440 board based on EXYNOS5440";
compatible = "samsung,ssdk5440", "samsung,exynos5440";
- memory {
- reg = <0x80000000 0x80000000>;
- };
-
chosen {
- bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x81000000,8M console=ttySAC0,115200 init=/linuxrc";
+ bootargs = "root=/dev/sda2 rw rootwait ignore_loglevel early_printk no_console_suspend mem=2048M@0x80000000 console=ttySAC0,115200";
};
spi {
diff --git a/arch/arm/boot/dts/exynos5440.dtsi b/arch/arm/boot/dts/exynos5440.dtsi
index 48cc96aa0b5f..f6b1c8973845 100644
--- a/arch/arm/boot/dts/exynos5440.dtsi
+++ b/arch/arm/boot/dts/exynos5440.dtsi
@@ -26,7 +26,11 @@
compatible = "arm,cortex-a15-gic";
#interrupt-cells = <3>;
interrupt-controller;
- reg = <0x2E1000 0x1000>, <0x2E2000 0x1000>;
+ reg = <0x2E1000 0x1000>,
+ <0x2E2000 0x1000>,
+ <0x2E4000 0x2000>,
+ <0x2E6000 0x2000>;
+ interrupts = <1 9 0xf04>;
};
cpus {
@@ -51,6 +55,14 @@
};
};
+ arm-pmu {
+ compatible = "arm,cortex-a15-pmu", "arm,cortex-a9-pmu";
+ interrupts = <0 52 4>,
+ <0 53 4>,
+ <0 54 4>,
+ <0 55 4>;
+ };
+
timer {
compatible = "arm,cortex-a15-timer",
"arm,armv7-timer";
@@ -61,6 +73,18 @@
clock-frequency = <50000000>;
};
+ cpufreq@160000 {
+ compatible = "samsung,exynos5440-cpufreq";
+ reg = <0x160000 0x1000>;
+ interrupts = <0 57 0>;
+ operating-points = <
+ /* KHz uV */
+ 1200000 1025000
+ 1000000 975000
+ 800000 925000
+ >;
+ };
+
serial@B0000 {
compatible = "samsung,exynos4210-uart";
reg = <0xB0000 0x1000>;
@@ -92,6 +116,8 @@
pinctrl {
compatible = "samsung,exynos5440-pinctrl";
reg = <0xE0000 0x1000>;
+ interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>,
+ <0 41 0>, <0 42 0>, <0 43 0>, <0 44 0>;
interrupt-controller;
#interrupt-cells = <2>;
#gpio-cells = <2>;
@@ -141,6 +167,17 @@
clock-names = "watchdog";
};
+ gmac: ethernet@00230000 {
+ compatible = "snps,dwmac-3.70a";
+ reg = <0x00230000 0x8000>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 31 4>;
+ interrupt-names = "macirq";
+ phy-mode = "sgmii";
+ clocks = <&clock 25>;
+ clock-names = "stmmaceth";
+ };
+
amba {
#address-cells = <1>;
#size-cells = <1>;
@@ -148,22 +185,22 @@
interrupt-parent = <&gic>;
ranges;
- pdma0: pdma@121A0000 {
+ pdma0: pdma@00121000 {
compatible = "arm,pl330", "arm,primecell";
- reg = <0x120000 0x1000>;
- interrupts = <0 34 0>;
- clocks = <&clock 21>;
+ reg = <0x121000 0x1000>;
+ interrupts = <0 46 0>;
+ clocks = <&clock 8>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
#dma-requests = <32>;
};
- pdma1: pdma@121B0000 {
+ pdma1: pdma@00120000 {
compatible = "arm,pl330", "arm,primecell";
- reg = <0x121000 0x1000>;
- interrupts = <0 35 0>;
- clocks = <&clock 21>;
+ reg = <0x120000 0x1000>;
+ interrupts = <0 47 0>;
+ clocks = <&clock 8>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
diff --git a/arch/arm/boot/dts/imx23-evk.dts b/arch/arm/boot/dts/imx23-evk.dts
index 035c13f9d3c0..da0588a04131 100644
--- a/arch/arm/boot/dts/imx23-evk.dts
+++ b/arch/arm/boot/dts/imx23-evk.dts
@@ -59,8 +59,33 @@
lcdif@80030000 {
pinctrl-names = "default";
pinctrl-0 = <&lcdif_24bit_pins_a>;
- panel-enable-gpios = <&gpio1 18 0>;
+ lcd-supply = <&reg_lcd_3v3>;
+ display = <&display>;
status = "okay";
+
+ display: display {
+ bits-per-pixel = <32>;
+ bus-width = <24>;
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <9200000>;
+ hactive = <480>;
+ vactive = <272>;
+ hback-porch = <15>;
+ hfront-porch = <8>;
+ vback-porch = <12>;
+ vfront-porch = <4>;
+ hsync-len = <1>;
+ vsync-len = <1>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+ };
};
};
@@ -95,6 +120,15 @@
regulator-max-microvolt = <3300000>;
gpio = <&gpio1 29 0>;
};
+
+ reg_lcd_3v3: lcd-3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "lcd-3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio1 18 0>;
+ enable-active-high;
+ };
};
backlight {
diff --git a/arch/arm/boot/dts/imx23-olinuxino.dts b/arch/arm/boot/dts/imx23-olinuxino.dts
index e7484e4ea659..d107c4af321f 100644
--- a/arch/arm/boot/dts/imx23-olinuxino.dts
+++ b/arch/arm/boot/dts/imx23-olinuxino.dts
@@ -29,6 +29,7 @@
pinctrl-names = "default";
pinctrl-0 = <&mmc0_4bit_pins_a &mmc0_pins_fixup>;
bus-width = <4>;
+ broken-cd;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx23.dtsi b/arch/arm/boot/dts/imx23.dtsi
index ad2d79324cd3..73fd7d0887b5 100644
--- a/arch/arm/boot/dts/imx23.dtsi
+++ b/arch/arm/boot/dts/imx23.dtsi
@@ -49,9 +49,15 @@
reg = <0x80000000 0x2000>;
};
- dma-apbh@80004000 {
+ dma_apbh: dma-apbh@80004000 {
compatible = "fsl,imx23-dma-apbh";
reg = <0x80004000 0x2000>;
+ interrupts = <0 14 20 0
+ 13 13 13 13>;
+ interrupt-names = "empty", "ssp0", "ssp1", "empty",
+ "gpmi0", "gpmi1", "gpmi2", "gpmi3";
+ #dma-cells = <1>;
+ dma-channels = <8>;
clocks = <&clks 15>;
};
@@ -70,6 +76,8 @@
interrupt-names = "gpmi-dma", "bch";
clocks = <&clks 34>;
clock-names = "gpmi_io";
+ dmas = <&dma_apbh 4>;
+ dma-names = "rx-tx";
fsl,gpmi-dma-channel = <4>;
status = "disabled";
};
@@ -78,6 +86,8 @@
reg = <0x80010000 0x2000>;
interrupts = <15 14>;
clocks = <&clks 33>;
+ dmas = <&dma_apbh 1>;
+ dma-names = "rx-tx";
fsl,ssp-dma-channel = <1>;
status = "disabled";
};
@@ -305,9 +315,19 @@
status = "disabled";
};
- dma-apbx@80024000 {
+ dma_apbx: dma-apbx@80024000 {
compatible = "fsl,imx23-dma-apbx";
reg = <0x80024000 0x2000>;
+ interrupts = <7 5 9 26
+ 19 0 25 23
+ 60 58 9 0
+ 0 0 0 0>;
+ interrupt-names = "audio-adc", "audio-dac", "spdif-tx", "i2c",
+ "saif0", "empty", "auart0-rx", "auart0-tx",
+ "auart1-rx", "auart1-tx", "saif1", "empty",
+ "empty", "empty", "empty", "empty";
+ #dma-cells = <1>;
+ dma-channels = <16>;
clocks = <&clks 16>;
};
@@ -344,6 +364,8 @@
reg = <0x80034000 0x2000>;
interrupts = <2 20>;
clocks = <&clks 33>;
+ dmas = <&dma_apbh 2>;
+ dma-names = "rx-tx";
fsl,ssp-dma-channel = <2>;
status = "disabled";
};
@@ -369,6 +391,8 @@
saif0: saif@80042000 {
reg = <0x80042000 0x2000>;
+ dmas = <&dma_apbx 4>;
+ dma-names = "rx-tx";
status = "disabled";
};
@@ -379,16 +403,22 @@
saif1: saif@80046000 {
reg = <0x80046000 0x2000>;
+ dmas = <&dma_apbx 10>;
+ dma-names = "rx-tx";
status = "disabled";
};
audio-out@80048000 {
reg = <0x80048000 0x2000>;
+ dmas = <&dma_apbx 1>;
+ dma-names = "tx";
status = "disabled";
};
audio-in@8004c000 {
reg = <0x8004c000 0x2000>;
+ dmas = <&dma_apbx 0>;
+ dma-names = "rx";
status = "disabled";
};
@@ -401,11 +431,15 @@
spdif@80054000 {
reg = <0x80054000 2000>;
+ dmas = <&dma_apbx 2>;
+ dma-names = "tx";
status = "disabled";
};
i2c@80058000 {
reg = <0x80058000 0x2000>;
+ dmas = <&dma_apbx 3>;
+ dma-names = "rx-tx";
status = "disabled";
};
@@ -436,6 +470,8 @@
reg = <0x8006c000 0x2000>;
interrupts = <24 25 23>;
clocks = <&clks 32>;
+ dmas = <&dma_apbx 6>, <&dma_apbx 7>;
+ dma-names = "rx", "tx";
status = "disabled";
};
@@ -444,6 +480,8 @@
reg = <0x8006e000 0x2000>;
interrupts = <59 60 58>;
clocks = <&clks 32>;
+ dmas = <&dma_apbx 8>, <&dma_apbx 9>;
+ dma-names = "rx", "tx";
status = "disabled";
};
diff --git a/arch/arm/boot/dts/imx28-apf28dev.dts b/arch/arm/boot/dts/imx28-apf28dev.dts
index 6d8865bfb4b7..3d905d16cbec 100644
--- a/arch/arm/boot/dts/imx28-apf28dev.dts
+++ b/arch/arm/boot/dts/imx28-apf28dev.dts
@@ -72,7 +72,32 @@
pinctrl-names = "default";
pinctrl-0 = <&lcdif_16bit_pins_a
&lcdif_pins_apf28dev>;
+ display = <&display>;
status = "okay";
+
+ display: display {
+ bits-per-pixel = <16>;
+ bus-width = <16>;
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <33000033>;
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <96>;
+ hfront-porch = <96>;
+ vback-porch = <20>;
+ vfront-porch = <21>;
+ hsync-len = <64>;
+ vsync-len = <4>;
+ hsync-active = <1>;
+ vsync-active = <1>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/imx28-apx4devkit.dts b/arch/arm/boot/dts/imx28-apx4devkit.dts
index 5171667a7763..43bf3c796cba 100644
--- a/arch/arm/boot/dts/imx28-apx4devkit.dts
+++ b/arch/arm/boot/dts/imx28-apx4devkit.dts
@@ -94,7 +94,32 @@
pinctrl-names = "default";
pinctrl-0 = <&lcdif_24bit_pins_a
&lcdif_pins_apx4>;
+ display = <&display>;
status = "okay";
+
+ display: display {
+ bits-per-pixel = <32>;
+ bus-width = <24>;
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <30000000>;
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <88>;
+ hfront-porch = <40>;
+ vback-porch = <32>;
+ vfront-porch = <13>;
+ hsync-len = <48>;
+ vsync-len = <3>;
+ hsync-active = <1>;
+ vsync-active = <1>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/imx28-cfa10049.dts b/arch/arm/boot/dts/imx28-cfa10049.dts
index a0d3e9f1738e..063e62059890 100644
--- a/arch/arm/boot/dts/imx28-cfa10049.dts
+++ b/arch/arm/boot/dts/imx28-cfa10049.dts
@@ -30,7 +30,6 @@
reg = <0>;
fsl,pinmux-ids = <
0x0073 /* MX28_PAD_GPMI_D7__GPIO_0_7 */
- 0x1153 /* MX28_PAD_LCD_D22__GPIO_1_21 */
0x1163 /* MX28_PAD_LCD_D22__GPIO_1_22 */
0x1173 /* MX28_PAD_LCD_D22__GPIO_1_23 */
0x2153 /* MX28_PAD_SSP2_D5__GPIO_2_21 */
@@ -120,13 +119,48 @@
fsl,voltage = <1>;
fsl,pull-up = <0>;
};
+
+ w1_gpio_pins: w1-gpio@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ 0x1153 /* MX28_PAD_LCD_D21__GPIO_1_21 */
+ >;
+ fsl,drive-strength = <1>;
+ fsl,voltage = <1>;
+ fsl,pull-up = <0>; /* 0 will enable the keeper */
+ };
};
lcdif@80030000 {
pinctrl-names = "default";
pinctrl-0 = <&lcdif_18bit_pins_cfa10049
&lcdif_pins_cfa10049>;
+ display = <&display>;
status = "okay";
+
+ display: display {
+ bits-per-pixel = <32>;
+ bus-width = <18>;
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <9216000>;
+ hactive = <320>;
+ vactive = <480>;
+ hback-porch = <2>;
+ hfront-porch = <2>;
+ vback-porch = <2>;
+ vfront-porch = <2>;
+ hsync-len = <15>;
+ vsync-len = <15>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <1>;
+ };
+ };
+ };
};
};
@@ -183,6 +217,11 @@
usbphy1: usbphy@8007e000 {
status = "okay";
};
+
+ lradc@80050000 {
+ status = "okay";
+ fsl,lradc-touchscreen-wires = <4>;
+ };
};
};
@@ -304,5 +343,14 @@
pwms = <&pwm 3 5000000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
+
+ };
+
+ onewire@0 {
+ compatible = "w1-gpio";
+ pinctrl-names = "default";
+ pinctrl-0 = <&w1_gpio_pins>;
+ status = "okay";
+ gpios = <&gpio1 21 0>;
};
};
diff --git a/arch/arm/boot/dts/imx28-evk.dts b/arch/arm/boot/dts/imx28-evk.dts
index 2da316e04409..3637bf3b1d59 100644
--- a/arch/arm/boot/dts/imx28-evk.dts
+++ b/arch/arm/boot/dts/imx28-evk.dts
@@ -123,8 +123,33 @@
pinctrl-names = "default";
pinctrl-0 = <&lcdif_24bit_pins_a
&lcdif_pins_evk>;
- panel-enable-gpios = <&gpio3 30 0>;
+ lcd-supply = <&reg_lcd_3v3>;
+ display = <&display>;
status = "okay";
+
+ display: display {
+ bits-per-pixel = <32>;
+ bus-width = <24>;
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <33500000>;
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <89>;
+ hfront-porch = <164>;
+ vback-porch = <23>;
+ vfront-porch = <10>;
+ hsync-len = <10>;
+ vsync-len = <10>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+ };
};
can0: can@80032000 {
@@ -285,6 +310,15 @@
gpio = <&gpio3 8 0>;
enable-active-high;
};
+
+ reg_lcd_3v3: lcd-3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "lcd-3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio3 30 0>;
+ enable-active-high;
+ };
};
sound {
diff --git a/arch/arm/boot/dts/imx28-m28evk.dts b/arch/arm/boot/dts/imx28-m28evk.dts
index fd36e1cca104..5aa44e05c9f5 100644
--- a/arch/arm/boot/dts/imx28-m28evk.dts
+++ b/arch/arm/boot/dts/imx28-m28evk.dts
@@ -119,7 +119,32 @@
pinctrl-names = "default";
pinctrl-0 = <&lcdif_24bit_pins_a
&lcdif_pins_m28>;
+ display = <&display>;
status = "okay";
+
+ display: display {
+ bits-per-pixel = <16>;
+ bus-width = <18>;
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <33260000>;
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <0>;
+ hfront-porch = <256>;
+ vback-porch = <0>;
+ vfront-porch = <45>;
+ hsync-len = <1>;
+ vsync-len = <1>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <1>;
+ };
+ };
+ };
};
can0: can@80032000 {
@@ -220,6 +245,8 @@
phy-mode = "rmii";
pinctrl-names = "default";
pinctrl-0 = <&mac0_pins_a>;
+ clocks = <&clks 57>, <&clks 57>;
+ clock-names = "ipg", "ahb";
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi
index 64af2381c1b0..600f7cb51f3e 100644
--- a/arch/arm/boot/dts/imx28.dtsi
+++ b/arch/arm/boot/dts/imx28.dtsi
@@ -61,12 +61,24 @@
hsadc@80002000 {
reg = <0x80002000 0x2000>;
interrupts = <13 87>;
+ dmas = <&dma_apbh 12>;
+ dma-names = "rx";
status = "disabled";
};
- dma-apbh@80004000 {
+ dma_apbh: dma-apbh@80004000 {
compatible = "fsl,imx28-dma-apbh";
reg = <0x80004000 0x2000>;
+ interrupts = <82 83 84 85
+ 88 88 88 88
+ 88 88 88 88
+ 87 86 0 0>;
+ interrupt-names = "ssp0", "ssp1", "ssp2", "ssp3",
+ "gpmi0", "gmpi1", "gpmi2", "gmpi3",
+ "gpmi4", "gmpi5", "gpmi6", "gmpi7",
+ "hsadc", "lcdif", "empty", "empty";
+ #dma-cells = <1>;
+ dma-channels = <16>;
clocks = <&clks 25>;
};
@@ -86,6 +98,8 @@
interrupt-names = "gpmi-dma", "bch";
clocks = <&clks 50>;
clock-names = "gpmi_io";
+ dmas = <&dma_apbh 4>;
+ dma-names = "rx-tx";
fsl,gpmi-dma-channel = <4>;
status = "disabled";
};
@@ -96,6 +110,8 @@
reg = <0x80010000 0x2000>;
interrupts = <96 82>;
clocks = <&clks 46>;
+ dmas = <&dma_apbh 0>;
+ dma-names = "rx-tx";
fsl,ssp-dma-channel = <0>;
status = "disabled";
};
@@ -106,6 +122,8 @@
reg = <0x80012000 0x2000>;
interrupts = <97 83>;
clocks = <&clks 47>;
+ dmas = <&dma_apbh 1>;
+ dma-names = "rx-tx";
fsl,ssp-dma-channel = <1>;
status = "disabled";
};
@@ -116,6 +134,8 @@
reg = <0x80014000 0x2000>;
interrupts = <98 84>;
clocks = <&clks 48>;
+ dmas = <&dma_apbh 2>;
+ dma-names = "rx-tx";
fsl,ssp-dma-channel = <2>;
status = "disabled";
};
@@ -126,6 +146,8 @@
reg = <0x80016000 0x2000>;
interrupts = <99 85>;
clocks = <&clks 49>;
+ dmas = <&dma_apbh 3>;
+ dma-names = "rx-tx";
fsl,ssp-dma-channel = <3>;
status = "disabled";
};
@@ -658,9 +680,19 @@
status = "disabled";
};
- dma-apbx@80024000 {
+ dma_apbx: dma-apbx@80024000 {
compatible = "fsl,imx28-dma-apbx";
reg = <0x80024000 0x2000>;
+ interrupts = <78 79 66 0
+ 80 81 68 69
+ 70 71 72 73
+ 74 75 76 77>;
+ interrupt-names = "auart4-rx", "aurat4-tx", "spdif-tx", "empty",
+ "saif0", "saif1", "i2c0", "i2c1",
+ "auart0-rx", "auart0-tx", "auart1-rx", "auart1-tx",
+ "auart2-rx", "auart2-tx", "auart3-rx", "auart3-tx";
+ #dma-cells = <1>;
+ dma-channels = <16>;
clocks = <&clks 26>;
};
@@ -692,6 +724,8 @@
reg = <0x80030000 0x2000>;
interrupts = <38 86>;
clocks = <&clks 55>;
+ dmas = <&dma_apbh 13>;
+ dma-names = "rx";
status = "disabled";
};
@@ -767,6 +801,8 @@
reg = <0x80042000 0x2000>;
interrupts = <59 80>;
clocks = <&clks 53>;
+ dmas = <&dma_apbx 4>;
+ dma-names = "rx-tx";
fsl,saif-dma-channel = <4>;
status = "disabled";
};
@@ -781,6 +817,8 @@
reg = <0x80046000 0x2000>;
interrupts = <58 81>;
clocks = <&clks 54>;
+ dmas = <&dma_apbx 5>;
+ dma-names = "rx-tx";
fsl,saif-dma-channel = <5>;
status = "disabled";
};
@@ -796,6 +834,8 @@
spdif@80054000 {
reg = <0x80054000 0x2000>;
interrupts = <45 66>;
+ dmas = <&dma_apbx 2>;
+ dma-names = "tx";
status = "disabled";
};
@@ -812,6 +852,8 @@
reg = <0x80058000 0x2000>;
interrupts = <111 68>;
clock-frequency = <100000>;
+ dmas = <&dma_apbx 6>;
+ dma-names = "rx-tx";
fsl,i2c-dma-channel = <6>;
status = "disabled";
};
@@ -823,6 +865,8 @@
reg = <0x8005a000 0x2000>;
interrupts = <110 69>;
clock-frequency = <100000>;
+ dmas = <&dma_apbx 7>;
+ dma-names = "rx-tx";
fsl,i2c-dma-channel = <7>;
status = "disabled";
};
@@ -847,6 +891,8 @@
compatible = "fsl,imx28-auart", "fsl,imx23-auart";
reg = <0x8006a000 0x2000>;
interrupts = <112 70 71>;
+ dmas = <&dma_apbx 8>, <&dma_apbx 9>;
+ dma-names = "rx", "tx";
fsl,auart-dma-channel = <8 9>;
clocks = <&clks 45>;
status = "disabled";
@@ -856,6 +902,8 @@
compatible = "fsl,imx28-auart", "fsl,imx23-auart";
reg = <0x8006c000 0x2000>;
interrupts = <113 72 73>;
+ dmas = <&dma_apbx 10>, <&dma_apbx 11>;
+ dma-names = "rx", "tx";
clocks = <&clks 45>;
status = "disabled";
};
@@ -864,6 +912,8 @@
compatible = "fsl,imx28-auart", "fsl,imx23-auart";
reg = <0x8006e000 0x2000>;
interrupts = <114 74 75>;
+ dmas = <&dma_apbx 12>, <&dma_apbx 13>;
+ dma-names = "rx", "tx";
clocks = <&clks 45>;
status = "disabled";
};
@@ -872,6 +922,8 @@
compatible = "fsl,imx28-auart", "fsl,imx23-auart";
reg = <0x80070000 0x2000>;
interrupts = <115 76 77>;
+ dmas = <&dma_apbx 14>, <&dma_apbx 15>;
+ dma-names = "rx", "tx";
clocks = <&clks 45>;
status = "disabled";
};
@@ -880,6 +932,8 @@
compatible = "fsl,imx28-auart", "fsl,imx23-auart";
reg = <0x80072000 0x2000>;
interrupts = <116 78 79>;
+ dmas = <&dma_apbx 0>, <&dma_apbx 1>;
+ dma-names = "rx", "tx";
clocks = <&clks 45>;
status = "disabled";
};
@@ -943,8 +997,8 @@
compatible = "fsl,imx28-fec";
reg = <0x800f0000 0x4000>;
interrupts = <101>;
- clocks = <&clks 57>, <&clks 57>;
- clock-names = "ipg", "ahb";
+ clocks = <&clks 57>, <&clks 57>, <&clks 64>;
+ clock-names = "ipg", "ahb", "enet_out";
status = "disabled";
};
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 3cca7d39529d..9e8296e4c343 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -65,9 +65,13 @@
interrupt-parent = <&intc>;
ranges;
- dma-apbh@00110000 {
+ dma_apbh: dma-apbh@00110000 {
compatible = "fsl,imx6q-dma-apbh", "fsl,imx28-dma-apbh";
reg = <0x00110000 0x2000>;
+ interrupts = <0 13 0x04>, <0 13 0x04>, <0 13 0x04>, <0 13 0x04>;
+ interrupt-names = "gpmi0", "gpmi1", "gpmi2", "gpmi3";
+ #dma-cells = <1>;
+ dma-channels = <4>;
clocks = <&clks 106>;
};
@@ -83,6 +87,8 @@
<&clks 150>, <&clks 149>;
clock-names = "gpmi_io", "gpmi_apb", "gpmi_bch",
"gpmi_bch_apb", "per1_bch";
+ dmas = <&dma_apbh 0>;
+ dma-names = "rx-tx";
fsl,gpmi-dma-channel = <0>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/integratorcp.dts b/arch/arm/boot/dts/integratorcp.dts
index 8b119399025a..ff1aea0ee043 100644
--- a/arch/arm/boot/dts/integratorcp.dts
+++ b/arch/arm/boot/dts/integratorcp.dts
@@ -24,15 +24,15 @@
};
timer0: timer@13000000 {
- compatible = "arm,sp804", "arm,primecell";
+ compatible = "arm,integrator-cp-timer";
};
timer1: timer@13000100 {
- compatible = "arm,sp804", "arm,primecell";
+ compatible = "arm,integrator-cp-timer";
};
timer2: timer@13000200 {
- compatible = "arm,sp804", "arm,primecell";
+ compatible = "arm,integrator-cp-timer";
};
pic: pic@14000000 {
diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi
index 761c4b69b25b..37aa7487d4d8 100644
--- a/arch/arm/boot/dts/omap2.dtsi
+++ b/arch/arm/boot/dts/omap2.dtsi
@@ -26,6 +26,11 @@
};
};
+ pmu {
+ compatible = "arm,arm1136-pmu";
+ interrupts = <3>;
+ };
+
soc {
compatible = "ti,omap-infra";
mpu {
@@ -49,6 +54,18 @@
reg = <0x480FE000 0x1000>;
};
+ sdma: dma-controller@48056000 {
+ compatible = "ti,omap2430-sdma", "ti,omap2420-sdma";
+ reg = <0x48056000 0x1000>;
+ interrupts = <12>,
+ <13>,
+ <14>,
+ <15>;
+ #dma-cells = <1>;
+ #dma-channels = <32>;
+ #dma-requests = <64>;
+ };
+
uart1: serial@4806a000 {
compatible = "ti,omap2-uart";
ti,hwmods = "uart1";
@@ -68,28 +85,28 @@
};
timer2: timer@4802a000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x4802a000 0x400>;
interrupts = <38>;
ti,hwmods = "timer2";
};
timer3: timer@48078000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x48078000 0x400>;
interrupts = <39>;
ti,hwmods = "timer3";
};
timer4: timer@4807a000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x4807a000 0x400>;
interrupts = <40>;
ti,hwmods = "timer4";
};
timer5: timer@4807c000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x4807c000 0x400>;
interrupts = <41>;
ti,hwmods = "timer5";
@@ -97,7 +114,7 @@
};
timer6: timer@4807e000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x4807e000 0x400>;
interrupts = <42>;
ti,hwmods = "timer6";
@@ -105,7 +122,7 @@
};
timer7: timer@48080000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x48080000 0x400>;
interrupts = <43>;
ti,hwmods = "timer7";
@@ -113,7 +130,7 @@
};
timer8: timer@48082000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x48082000 0x400>;
interrupts = <44>;
ti,hwmods = "timer8";
@@ -121,7 +138,7 @@
};
timer9: timer@48084000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x48084000 0x400>;
interrupts = <45>;
ti,hwmods = "timer9";
@@ -129,7 +146,7 @@
};
timer10: timer@48086000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x48086000 0x400>;
interrupts = <46>;
ti,hwmods = "timer10";
@@ -137,7 +154,7 @@
};
timer11: timer@48088000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x48088000 0x400>;
interrupts = <47>;
ti,hwmods = "timer11";
@@ -145,7 +162,7 @@
};
timer12: timer@4808a000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x4808a000 0x400>;
interrupts = <48>;
ti,hwmods = "timer12";
diff --git a/arch/arm/boot/dts/omap2420-h4.dts b/arch/arm/boot/dts/omap2420-h4.dts
index 9b0d07746cba..68282ee13e26 100644
--- a/arch/arm/boot/dts/omap2420-h4.dts
+++ b/arch/arm/boot/dts/omap2420-h4.dts
@@ -18,3 +18,49 @@
reg = <0x80000000 0x4000000>; /* 64 MB */
};
};
+
+&gpmc {
+ ranges = <0 0 0x08000000 0x04000000>;
+
+ nor@0,0 {
+ compatible = "cfi-flash";
+ linux,mtd-name= "intel,ge28f256l18b85";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0 0 0x04000000>;
+ bank-width = <2>;
+
+ gpmc,mux-add-data = <2>;
+ gpmc,cs-on-ns = <10>;
+ gpmc,cs-rd-off-ns = <160>;
+ gpmc,cs-wr-off-ns = <160>;
+ gpmc,adv-on-ns = <20>;
+ gpmc,adv-rd-off-ns = <50>;
+ gpmc,adv-wr-off-ns = <50>;
+ gpmc,oe-on-ns = <60>;
+ gpmc,oe-off-ns = <120>;
+ gpmc,we-on-ns = <60>;
+ gpmc,we-off-ns = <120>;
+ gpmc,rd-cycle-ns = <170>;
+ gpmc,wr-cycle-ns = <170>;
+ gpmc,access-ns = <150>;
+ gpmc,page-burst-access-ns = <10>;
+
+ partition@0 {
+ label = "bootloader";
+ reg = <0 0x20000>;
+ };
+ partition@0x20000 {
+ label = "params";
+ reg = <0x20000 0x20000>;
+ };
+ partition@0x40000 {
+ label = "kernel";
+ reg = <0x40000 0x200000>;
+ };
+ partition@0x240000 {
+ label = "file-system";
+ reg = <0x240000 0x3dc0000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi
index af6560908905..da5b285b73be 100644
--- a/arch/arm/boot/dts/omap2420.dtsi
+++ b/arch/arm/boot/dts/omap2420.dtsi
@@ -29,6 +29,65 @@
pinctrl-single,function-mask = <0x3f>;
};
+ gpio1: gpio@48018000 {
+ compatible = "ti,omap2-gpio";
+ reg = <0x48018000 0x200>;
+ interrupts = <29>;
+ ti,hwmods = "gpio1";
+ ti,gpio-always-on;
+ #gpio-cells = <2>;
+ gpio-controller;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+
+ gpio2: gpio@4801a000 {
+ compatible = "ti,omap2-gpio";
+ reg = <0x4801a000 0x200>;
+ interrupts = <30>;
+ ti,hwmods = "gpio2";
+ ti,gpio-always-on;
+ #gpio-cells = <2>;
+ gpio-controller;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+
+ gpio3: gpio@4801c000 {
+ compatible = "ti,omap2-gpio";
+ reg = <0x4801c000 0x200>;
+ interrupts = <31>;
+ ti,hwmods = "gpio3";
+ ti,gpio-always-on;
+ #gpio-cells = <2>;
+ gpio-controller;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+
+ gpio4: gpio@4801e000 {
+ compatible = "ti,omap2-gpio";
+ reg = <0x4801e000 0x200>;
+ interrupts = <32>;
+ ti,hwmods = "gpio4";
+ ti,gpio-always-on;
+ #gpio-cells = <2>;
+ gpio-controller;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+
+ gpmc: gpmc@6800a000 {
+ compatible = "ti,omap2420-gpmc";
+ reg = <0x6800a000 0x1000>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ interrupts = <20>;
+ gpmc,num-cs = <8>;
+ gpmc,num-waitpins = <4>;
+ ti,hwmods = "gpmc";
+ };
+
mcbsp1: mcbsp@48074000 {
compatible = "ti,omap2420-mcbsp";
reg = <0x48074000 0xff>;
@@ -37,6 +96,9 @@
<60>; /* RX interrupt */
interrupt-names = "tx", "rx";
ti,hwmods = "mcbsp1";
+ dmas = <&sdma 31>,
+ <&sdma 32>;
+ dma-names = "tx", "rx";
};
mcbsp2: mcbsp@48076000 {
@@ -47,10 +109,13 @@
<63>; /* RX interrupt */
interrupt-names = "tx", "rx";
ti,hwmods = "mcbsp2";
+ dmas = <&sdma 33>,
+ <&sdma 34>;
+ dma-names = "tx", "rx";
};
timer1: timer@48028000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x48028000 0x400>;
interrupts = <37>;
ti,hwmods = "timer1";
diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi
index c3924457c9b6..054bc4439568 100644
--- a/arch/arm/boot/dts/omap2430.dtsi
+++ b/arch/arm/boot/dts/omap2430.dtsi
@@ -29,6 +29,76 @@
pinctrl-single,function-mask = <0x3f>;
};
+ gpio1: gpio@4900c000 {
+ compatible = "ti,omap2-gpio";
+ reg = <0x4900c000 0x200>;
+ interrupts = <29>;
+ ti,hwmods = "gpio1";
+ ti,gpio-always-on;
+ #gpio-cells = <2>;
+ gpio-controller;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+
+ gpio2: gpio@4900e000 {
+ compatible = "ti,omap2-gpio";
+ reg = <0x4900e000 0x200>;
+ interrupts = <30>;
+ ti,hwmods = "gpio2";
+ ti,gpio-always-on;
+ #gpio-cells = <2>;
+ gpio-controller;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+
+ gpio3: gpio@49010000 {
+ compatible = "ti,omap2-gpio";
+ reg = <0x49010000 0x200>;
+ interrupts = <31>;
+ ti,hwmods = "gpio3";
+ ti,gpio-always-on;
+ #gpio-cells = <2>;
+ gpio-controller;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+
+ gpio4: gpio@49012000 {
+ compatible = "ti,omap2-gpio";
+ reg = <0x49012000 0x200>;
+ interrupts = <32>;
+ ti,hwmods = "gpio4";
+ ti,gpio-always-on;
+ #gpio-cells = <2>;
+ gpio-controller;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+
+ gpio5: gpio@480b6000 {
+ compatible = "ti,omap2-gpio";
+ reg = <0x480b6000 0x200>;
+ interrupts = <33>;
+ ti,hwmods = "gpio5";
+ #gpio-cells = <2>;
+ gpio-controller;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+
+ gpmc: gpmc@6e000000 {
+ compatible = "ti,omap2430-gpmc";
+ reg = <0x6e000000 0x1000>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ interrupts = <20>;
+ gpmc,num-cs = <8>;
+ gpmc,num-waitpins = <4>;
+ ti,hwmods = "gpmc";
+ };
+
mcbsp1: mcbsp@48074000 {
compatible = "ti,omap2430-mcbsp";
reg = <0x48074000 0xff>;
@@ -40,6 +110,9 @@
interrupt-names = "common", "tx", "rx", "rx_overflow";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp1";
+ dmas = <&sdma 31>,
+ <&sdma 32>;
+ dma-names = "tx", "rx";
};
mcbsp2: mcbsp@48076000 {
@@ -52,6 +125,9 @@
interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp2";
+ dmas = <&sdma 33>,
+ <&sdma 34>;
+ dma-names = "tx", "rx";
};
mcbsp3: mcbsp@4808c000 {
@@ -64,6 +140,9 @@
interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp3";
+ dmas = <&sdma 17>,
+ <&sdma 18>;
+ dma-names = "tx", "rx";
};
mcbsp4: mcbsp@4808e000 {
@@ -76,6 +155,9 @@
interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp4";
+ dmas = <&sdma 19>,
+ <&sdma 20>;
+ dma-names = "tx", "rx";
};
mcbsp5: mcbsp@48096000 {
@@ -88,10 +170,13 @@
interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp5";
+ dmas = <&sdma 21>,
+ <&sdma 22>;
+ dma-names = "tx", "rx";
};
timer1: timer@49018000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap2420-timer";
reg = <0x49018000 0x400>;
interrupts = <37>;
ti,hwmods = "timer1";
diff --git a/arch/arm/boot/dts/omap3-beagle-xm.dts b/arch/arm/boot/dts/omap3-beagle-xm.dts
index 3705a81c1fc2..5a31964ae339 100644
--- a/arch/arm/boot/dts/omap3-beagle-xm.dts
+++ b/arch/arm/boot/dts/omap3-beagle-xm.dts
@@ -13,6 +13,12 @@
model = "TI OMAP3 BeagleBoard xM";
compatible = "ti,omap3-beagle-xm, ti,omap3-beagle", "ti,omap3";
+ cpus {
+ cpu@0 {
+ cpu0-supply = <&vcc>;
+ };
+ };
+
memory {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512 MB */
@@ -20,10 +26,6 @@
leds {
compatible = "gpio-leds";
- pmu_stat {
- label = "beagleboard::pmu_stat";
- gpios = <&twl_gpio 19 0>; /* LEDB */
- };
heartbeat {
label = "beagleboard::usr0";
@@ -38,6 +40,16 @@
};
};
+ pwmleds {
+ compatible = "pwm-leds";
+
+ pmu_stat {
+ label = "beagleboard::pmu_stat";
+ pwms = <&twl_pwmled 1 7812500>;
+ max-brightness = <127>;
+ };
+ };
+
sound {
compatible = "ti,omap-twl4030";
ti,model = "omap3beagle";
@@ -107,3 +119,9 @@
*/
ti,pulldowns = <0x03a1c4>;
};
+
+&usb_otg_hs {
+ interface-type = <0>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts
index 02d23f15fd86..6eec69997607 100644
--- a/arch/arm/boot/dts/omap3-beagle.dts
+++ b/arch/arm/boot/dts/omap3-beagle.dts
@@ -7,12 +7,18 @@
*/
/dts-v1/;
-/include/ "omap3.dtsi"
+/include/ "omap34xx.dtsi"
/ {
model = "TI OMAP3 BeagleBoard";
compatible = "ti,omap3-beagle", "ti,omap3";
+ cpus {
+ cpu@0 {
+ cpu0-supply = <&vcc>;
+ };
+ };
+
memory {
device_type = "memory";
reg = <0x80000000 0x10000000>; /* 256 MB */
diff --git a/arch/arm/boot/dts/omap3-devkit8000.dts b/arch/arm/boot/dts/omap3-devkit8000.dts
new file mode 100644
index 000000000000..8a5cdcc6debd
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-devkit8000.dts
@@ -0,0 +1,169 @@
+/*
+ * Author: Anil Kumar <anilk4.v@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.
+ */
+/dts-v1/;
+
+/include/ "omap34xx.dtsi"
+/ {
+ model = "TimLL OMAP3 Devkit8000";
+ compatible = "timll,omap3-devkit8000", "ti,omap3";
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x10000000>; /* 256 MB */
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ heartbeat {
+ label = "devkit8000::led1";
+ gpios = <&gpio6 26 0>; /* 186 -> LED1 */
+ default-state = "on";
+ linux,default-trigger = "heartbeat";
+ };
+
+ mmc {
+ label = "devkit8000::led2";
+ gpios = <&gpio6 3 0>; /* 163 -> LED2 */
+ default-state = "on";
+ linux,default-trigger = "none";
+ };
+
+ usr {
+ label = "devkit8000::led3";
+ gpios = <&gpio6 4 0>; /* 164 -> LED3 */
+ default-state = "on";
+ linux,default-trigger = "usr";
+ };
+
+ };
+
+ sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "devkit8000";
+
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&twl_audio>;
+ ti,audio-routing =
+ "Ext Spk", "PREDRIVEL",
+ "Ext Spk", "PREDRIVER",
+ "MAINMIC", "Main Mic",
+ "Main Mic", "Mic Bias 1";
+ };
+};
+
+&i2c1 {
+ clock-frequency = <2600000>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+
+ twl_audio: audio {
+ compatible = "ti,twl4030-audio";
+ codec {
+ };
+ };
+ };
+};
+
+&i2c2 {
+ status = "disabled";
+};
+
+&i2c3 {
+ status = "disabled";
+};
+
+/include/ "twl4030.dtsi"
+
+&mmc1 {
+ vmmc-supply = <&vmmc1>;
+ vmmc_aux-supply = <&vsim>;
+ bus-width = <8>;
+};
+
+&mmc2 {
+ status = "disabled";
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+&wdt2 {
+ status = "disabled";
+};
+
+&mcbsp1 {
+ status = "disabled";
+};
+
+&mcbsp3 {
+ status = "disabled";
+};
+
+&mcbsp4 {
+ status = "disabled";
+};
+
+&mcbsp5 {
+ status = "disabled";
+};
+
+&gpmc {
+ ranges = <0 0 0x30000000 0x04>; /* CS0: NAND */
+
+ nand@0,0 {
+ reg = <0 0 0>; /* CS0, offset 0 */
+ nand-bus-width = <16>;
+
+ gpmc,sync-clk = <0>;
+ gpmc,cs-on = <0>;
+ gpmc,cs-rd-off = <44>;
+ gpmc,cs-wr-off = <44>;
+ gpmc,adv-on = <6>;
+ gpmc,adv-rd-off = <34>;
+ gpmc,adv-wr-off = <44>;
+ gpmc,we-off = <40>;
+ gpmc,oe-off = <54>;
+ gpmc,access = <64>;
+ gpmc,rd-cycle = <82>;
+ gpmc,wr-cycle = <82>;
+ gpmc,wr-access = <40>;
+ gpmc,wr-data-mux-bus = <0>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ x-loader@0 {
+ label = "X-Loader";
+ reg = <0 0x80000>;
+ };
+
+ bootloaders@80000 {
+ label = "U-Boot";
+ reg = <0x80000 0x1e0000>;
+ };
+
+ bootloaders_env@260000 {
+ label = "U-Boot Env";
+ reg = <0x260000 0x20000>;
+ };
+
+ kernel@280000 {
+ label = "Kernel";
+ reg = <0x280000 0x400000>;
+ };
+
+ filesystem@680000 {
+ label = "File System";
+ reg = <0x680000 0xf980000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap3-evm.dts b/arch/arm/boot/dts/omap3-evm.dts
index e8ba1c247a39..05f51e10ddd6 100644
--- a/arch/arm/boot/dts/omap3-evm.dts
+++ b/arch/arm/boot/dts/omap3-evm.dts
@@ -7,12 +7,18 @@
*/
/dts-v1/;
-/include/ "omap3.dtsi"
+/include/ "omap34xx.dtsi"
/ {
model = "TI OMAP3 EVM (OMAP3530, AM/DM37x)";
compatible = "ti,omap3-evm", "ti,omap3";
+ cpus {
+ cpu@0 {
+ cpu0-supply = <&vcc>;
+ };
+ };
+
memory {
device_type = "memory";
reg = <0x80000000 0x10000000>; /* 256 MB */
@@ -59,3 +65,9 @@
&twl_gpio {
ti,use-leds;
};
+
+&usb_otg_hs {
+ interface-type = <0>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap3-igep.dtsi b/arch/arm/boot/dts/omap3-igep.dtsi
new file mode 100644
index 000000000000..f8fe3b748c3e
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-igep.dtsi
@@ -0,0 +1,122 @@
+/*
+ * Device Tree Source for IGEP Technology devices
+ *
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@collabora.co.uk>
+ * Copyright (C) 2012 Enric Balletbo i Serra <eballetbo@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.
+ */
+/dts-v1/;
+
+/include/ "omap34xx.dtsi"
+
+/ {
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x20000000>; /* 512 MB */
+ };
+
+ sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "igep2";
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&twl_audio>;
+ };
+};
+
+&omap3_pmx_core {
+ uart1_pins: pinmux_uart1_pins {
+ pinctrl-single,pins = <
+ 0x152 0x100 /* uart1_rx.uart1_rx INPUT | MODE0 */
+ 0x14c 0 /* uart1_tx.uart1_tx OUTPUT | MODE0 */
+ >;
+ };
+
+ uart2_pins: pinmux_uart2_pins {
+ pinctrl-single,pins = <
+ 0x14a 0x100 /* uart2_rx.uart2_rx INPUT | MODE0 */
+ 0x148 0 /* uart2_tx.uart2_tx OUTPUT | MODE0 */
+ >;
+ };
+
+ uart3_pins: pinmux_uart3_pins {
+ pinctrl-single,pins = <
+ 0x16e 0x100 /* uart3_rx.uart3_rx INPUT | MODE0 */
+ 0x170 0 /* uart3_tx.uart3_tx OUTPUT | MODE0 */
+ >;
+ };
+
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ 0x114 0x0118 /* sdmmc1_clk.sdmmc1_clk INPUT PULLUP | MODE 0 */
+ 0x116 0x0118 /* sdmmc1_cmd.sdmmc1_cmd INPUT PULLUP | MODE 0 */
+ 0x118 0x0118 /* sdmmc1_dat0.sdmmc1_dat0 INPUT PULLUP | MODE 0 */
+ 0x11a 0x0118 /* sdmmc1_dat1.sdmmc1_dat1 INPUT PULLUP | MODE 0 */
+ 0x11c 0x0118 /* sdmmc1_dat2.sdmmc1_dat2 INPUT PULLUP | MODE 0 */
+ 0x11e 0x0118 /* sdmmc1_dat3.sdmmc1_dat3 INPUT PULLUP | MODE 0 */
+ 0x120 0x0100 /* sdmmc1_dat4.sdmmc1_dat4 INPUT | MODE 0 */
+ 0x122 0x0100 /* sdmmc1_dat5.sdmmc1_dat5 INPUT | MODE 0 */
+ 0x124 0x0100 /* sdmmc1_dat6.sdmmc1_dat6 INPUT | MODE 0 */
+ 0x126 0x0100 /* sdmmc1_dat7.sdmmc1_dat7 INPUT | MODE 0 */
+ >;
+ };
+};
+
+&i2c1 {
+ clock-frequency = <2600000>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+ interrupt-parent = <&intc>;
+
+ twl_audio: audio {
+ compatible = "ti,twl4030-audio";
+ codec {
+ };
+ };
+ };
+};
+
+/include/ "twl4030.dtsi"
+
+&i2c2 {
+ clock-frequency = <400000>;
+};
+
+&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ vmmc-supply = <&vmmc1>;
+ vmmc_aux-supply = <&vsim>;
+ bus-width = <8>;
+};
+
+&mmc2 {
+ status = "disabled";
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart1_pins>;
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_pins>;
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pins>;
+};
+
+&twl_gpio {
+ ti,use-leds;
+};
diff --git a/arch/arm/boot/dts/omap3-igep0020.dts b/arch/arm/boot/dts/omap3-igep0020.dts
new file mode 100644
index 000000000000..e2b98490cc9a
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-igep0020.dts
@@ -0,0 +1,56 @@
+/*
+ * Device Tree Source for IGEPv2 board
+ *
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@collabora.co.uk>
+ * Copyright (C) 2012 Enric Balletbo i Serra <eballetbo@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/ "omap3-igep.dtsi"
+
+/ {
+ model = "IGEPv2";
+ compatible = "isee,omap3-igep0020", "ti,omap3";
+
+ leds {
+ compatible = "gpio-leds";
+ boot {
+ label = "omap3:green:boot";
+ gpios = <&gpio1 26 0>;
+ default-state = "on";
+ };
+
+ user0 {
+ label = "omap3:red:user0";
+ gpios = <&gpio1 27 0>;
+ default-state = "off";
+ };
+
+ user1 {
+ label = "omap3:red:user1";
+ gpios = <&gpio1 28 0>;
+ default-state = "off";
+ };
+
+ user2 {
+ label = "omap3:green:user1";
+ gpios = <&twl_gpio 19 1>;
+ };
+ };
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+
+ /*
+ * Display monitor features are burnt in the EEPROM
+ * as EDID data.
+ */
+ eeprom@50 {
+ compatible = "ti,eeprom";
+ reg = <0x50>;
+ };
+};
diff --git a/arch/arm/boot/dts/omap3-igep0030.dts b/arch/arm/boot/dts/omap3-igep0030.dts
new file mode 100644
index 000000000000..9dc48d262ffb
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-igep0030.dts
@@ -0,0 +1,44 @@
+/*
+ * Device Tree Source for IGEP COM Module
+ *
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@collabora.co.uk>
+ * Copyright (C) 2012 Enric Balletbo i Serra <eballetbo@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/ "omap3-igep.dtsi"
+
+/ {
+ model = "IGEP COM Module";
+ compatible = "isee,omap3-igep0030", "ti,omap3";
+
+ leds {
+ compatible = "gpio-leds";
+ boot {
+ label = "omap3:green:boot";
+ gpios = <&twl_gpio 13 1>;
+ default-state = "on";
+ };
+
+ user0 {
+ label = "omap3:red:user0";
+ gpios = <&twl_gpio 18 1>; /* LEDA */
+ default-state = "off";
+ };
+
+ user1 {
+ label = "omap3:green:user1";
+ gpios = <&twl_gpio 19 1>; /* LEDB */
+ default-state = "off";
+ };
+
+ user2 {
+ label = "omap3:red:user1";
+ gpios = <&gpio1 16 1>;
+ default-state = "off";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap3-overo.dtsi b/arch/arm/boot/dts/omap3-overo.dtsi
index 89808ce01673..d4a7280d18b7 100644
--- a/arch/arm/boot/dts/omap3-overo.dtsi
+++ b/arch/arm/boot/dts/omap3-overo.dtsi
@@ -11,17 +11,26 @@
*/
/dts-v1/;
-/include/ "omap3.dtsi"
+/include/ "omap34xx.dtsi"
/ {
- leds {
- compatible = "gpio-leds";
+ pwmleds {
+ compatible = "pwm-leds";
+
overo {
label = "overo:blue:COM";
- gpios = <&twl_gpio 19 0>;
- linux,default-trigger = "mmc0";
+ pwms = <&twl_pwmled 1 7812500>;
+ max-brightness = <127>;
};
};
+
+ sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "overo";
+
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&twl_audio>;
+ };
};
&i2c1 {
@@ -31,6 +40,12 @@
reg = <0x48>;
interrupts = <7>; /* SYS_NIRQ cascaded to intc */
interrupt-parent = <&intc>;
+
+ twl_audio: audio {
+ compatible = "ti,twl4030-audio";
+ codec {
+ };
+ };
};
};
@@ -55,3 +70,9 @@
&twl_gpio {
ti,use-leds;
};
+
+&usb_otg_hs {
+ interface-type = <0>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi
index a14f74bbce7c..4ad03d9dbf0c 100644
--- a/arch/arm/boot/dts/omap3.dtsi
+++ b/arch/arm/boot/dts/omap3.dtsi
@@ -26,8 +26,14 @@
};
};
+ pmu {
+ compatible = "arm,cortex-a8-pmu";
+ interrupts = <3>;
+ ti,hwmods = "debugss";
+ };
+
/*
- * The soc node represents the soc top level view. It is uses for IPs
+ * The soc node represents the soc top level view. It is used for IPs
* that are not memory mapped in the MPU view or for the MPU itself.
*/
soc {
@@ -75,76 +81,101 @@
reg = <0x48200000 0x1000>;
};
+ sdma: dma-controller@48056000 {
+ compatible = "ti,omap3630-sdma", "ti,omap3430-sdma";
+ reg = <0x48056000 0x1000>;
+ interrupts = <12>,
+ <13>,
+ <14>,
+ <15>;
+ #dma-cells = <1>;
+ #dma-channels = <32>;
+ #dma-requests = <96>;
+ };
+
omap3_pmx_core: pinmux@48002030 {
compatible = "ti,omap3-padconf", "pinctrl-single";
reg = <0x48002030 0x05cc>;
#address-cells = <1>;
#size-cells = <0>;
pinctrl-single,register-width = <16>;
- pinctrl-single,function-mask = <0x7fff>;
+ pinctrl-single,function-mask = <0x7f1f>;
};
- omap3_pmx_wkup: pinmux@0x48002a58 {
+ omap3_pmx_wkup: pinmux@0x48002a00 {
compatible = "ti,omap3-padconf", "pinctrl-single";
- reg = <0x48002a58 0x5c>;
+ reg = <0x48002a00 0x5c>;
#address-cells = <1>;
#size-cells = <0>;
pinctrl-single,register-width = <16>;
- pinctrl-single,function-mask = <0x7fff>;
+ pinctrl-single,function-mask = <0x7f1f>;
};
gpio1: gpio@48310000 {
compatible = "ti,omap3-gpio";
+ reg = <0x48310000 0x200>;
+ interrupts = <29>;
ti,hwmods = "gpio1";
+ ti,gpio-always-on;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio2: gpio@49050000 {
compatible = "ti,omap3-gpio";
+ reg = <0x49050000 0x200>;
+ interrupts = <30>;
ti,hwmods = "gpio2";
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio3: gpio@49052000 {
compatible = "ti,omap3-gpio";
+ reg = <0x49052000 0x200>;
+ interrupts = <31>;
ti,hwmods = "gpio3";
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio4: gpio@49054000 {
compatible = "ti,omap3-gpio";
+ reg = <0x49054000 0x200>;
+ interrupts = <32>;
ti,hwmods = "gpio4";
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio5: gpio@49056000 {
compatible = "ti,omap3-gpio";
+ reg = <0x49056000 0x200>;
+ interrupts = <33>;
ti,hwmods = "gpio5";
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio6: gpio@49058000 {
compatible = "ti,omap3-gpio";
+ reg = <0x49058000 0x200>;
+ interrupts = <34>;
ti,hwmods = "gpio6";
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
uart1: serial@4806a000 {
@@ -192,6 +223,16 @@
#size-cells = <0>;
ti,hwmods = "mcspi1";
ti,spi-num-cs = <4>;
+ dmas = <&sdma 35>,
+ <&sdma 36>,
+ <&sdma 37>,
+ <&sdma 38>,
+ <&sdma 39>,
+ <&sdma 40>,
+ <&sdma 41>,
+ <&sdma 42>;
+ dma-names = "tx0", "rx0", "tx1", "rx1",
+ "tx2", "rx2", "tx3", "rx3";
};
mcspi2: spi@4809a000 {
@@ -200,6 +241,11 @@
#size-cells = <0>;
ti,hwmods = "mcspi2";
ti,spi-num-cs = <2>;
+ dmas = <&sdma 43>,
+ <&sdma 44>,
+ <&sdma 45>,
+ <&sdma 46>;
+ dma-names = "tx0", "rx0", "tx1", "rx1";
};
mcspi3: spi@480b8000 {
@@ -208,6 +254,11 @@
#size-cells = <0>;
ti,hwmods = "mcspi3";
ti,spi-num-cs = <2>;
+ dmas = <&sdma 15>,
+ <&sdma 16>,
+ <&sdma 23>,
+ <&sdma 24>;
+ dma-names = "tx0", "rx0", "tx1", "rx1";
};
mcspi4: spi@480ba000 {
@@ -216,22 +267,30 @@
#size-cells = <0>;
ti,hwmods = "mcspi4";
ti,spi-num-cs = <1>;
+ dmas = <&sdma 70>, <&sdma 71>;
+ dma-names = "tx0", "rx0";
};
mmc1: mmc@4809c000 {
compatible = "ti,omap3-hsmmc";
ti,hwmods = "mmc1";
ti,dual-volt;
+ dmas = <&sdma 61>, <&sdma 62>;
+ dma-names = "tx", "rx";
};
mmc2: mmc@480b4000 {
compatible = "ti,omap3-hsmmc";
ti,hwmods = "mmc2";
+ dmas = <&sdma 47>, <&sdma 48>;
+ dma-names = "tx", "rx";
};
mmc3: mmc@480ad000 {
compatible = "ti,omap3-hsmmc";
ti,hwmods = "mmc3";
+ dmas = <&sdma 77>, <&sdma 78>;
+ dma-names = "tx", "rx";
};
wdt2: wdt@48314000 {
@@ -249,6 +308,9 @@
interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp1";
+ dmas = <&sdma 31>,
+ <&sdma 32>;
+ dma-names = "tx", "rx";
};
mcbsp2: mcbsp@49022000 {
@@ -263,6 +325,9 @@
interrupt-names = "common", "tx", "rx", "sidetone";
ti,buffer-size = <1280>;
ti,hwmods = "mcbsp2", "mcbsp2_sidetone";
+ dmas = <&sdma 33>,
+ <&sdma 34>;
+ dma-names = "tx", "rx";
};
mcbsp3: mcbsp@49024000 {
@@ -277,6 +342,9 @@
interrupt-names = "common", "tx", "rx", "sidetone";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp3", "mcbsp3_sidetone";
+ dmas = <&sdma 17>,
+ <&sdma 18>;
+ dma-names = "tx", "rx";
};
mcbsp4: mcbsp@49026000 {
@@ -289,6 +357,9 @@
interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp4";
+ dmas = <&sdma 19>,
+ <&sdma 20>;
+ dma-names = "tx", "rx";
};
mcbsp5: mcbsp@48096000 {
@@ -301,10 +372,13 @@
interrupt-names = "common", "tx", "rx";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp5";
+ dmas = <&sdma 21>,
+ <&sdma 22>;
+ dma-names = "tx", "rx";
};
timer1: timer@48318000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x48318000 0x400>;
interrupts = <37>;
ti,hwmods = "timer1";
@@ -312,28 +386,28 @@
};
timer2: timer@49032000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x49032000 0x400>;
interrupts = <38>;
ti,hwmods = "timer2";
};
timer3: timer@49034000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x49034000 0x400>;
interrupts = <39>;
ti,hwmods = "timer3";
};
timer4: timer@49036000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x49036000 0x400>;
interrupts = <40>;
ti,hwmods = "timer4";
};
timer5: timer@49038000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x49038000 0x400>;
interrupts = <41>;
ti,hwmods = "timer5";
@@ -341,7 +415,7 @@
};
timer6: timer@4903a000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x4903a000 0x400>;
interrupts = <42>;
ti,hwmods = "timer6";
@@ -349,7 +423,7 @@
};
timer7: timer@4903c000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x4903c000 0x400>;
interrupts = <43>;
ti,hwmods = "timer7";
@@ -357,7 +431,7 @@
};
timer8: timer@4903e000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x4903e000 0x400>;
interrupts = <44>;
ti,hwmods = "timer8";
@@ -366,7 +440,7 @@
};
timer9: timer@49040000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x49040000 0x400>;
interrupts = <45>;
ti,hwmods = "timer9";
@@ -374,7 +448,7 @@
};
timer10: timer@48086000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x48086000 0x400>;
interrupts = <46>;
ti,hwmods = "timer10";
@@ -382,7 +456,7 @@
};
timer11: timer@48088000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x48088000 0x400>;
interrupts = <47>;
ti,hwmods = "timer11";
@@ -390,7 +464,7 @@
};
timer12: timer@48304000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x48304000 0x400>;
interrupts = <95>;
ti,hwmods = "timer12";
@@ -428,5 +502,27 @@
};
};
+ gpmc: gpmc@6e000000 {
+ compatible = "ti,omap3430-gpmc";
+ ti,hwmods = "gpmc";
+ reg = <0x6e000000 0x02d0>;
+ interrupts = <20>;
+ gpmc,num-cs = <8>;
+ gpmc,num-waitpins = <4>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ };
+
+ usb_otg_hs: usb_otg_hs@480ab000 {
+ compatible = "ti,omap3-musb";
+ reg = <0x480ab000 0x1000>;
+ interrupts = <0 92 0x4>, <0 93 0x4>;
+ interrupt-names = "mc", "dma";
+ ti,hwmods = "usb_otg_hs";
+ usb-phy = <&usb2_phy>;
+ multipoint = <1>;
+ num-eps = <16>;
+ ram-bits = <12>;
+ };
};
};
diff --git a/arch/arm/boot/dts/omap3430-sdp.dts b/arch/arm/boot/dts/omap3430-sdp.dts
new file mode 100644
index 000000000000..144ae43453c4
--- /dev/null
+++ b/arch/arm/boot/dts/omap3430-sdp.dts
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.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.
+ */
+/dts-v1/;
+
+/include/ "omap34xx.dtsi"
+
+/ {
+ model = "TI OMAP3430 SDP";
+ compatible = "ti,omap3430-sdp", "ti,omap3";
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x10000000>; /* 256 MB */
+ };
+};
+
+&i2c1 {
+ clock-frequency = <2600000>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+ };
+};
+
+/include/ "twl4030.dtsi"
+
+&mmc1 {
+ vmmc-supply = <&vmmc1>;
+ vmmc_aux-supply = <&vsim>;
+ bus-width = <8>;
+};
+
+&mmc2 {
+ status = "disabled";
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+&gpmc {
+ ranges = <0 0 0x10000000 0x08000000>,
+ <1 0 0x28000000 0x08000000>,
+ <2 0 0x20000000 0x10000000>;
+
+ nor@0,0 {
+ compatible = "cfi-flash";
+ linux,mtd-name= "intel,pf48f6000m0y1be";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0 0 0x08000000>;
+ bank-width = <2>;
+
+ gpmc,mux-add-data = <2>;
+ gpmc,cs-on-ns = <0>;
+ gpmc,cs-rd-off-ns = <186>;
+ gpmc,cs-wr-off-ns = <186>;
+ gpmc,adv-on-ns = <12>;
+ gpmc,adv-rd-off-ns = <48>;
+ gpmc,adv-wr-off-ns = <48>;
+ gpmc,oe-on-ns = <54>;
+ gpmc,oe-off-ns = <168>;
+ gpmc,we-on-ns = <54>;
+ gpmc,we-off-ns = <168>;
+ gpmc,rd-cycle-ns = <186>;
+ gpmc,wr-cycle-ns = <186>;
+ gpmc,access-ns = <114>;
+ gpmc,page-burst-access-ns = <6>;
+ gpmc,bus-turnaround-ns = <12>;
+ gpmc,cycle2cycle-delay-ns = <18>;
+ gpmc,wr-data-mux-bus-ns = <90>;
+ gpmc,wr-access-ns = <186>;
+ gpmc,cycle2cycle-samecsen;
+ gpmc,cycle2cycle-diffcsen;
+
+ partition@0 {
+ label = "bootloader-nor";
+ reg = <0 0x40000>;
+ };
+ partition@0x40000 {
+ label = "params-nor";
+ reg = <0x40000 0x40000>;
+ };
+ partition@0x80000 {
+ label = "kernel-nor";
+ reg = <0x80000 0x200000>;
+ };
+ partition@0x280000 {
+ label = "filesystem-nor";
+ reg = <0x240000 0x7d80000>;
+ };
+ };
+
+ nand@1,0 {
+ linux,mtd-name= "micron,mt29f1g08abb";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <1 0 0x08000000>;
+ nand-bus-width = <8>;
+
+ ti,nand-ecc-opt = "sw";
+ gpmc,device-nand;
+ gpmc,cs-on-ns = <0>;
+ gpmc,cs-rd-off-ns = <36>;
+ gpmc,cs-wr-off-ns = <36>;
+ gpmc,adv-on-ns = <6>;
+ gpmc,adv-rd-off-ns = <24>;
+ gpmc,adv-wr-off-ns = <36>;
+ gpmc,oe-on-ns = <6>;
+ gpmc,oe-off-ns = <48>;
+ gpmc,we-on-ns = <6>;
+ gpmc,we-off-ns = <30>;
+ gpmc,rd-cycle-ns = <72>;
+ gpmc,wr-cycle-ns = <72>;
+ gpmc,access-ns = <54>;
+ gpmc,wr-access-ns = <30>;
+
+ partition@0 {
+ label = "xloader-nand";
+ reg = <0 0x80000>;
+ };
+ partition@0x80000 {
+ label = "bootloader-nand";
+ reg = <0x80000 0x140000>;
+ };
+ partition@0x1c0000 {
+ label = "params-nand";
+ reg = <0x1c0000 0xc0000>;
+ };
+ partition@0x280000 {
+ label = "kernel-nand";
+ reg = <0x280000 0x500000>;
+ };
+ partition@0x780000 {
+ label = "filesystem-nand";
+ reg = <0x780000 0x7880000>;
+ };
+ };
+
+ onenand@2,0 {
+ linux,mtd-name= "samsung,kfm2g16q2m-deb8";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <2 0 0x10000000>;
+
+ gpmc,device-width = <2>;
+ gpmc,mux-add-data = <2>;
+ gpmc,cs-on-ns = <0>;
+ gpmc,cs-rd-off-ns = <84>;
+ gpmc,cs-wr-off-ns = <72>;
+ gpmc,adv-on-ns = <0>;
+ gpmc,adv-rd-off-ns = <18>;
+ gpmc,adv-wr-off-ns = <18>;
+ gpmc,oe-on-ns = <30>;
+ gpmc,oe-off-ns = <84>;
+ gpmc,we-on-ns = <0>;
+ gpmc,we-off-ns = <42>;
+ gpmc,rd-cycle-ns = <108>;
+ gpmc,wr-cycle-ns = <96>;
+ gpmc,access-ns = <78>;
+ gpmc,wr-data-mux-bus-ns = <30>;
+
+ partition@0 {
+ label = "xloader-onenand";
+ reg = <0 0x80000>;
+ };
+ partition@0x80000 {
+ label = "bootloader-onenand";
+ reg = <0x80000 0x40000>;
+ };
+ partition@0xc0000 {
+ label = "params-onenand";
+ reg = <0xc0000 0x20000>;
+ };
+ partition@0xe0000 {
+ label = "kernel-onenand";
+ reg = <0xe0000 0x200000>;
+ };
+ partition@0x2e0000 {
+ label = "filesystem-onenand";
+ reg = <0x2e0000 0xfd20000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap34xx.dtsi b/arch/arm/boot/dts/omap34xx.dtsi
new file mode 100644
index 000000000000..75ed4ae2e631
--- /dev/null
+++ b/arch/arm/boot/dts/omap34xx.dtsi
@@ -0,0 +1,28 @@
+/*
+ * Device Tree Source for OMAP34xx/OMAP35xx SoC
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/include/ "omap3.dtsi"
+
+/ {
+ cpus {
+ cpu@0 {
+ /* OMAP343x/OMAP35xx variants OPP1-5 */
+ operating-points = <
+ /* kHz uV */
+ 125000 975000
+ 250000 1075000
+ 500000 1200000
+ 550000 1270000
+ 600000 1350000
+ >;
+ clock-latency = <300000>; /* From legacy driver */
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap36xx.dtsi b/arch/arm/boot/dts/omap36xx.dtsi
index 96bf0287cb9f..b89233e43b0f 100644
--- a/arch/arm/boot/dts/omap36xx.dtsi
+++ b/arch/arm/boot/dts/omap36xx.dtsi
@@ -15,6 +15,19 @@
serial3 = &uart4;
};
+ cpus {
+ /* OMAP3630/OMAP37xx 'standard device' variants OPP50 to OPP130 */
+ cpu@0 {
+ operating-points = <
+ /* kHz uV */
+ 300000 975000
+ 600000 1075000
+ 800000 1200000
+ >;
+ clock-latency = <300000>; /* From legacy driver */
+ };
+ };
+
ocp {
uart4: serial@49042000 {
compatible = "ti,omap3-uart";
diff --git a/arch/arm/boot/dts/omap4-panda-a4.dts b/arch/arm/boot/dts/omap4-panda-a4.dts
index 75466d2abfb5..e30cdf0f5ac1 100644
--- a/arch/arm/boot/dts/omap4-panda-a4.dts
+++ b/arch/arm/boot/dts/omap4-panda-a4.dts
@@ -5,7 +5,10 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-/include/ "omap4-panda.dts"
+/dts-v1/;
+
+/include/ "omap443x.dtsi"
+/include/ "omap4-panda-common.dtsi"
/* Pandaboard Rev A4+ have external pullups on SCL & SDA */
&dss_hdmi_pins {
diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi
new file mode 100644
index 000000000000..03bd60deb52b
--- /dev/null
+++ b/arch/arm/boot/dts/omap4-panda-common.dtsi
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2011-2013 Texas Instruments Incorporated - http://www.ti.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/ "elpida_ecb240abacn.dtsi"
+
+/ {
+ model = "TI OMAP4 PandaBoard";
+ compatible = "ti,omap4-panda", "ti,omap4430", "ti,omap4";
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x40000000>; /* 1 GB */
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ heartbeat {
+ label = "pandaboard::status1";
+ gpios = <&gpio1 7 0>;
+ linux,default-trigger = "heartbeat";
+ };
+
+ mmc {
+ label = "pandaboard::status2";
+ gpios = <&gpio1 8 0>;
+ linux,default-trigger = "mmc0";
+ };
+ };
+
+ sound: sound {
+ compatible = "ti,abe-twl6040";
+ ti,model = "PandaBoard";
+
+ ti,mclk-freq = <38400000>;
+
+ ti,mcpdm = <&mcpdm>;
+
+ ti,twl6040 = <&twl6040>;
+
+ /* Audio routing */
+ ti,audio-routing =
+ "Headset Stereophone", "HSOL",
+ "Headset Stereophone", "HSOR",
+ "Ext Spk", "HFL",
+ "Ext Spk", "HFR",
+ "Line Out", "AUXL",
+ "Line Out", "AUXR",
+ "HSMIC", "Headset Mic",
+ "Headset Mic", "Headset Mic Bias",
+ "AFML", "Line In",
+ "AFMR", "Line In";
+ };
+};
+
+&omap4_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &twl6040_pins
+ &mcpdm_pins
+ &mcbsp1_pins
+ &dss_hdmi_pins
+ &tpd12s015_pins
+ >;
+
+ twl6040_pins: pinmux_twl6040_pins {
+ pinctrl-single,pins = <
+ 0xe0 0x3 /* hdq_sio.gpio_127 OUTPUT | MODE3 */
+ 0x160 0x100 /* sys_nirq2.sys_nirq2 INPUT | MODE0 */
+ >;
+ };
+
+ mcpdm_pins: pinmux_mcpdm_pins {
+ pinctrl-single,pins = <
+ 0xc6 0x108 /* abe_pdm_ul_data.abe_pdm_ul_data INPUT PULLDOWN | MODE0 */
+ 0xc8 0x108 /* abe_pdm_dl_data.abe_pdm_dl_data INPUT PULLDOWN | MODE0 */
+ 0xca 0x118 /* abe_pdm_frame.abe_pdm_frame INPUT PULLUP | MODE0 */
+ 0xcc 0x108 /* abe_pdm_lb_clk.abe_pdm_lb_clk INPUT PULLDOWN | MODE0 */
+ 0xce 0x108 /* abe_clks.abe_clks INPUT PULLDOWN | MODE0 */
+ >;
+ };
+
+ mcbsp1_pins: pinmux_mcbsp1_pins {
+ pinctrl-single,pins = <
+ 0xbe 0x100 /* abe_mcbsp1_clkx.abe_mcbsp1_clkx INPUT | MODE0 */
+ 0xc0 0x108 /* abe_mcbsp1_dr.abe_mcbsp1_dr INPUT PULLDOWN | MODE0 */
+ 0xc2 0x8 /* abe_mcbsp1_dx.abe_mcbsp1_dx OUTPUT PULLDOWN | MODE0 */
+ 0xc4 0x100 /* abe_mcbsp1_fsx.abe_mcbsp1_fsx INPUT | MODE0 */
+ >;
+ };
+
+ dss_hdmi_pins: pinmux_dss_hdmi_pins {
+ pinctrl-single,pins = <
+ 0x5a 0x118 /* hdmi_cec.hdmi_cec INPUT PULLUP | MODE 0 */
+ 0x5c 0x118 /* hdmi_scl.hdmi_scl INPUT PULLUP | MODE 0 */
+ 0x5e 0x118 /* hdmi_sda.hdmi_sda INPUT PULLUP | MODE 0 */
+ >;
+ };
+
+ tpd12s015_pins: pinmux_tpd12s015_pins {
+ pinctrl-single,pins = <
+ 0x22 0x3 /* gpmc_a17.gpio_41 OUTPUT | MODE3 */
+ 0x48 0x3 /* gpmc_nbe1.gpio_60 OUTPUT | MODE3 */
+ 0x58 0x10b /* hdmi_hpd.gpio_63 INPUT PULLDOWN | MODE3 */
+ >;
+ };
+
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ 0xe2 0x118 /* i2c1_scl PULLUP | INPUTENABLE | MODE0 */
+ 0xe4 0x118 /* i2c1_sda PULLUP | INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ 0xe6 0x118 /* i2c2_scl PULLUP | INPUTENABLE | MODE0 */
+ 0xe8 0x118 /* i2c2_sda PULLUP | INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c3_pins: pinmux_i2c3_pins {
+ pinctrl-single,pins = <
+ 0xea 0x118 /* i2c3_scl PULLUP | INPUTENABLE | MODE0 */
+ 0xec 0x118 /* i2c3_sda PULLUP | INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c4_pins: pinmux_i2c4_pins {
+ pinctrl-single,pins = <
+ 0xee 0x118 /* i2c4_scl PULLUP | INPUTENABLE | MODE0 */
+ 0xf0 0x118 /* i2c4_sda PULLUP | INPUTENABLE | MODE0 */
+ >;
+ };
+};
+
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+
+ clock-frequency = <400000>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ /* SPI = 0, IRQ# = 7, 4 = active high level-sensitive */
+ interrupts = <0 7 4>; /* IRQ_SYS_1N cascaded to gic */
+ interrupt-parent = <&gic>;
+ };
+
+ twl6040: twl@4b {
+ compatible = "ti,twl6040";
+ reg = <0x4b>;
+ /* SPI = 0, IRQ# = 119, 4 = active high level-sensitive */
+ interrupts = <0 119 4>; /* IRQ_SYS_2N cascaded to gic */
+ interrupt-parent = <&gic>;
+ ti,audpwron-gpio = <&gpio4 31 0>; /* gpio line 127 */
+
+ vio-supply = <&v1v8>;
+ v2v1-supply = <&v2v1>;
+ enable-active-high;
+ };
+};
+
+/include/ "twl6030.dtsi"
+
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+
+ clock-frequency = <400000>;
+};
+
+&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+
+ clock-frequency = <100000>;
+
+ /*
+ * Display monitor features are burnt in their EEPROM as EDID data.
+ * The EEPROM is connected as I2C slave device.
+ */
+ eeprom@50 {
+ compatible = "ti,eeprom";
+ reg = <0x50>;
+ };
+};
+
+&i2c4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c4_pins>;
+
+ clock-frequency = <400000>;
+};
+
+&mmc1 {
+ vmmc-supply = <&vmmc>;
+ bus-width = <8>;
+};
+
+&mmc2 {
+ status = "disabled";
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+&mmc4 {
+ status = "disabled";
+};
+
+&mmc5 {
+ ti,non-removable;
+ bus-width = <4>;
+};
+
+&emif1 {
+ cs1-used;
+ device-handle = <&elpida_ECB240ABACN>;
+};
+
+&emif2 {
+ cs1-used;
+ device-handle = <&elpida_ECB240ABACN>;
+};
+
+&mcbsp2 {
+ status = "disabled";
+};
+
+&mcbsp3 {
+ status = "disabled";
+};
+
+&dmic {
+ status = "disabled";
+};
+
+&twl_usb_comparator {
+ usb-supply = <&vusb>;
+};
+
+&usb_otg_hs {
+ interface-type = <1>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 73bc1a67e444..f1d8c217ce12 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -5,7 +5,10 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-/include/ "omap4-panda.dts"
+/dts-v1/;
+
+/include/ "omap4460.dtsi"
+/include/ "omap4-panda-common.dtsi"
/* Audio routing is differnet between PandaBoard4430 and PandaBoardES */
&sound {
diff --git a/arch/arm/boot/dts/omap4-panda.dts b/arch/arm/boot/dts/omap4-panda.dts
index 4122efe31cfd..f8b221f0168e 100644
--- a/arch/arm/boot/dts/omap4-panda.dts
+++ b/arch/arm/boot/dts/omap4-panda.dts
@@ -7,202 +7,5 @@
*/
/dts-v1/;
-/include/ "omap4.dtsi"
-/include/ "elpida_ecb240abacn.dtsi"
-
-/ {
- model = "TI OMAP4 PandaBoard";
- compatible = "ti,omap4-panda", "ti,omap4430", "ti,omap4";
-
- memory {
- device_type = "memory";
- reg = <0x80000000 0x40000000>; /* 1 GB */
- };
-
- leds {
- compatible = "gpio-leds";
- heartbeat {
- label = "pandaboard::status1";
- gpios = <&gpio1 7 0>;
- linux,default-trigger = "heartbeat";
- };
-
- mmc {
- label = "pandaboard::status2";
- gpios = <&gpio1 8 0>;
- linux,default-trigger = "mmc0";
- };
- };
-
- sound: sound {
- compatible = "ti,abe-twl6040";
- ti,model = "PandaBoard";
-
- ti,mclk-freq = <38400000>;
-
- ti,mcpdm = <&mcpdm>;
-
- ti,twl6040 = <&twl6040>;
-
- /* Audio routing */
- ti,audio-routing =
- "Headset Stereophone", "HSOL",
- "Headset Stereophone", "HSOR",
- "Ext Spk", "HFL",
- "Ext Spk", "HFR",
- "Line Out", "AUXL",
- "Line Out", "AUXR",
- "HSMIC", "Headset Mic",
- "Headset Mic", "Headset Mic Bias",
- "AFML", "Line In",
- "AFMR", "Line In";
- };
-};
-
-&omap4_pmx_core {
- pinctrl-names = "default";
- pinctrl-0 = <
- &twl6040_pins
- &mcpdm_pins
- &mcbsp1_pins
- &dss_hdmi_pins
- &tpd12s015_pins
- >;
-
- twl6040_pins: pinmux_twl6040_pins {
- pinctrl-single,pins = <
- 0xe0 0x3 /* hdq_sio.gpio_127 OUTPUT | MODE3 */
- 0x160 0x100 /* sys_nirq2.sys_nirq2 INPUT | MODE0 */
- >;
- };
-
- mcpdm_pins: pinmux_mcpdm_pins {
- pinctrl-single,pins = <
- 0xc6 0x108 /* abe_pdm_ul_data.abe_pdm_ul_data INPUT PULLDOWN | MODE0 */
- 0xc8 0x108 /* abe_pdm_dl_data.abe_pdm_dl_data INPUT PULLDOWN | MODE0 */
- 0xca 0x118 /* abe_pdm_frame.abe_pdm_frame INPUT PULLUP | MODE0 */
- 0xcc 0x108 /* abe_pdm_lb_clk.abe_pdm_lb_clk INPUT PULLDOWN | MODE0 */
- 0xce 0x108 /* abe_clks.abe_clks INPUT PULLDOWN | MODE0 */
- >;
- };
-
- mcbsp1_pins: pinmux_mcbsp1_pins {
- pinctrl-single,pins = <
- 0xbe 0x100 /* abe_mcbsp1_clkx.abe_mcbsp1_clkx INPUT | MODE0 */
- 0xc0 0x108 /* abe_mcbsp1_dr.abe_mcbsp1_dr INPUT PULLDOWN | MODE0 */
- 0xc2 0x8 /* abe_mcbsp1_dx.abe_mcbsp1_dx OUTPUT PULLDOWN | MODE0 */
- 0xc4 0x100 /* abe_mcbsp1_fsx.abe_mcbsp1_fsx INPUT | MODE0 */
- >;
- };
-
- dss_hdmi_pins: pinmux_dss_hdmi_pins {
- pinctrl-single,pins = <
- 0x5a 0x118 /* hdmi_cec.hdmi_cec INPUT PULLUP | MODE 0 */
- 0x5c 0x118 /* hdmi_scl.hdmi_scl INPUT PULLUP | MODE 0 */
- 0x5e 0x118 /* hdmi_sda.hdmi_sda INPUT PULLUP | MODE 0 */
- >;
- };
-
- tpd12s015_pins: pinmux_tpd12s015_pins {
- pinctrl-single,pins = <
- 0x22 0x3 /* gpmc_a17.gpio_41 OUTPUT | MODE3 */
- 0x48 0x3 /* gpmc_nbe1.gpio_60 OUTPUT | MODE3 */
- 0x58 0x10b /* hdmi_hpd.gpio_63 INPUT PULLDOWN | MODE3 */
- >;
- };
-};
-
-&i2c1 {
- clock-frequency = <400000>;
-
- twl: twl@48 {
- reg = <0x48>;
- /* SPI = 0, IRQ# = 7, 4 = active high level-sensitive */
- interrupts = <0 7 4>; /* IRQ_SYS_1N cascaded to gic */
- interrupt-parent = <&gic>;
- };
-
- twl6040: twl@4b {
- compatible = "ti,twl6040";
- reg = <0x4b>;
- /* SPI = 0, IRQ# = 119, 4 = active high level-sensitive */
- interrupts = <0 119 4>; /* IRQ_SYS_2N cascaded to gic */
- interrupt-parent = <&gic>;
- ti,audpwron-gpio = <&gpio4 31 0>; /* gpio line 127 */
-
- vio-supply = <&v1v8>;
- v2v1-supply = <&v2v1>;
- enable-active-high;
- };
-};
-
-/include/ "twl6030.dtsi"
-
-&i2c2 {
- clock-frequency = <400000>;
-};
-
-&i2c3 {
- clock-frequency = <100000>;
-
- /*
- * Display monitor features are burnt in their EEPROM as EDID data.
- * The EEPROM is connected as I2C slave device.
- */
- eeprom@50 {
- compatible = "ti,eeprom";
- reg = <0x50>;
- };
-};
-
-&i2c4 {
- clock-frequency = <400000>;
-};
-
-&mmc1 {
- vmmc-supply = <&vmmc>;
- bus-width = <8>;
-};
-
-&mmc2 {
- status = "disabled";
-};
-
-&mmc3 {
- status = "disabled";
-};
-
-&mmc4 {
- status = "disabled";
-};
-
-&mmc5 {
- ti,non-removable;
- bus-width = <4>;
-};
-
-&emif1 {
- cs1-used;
- device-handle = <&elpida_ECB240ABACN>;
-};
-
-&emif2 {
- cs1-used;
- device-handle = <&elpida_ECB240ABACN>;
-};
-
-&mcbsp2 {
- status = "disabled";
-};
-
-&mcbsp3 {
- status = "disabled";
-};
-
-&dmic {
- status = "disabled";
-};
-
-&twl_usb_comparator {
- usb-supply = <&vusb>;
-};
+/include/ "omap443x.dtsi"
+/include/ "omap4-panda-common.dtsi"
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index 43e5258a9372..c387bdc1b1d1 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -7,7 +7,7 @@
*/
/dts-v1/;
-/include/ "omap4.dtsi"
+/include/ "omap443x.dtsi"
/include/ "elpida_ecb240abacn.dtsi"
/ {
@@ -80,6 +80,32 @@
};
};
+ pwmleds {
+ compatible = "pwm-leds";
+ kpad {
+ label = "omap4::keypad";
+ pwms = <&twl_pwm 0 7812500>;
+ max-brightness = <127>;
+ };
+
+ charging {
+ label = "omap4:green:chrg";
+ pwms = <&twl_pwmled 0 7812500>;
+ max-brightness = <255>;
+ };
+ };
+
+ backlight {
+ compatible = "pwm-backlight";
+ pwms = <&twl_pwm 1 7812500>;
+ brightness-levels = <
+ 0 10 20 30 40
+ 50 60 70 80 90
+ 100 110 120 127
+ >;
+ default-brightness-level = <13>;
+ };
+
sound {
compatible = "ti,abe-twl6040";
ti,model = "SDP4430";
@@ -212,9 +238,40 @@
0x58 0x10b /* hdmi_hpd.gpio_63 INPUT PULLDOWN | MODE3 */
>;
};
+
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ 0xe2 0x118 /* i2c1_scl PULLUP | INPUTENABLE | MODE0 */
+ 0xe4 0x118 /* i2c1_sda PULLUP | INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ 0xe6 0x118 /* i2c2_scl PULLUP | INPUTENABLE | MODE0 */
+ 0xe8 0x118 /* i2c2_sda PULLUP | INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c3_pins: pinmux_i2c3_pins {
+ pinctrl-single,pins = <
+ 0xea 0x118 /* i2c3_scl PULLUP | INPUTENABLE | MODE0 */
+ 0xec 0x118 /* i2c3_sda PULLUP | INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c4_pins: pinmux_i2c4_pins {
+ pinctrl-single,pins = <
+ 0xee 0x118 /* i2c4_scl PULLUP | INPUTENABLE | MODE0 */
+ 0xf0 0x118 /* i2c4_sda PULLUP | INPUTENABLE | MODE0 */
+ >;
+ };
};
&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+
clock-frequency = <400000>;
twl: twl@48 {
@@ -253,10 +310,16 @@
/include/ "twl6030.dtsi"
&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+
clock-frequency = <400000>;
};
&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+
clock-frequency = <400000>;
/*
@@ -279,6 +342,9 @@
};
&i2c4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c4_pins>;
+
clock-frequency = <400000>;
/*
@@ -428,3 +494,9 @@
&twl_usb_comparator {
usb-supply = <&vusb>;
};
+
+&usb_otg_hs {
+ interface-type = <1>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap4-var-som.dts b/arch/arm/boot/dts/omap4-var-som.dts
index 6601e6af6092..222a413c2c51 100644
--- a/arch/arm/boot/dts/omap4-var-som.dts
+++ b/arch/arm/boot/dts/omap4-var-som.dts
@@ -7,7 +7,7 @@
*/
/dts-v1/;
-/include/ "omap4.dtsi"
+/include/ "omap443x.dtsi"
/ {
model = "Variscite OMAP4 SOM";
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index b7db1a2b6ca7..2a5642882c8a 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -94,6 +94,11 @@
#size-cells = <1>;
ranges;
ti,hwmods = "l3_main_1", "l3_main_2", "l3_main_3";
+ reg = <0x44000000 0x1000>,
+ <0x44800000 0x2000>,
+ <0x45000000 0x1000>;
+ interrupts = <0 9 0x4>,
+ <0 10 0x4>;
counter32k: counter@4a304000 {
compatible = "ti,omap-counter32k";
@@ -118,15 +123,28 @@
pinctrl-single,function-mask = <0x7fff>;
};
+ sdma: dma-controller@4a056000 {
+ compatible = "ti,omap4430-sdma";
+ reg = <0x4a056000 0x1000>;
+ interrupts = <0 12 0x4>,
+ <0 13 0x4>,
+ <0 14 0x4>,
+ <0 15 0x4>;
+ #dma-cells = <1>;
+ #dma-channels = <32>;
+ #dma-requests = <127>;
+ };
+
gpio1: gpio@4a310000 {
compatible = "ti,omap4-gpio";
reg = <0x4a310000 0x200>;
interrupts = <0 29 0x4>;
ti,hwmods = "gpio1";
+ ti,gpio-always-on;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio2: gpio@48055000 {
@@ -137,7 +155,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio3: gpio@48057000 {
@@ -148,7 +166,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio4: gpio@48059000 {
@@ -159,7 +177,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio5: gpio@4805b000 {
@@ -170,7 +188,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio6: gpio@4805d000 {
@@ -181,7 +199,18 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
+ };
+
+ gpmc: gpmc@50000000 {
+ compatible = "ti,omap4430-gpmc";
+ reg = <0x50000000 0x1000>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ interrupts = <0 20 0x4>;
+ gpmc,num-cs = <8>;
+ gpmc,num-waitpins = <4>;
+ ti,hwmods = "gpmc";
};
uart1: serial@4806a000 {
@@ -260,6 +289,16 @@
#size-cells = <0>;
ti,hwmods = "mcspi1";
ti,spi-num-cs = <4>;
+ dmas = <&sdma 35>,
+ <&sdma 36>,
+ <&sdma 37>,
+ <&sdma 38>,
+ <&sdma 39>,
+ <&sdma 40>,
+ <&sdma 41>,
+ <&sdma 42>;
+ dma-names = "tx0", "rx0", "tx1", "rx1",
+ "tx2", "rx2", "tx3", "rx3";
};
mcspi2: spi@4809a000 {
@@ -270,6 +309,11 @@
#size-cells = <0>;
ti,hwmods = "mcspi2";
ti,spi-num-cs = <2>;
+ dmas = <&sdma 43>,
+ <&sdma 44>,
+ <&sdma 45>,
+ <&sdma 46>;
+ dma-names = "tx0", "rx0", "tx1", "rx1";
};
mcspi3: spi@480b8000 {
@@ -280,6 +324,8 @@
#size-cells = <0>;
ti,hwmods = "mcspi3";
ti,spi-num-cs = <2>;
+ dmas = <&sdma 15>, <&sdma 16>;
+ dma-names = "tx0", "rx0";
};
mcspi4: spi@480ba000 {
@@ -290,6 +336,8 @@
#size-cells = <0>;
ti,hwmods = "mcspi4";
ti,spi-num-cs = <1>;
+ dmas = <&sdma 70>, <&sdma 71>;
+ dma-names = "tx0", "rx0";
};
mmc1: mmc@4809c000 {
@@ -299,6 +347,8 @@
ti,hwmods = "mmc1";
ti,dual-volt;
ti,needs-special-reset;
+ dmas = <&sdma 61>, <&sdma 62>;
+ dma-names = "tx", "rx";
};
mmc2: mmc@480b4000 {
@@ -307,6 +357,8 @@
interrupts = <0 86 0x4>;
ti,hwmods = "mmc2";
ti,needs-special-reset;
+ dmas = <&sdma 47>, <&sdma 48>;
+ dma-names = "tx", "rx";
};
mmc3: mmc@480ad000 {
@@ -315,6 +367,8 @@
interrupts = <0 94 0x4>;
ti,hwmods = "mmc3";
ti,needs-special-reset;
+ dmas = <&sdma 77>, <&sdma 78>;
+ dma-names = "tx", "rx";
};
mmc4: mmc@480d1000 {
@@ -323,6 +377,8 @@
interrupts = <0 96 0x4>;
ti,hwmods = "mmc4";
ti,needs-special-reset;
+ dmas = <&sdma 57>, <&sdma 58>;
+ dma-names = "tx", "rx";
};
mmc5: mmc@480d5000 {
@@ -331,6 +387,8 @@
interrupts = <0 59 0x4>;
ti,hwmods = "mmc5";
ti,needs-special-reset;
+ dmas = <&sdma 59>, <&sdma 60>;
+ dma-names = "tx", "rx";
};
wdt2: wdt@4a314000 {
@@ -347,6 +405,9 @@
reg-names = "mpu", "dma";
interrupts = <0 112 0x4>;
ti,hwmods = "mcpdm";
+ dmas = <&sdma 65>,
+ <&sdma 66>;
+ dma-names = "up_link", "dn_link";
};
dmic: dmic@4012e000 {
@@ -356,6 +417,8 @@
reg-names = "mpu", "dma";
interrupts = <0 114 0x4>;
ti,hwmods = "dmic";
+ dmas = <&sdma 67>;
+ dma-names = "up_link";
};
mcbsp1: mcbsp@40122000 {
@@ -367,6 +430,9 @@
interrupt-names = "common";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp1";
+ dmas = <&sdma 33>,
+ <&sdma 34>;
+ dma-names = "tx", "rx";
};
mcbsp2: mcbsp@40124000 {
@@ -378,6 +444,9 @@
interrupt-names = "common";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp2";
+ dmas = <&sdma 17>,
+ <&sdma 18>;
+ dma-names = "tx", "rx";
};
mcbsp3: mcbsp@40126000 {
@@ -389,6 +458,9 @@
interrupt-names = "common";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp3";
+ dmas = <&sdma 19>,
+ <&sdma 20>;
+ dma-names = "tx", "rx";
};
mcbsp4: mcbsp@48096000 {
@@ -399,6 +471,9 @@
interrupt-names = "common";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp4";
+ dmas = <&sdma 31>,
+ <&sdma 32>;
+ dma-names = "tx", "rx";
};
keypad: keypad@4a31c000 {
@@ -438,10 +513,15 @@
#size-cells = <1>;
ranges;
ti,hwmods = "ocp2scp_usb_phy";
+ usb2_phy: usb2phy@4a0ad080 {
+ compatible = "ti,omap-usb2";
+ reg = <0x4a0ad080 0x58>;
+ ctrl-module = <&omap_control_usb>;
+ };
};
timer1: timer@4a318000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x4a318000 0x80>;
interrupts = <0 37 0x4>;
ti,hwmods = "timer1";
@@ -449,28 +529,28 @@
};
timer2: timer@48032000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x48032000 0x80>;
interrupts = <0 38 0x4>;
ti,hwmods = "timer2";
};
timer3: timer@48034000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap4430-timer";
reg = <0x48034000 0x80>;
interrupts = <0 39 0x4>;
ti,hwmods = "timer3";
};
timer4: timer@48036000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap4430-timer";
reg = <0x48036000 0x80>;
interrupts = <0 40 0x4>;
ti,hwmods = "timer4";
};
timer5: timer@40138000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap4430-timer";
reg = <0x40138000 0x80>,
<0x49038000 0x80>;
interrupts = <0 41 0x4>;
@@ -479,7 +559,7 @@
};
timer6: timer@4013a000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap4430-timer";
reg = <0x4013a000 0x80>,
<0x4903a000 0x80>;
interrupts = <0 42 0x4>;
@@ -488,7 +568,7 @@
};
timer7: timer@4013c000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap4430-timer";
reg = <0x4013c000 0x80>,
<0x4903c000 0x80>;
interrupts = <0 43 0x4>;
@@ -497,7 +577,7 @@
};
timer8: timer@4013e000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap4430-timer";
reg = <0x4013e000 0x80>,
<0x4903e000 0x80>;
interrupts = <0 44 0x4>;
@@ -507,7 +587,7 @@
};
timer9: timer@4803e000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap4430-timer";
reg = <0x4803e000 0x80>;
interrupts = <0 45 0x4>;
ti,hwmods = "timer9";
@@ -515,7 +595,7 @@
};
timer10: timer@48086000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap3430-timer";
reg = <0x48086000 0x80>;
interrupts = <0 46 0x4>;
ti,hwmods = "timer10";
@@ -523,7 +603,7 @@
};
timer11: timer@48088000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap4430-timer";
reg = <0x48088000 0x80>;
interrupts = <0 47 0x4>;
ti,hwmods = "timer11";
@@ -559,5 +639,26 @@
interrupts = <0 77 0x4>;
};
};
+
+ omap_control_usb: omap-control-usb@4a002300 {
+ compatible = "ti,omap-control-usb";
+ reg = <0x4a002300 0x4>,
+ <0x4a00233c 0x4>;
+ reg-names = "control_dev_conf", "otghs_control";
+ ti,type = <1>;
+ };
+
+ usb_otg_hs: usb_otg_hs@4a0ab000 {
+ compatible = "ti,omap4-musb";
+ reg = <0x4a0ab000 0x7ff>;
+ interrupts = <0 92 0x4>, <0 93 0x4>;
+ interrupt-names = "mc", "dma";
+ ti,hwmods = "usb_otg_hs";
+ usb-phy = <&usb2_phy>;
+ multipoint = <1>;
+ num-eps = <16>;
+ ram-bits = <12>;
+ ti,has-mailbox;
+ };
};
};
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
new file mode 100644
index 000000000000..cccf39af4925
--- /dev/null
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -0,0 +1,27 @@
+/*
+ * Device Tree Source for OMAP443x SoC
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/include/ "omap4.dtsi"
+
+/ {
+ cpus {
+ cpu@0 {
+ /* OMAP443x variants OPP50-OPPNT */
+ operating-points = <
+ /* kHz uV */
+ 300000 1025000
+ 600000 1200000
+ 800000 1313000
+ 1008000 1375000
+ >;
+ clock-latency = <300000>; /* From legacy driver */
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
new file mode 100644
index 000000000000..7c2c23cc17ef
--- /dev/null
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -0,0 +1,32 @@
+/*
+ * Device Tree Source for OMAP4460 SoC
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+/include/ "omap4.dtsi"
+
+/ {
+ cpus {
+ /* OMAP446x 'standard device' variants OPP50 to OPPTurbo */
+ cpu@0 {
+ operating-points = <
+ /* kHz uV */
+ 350000 975000
+ 700000 1075000
+ 920000 1200000
+ >;
+ clock-latency = <300000>; /* From legacy driver */
+ };
+ };
+
+ pmu {
+ compatible = "arm,cortex-a9-pmu";
+ interrupts = <0 54 0x4>,
+ <0 55 0x4>;
+ ti,hwmods = "debugss";
+ };
+};
diff --git a/arch/arm/boot/dts/omap5-evm.dts b/arch/arm/boot/dts/omap5-evm.dts
index 8722c15bbba2..982acd19477d 100644
--- a/arch/arm/boot/dts/omap5-evm.dts
+++ b/arch/arm/boot/dts/omap5-evm.dts
@@ -16,7 +16,7 @@
memory {
device_type = "memory";
- reg = <0x80000000 0x80000000>; /* 2 GB */
+ reg = <0x80000000 0x7F000000>; /* 2032 MB */
};
vmmcsd_fixed: fixedregulator-mmcsd {
@@ -80,6 +80,68 @@
0x15a 0x100 /* abemcbsp2_clkx.abemcbsp2_clkx INPUT | MODE0 */
>;
};
+
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ 0x1b2 0x118 /* i2c1_scl PULLUP | INPUTENABLE | MODE0 */
+ 0x1b4 0x118 /* i2c1_sda PULLUP | INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ 0x178 0x100 /* i2c2_scl INPUTENABLE | MODE0 */
+ 0x17a 0x100 /* i2c2_sda INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c3_pins: pinmux_i2c3_pins {
+ pinctrl-single,pins = <
+ 0x13a 0x100 /* i2c3_scl INPUTENABLE | MODE0 */
+ 0x13c 0x100 /* i2c3_sda INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c4_pins: pinmux_i2c4_pins {
+ pinctrl-single,pins = <
+ 0xb8 0x100 /* i2c4_scl INPUTENABLE | MODE0 */
+ 0xba 0x100 /* i2c4_sda INPUTENABLE | MODE0 */
+ >;
+ };
+
+ i2c5_pins: pinmux_i2c5_pins {
+ pinctrl-single,pins = <
+ 0x184 0x100 /* i2c5_scl INPUTENABLE | MODE0 */
+ 0x186 0x100 /* i2c5_sda INPUTENABLE | MODE0 */
+ >;
+ };
+
+ mcspi2_pins: pinmux_mcspi2_pins {
+ pinctrl-single,pins = <
+ 0xbc 0x100 /* MCSPI2_CLK INPUTENABLE | MODE0 */
+ 0xbe 0x100 /* MCSPI2_SIMO INPUTENABLE | MODE0 */
+ 0xc0 0x118 /* MCSPI2_SOMI PULLUP | INPUTENABLE | MODE0*/
+ 0xc2 0x0 /* MCSPI2_CS MODE0*/
+ >;
+ };
+
+ mcspi3_pins: pinmux_mcspi3_pins {
+ pinctrl-single,pins = <
+ 0x78 0x101 /* MCSPI2_SOMI INPUTENABLE | MODE1 */
+ 0x7a 0x101 /* MCSPI2_CS INPUTENABLE | MODE1 */
+ 0x7c 0x101 /* MCSPI2_SIMO INPUTENABLE | MODE1 */
+ 0x7e 0x101 /* MCSPI2_CLK INPUTENABLE | MODE1 */
+ >;
+ };
+
+ mcspi4_pins: pinmux_mcspi4_pins {
+ pinctrl-single,pins = <
+ 0x164 0x101 /* MCSPI2_CLK INPUTENABLE | MODE1 */
+ 0x168 0x101 /* MCSPI2_SIMO INPUTENABLE | MODE1 */
+ 0x16a 0x101 /* MCSPI2_SOMI INPUTENABLE | MODE1 */
+ 0x16c 0x101 /* MCSPI2_CS INPUTENABLE | MODE1 */
+ >;
+ };
};
&mmc1 {
@@ -106,7 +168,17 @@
status = "disabled";
};
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+
+ clock-frequency = <400000>;
+};
+
&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+
clock-frequency = <400000>;
/* Pressure Sensor */
@@ -116,7 +188,17 @@
};
};
+&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+
+ clock-frequency = <400000>;
+};
+
&i2c4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c4_pins>;
+
clock-frequency = <400000>;
/* Temperature Sensor */
@@ -126,6 +208,13 @@
};
};
+&i2c5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c5_pins>;
+
+ clock-frequency = <400000>;
+};
+
&keypad {
keypad,num-rows = <8>;
keypad,num-columns = <8>;
@@ -151,3 +240,22 @@
cs1-used;
device-handle = <&samsung_K3PE0E000B>;
};
+
+&mcspi1 {
+
+};
+
+&mcspi2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcspi2_pins>;
+};
+
+&mcspi3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcspi3_pins>;
+};
+
+&mcspi4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcspi4_pins>;
+};
diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi
index 790bb2a4b343..3dd7ff825828 100644
--- a/arch/arm/boot/dts/omap5.dtsi
+++ b/arch/arm/boot/dts/omap5.dtsi
@@ -18,6 +18,9 @@
/include/ "skeleton.dtsi"
/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
compatible = "ti,omap5";
interrupt-parent = <&gic>;
@@ -33,24 +36,32 @@
cpus {
cpu@0 {
compatible = "arm,cortex-a15";
- timer {
- compatible = "arm,armv7-timer";
- /* 14th PPI IRQ, active low level-sensitive */
- interrupts = <1 14 0x308>;
- clock-frequency = <6144000>;
- };
};
cpu@1 {
compatible = "arm,cortex-a15";
- timer {
- compatible = "arm,armv7-timer";
- /* 14th PPI IRQ, active low level-sensitive */
- interrupts = <1 14 0x308>;
- clock-frequency = <6144000>;
- };
};
};
+ timer {
+ compatible = "arm,armv7-timer";
+ /* PPI secure/nonsecure IRQ, active low level-sensitive */
+ interrupts = <1 13 0x308>,
+ <1 14 0x308>,
+ <1 11 0x308>,
+ <1 10 0x308>;
+ clock-frequency = <6144000>;
+ };
+
+ gic: interrupt-controller@48211000 {
+ compatible = "arm,cortex-a15-gic";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0x48211000 0x1000>,
+ <0x48212000 0x1000>,
+ <0x48214000 0x2000>,
+ <0x48216000 0x2000>;
+ };
+
/*
* The soc node represents the soc top level view. It is uses for IPs
* that are not memory mapped in the MPU view or for the MPU itself.
@@ -76,6 +87,11 @@
#size-cells = <1>;
ranges;
ti,hwmods = "l3_main_1", "l3_main_2", "l3_main_3";
+ reg = <0x44000000 0x2000>,
+ <0x44800000 0x3000>,
+ <0x45000000 0x4000>;
+ interrupts = <0 9 0x4>,
+ <0 10 0x4>;
counter32k: counter@4ae04000 {
compatible = "ti,omap-counter32k";
@@ -100,12 +116,16 @@
pinctrl-single,function-mask = <0x7fff>;
};
- gic: interrupt-controller@48211000 {
- compatible = "arm,cortex-a15-gic";
- interrupt-controller;
- #interrupt-cells = <3>;
- reg = <0x48211000 0x1000>,
- <0x48212000 0x1000>;
+ sdma: dma-controller@4a056000 {
+ compatible = "ti,omap4430-sdma";
+ reg = <0x4a056000 0x1000>;
+ interrupts = <0 12 0x4>,
+ <0 13 0x4>,
+ <0 14 0x4>,
+ <0 15 0x4>;
+ #dma-cells = <1>;
+ #dma-channels = <32>;
+ #dma-requests = <127>;
};
gpio1: gpio@4ae10000 {
@@ -113,10 +133,11 @@
reg = <0x4ae10000 0x200>;
interrupts = <0 29 0x4>;
ti,hwmods = "gpio1";
+ ti,gpio-always-on;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio2: gpio@48055000 {
@@ -127,7 +148,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio3: gpio@48057000 {
@@ -138,7 +159,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio4: gpio@48059000 {
@@ -149,7 +170,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio5: gpio@4805b000 {
@@ -160,7 +181,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio6: gpio@4805d000 {
@@ -171,7 +192,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio7: gpio@48051000 {
@@ -182,7 +203,7 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio8: gpio@48053000 {
@@ -193,7 +214,18 @@
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
+ };
+
+ gpmc: gpmc@50000000 {
+ compatible = "ti,omap4430-gpmc";
+ reg = <0x50000000 0x1000>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ interrupts = <0 20 0x4>;
+ gpmc,num-cs = <8>;
+ gpmc,num-waitpins = <4>;
+ ti,hwmods = "gpmc";
};
i2c1: i2c@48070000 {
@@ -241,6 +273,65 @@
ti,hwmods = "i2c5";
};
+ mcspi1: spi@48098000 {
+ compatible = "ti,omap4-mcspi";
+ reg = <0x48098000 0x200>;
+ interrupts = <0 65 0x4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ti,hwmods = "mcspi1";
+ ti,spi-num-cs = <4>;
+ dmas = <&sdma 35>,
+ <&sdma 36>,
+ <&sdma 37>,
+ <&sdma 38>,
+ <&sdma 39>,
+ <&sdma 40>,
+ <&sdma 41>,
+ <&sdma 42>;
+ dma-names = "tx0", "rx0", "tx1", "rx1",
+ "tx2", "rx2", "tx3", "rx3";
+ };
+
+ mcspi2: spi@4809a000 {
+ compatible = "ti,omap4-mcspi";
+ reg = <0x4809a000 0x200>;
+ interrupts = <0 66 0x4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ti,hwmods = "mcspi2";
+ ti,spi-num-cs = <2>;
+ dmas = <&sdma 43>,
+ <&sdma 44>,
+ <&sdma 45>,
+ <&sdma 46>;
+ dma-names = "tx0", "rx0", "tx1", "rx1";
+ };
+
+ mcspi3: spi@480b8000 {
+ compatible = "ti,omap4-mcspi";
+ reg = <0x480b8000 0x200>;
+ interrupts = <0 91 0x4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ti,hwmods = "mcspi3";
+ ti,spi-num-cs = <2>;
+ dmas = <&sdma 15>, <&sdma 16>;
+ dma-names = "tx0", "rx0";
+ };
+
+ mcspi4: spi@480ba000 {
+ compatible = "ti,omap4-mcspi";
+ reg = <0x480ba000 0x200>;
+ interrupts = <0 48 0x4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ti,hwmods = "mcspi4";
+ ti,spi-num-cs = <1>;
+ dmas = <&sdma 70>, <&sdma 71>;
+ dma-names = "tx0", "rx0";
+ };
+
uart1: serial@4806a000 {
compatible = "ti,omap4-uart";
reg = <0x4806a000 0x100>;
@@ -296,6 +387,8 @@
ti,hwmods = "mmc1";
ti,dual-volt;
ti,needs-special-reset;
+ dmas = <&sdma 61>, <&sdma 62>;
+ dma-names = "tx", "rx";
};
mmc2: mmc@480b4000 {
@@ -304,6 +397,8 @@
interrupts = <0 86 0x4>;
ti,hwmods = "mmc2";
ti,needs-special-reset;
+ dmas = <&sdma 47>, <&sdma 48>;
+ dma-names = "tx", "rx";
};
mmc3: mmc@480ad000 {
@@ -312,6 +407,8 @@
interrupts = <0 94 0x4>;
ti,hwmods = "mmc3";
ti,needs-special-reset;
+ dmas = <&sdma 77>, <&sdma 78>;
+ dma-names = "tx", "rx";
};
mmc4: mmc@480d1000 {
@@ -320,6 +417,8 @@
interrupts = <0 96 0x4>;
ti,hwmods = "mmc4";
ti,needs-special-reset;
+ dmas = <&sdma 57>, <&sdma 58>;
+ dma-names = "tx", "rx";
};
mmc5: mmc@480d5000 {
@@ -328,10 +427,13 @@
interrupts = <0 59 0x4>;
ti,hwmods = "mmc5";
ti,needs-special-reset;
+ dmas = <&sdma 59>, <&sdma 60>;
+ dma-names = "tx", "rx";
};
keypad: keypad@4ae1c000 {
compatible = "ti,omap4-keypad";
+ reg = <0x4ae1c000 0x400>;
ti,hwmods = "kbd";
};
@@ -342,6 +444,9 @@
reg-names = "mpu", "dma";
interrupts = <0 112 0x4>;
ti,hwmods = "mcpdm";
+ dmas = <&sdma 65>,
+ <&sdma 66>;
+ dma-names = "up_link", "dn_link";
};
dmic: dmic@4012e000 {
@@ -351,6 +456,8 @@
reg-names = "mpu", "dma";
interrupts = <0 114 0x4>;
ti,hwmods = "dmic";
+ dmas = <&sdma 67>;
+ dma-names = "up_link";
};
mcbsp1: mcbsp@40122000 {
@@ -362,6 +469,9 @@
interrupt-names = "common";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp1";
+ dmas = <&sdma 33>,
+ <&sdma 34>;
+ dma-names = "tx", "rx";
};
mcbsp2: mcbsp@40124000 {
@@ -373,6 +483,9 @@
interrupt-names = "common";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp2";
+ dmas = <&sdma 17>,
+ <&sdma 18>;
+ dma-names = "tx", "rx";
};
mcbsp3: mcbsp@40126000 {
@@ -384,10 +497,13 @@
interrupt-names = "common";
ti,buffer-size = <128>;
ti,hwmods = "mcbsp3";
+ dmas = <&sdma 19>,
+ <&sdma 20>;
+ dma-names = "tx", "rx";
};
timer1: timer@4ae18000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x4ae18000 0x80>;
interrupts = <0 37 0x4>;
ti,hwmods = "timer1";
@@ -395,28 +511,28 @@
};
timer2: timer@48032000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x48032000 0x80>;
interrupts = <0 38 0x4>;
ti,hwmods = "timer2";
};
timer3: timer@48034000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x48034000 0x80>;
interrupts = <0 39 0x4>;
ti,hwmods = "timer3";
};
timer4: timer@48036000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x48036000 0x80>;
interrupts = <0 40 0x4>;
ti,hwmods = "timer4";
};
timer5: timer@40138000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x40138000 0x80>,
<0x49038000 0x80>;
interrupts = <0 41 0x4>;
@@ -425,7 +541,7 @@
};
timer6: timer@4013a000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x4013a000 0x80>,
<0x4903a000 0x80>;
interrupts = <0 42 0x4>;
@@ -435,7 +551,7 @@
};
timer7: timer@4013c000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x4013c000 0x80>,
<0x4903c000 0x80>;
interrupts = <0 43 0x4>;
@@ -444,7 +560,7 @@
};
timer8: timer@4013e000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x4013e000 0x80>,
<0x4903e000 0x80>;
interrupts = <0 44 0x4>;
@@ -454,27 +570,34 @@
};
timer9: timer@4803e000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x4803e000 0x80>;
interrupts = <0 45 0x4>;
ti,hwmods = "timer9";
};
timer10: timer@48086000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x48086000 0x80>;
interrupts = <0 46 0x4>;
ti,hwmods = "timer10";
};
timer11: timer@48088000 {
- compatible = "ti,omap2-timer";
+ compatible = "ti,omap5430-timer";
reg = <0x48088000 0x80>;
interrupts = <0 47 0x4>;
ti,hwmods = "timer11";
ti,timer-pwm;
};
+ wdt2: wdt@4ae14000 {
+ compatible = "ti,omap5-wdt", "ti,omap3-wdt";
+ reg = <0x4ae14000 0x80>;
+ interrupts = <0 80 0x4>;
+ ti,hwmods = "wd_timer2";
+ };
+
emif1: emif@0x4c000000 {
compatible = "ti,emif-4d5";
ti,hwmods = "emif1";
@@ -496,5 +619,53 @@
hw-caps-ll-interface;
hw-caps-temp-alert;
};
+
+ omap_control_usb: omap-control-usb@4a002300 {
+ compatible = "ti,omap-control-usb";
+ reg = <0x4a002300 0x4>,
+ <0x4a002370 0x4>;
+ reg-names = "control_dev_conf", "phy_power_usb";
+ ti,type = <2>;
+ };
+
+ omap_dwc3@4a020000 {
+ compatible = "ti,dwc3";
+ ti,hwmods = "usb_otg_ss";
+ reg = <0x4a020000 0x1000>;
+ interrupts = <0 93 4>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ utmi-mode = <2>;
+ ranges;
+ dwc3@4a030000 {
+ compatible = "synopsys,dwc3";
+ reg = <0x4a030000 0x1000>;
+ interrupts = <0 92 4>;
+ usb-phy = <&usb2_phy>, <&usb3_phy>;
+ tx-fifo-resize;
+ };
+ };
+
+ ocp2scp {
+ compatible = "ti,omap-ocp2scp";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ ti,hwmods = "ocp2scp1";
+ usb2_phy: usb2phy@4a084000 {
+ compatible = "ti,omap-usb2";
+ reg = <0x4a084000 0x7c>;
+ ctrl-module = <&omap_control_usb>;
+ };
+
+ usb3_phy: usb3phy@4a084400 {
+ compatible = "ti,omap-usb3";
+ reg = <0x4a084400 0x80>,
+ <0x4a084800 0x64>,
+ <0x4a084c00 0x40>;
+ reg-names = "phy_rx", "phy_tx", "pll_ctrl";
+ ctrl-module = <&omap_control_usb>;
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/r8a73a4-ape6evm.dts b/arch/arm/boot/dts/r8a73a4-ape6evm.dts
new file mode 100644
index 000000000000..f603c6946c29
--- /dev/null
+++ b/arch/arm/boot/dts/r8a73a4-ape6evm.dts
@@ -0,0 +1,52 @@
+/*
+ * Device Tree Source for the APE6EVM board
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/dts-v1/;
+/include/ "r8a73a4.dtsi"
+
+/ {
+ model = "APE6EVM";
+ compatible = "renesas,ape6evm", "renesas,r8a73a4";
+
+ chosen {
+ bootargs = "console=ttySC0,115200 ignore_loglevel root=/dev/nfs ip=dhcp";
+ };
+
+ memory@40000000 {
+ device_type = "memory";
+ reg = <0 0x40000000 0 0x40000000>;
+ };
+
+ ape6evm_fixed_3v3: fixedregulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ lbsc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ ethernet@8000000 {
+ compatible = "smsc,lan9118", "smsc,lan9115";
+ reg = <0x08000000 0x1000>;
+ interrupt-parent = <&irqc1>;
+ interrupts = <8 0x4>;
+ phy-mode = "mii";
+ reg-io-width = <4>;
+ smsc,irq-active-high;
+ smsc,irq-push-pull;
+ vdd33a-supply = <&ape6evm_fixed_3v3>;
+ vddvario-supply = <&ape6evm_fixed_3v3>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/r8a73a4.dtsi b/arch/arm/boot/dts/r8a73a4.dtsi
new file mode 100644
index 000000000000..fde2a337d1ff
--- /dev/null
+++ b/arch/arm/boot/dts/r8a73a4.dtsi
@@ -0,0 +1,94 @@
+/*
+ * Device Tree Source for the r8a73a4 SoC
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/ {
+ compatible = "renesas,r8a73a4";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ clock-frequency = <1500000000>;
+ };
+ };
+
+ gic: interrupt-controller@f1001000 {
+ compatible = "arm,cortex-a15-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0 0xf1001000 0 0x1000>,
+ <0 0xf1002000 0 0x1000>,
+ <0 0xf1004000 0 0x2000>,
+ <0 0xf1006000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+
+ gic-cpuif@4 {
+ compatible = "arm,gic-cpuif";
+ cpuif-id = <4>;
+ cpu = <&cpu0>;
+ };
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
+ };
+
+ irqc0: interrupt-controller@e61c0000 {
+ compatible = "renesas,irqc";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0 0xe61c0000 0 0x200>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 0 4>, <0 1 4>, <0 2 4>, <0 3 4>,
+ <0 4 4>, <0 5 4>, <0 6 4>, <0 7 4>,
+ <0 8 4>, <0 9 4>, <0 10 4>, <0 11 4>,
+ <0 12 4>, <0 13 4>, <0 14 4>, <0 15 4>,
+ <0 16 4>, <0 17 4>, <0 18 4>, <0 19 4>,
+ <0 20 4>, <0 21 4>, <0 22 4>, <0 23 4>,
+ <0 24 4>, <0 25 4>, <0 26 4>, <0 27 4>,
+ <0 28 4>, <0 29 4>, <0 30 4>, <0 31 4>;
+ };
+
+ irqc1: interrupt-controller@e61c0200 {
+ compatible = "renesas,irqc";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0 0xe61c0200 0 0x200>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 32 4>, <0 33 4>, <0 34 4>, <0 35 4>,
+ <0 36 4>, <0 37 4>, <0 38 4>, <0 39 4>,
+ <0 40 4>, <0 41 4>, <0 42 4>, <0 43 4>,
+ <0 44 4>, <0 45 4>, <0 46 4>, <0 47 4>,
+ <0 48 4>, <0 49 4>, <0 50 4>, <0 51 4>,
+ <0 52 4>, <0 53 4>, <0 54 4>, <0 55 4>,
+ <0 56 4>, <0 57 4>;
+ };
+
+ thermal@e61f0000 {
+ compatible = "renesas,rcar-thermal";
+ reg = <0 0xe61f0000 0 0x14>, <0 0xe61f0100 0 0x38>,
+ <0 0xe61f0200 0 0x38>, <0 0xe61f0300 0 0x38>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 69 4>;
+ };
+};
diff --git a/arch/arm/boot/dts/r8a7778-bockw.dts b/arch/arm/boot/dts/r8a7778-bockw.dts
new file mode 100644
index 000000000000..0076b1e8a0fb
--- /dev/null
+++ b/arch/arm/boot/dts/r8a7778-bockw.dts
@@ -0,0 +1,32 @@
+/*
+ * Reference Device Tree Source for the Bock-W board
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on r8a7779
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Simon Horman
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/dts-v1/;
+/include/ "r8a7778.dtsi"
+
+/ {
+ model = "bockw";
+ compatible = "renesas,bockw", "renesas,r8a7778";
+
+ chosen {
+ bootargs = "console=ttySC0,115200 ignore_loglevel ip=dhcp root=/dev/nfs";
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x60000000 0x10000000>;
+ };
+};
diff --git a/arch/arm/boot/dts/r8a7778.dtsi b/arch/arm/boot/dts/r8a7778.dtsi
new file mode 100644
index 000000000000..474373559bdc
--- /dev/null
+++ b/arch/arm/boot/dts/r8a7778.dtsi
@@ -0,0 +1,35 @@
+/*
+ * Device Tree Source for Renesas r8a7778
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on r8a7779
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Simon Horman
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ compatible = "renesas,r8a7778";
+
+ cpus {
+ cpu@0 {
+ compatible = "arm,cortex-a9";
+ };
+ };
+
+ gic: interrupt-controller@fe438000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ interrupt-controller;
+ reg = <0xfe438000 0x1000>,
+ <0xfe430000 0x100>;
+ };
+};
diff --git a/arch/arm/boot/dts/r8a7790-lager.dts b/arch/arm/boot/dts/r8a7790-lager.dts
new file mode 100644
index 000000000000..09a84fce89d6
--- /dev/null
+++ b/arch/arm/boot/dts/r8a7790-lager.dts
@@ -0,0 +1,31 @@
+/*
+ * Device Tree Source for the Lager board
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/dts-v1/;
+/include/ "r8a7790.dtsi"
+
+/ {
+ model = "Lager";
+ compatible = "renesas,lager", "renesas,r8a7790";
+
+ chosen {
+ bootargs = "console=ttySC6,115200 ignore_loglevel";
+ };
+
+ memory@40000000 {
+ device_type = "memory";
+ reg = <0 0x40000000 0 0x80000000>;
+ };
+
+ lbsc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ };
+};
diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
new file mode 100644
index 000000000000..7a1711027e41
--- /dev/null
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -0,0 +1,63 @@
+/*
+ * Device Tree Source for the r8a7790 SoC
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/ {
+ compatible = "renesas,r8a7790";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ clock-frequency = <1300000000>;
+ };
+ };
+
+ gic: interrupt-controller@f1001000 {
+ compatible = "arm,cortex-a15-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0 0xf1001000 0 0x1000>,
+ <0 0xf1002000 0 0x1000>,
+ <0 0xf1004000 0 0x2000>,
+ <0 0xf1006000 0 0x2000>;
+ interrupts = <1 9 0xf04>;
+
+ gic-cpuif@4 {
+ compatible = "arm,gic-cpuif";
+ cpuif-id = <4>;
+ cpu = <&cpu0>;
+ };
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
+ };
+
+ irqc0: interrupt-controller@e61c0000 {
+ compatible = "renesas,irqc";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0 0xe61c0000 0 0x200>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 0 4>, <0 1 4>, <0 2 4>, <0 3 4>;
+ };
+};
diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi
index 39b0458d365a..2e643ea51cce 100644
--- a/arch/arm/boot/dts/sama5d3.dtsi
+++ b/arch/arm/boot/dts/sama5d3.dtsi
@@ -60,6 +60,8 @@
compatible = "atmel,hsmci";
reg = <0xf0000000 0x600>;
interrupts = <21 4 0>;
+ dmas = <&dma0 2 0>;
+ dma-names = "rxtx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_mmc0_clk_cmd_dat0 &pinctrl_mmc0_dat1_3 &pinctrl_mmc0_dat4_7>;
status = "disabled";
@@ -111,6 +113,9 @@
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf0014000 0x4000>;
interrupts = <18 4 6>;
+ dmas = <&dma0 2 7>,
+ <&dma0 2 8>;
+ dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c0>;
#address-cells = <1>;
@@ -122,6 +127,9 @@
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf0018000 0x4000>;
interrupts = <19 4 6>;
+ dmas = <&dma0 2 9>,
+ <&dma0 2 10>;
+ dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
#address-cells = <1>;
@@ -167,6 +175,8 @@
compatible = "atmel,hsmci";
reg = <0xf8000000 0x600>;
interrupts = <22 4 0>;
+ dmas = <&dma1 2 0>;
+ dma-names = "rxtx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_mmc1_clk_cmd_dat0 &pinctrl_mmc1_dat1_3>;
status = "disabled";
@@ -178,6 +188,8 @@
compatible = "atmel,hsmci";
reg = <0xf8004000 0x600>;
interrupts = <23 4 0>;
+ dmas = <&dma1 2 1>;
+ dma-names = "rxtx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_mmc2_clk_cmd_dat0 &pinctrl_mmc2_dat1_3>;
status = "disabled";
@@ -294,6 +306,9 @@
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf801c000 0x4000>;
interrupts = <20 4 6>;
+ dmas = <&dma1 2 11>,
+ <&dma1 2 12>;
+ dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -348,14 +363,14 @@
compatible = "atmel,at91sam9g45-dma";
reg = <0xffffe600 0x200>;
interrupts = <30 4 0>;
- #dma-cells = <1>;
+ #dma-cells = <2>;
};
dma1: dma-controller@ffffe800 {
compatible = "atmel,at91sam9g45-dma";
reg = <0xffffe800 0x200>;
interrupts = <31 4 0>;
- #dma-cells = <1>;
+ #dma-cells = <2>;
};
ramc0: ramc@ffffea00 {
diff --git a/arch/arm/boot/dts/sama5d34ek.dts b/arch/arm/boot/dts/sama5d34ek.dts
index d2739f8d7ae9..6bebfcdcb1d1 100644
--- a/arch/arm/boot/dts/sama5d34ek.dts
+++ b/arch/arm/boot/dts/sama5d34ek.dts
@@ -12,7 +12,7 @@
/ {
model = "Atmel SAMA5D34-EK";
- compatible = "atmel,sama5d34ek", "atmel,sama5ek", "atmel,sama5d3xmb", "atmel,sama5d3xcm", "atmel,sama5d3", "atmel,sama5";
+ compatible = "atmel,sama5d34ek", "atmel,sama5d3xmb", "atmel,sama5d3xcm", "atmel,sama5d3", "atmel,sama5";
ahb {
apb {
diff --git a/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts b/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts
index f33b5ccb7446..5972abb55f9c 100644
--- a/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts
+++ b/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts
@@ -44,6 +44,19 @@
regulator-always-on;
regulator-boot-on;
};
+
+ lan9220@10000000 {
+ compatible = "smsc,lan9220", "smsc,lan9115";
+ reg = <0x10000000 0x100>;
+ phy-mode = "mii";
+ interrupt-parent = <&irqpin0>;
+ interrupts = <3 0>; /* active low */
+ reg-io-width = <4>;
+ smsc,irq-push-pull;
+ smsc,save-mac-address;
+ vddvario-supply = <&reg_1p8v>;
+ vdd33a-supply = <&reg_3p3v>;
+ };
};
&mmcif {
diff --git a/arch/arm/boot/dts/sh73a0.dtsi b/arch/arm/boot/dts/sh73a0.dtsi
index 3e4d383ac6d9..ec40bf78289e 100644
--- a/arch/arm/boot/dts/sh73a0.dtsi
+++ b/arch/arm/boot/dts/sh73a0.dtsi
@@ -38,6 +38,87 @@
<0xf0000100 0x100>;
};
+ irqpin0: irqpin@e6900000 {
+ compatible = "renesas,intc-irqpin";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0xe6900000 4>,
+ <0xe6900010 4>,
+ <0xe6900020 1>,
+ <0xe6900040 1>,
+ <0xe6900060 1>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 1 0x4
+ 0 2 0x4
+ 0 3 0x4
+ 0 4 0x4
+ 0 5 0x4
+ 0 6 0x4
+ 0 7 0x4
+ 0 8 0x4>;
+ };
+
+ irqpin1: irqpin@e6900004 {
+ compatible = "renesas,intc-irqpin";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0xe6900004 4>,
+ <0xe6900014 4>,
+ <0xe6900024 1>,
+ <0xe6900044 1>,
+ <0xe6900064 1>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 9 0x4
+ 0 10 0x4
+ 0 11 0x4
+ 0 12 0x4
+ 0 13 0x4
+ 0 14 0x4
+ 0 15 0x4
+ 0 16 0x4>;
+ control-parent;
+ };
+
+ irqpin2: irqpin@e6900008 {
+ compatible = "renesas,intc-irqpin";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0xe6900008 4>,
+ <0xe6900018 4>,
+ <0xe6900028 1>,
+ <0xe6900048 1>,
+ <0xe6900068 1>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 17 0x4
+ 0 18 0x4
+ 0 19 0x4
+ 0 20 0x4
+ 0 21 0x4
+ 0 22 0x4
+ 0 23 0x4
+ 0 24 0x4>;
+ };
+
+ irqpin3: irqpin@e690000c {
+ compatible = "renesas,intc-irqpin";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0xe690000c 4>,
+ <0xe690001c 4>,
+ <0xe690002c 1>,
+ <0xe690004c 1>,
+ <0xe690006c 1>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 25 0x4
+ 0 26 0x4
+ 0 27 0x4
+ 0 28 0x4
+ 0 29 0x4
+ 0 30 0x4
+ 0 31 0x4
+ 0 32 0x4>;
+ };
+
i2c0: i2c@0xe6820000 {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/spear1340.dtsi b/arch/arm/boot/dts/spear1340.dtsi
index c511c4772efd..54d128d35681 100644
--- a/arch/arm/boot/dts/spear1340.dtsi
+++ b/arch/arm/boot/dts/spear1340.dtsi
@@ -113,6 +113,9 @@
reg = <0xb4100000 0x1000>;
interrupts = <0 105 0x4>;
status = "disabled";
+ dmas = <&dwdma0 0x600 0 0 1>, /* 0xC << 11 */
+ <&dwdma0 0x680 0 1 0>; /* 0xD << 7 */
+ dma-names = "tx", "rx";
};
thermal@e07008c4 {
diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi
index b4ca60f4eb42..45597fd91050 100644
--- a/arch/arm/boot/dts/spear13xx.dtsi
+++ b/arch/arm/boot/dts/spear13xx.dtsi
@@ -98,13 +98,24 @@
reg = <0xb2800000 0x1000>;
interrupts = <0 29 0x4>;
status = "disabled";
+ dmas = <&dwdma0 0 0 0 0>;
+ dma-names = "data";
};
- dma@ea800000 {
+ dwdma0: dma@ea800000 {
compatible = "snps,dma-spear1340";
reg = <0xea800000 0x1000>;
interrupts = <0 19 0x4>;
status = "disabled";
+
+ dma-channels = <8>;
+ #dma-cells = <3>;
+ dma-requests = <32>;
+ chan_allocation_order = <1>;
+ chan_priority = <1>;
+ block_size = <0xfff>;
+ dma-masters = <2>;
+ data_width = <3 3 0 0>;
};
dma@eb000000 {
@@ -112,6 +123,15 @@
reg = <0xeb000000 0x1000>;
interrupts = <0 59 0x4>;
status = "disabled";
+
+ dma-requests = <32>;
+ dma-channels = <8>;
+ dma-masters = <2>;
+ #dma-cells = <3>;
+ chan_allocation_order = <1>;
+ chan_priority = <1>;
+ block_size = <0xfff>;
+ data_width = <3 3 0 0>;
};
fsmc: flash@b0000000 {
@@ -261,6 +281,9 @@
#size-cells = <0>;
interrupts = <0 31 0x4>;
status = "disabled";
+ dmas = <&dwdma0 0x2000 0 0 0>, /* 0x4 << 11 */
+ <&dwdma0 0x0280 0 0 0>; /* 0x5 << 7 */
+ dma-names = "tx", "rx";
};
rtc@e0580000 {
diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts
index 616990dc92db..72c1f27af7f3 100644
--- a/arch/arm/boot/dts/tegra114-dalmore.dts
+++ b/arch/arm/boot/dts/tegra114-dalmore.dts
@@ -10,14 +10,835 @@
reg = <0x80000000 0x40000000>;
};
+ pinmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&state_default>;
+
+ state_default: pinmux {
+ clk1_out_pw4 {
+ nvidia,pins = "clk1_out_pw4";
+ nvidia,function = "extperiph1";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ dap1_din_pn1 {
+ nvidia,pins = "dap1_din_pn1";
+ nvidia,function = "i2s0";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ dap1_dout_pn2 {
+ nvidia,pins = "dap1_dout_pn2",
+ "dap1_fs_pn0",
+ "dap1_sclk_pn3";
+ nvidia,function = "i2s0";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ dap2_din_pa4 {
+ nvidia,pins = "dap2_din_pa4";
+ nvidia,function = "i2s1";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ dap2_dout_pa5 {
+ nvidia,pins = "dap2_dout_pa5",
+ "dap2_fs_pa2",
+ "dap2_sclk_pa3";
+ nvidia,function = "i2s1";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ dap4_din_pp5 {
+ nvidia,pins = "dap4_din_pp5",
+ "dap4_dout_pp6",
+ "dap4_fs_pp4",
+ "dap4_sclk_pp7";
+ nvidia,function = "i2s3";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ dvfs_pwm_px0 {
+ nvidia,pins = "dvfs_pwm_px0",
+ "dvfs_clk_px2";
+ nvidia,function = "cldvfs";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ ulpi_clk_py0 {
+ nvidia,pins = "ulpi_clk_py0",
+ "ulpi_data0_po1",
+ "ulpi_data1_po2",
+ "ulpi_data2_po3",
+ "ulpi_data3_po4",
+ "ulpi_data4_po5",
+ "ulpi_data5_po6",
+ "ulpi_data6_po7",
+ "ulpi_data7_po0";
+ nvidia,function = "ulpi";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ ulpi_dir_py1 {
+ nvidia,pins = "ulpi_dir_py1",
+ "ulpi_nxt_py2";
+ nvidia,function = "ulpi";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ ulpi_stp_py3 {
+ nvidia,pins = "ulpi_stp_py3";
+ nvidia,function = "ulpi";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ cam_i2c_scl_pbb1 {
+ nvidia,pins = "cam_i2c_scl_pbb1",
+ "cam_i2c_sda_pbb2";
+ nvidia,function = "i2c3";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ nvidia,lock = <0>;
+ nvidia,open-drain = <0>;
+ };
+ cam_mclk_pcc0 {
+ nvidia,pins = "cam_mclk_pcc0",
+ "pbb0";
+ nvidia,function = "vi_alt3";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ nvidia,lock = <0>;
+ };
+ gen2_i2c_scl_pt5 {
+ nvidia,pins = "gen2_i2c_scl_pt5",
+ "gen2_i2c_sda_pt6";
+ nvidia,function = "i2c2";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ nvidia,lock = <0>;
+ nvidia,open-drain = <0>;
+ };
+ gmi_a16_pj7 {
+ nvidia,pins = "gmi_a16_pj7";
+ nvidia,function = "uartd";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ gmi_a17_pb0 {
+ nvidia,pins = "gmi_a17_pb0",
+ "gmi_a18_pb1";
+ nvidia,function = "uartd";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ gmi_a19_pk7 {
+ nvidia,pins = "gmi_a19_pk7";
+ nvidia,function = "uartd";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ gmi_ad5_pg5 {
+ nvidia,pins = "gmi_ad5_pg5",
+ "gmi_cs6_n_pi3",
+ "gmi_wr_n_pi0";
+ nvidia,function = "spi4";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ gmi_ad6_pg6 {
+ nvidia,pins = "gmi_ad6_pg6",
+ "gmi_ad7_pg7";
+ nvidia,function = "spi4";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ gmi_ad12_ph4 {
+ nvidia,pins = "gmi_ad12_ph4";
+ nvidia,function = "rsvd4";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ gmi_ad9_ph1 {
+ nvidia,pins = "gmi_ad9_ph1";
+ nvidia,function = "pwm1";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ gmi_cs1_n_pj2 {
+ nvidia,pins = "gmi_cs1_n_pj2",
+ "gmi_oe_n_pi1";
+ nvidia,function = "soc";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ clk2_out_pw5 {
+ nvidia,pins = "clk2_out_pw5";
+ nvidia,function = "extperiph2";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ sdmmc1_clk_pz0 {
+ nvidia,pins = "sdmmc1_clk_pz0";
+ nvidia,function = "sdmmc1";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ sdmmc1_cmd_pz1 {
+ nvidia,pins = "sdmmc1_cmd_pz1",
+ "sdmmc1_dat0_py7",
+ "sdmmc1_dat1_py6",
+ "sdmmc1_dat2_py5",
+ "sdmmc1_dat3_py4";
+ nvidia,function = "sdmmc1";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ sdmmc1_wp_n_pv3 {
+ nvidia,pins = "sdmmc1_wp_n_pv3";
+ nvidia,function = "spi4";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ sdmmc3_clk_pa6 {
+ nvidia,pins = "sdmmc3_clk_pa6";
+ nvidia,function = "sdmmc3";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ sdmmc3_cmd_pa7 {
+ nvidia,pins = "sdmmc3_cmd_pa7",
+ "sdmmc3_dat0_pb7",
+ "sdmmc3_dat1_pb6",
+ "sdmmc3_dat2_pb5",
+ "sdmmc3_dat3_pb4",
+ "kb_col4_pq4",
+ "sdmmc3_clk_lb_out_pee4",
+ "sdmmc3_clk_lb_in_pee5";
+ nvidia,function = "sdmmc3";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ sdmmc4_clk_pcc4 {
+ nvidia,pins = "sdmmc4_clk_pcc4";
+ nvidia,function = "sdmmc4";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ sdmmc4_cmd_pt7 {
+ nvidia,pins = "sdmmc4_cmd_pt7",
+ "sdmmc4_dat0_paa0",
+ "sdmmc4_dat1_paa1",
+ "sdmmc4_dat2_paa2",
+ "sdmmc4_dat3_paa3",
+ "sdmmc4_dat4_paa4",
+ "sdmmc4_dat5_paa5",
+ "sdmmc4_dat6_paa6",
+ "sdmmc4_dat7_paa7";
+ nvidia,function = "sdmmc4";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ clk_32k_out_pa0 {
+ nvidia,pins = "clk_32k_out_pa0";
+ nvidia,function = "blink";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ kb_col0_pq0 {
+ nvidia,pins = "kb_col0_pq0",
+ "kb_col1_pq1",
+ "kb_col2_pq2",
+ "kb_row0_pr0",
+ "kb_row1_pr1",
+ "kb_row2_pr2";
+ nvidia,function = "kbc";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ dap3_din_pp1 {
+ nvidia,pins = "dap3_din_pp1",
+ "dap3_sclk_pp3";
+ nvidia,function = "displayb";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <0>;
+ };
+ pv0 {
+ nvidia,pins = "pv0";
+ nvidia,function = "rsvd4";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <0>;
+ };
+ kb_row7_pr7 {
+ nvidia,pins = "kb_row7_pr7";
+ nvidia,function = "rsvd2";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ kb_row10_ps2 {
+ nvidia,pins = "kb_row10_ps2";
+ nvidia,function = "uarta";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ kb_row9_ps1 {
+ nvidia,pins = "kb_row9_ps1";
+ nvidia,function = "uarta";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ pwr_i2c_scl_pz6 {
+ nvidia,pins = "pwr_i2c_scl_pz6",
+ "pwr_i2c_sda_pz7";
+ nvidia,function = "i2cpwr";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ nvidia,lock = <0>;
+ nvidia,open-drain = <0>;
+ };
+ sys_clk_req_pz5 {
+ nvidia,pins = "sys_clk_req_pz5";
+ nvidia,function = "sysclk";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ core_pwr_req {
+ nvidia,pins = "core_pwr_req";
+ nvidia,function = "pwron";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ cpu_pwr_req {
+ nvidia,pins = "cpu_pwr_req";
+ nvidia,function = "cpu";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ pwr_int_n {
+ nvidia,pins = "pwr_int_n";
+ nvidia,function = "pmi";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ reset_out_n {
+ nvidia,pins = "reset_out_n";
+ nvidia,function = "reset_out_n";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ clk3_out_pee0 {
+ nvidia,pins = "clk3_out_pee0";
+ nvidia,function = "extperiph3";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ gen1_i2c_scl_pc4 {
+ nvidia,pins = "gen1_i2c_scl_pc4",
+ "gen1_i2c_sda_pc5";
+ nvidia,function = "i2c1";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ nvidia,lock = <0>;
+ nvidia,open-drain = <0>;
+ };
+ uart2_cts_n_pj5 {
+ nvidia,pins = "uart2_cts_n_pj5";
+ nvidia,function = "uartb";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ uart2_rts_n_pj6 {
+ nvidia,pins = "uart2_rts_n_pj6";
+ nvidia,function = "uartb";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ uart2_rxd_pc3 {
+ nvidia,pins = "uart2_rxd_pc3";
+ nvidia,function = "irda";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ uart2_txd_pc2 {
+ nvidia,pins = "uart2_txd_pc2";
+ nvidia,function = "irda";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ uart3_cts_n_pa1 {
+ nvidia,pins = "uart3_cts_n_pa1",
+ "uart3_rxd_pw7";
+ nvidia,function = "uartc";
+ nvidia,pull = <0>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ uart3_rts_n_pc0 {
+ nvidia,pins = "uart3_rts_n_pc0",
+ "uart3_txd_pw6";
+ nvidia,function = "uartc";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ owr {
+ nvidia,pins = "owr";
+ nvidia,function = "owr";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ hdmi_cec_pee3 {
+ nvidia,pins = "hdmi_cec_pee3";
+ nvidia,function = "cec";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ nvidia,lock = <0>;
+ nvidia,open-drain = <0>;
+ };
+ ddc_scl_pv4 {
+ nvidia,pins = "ddc_scl_pv4",
+ "ddc_sda_pv5";
+ nvidia,function = "i2c4";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ nvidia,lock = <0>;
+ nvidia,rcv-sel = <1>;
+ };
+ spdif_in_pk6 {
+ nvidia,pins = "spdif_in_pk6";
+ nvidia,function = "usb";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ nvidia,lock = <0>;
+ };
+ usb_vbus_en0_pn4 {
+ nvidia,pins = "usb_vbus_en0_pn4";
+ nvidia,function = "usb";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ nvidia,lock = <0>;
+ nvidia,open-drain = <1>;
+ };
+ gpio_x6_aud_px6 {
+ nvidia,pins = "gpio_x6_aud_px6";
+ nvidia,function = "spi6";
+ nvidia,pull = <2>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <1>;
+ };
+ gpio_x4_aud_px4 {
+ nvidia,pins = "gpio_x4_aud_px4",
+ "gpio_x7_aud_px7";
+ nvidia,function = "rsvd1";
+ nvidia,pull = <1>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ gpio_x5_aud_px5 {
+ nvidia,pins = "gpio_x5_aud_px5";
+ nvidia,function = "rsvd1";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ gpio_w2_aud_pw2 {
+ nvidia,pins = "gpio_w2_aud_pw2";
+ nvidia,function = "rsvd2";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ gpio_w3_aud_pw3 {
+ nvidia,pins = "gpio_w3_aud_pw3";
+ nvidia,function = "spi6";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ gpio_x1_aud_px1 {
+ nvidia,pins = "gpio_x1_aud_px1";
+ nvidia,function = "rsvd4";
+ nvidia,pull = <1>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ gpio_x3_aud_px3 {
+ nvidia,pins = "gpio_x3_aud_px3";
+ nvidia,function = "rsvd4";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ dap3_fs_pp0 {
+ nvidia,pins = "dap3_fs_pp0";
+ nvidia,function = "i2s2";
+ nvidia,pull = <1>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ dap3_dout_pp2 {
+ nvidia,pins = "dap3_dout_pp2";
+ nvidia,function = "i2s2";
+ nvidia,pull = <1>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ pv1 {
+ nvidia,pins = "pv1";
+ nvidia,function = "rsvd1";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ pbb3 {
+ nvidia,pins = "pbb3",
+ "pbb5",
+ "pbb6",
+ "pbb7";
+ nvidia,function = "rsvd4";
+ nvidia,pull = <1>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ pcc1 {
+ nvidia,pins = "pcc1",
+ "pcc2";
+ nvidia,function = "rsvd4";
+ nvidia,pull = <1>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ gmi_ad0_pg0 {
+ nvidia,pins = "gmi_ad0_pg0",
+ "gmi_ad1_pg1";
+ nvidia,function = "gmi";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ gmi_ad10_ph2 {
+ nvidia,pins = "gmi_ad10_ph2",
+ "gmi_ad11_ph3",
+ "gmi_ad13_ph5",
+ "gmi_ad8_ph0",
+ "gmi_clk_pk1";
+ nvidia,function = "gmi";
+ nvidia,pull = <1>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ gmi_ad2_pg2 {
+ nvidia,pins = "gmi_ad2_pg2",
+ "gmi_ad3_pg3";
+ nvidia,function = "gmi";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ gmi_adv_n_pk0 {
+ nvidia,pins = "gmi_adv_n_pk0",
+ "gmi_cs0_n_pj0",
+ "gmi_cs2_n_pk3",
+ "gmi_cs4_n_pk2",
+ "gmi_cs7_n_pi6",
+ "gmi_dqs_p_pj3",
+ "gmi_iordy_pi5",
+ "gmi_wp_n_pc7";
+ nvidia,function = "gmi";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ gmi_cs3_n_pk4 {
+ nvidia,pins = "gmi_cs3_n_pk4";
+ nvidia,function = "gmi";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ clk2_req_pcc5 {
+ nvidia,pins = "clk2_req_pcc5";
+ nvidia,function = "rsvd4";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ kb_col3_pq3 {
+ nvidia,pins = "kb_col3_pq3",
+ "kb_col6_pq6",
+ "kb_col7_pq7";
+ nvidia,function = "kbc";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ kb_col5_pq5 {
+ nvidia,pins = "kb_col5_pq5";
+ nvidia,function = "kbc";
+ nvidia,pull = <2>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ kb_row3_pr3 {
+ nvidia,pins = "kb_row3_pr3",
+ "kb_row4_pr4",
+ "kb_row6_pr6",
+ "kb_row8_ps0";
+ nvidia,function = "kbc";
+ nvidia,pull = <1>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ clk3_req_pee1 {
+ nvidia,pins = "clk3_req_pee1";
+ nvidia,function = "rsvd4";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ pu4 {
+ nvidia,pins = "pu4";
+ nvidia,function = "displayb";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <0>;
+ };
+ pu5 {
+ nvidia,pins = "pu5",
+ "pu6";
+ nvidia,function = "displayb";
+ nvidia,pull = <0>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ hdmi_int_pn7 {
+ nvidia,pins = "hdmi_int_pn7";
+ nvidia,function = "rsvd1";
+ nvidia,pull = <1>;
+ nvidia,tristate = <0>;
+ nvidia,enable-input = <1>;
+ };
+ clk1_req_pee2 {
+ nvidia,pins = "clk1_req_pee2",
+ "usb_vbus_en1_pn5";
+ nvidia,function = "rsvd4";
+ nvidia,pull = <1>;
+ nvidia,tristate = <1>;
+ nvidia,enable-input = <0>;
+ };
+
+ drive_sdio1 {
+ nvidia,pins = "drive_sdio1";
+ nvidia,high-speed-mode = <1>;
+ nvidia,schmitt = <0>;
+ nvidia,low-power-mode = <3>;
+ nvidia,pull-down-strength = <36>;
+ nvidia,pull-up-strength = <20>;
+ nvidia,slew-rate-rising = <2>;
+ nvidia,slew-rate-falling = <2>;
+ };
+ drive_sdio3 {
+ nvidia,pins = "drive_sdio3";
+ nvidia,high-speed-mode = <1>;
+ nvidia,schmitt = <0>;
+ nvidia,low-power-mode = <3>;
+ nvidia,pull-down-strength = <22>;
+ nvidia,pull-up-strength = <36>;
+ nvidia,slew-rate-rising = <0>;
+ nvidia,slew-rate-falling = <0>;
+ };
+ drive_gma {
+ nvidia,pins = "drive_gma";
+ nvidia,high-speed-mode = <1>;
+ nvidia,schmitt = <0>;
+ nvidia,low-power-mode = <3>;
+ nvidia,pull-down-strength = <2>;
+ nvidia,pull-up-strength = <1>;
+ nvidia,slew-rate-rising = <0>;
+ nvidia,slew-rate-falling = <0>;
+ nvidia,drive-type = <1>;
+ };
+ };
+ };
+
serial@70006300 {
status = "okay";
};
+ i2c@7000c000 {
+ status = "okay";
+ clock-frequency = <100000>;
+
+ battery: smart-battery {
+ compatible = "ti,bq20z45", "sbs,sbs-battery";
+ reg = <0xb>;
+ battery-name = "battery";
+ sbs,i2c-retry-count = <2>;
+ sbs,poll-retry-count = <100>;
+ };
+ };
+
+ i2c@7000d000 {
+ status = "okay";
+ clock-frequency = <400000>;
+
+ tps51632 {
+ compatible = "ti,tps51632";
+ reg = <0x43>;
+ regulator-name = "vdd-cpu";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1520000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ tps65090 {
+ compatible = "ti,tps65090";
+ reg = <0x48>;
+ interrupt-parent = <&gpio>;
+ interrupts = <72 0x04>; /* gpio PJ0 */
+
+ vsys1-supply = <&vdd_ac_bat_reg>;
+ vsys2-supply = <&vdd_ac_bat_reg>;
+ vsys3-supply = <&vdd_ac_bat_reg>;
+ infet1-supply = <&vdd_ac_bat_reg>;
+ infet2-supply = <&vdd_ac_bat_reg>;
+ infet3-supply = <&tps65090_dcdc2_reg>;
+ infet4-supply = <&tps65090_dcdc2_reg>;
+ infet5-supply = <&tps65090_dcdc2_reg>;
+ infet6-supply = <&tps65090_dcdc2_reg>;
+ infet7-supply = <&tps65090_dcdc2_reg>;
+ vsys-l1-supply = <&vdd_ac_bat_reg>;
+ vsys-l2-supply = <&vdd_ac_bat_reg>;
+
+ regulators {
+ tps65090_dcdc1_reg: dcdc1 {
+ regulator-name = "vdd-sys-5v0";
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ tps65090_dcdc2_reg: dcdc2 {
+ regulator-name = "vdd-sys-3v3";
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ dcdc3 {
+ regulator-name = "vdd-ao";
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ fet1 {
+ regulator-name = "vdd-lcd-bl";
+ };
+
+ fet3 {
+ regulator-name = "vdd-modem-3v3";
+ };
+
+ fet4 {
+ regulator-name = "avdd-lcd";
+ };
+
+ fet5 {
+ regulator-name = "vdd-lvds";
+ };
+
+ fet6 {
+ regulator-name = "vdd-sd-slot";
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ fet7 {
+ regulator-name = "vdd-com-3v3";
+ };
+
+ ldo1 {
+ regulator-name = "vdd-sby-5v0";
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ ldo2 {
+ regulator-name = "vdd-sby-3v3";
+ regulator-always-on;
+ regulator-boot-on;
+ };
+ };
+ };
+ };
+
pmc {
nvidia,invert-interrupt;
};
+ sdhci@78000400 {
+ cd-gpios = <&gpio 170 1>; /* gpio PV2 */
+ bus-width = <4>;
+ status = "okay";
+ };
+
+ sdhci@78000600 {
+ bus-width = <8>;
+ status = "okay";
+ non-removable;
+ };
+
clocks {
compatible = "simple-bus";
#address-cells = <1>;
@@ -30,4 +851,74 @@
clock-frequency = <32768>;
};
};
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ vdd_ac_bat_reg: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vdd_ac_bat";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ dvdd_ts_reg: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "dvdd_ts";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ enable-active-high;
+ gpio = <&gpio 61 0>; /* GPIO PH5 */
+ };
+
+ lcd_bl_en_reg: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "lcd_bl_en";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&gpio 58 0>; /* GPIO PH2 */
+ };
+
+ usb1_vbus_reg: regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "usb1_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&gpio 108 0>; /* GPIO PN4 */
+ gpio-open-drain;
+ vin-supply = <&tps65090_dcdc1_reg>;
+ };
+
+ usb3_vbus_reg: regulator@4 {
+ compatible = "regulator-fixed";
+ reg = <4>;
+ regulator-name = "usb2_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&gpio 86 0>; /* GPIO PK6 */
+ gpio-open-drain;
+ vin-supply = <&tps65090_dcdc1_reg>;
+ };
+
+ vdd_hdmi_reg: regulator@5 {
+ compatible = "regulator-fixed";
+ reg = <5>;
+ regulator-name = "vdd_hdmi_5v0";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&gpio 81 0>; /* GPIO PK1 */
+ vin-supply = <&tps65090_dcdc1_reg>;
+ };
+ };
};
diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi
index c1110a9b2a91..629415ffd8dc 100644
--- a/arch/arm/boot/dts/tegra114.dtsi
+++ b/arch/arm/boot/dts/tegra114.dtsi
@@ -4,6 +4,13 @@
compatible = "nvidia,tegra114";
interrupt-parent = <&gic>;
+ aliases {
+ serial0 = &uarta;
+ serial1 = &uartb;
+ serial2 = &uartc;
+ serial3 = &uartd;
+ };
+
gic: interrupt-controller {
compatible = "arm,cortex-a15-gic";
#interrupt-cells = <3>;
@@ -33,6 +40,44 @@
#clock-cells = <1>;
};
+ apbdma: dma {
+ compatible = "nvidia,tegra114-apbdma";
+ reg = <0x6000a000 0x1400>;
+ interrupts = <0 104 0x04
+ 0 105 0x04
+ 0 106 0x04
+ 0 107 0x04
+ 0 108 0x04
+ 0 109 0x04
+ 0 110 0x04
+ 0 111 0x04
+ 0 112 0x04
+ 0 113 0x04
+ 0 114 0x04
+ 0 115 0x04
+ 0 116 0x04
+ 0 117 0x04
+ 0 118 0x04
+ 0 119 0x04
+ 0 128 0x04
+ 0 129 0x04
+ 0 130 0x04
+ 0 131 0x04
+ 0 132 0x04
+ 0 133 0x04
+ 0 134 0x04
+ 0 135 0x04
+ 0 136 0x04
+ 0 137 0x04
+ 0 138 0x04
+ 0 139 0x04
+ 0 140 0x04
+ 0 141 0x04
+ 0 142 0x04
+ 0 143 0x04>;
+ clocks = <&tegra_car 34>;
+ };
+
ahb: ahb {
compatible = "nvidia,tegra114-ahb", "nvidia,tegra30-ahb";
reg = <0x6000c004 0x14c>;
@@ -61,42 +106,189 @@
0x70003000 0x40c>; /* Mux registers */
};
- serial@70006000 {
+ /*
+ * There are two serial driver i.e. 8250 based simple serial
+ * driver and APB DMA based serial driver for higher baudrate
+ * and performace. To enable the 8250 based driver, the compatible
+ * is "nvidia,tegra114-uart", "nvidia,tegra20-uart" and to enable
+ * the APB DMA based serial driver, the comptible is
+ * "nvidia,tegra114-hsuart", "nvidia,tegra30-hsuart".
+ */
+ uarta: serial@70006000 {
compatible = "nvidia,tegra114-uart", "nvidia,tegra20-uart";
reg = <0x70006000 0x40>;
reg-shift = <2>;
interrupts = <0 36 0x04>;
+ nvidia,dma-request-selector = <&apbdma 8>;
status = "disabled";
clocks = <&tegra_car 6>;
};
- serial@70006040 {
+ uartb: serial@70006040 {
compatible = "nvidia,tegra114-uart", "nvidia,tegra20-uart";
reg = <0x70006040 0x40>;
reg-shift = <2>;
interrupts = <0 37 0x04>;
+ nvidia,dma-request-selector = <&apbdma 9>;
status = "disabled";
clocks = <&tegra_car 192>;
};
- serial@70006200 {
+ uartc: serial@70006200 {
compatible = "nvidia,tegra114-uart", "nvidia,tegra20-uart";
reg = <0x70006200 0x100>;
reg-shift = <2>;
interrupts = <0 46 0x04>;
+ nvidia,dma-request-selector = <&apbdma 10>;
status = "disabled";
clocks = <&tegra_car 55>;
};
- serial@70006300 {
+ uartd: serial@70006300 {
compatible = "nvidia,tegra114-uart", "nvidia,tegra20-uart";
reg = <0x70006300 0x100>;
reg-shift = <2>;
interrupts = <0 90 0x04>;
+ nvidia,dma-request-selector = <&apbdma 19>;
status = "disabled";
clocks = <&tegra_car 65>;
};
+ pwm: pwm {
+ compatible = "nvidia,tegra114-pwm", "nvidia,tegra20-pwm";
+ reg = <0x7000a000 0x100>;
+ #pwm-cells = <2>;
+ clocks = <&tegra_car 17>;
+ status = "disabled";
+ };
+
+ i2c@7000c000 {
+ compatible = "nvidia,tegra114-i2c";
+ reg = <0x7000c000 0x100>;
+ interrupts = <0 38 0x04>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 12>;
+ clock-names = "div-clk";
+ status = "disabled";
+ };
+
+ i2c@7000c400 {
+ compatible = "nvidia,tegra114-i2c";
+ reg = <0x7000c400 0x100>;
+ interrupts = <0 84 0x04>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 54>;
+ clock-names = "div-clk";
+ status = "disabled";
+ };
+
+ i2c@7000c500 {
+ compatible = "nvidia,tegra114-i2c";
+ reg = <0x7000c500 0x100>;
+ interrupts = <0 92 0x04>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 67>;
+ clock-names = "div-clk";
+ status = "disabled";
+ };
+
+ i2c@7000c700 {
+ compatible = "nvidia,tegra114-i2c";
+ reg = <0x7000c700 0x100>;
+ interrupts = <0 120 0x04>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 103>;
+ clock-names = "div-clk";
+ status = "disabled";
+ };
+
+ i2c@7000d000 {
+ compatible = "nvidia,tegra114-i2c";
+ reg = <0x7000d000 0x100>;
+ interrupts = <0 53 0x04>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 47>;
+ clock-names = "div-clk";
+ status = "disabled";
+ };
+
+ spi@7000d400 {
+ compatible = "nvidia,tegra114-spi";
+ reg = <0x7000d400 0x200>;
+ interrupts = <0 59 0x04>;
+ nvidia,dma-request-selector = <&apbdma 15>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 41>;
+ clock-names = "spi";
+ status = "disabled";
+ };
+
+ spi@7000d600 {
+ compatible = "nvidia,tegra114-spi";
+ reg = <0x7000d600 0x200>;
+ interrupts = <0 82 0x04>;
+ nvidia,dma-request-selector = <&apbdma 16>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 44>;
+ clock-names = "spi";
+ status = "disabled";
+ };
+
+ spi@7000d800 {
+ compatible = "nvidia,tegra114-spi";
+ reg = <0x7000d800 0x200>;
+ interrupts = <0 83 0x04>;
+ nvidia,dma-request-selector = <&apbdma 17>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 46>;
+ clock-names = "spi";
+ status = "disabled";
+ };
+
+ spi@7000da00 {
+ compatible = "nvidia,tegra114-spi";
+ reg = <0x7000da00 0x200>;
+ interrupts = <0 93 0x04>;
+ nvidia,dma-request-selector = <&apbdma 18>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 68>;
+ clock-names = "spi";
+ status = "disabled";
+ };
+
+ spi@7000dc00 {
+ compatible = "nvidia,tegra114-spi";
+ reg = <0x7000dc00 0x200>;
+ interrupts = <0 94 0x04>;
+ nvidia,dma-request-selector = <&apbdma 27>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 104>;
+ clock-names = "spi";
+ status = "disabled";
+ };
+
+ spi@7000de00 {
+ compatible = "nvidia,tegra114-spi";
+ reg = <0x7000de00 0x200>;
+ interrupts = <0 79 0x04>;
+ nvidia,dma-request-selector = <&apbdma 28>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&tegra_car 105>;
+ clock-names = "spi";
+ status = "disabled";
+ };
+
rtc {
compatible = "nvidia,tegra114-rtc", "nvidia,tegra20-rtc";
reg = <0x7000e000 0x100>;
@@ -104,6 +296,14 @@
clocks = <&tegra_car 4>;
};
+ kbc {
+ compatible = "nvidia,tegra114-kbc";
+ reg = <0x7000e200 0x100>;
+ interrupts = <0 85 0x04>;
+ clocks = <&tegra_car 36>;
+ status = "disabled";
+ };
+
pmc {
compatible = "nvidia,tegra114-pmc";
reg = <0x7000e400 0x400>;
@@ -122,6 +322,38 @@
nvidia,ahb = <&ahb>;
};
+ sdhci@78000000 {
+ compatible = "nvidia,tegra114-sdhci", "nvidia,tegra30-sdhci";
+ reg = <0x78000000 0x200>;
+ interrupts = <0 14 0x04>;
+ clocks = <&tegra_car 14>;
+ status = "disable";
+ };
+
+ sdhci@78000200 {
+ compatible = "nvidia,tegra114-sdhci", "nvidia,tegra30-sdhci";
+ reg = <0x78000200 0x200>;
+ interrupts = <0 15 0x04>;
+ clocks = <&tegra_car 9>;
+ status = "disable";
+ };
+
+ sdhci@78000400 {
+ compatible = "nvidia,tegra114-sdhci", "nvidia,tegra30-sdhci";
+ reg = <0x78000400 0x200>;
+ interrupts = <0 19 0x04>;
+ clocks = <&tegra_car 69>;
+ status = "disable";
+ };
+
+ sdhci@78000600 {
+ compatible = "nvidia,tegra114-sdhci", "nvidia,tegra30-sdhci";
+ reg = <0x78000600 0x200>;
+ interrupts = <0 31 0x04>;
+ clocks = <&tegra_car 15>;
+ status = "disable";
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/tegra20-colibri-512.dtsi b/arch/arm/boot/dts/tegra20-colibri-512.dtsi
index 4e3afdef28a8..a573b94b7c93 100644
--- a/arch/arm/boot/dts/tegra20-colibri-512.dtsi
+++ b/arch/arm/boot/dts/tegra20-colibri-512.dtsi
@@ -361,6 +361,15 @@
};
};
+ pmc {
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <5000>;
+ nvidia,cpu-pwr-off-time = <5000>;
+ nvidia,core-pwr-good-time = <3845 3845>;
+ nvidia,core-pwr-off-time = <3875>;
+ nvidia,sys-clock-req-active-high;
+ };
+
memory-controller@7000f400 {
emc-table@83250 {
reg = <83250>;
@@ -473,6 +482,9 @@
"Mic", "MIC1";
nvidia,ac97-controller = <&ac97>;
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
regulators {
diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts
index ae9d5a20834e..e7d5de4e00b9 100644
--- a/arch/arm/boot/dts/tegra20-harmony.dts
+++ b/arch/arm/boot/dts/tegra20-harmony.dts
@@ -416,6 +416,12 @@
pmc {
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <5000>;
+ nvidia,cpu-pwr-off-time = <5000>;
+ nvidia,core-pwr-good-time = <3845 3845>;
+ nvidia,core-pwr-off-time = <3875>;
+ nvidia,sys-clock-req-active-high;
};
usb@c5000000 {
@@ -464,6 +470,17 @@
};
};
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ power {
+ label = "Power";
+ gpios = <&gpio 170 1>; /* gpio PV2, active low */
+ linux,code = <116>; /* KEY_POWER */
+ gpio-key,wakeup;
+ };
+ };
+
kbc {
status = "okay";
nvidia,debounce-delay-ms = <2>;
@@ -669,5 +686,8 @@
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
nvidia,int-mic-en-gpios = <&gpio 184 0>; /*gpio PX0 */
nvidia,ext-mic-en-gpios = <&gpio 185 0>; /* gpio PX1 */
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra20-medcom-wide.dts b/arch/arm/boot/dts/tegra20-medcom-wide.dts
index a2d6d6541f83..ace23437da89 100644
--- a/arch/arm/boot/dts/tegra20-medcom-wide.dts
+++ b/arch/arm/boot/dts/tegra20-medcom-wide.dts
@@ -6,6 +6,10 @@
model = "Avionic Design Medcom-Wide board";
compatible = "ad,medcom-wide", "ad,tamonten", "nvidia,tegra20";
+ pwm {
+ status = "okay";
+ };
+
i2c@7000c000 {
wm8903: wm8903@1a {
compatible = "wlf,wm8903";
@@ -54,5 +58,8 @@
nvidia,spkr-en-gpios = <&wm8903 2 0>;
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts
index fd60940e4063..e3e0c9977df4 100644
--- a/arch/arm/boot/dts/tegra20-paz00.dts
+++ b/arch/arm/boot/dts/tegra20-paz00.dts
@@ -415,6 +415,12 @@
pmc {
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <2000>;
+ nvidia,cpu-pwr-off-time = <0>;
+ nvidia,core-pwr-good-time = <3845 3845>;
+ nvidia,core-pwr-off-time = <0>;
+ nvidia,sys-clock-req-active-high;
};
usb@c5000000 {
@@ -445,6 +451,7 @@
sdhci@c8000600 {
status = "okay";
bus-width = <8>;
+ non-removable;
};
clocks {
@@ -514,5 +521,8 @@
nvidia,audio-codec = <&alc5632>;
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra20-plutux.dts b/arch/arm/boot/dts/tegra20-plutux.dts
index 289480026fbf..1a17cc30bb9d 100644
--- a/arch/arm/boot/dts/tegra20-plutux.dts
+++ b/arch/arm/boot/dts/tegra20-plutux.dts
@@ -52,5 +52,8 @@
nvidia,spkr-en-gpios = <&wm8903 2 0>;
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts
index 4ee700a33ca5..cee4c34010fe 100644
--- a/arch/arm/boot/dts/tegra20-seaboard.dts
+++ b/arch/arm/boot/dts/tegra20-seaboard.dts
@@ -517,6 +517,12 @@
pmc {
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <5000>;
+ nvidia,cpu-pwr-off-time = <5000>;
+ nvidia,core-pwr-good-time = <3845 3845>;
+ nvidia,core-pwr-off-time = <3875>;
+ nvidia,sys-clock-req-active-high;
};
memory-controller@7000f400 {
@@ -580,6 +586,7 @@
status = "okay";
power-gpios = <&gpio 86 0>; /* gpio PK6 */
bus-width = <4>;
+ keep-power-in-suspend;
};
sdhci@c8000400 {
@@ -593,6 +600,7 @@
sdhci@c8000600 {
status = "okay";
bus-width = <8>;
+ non-removable;
};
clocks {
@@ -821,5 +829,8 @@
nvidia,spkr-en-gpios = <&wm8903 2 0>;
nvidia,hp-det-gpios = <&gpio 185 0>; /* gpio PX1 */
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi
index c19025725918..50b3ec16b93a 100644
--- a/arch/arm/boot/dts/tegra20-tamonten.dtsi
+++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi
@@ -458,6 +458,12 @@
pmc {
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <5000>;
+ nvidia,cpu-pwr-off-time = <5000>;
+ nvidia,core-pwr-good-time = <3845 3845>;
+ nvidia,core-pwr-off-time = <3875>;
+ nvidia,sys-clock-req-active-high;
};
usb@c5008000 {
diff --git a/arch/arm/boot/dts/tegra20-tec.dts b/arch/arm/boot/dts/tegra20-tec.dts
index 402b21004bef..742f0b38d21d 100644
--- a/arch/arm/boot/dts/tegra20-tec.dts
+++ b/arch/arm/boot/dts/tegra20-tec.dts
@@ -52,5 +52,8 @@
nvidia,spkr-en-gpios = <&wm8903 2 0>;
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts
index a9f3f06580f5..9cc78a15d739 100644
--- a/arch/arm/boot/dts/tegra20-trimslice.dts
+++ b/arch/arm/boot/dts/tegra20-trimslice.dts
@@ -300,6 +300,15 @@
};
};
+ pmc {
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <5000>;
+ nvidia,cpu-pwr-off-time = <5000>;
+ nvidia,core-pwr-good-time = <3845 3845>;
+ nvidia,core-pwr-off-time = <3875>;
+ nvidia,sys-clock-req-active-high;
+ };
+
usb@c5000000 {
status = "okay";
nvidia,vbus-gpio = <&gpio 170 0>; /* gpio PV2 */
@@ -343,6 +352,17 @@
};
};
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ power {
+ label = "Power";
+ gpios = <&gpio 190 1>; /* gpio PX6, active low */
+ linux,code = <116>; /* KEY_POWER */
+ gpio-key,wakeup;
+ };
+ };
+
poweroff {
compatible = "gpio-poweroff";
gpios = <&gpio 191 1>; /* gpio PX7, active low */
@@ -376,5 +396,8 @@
compatible = "nvidia,tegra-audio-trimslice";
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,audio-codec = <&codec>;
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts
index f544806e9618..dd38f1f03834 100644
--- a/arch/arm/boot/dts/tegra20-ventana.dts
+++ b/arch/arm/boot/dts/tegra20-ventana.dts
@@ -493,6 +493,12 @@
pmc {
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <2000>;
+ nvidia,cpu-pwr-off-time = <100>;
+ nvidia,core-pwr-good-time = <3845 3845>;
+ nvidia,core-pwr-off-time = <458>;
+ nvidia,sys-clock-req-active-high;
};
usb@c5000000 {
@@ -516,6 +522,7 @@
status = "okay";
power-gpios = <&gpio 86 0>; /* gpio PK6 */
bus-width = <4>;
+ keep-power-in-suspend;
};
sdhci@c8000400 {
@@ -529,6 +536,7 @@
sdhci@c8000600 {
status = "okay";
bus-width = <8>;
+ non-removable;
};
clocks {
@@ -544,6 +552,17 @@
};
};
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ power {
+ label = "Power";
+ gpios = <&gpio 170 1>; /* gpio PV2, active low */
+ linux,code = <116>; /* KEY_POWER */
+ gpio-key,wakeup;
+ };
+ };
+
regulators {
compatible = "simple-bus";
#address-cells = <1>;
@@ -620,5 +639,8 @@
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
nvidia,int-mic-en-gpios = <&gpio 184 0>; /* gpio PX0 */
nvidia,ext-mic-en-gpios = <&gpio 185 0>; /* gpio PX1 */
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts
index 258cf945f515..d2567f83aaff 100644
--- a/arch/arm/boot/dts/tegra20-whistler.dts
+++ b/arch/arm/boot/dts/tegra20-whistler.dts
@@ -496,6 +496,14 @@
pmc {
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <2000>;
+ nvidia,cpu-pwr-off-time = <1000>;
+ nvidia,core-pwr-good-time = <0 3845>;
+ nvidia,core-pwr-off-time = <93727>;
+ nvidia,core-power-req-active-high;
+ nvidia,sys-clock-req-active-high;
+ nvidia,combined-power-req;
};
usb@c5000000 {
@@ -518,6 +526,7 @@
sdhci@c8000600 {
status = "okay";
bus-width = <8>;
+ non-removable;
};
clocks {
@@ -539,6 +548,7 @@
nvidia,repeat-delay-ms = <160>;
nvidia,kbc-row-pins = <0 1 2>;
nvidia,kbc-col-pins = <16 17>;
+ nvidia,wakeup-source;
linux,keymap = <0x00000074 /* KEY_POWER */
0x01000066 /* KEY_HOME */
0x0101009E /* KEY_BACK */
@@ -573,5 +583,8 @@
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,audio-codec = <&codec>;
+
+ clocks = <&tegra_car 112>, <&tegra_car 113>, <&tegra_car 94>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index fc7febc2b386..56a91106041b 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -209,7 +209,7 @@
compatible = "nvidia,tegra20-das";
reg = <0x70000c00 0x80>;
};
-
+
tegra_ac97: ac97 {
compatible = "nvidia,tegra20-ac97";
reg = <0x70002000 0x200>;
@@ -299,6 +299,7 @@
reg = <0x7000a000 0x100>;
#pwm-cells = <2>;
clocks = <&tegra_car 17>;
+ status = "disabled";
};
rtc {
@@ -442,31 +443,6 @@
#size-cells = <0>;
};
- phy1: usb-phy@c5000400 {
- compatible = "nvidia,tegra20-usb-phy";
- reg = <0xc5000400 0x3c00>;
- phy_type = "utmi";
- nvidia,has-legacy-mode;
- clocks = <&tegra_car 22>, <&tegra_car 127>;
- clock-names = "phy", "pll_u";
- };
-
- phy2: usb-phy@c5004400 {
- compatible = "nvidia,tegra20-usb-phy";
- reg = <0xc5004400 0x3c00>;
- phy_type = "ulpi";
- clocks = <&tegra_car 94>, <&tegra_car 127>;
- clock-names = "phy", "pll_u";
- };
-
- phy3: usb-phy@c5008400 {
- compatible = "nvidia,tegra20-usb-phy";
- reg = <0xc5008400 0x3C00>;
- phy_type = "utmi";
- clocks = <&tegra_car 22>, <&tegra_car 127>;
- clock-names = "phy", "pll_u";
- };
-
usb@c5000000 {
compatible = "nvidia,tegra20-ehci", "usb-ehci";
reg = <0xc5000000 0x4000>;
@@ -479,6 +455,15 @@
status = "disabled";
};
+ phy1: usb-phy@c5000400 {
+ compatible = "nvidia,tegra20-usb-phy";
+ reg = <0xc5000400 0x3c00>;
+ phy_type = "utmi";
+ nvidia,has-legacy-mode;
+ clocks = <&tegra_car 22>, <&tegra_car 127>;
+ clock-names = "phy", "pll_u";
+ };
+
usb@c5004000 {
compatible = "nvidia,tegra20-ehci", "usb-ehci";
reg = <0xc5004000 0x4000>;
@@ -489,6 +474,14 @@
status = "disabled";
};
+ phy2: usb-phy@c5004400 {
+ compatible = "nvidia,tegra20-usb-phy";
+ reg = <0xc5004400 0x3c00>;
+ phy_type = "ulpi";
+ clocks = <&tegra_car 93>, <&tegra_car 127>;
+ clock-names = "phy", "pll_u";
+ };
+
usb@c5008000 {
compatible = "nvidia,tegra20-ehci", "usb-ehci";
reg = <0xc5008000 0x4000>;
@@ -499,6 +492,14 @@
status = "disabled";
};
+ phy3: usb-phy@c5008400 {
+ compatible = "nvidia,tegra20-usb-phy";
+ reg = <0xc5008400 0x3c00>;
+ phy_type = "utmi";
+ clocks = <&tegra_car 22>, <&tegra_car 127>;
+ clock-names = "phy", "pll_u";
+ };
+
sdhci@c8000000 {
compatible = "nvidia,tegra20-sdhci";
reg = <0xc8000000 0x200>;
diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts
index 6248b2445b32..b732f7c13a66 100644
--- a/arch/arm/boot/dts/tegra30-beaver.dts
+++ b/arch/arm/boot/dts/tegra30-beaver.dts
@@ -253,6 +253,13 @@
pmc {
status = "okay";
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <2000>;
+ nvidia,cpu-pwr-off-time = <200>;
+ nvidia,core-pwr-good-time = <3845 3845>;
+ nvidia,core-pwr-off-time = <0>;
+ nvidia,core-power-req-active-high;
+ nvidia,sys-clock-req-active-high;
};
sdhci@78000000 {
@@ -266,6 +273,7 @@
sdhci@78000600 {
status = "okay";
bus-width = <8>;
+ non-removable;
};
clocks {
diff --git a/arch/arm/boot/dts/tegra30-cardhu-a02.dts b/arch/arm/boot/dts/tegra30-cardhu-a02.dts
index adc88aa50eb6..e392bd2dab9b 100644
--- a/arch/arm/boot/dts/tegra30-cardhu-a02.dts
+++ b/arch/arm/boot/dts/tegra30-cardhu-a02.dts
@@ -88,6 +88,7 @@
status = "okay";
power-gpios = <&gpio 28 0>; /* gpio PD4 */
bus-width = <4>;
+ keep-power-in-suspend;
};
};
diff --git a/arch/arm/boot/dts/tegra30-cardhu-a04.dts b/arch/arm/boot/dts/tegra30-cardhu-a04.dts
index 08163e145d57..d0db6c7e774f 100644
--- a/arch/arm/boot/dts/tegra30-cardhu-a04.dts
+++ b/arch/arm/boot/dts/tegra30-cardhu-a04.dts
@@ -100,5 +100,6 @@
status = "okay";
power-gpios = <&gpio 27 0>; /* gpio PD3 */
bus-width = <4>;
+ keep-power-in-suspend;
};
};
diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi
index 65bf2b63174e..01b4c26fad96 100644
--- a/arch/arm/boot/dts/tegra30-cardhu.dtsi
+++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi
@@ -307,6 +307,13 @@
pmc {
status = "okay";
nvidia,invert-interrupt;
+ nvidia,suspend-mode = <2>;
+ nvidia,cpu-pwr-good-time = <2000>;
+ nvidia,cpu-pwr-off-time = <200>;
+ nvidia,core-pwr-good-time = <3845 3845>;
+ nvidia,core-pwr-off-time = <0>;
+ nvidia,core-power-req-active-high;
+ nvidia,sys-clock-req-active-high;
};
sdhci@78000000 {
@@ -320,6 +327,7 @@
sdhci@78000600 {
status = "okay";
bus-width = <8>;
+ non-removable;
};
clocks {
@@ -509,5 +517,8 @@
nvidia,spkr-en-gpios = <&wm8903 2 0>;
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
+
+ clocks = <&tegra_car 184>, <&tegra_car 185>, <&tegra_car 120>;
+ clock-names = "pll_a", "pll_a_out0", "mclk";
};
};
diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index 9fe7a92b4c85..15ded605142a 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -286,6 +286,7 @@
reg = <0x7000a000 0x100>;
#pwm-cells = <2>;
clocks = <&tegra_car 17>;
+ status = "disabled";
};
rtc {
diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi
index ed0bc9546837..b3034da00a37 100644
--- a/arch/arm/boot/dts/twl4030.dtsi
+++ b/arch/arm/boot/dts/twl4030.dtsi
@@ -23,6 +23,12 @@
compatible = "ti,twl4030-wdt";
};
+ vcc: regulator-vdd1 {
+ compatible = "ti,twl4030-vdd1";
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <1450000>;
+ };
+
vdac: regulator-vdac {
compatible = "ti,twl4030-vdac";
regulator-min-microvolt = <1800000>;
@@ -67,7 +73,7 @@
#interrupt-cells = <1>;
};
- twl4030-usb {
+ usb2_phy: twl4030-usb {
compatible = "ti,twl4030-usb";
interrupts = <10>, <4>;
usb1v5-supply = <&vusb1v5>;
@@ -75,4 +81,14 @@
usb3v1-supply = <&vusb3v1>;
usb_mode = <1>;
};
+
+ twl_pwm: pwm {
+ compatible = "ti,twl4030-pwm";
+ #pwm-cells = <2>;
+ };
+
+ twl_pwmled: pwmled {
+ compatible = "ti,twl4030-pwmled";
+ #pwm-cells = <2>;
+ };
};
diff --git a/arch/arm/boot/dts/twl6030.dtsi b/arch/arm/boot/dts/twl6030.dtsi
index 9996cfc5ee80..2e3bd3172b23 100644
--- a/arch/arm/boot/dts/twl6030.dtsi
+++ b/arch/arm/boot/dts/twl6030.dtsi
@@ -91,4 +91,16 @@
compatible = "ti,twl6030-usb";
interrupts = <4>, <10>;
};
+
+ twl_pwm: pwm {
+ /* provides two PWMs (id 0, 1 for PWM1 and PWM2) */
+ compatible = "ti,twl6030-pwm";
+ #pwm-cells = <2>;
+ };
+
+ twl_pwmled: pwmled {
+ /* provides one PWM (id 0 for Charging indicator LED) */
+ compatible = "ti,twl6030-pwmled";
+ #pwm-cells = <2>;
+ };
};
diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts
index e2fe3195c0d1..dde75ae8b4b1 100644
--- a/arch/arm/boot/dts/versatile-ab.dts
+++ b/arch/arm/boot/dts/versatile-ab.dts
@@ -121,6 +121,18 @@
interrupts = <0>;
};
+ timer@101e2000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0x101e2000 0x1000>;
+ interrupts = <4>;
+ };
+
+ timer@101e3000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0x101e3000 0x1000>;
+ interrupts = <5>;
+ };
+
gpio0: gpio@101e4000 {
compatible = "arm,pl061", "arm,primecell";
reg = <0x101e4000 0x1000>;
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca9.dts b/arch/arm/boot/dts/vexpress-v2p-ca9.dts
index 1420bb14d95c..62d9b225dcce 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca9.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca9.dts
@@ -98,6 +98,7 @@
<0 49 4>;
clocks = <&oscclk2>, <&oscclk2>;
clock-names = "timclk", "apb_pclk";
+ status = "disabled";
};
watchdog@100e5000 {
diff --git a/arch/arm/boot/dts/xenvm-4.2.dts b/arch/arm/boot/dts/xenvm-4.2.dts
index ec3f9528e180..336915151398 100644
--- a/arch/arm/boot/dts/xenvm-4.2.dts
+++ b/arch/arm/boot/dts/xenvm-4.2.dts
@@ -29,6 +29,19 @@
compatible = "arm,cortex-a15";
reg = <0>;
};
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <1>;
+ };
+ };
+
+ psci {
+ compatible = "arm,psci";
+ method = "hvc";
+ cpu_off = <1>;
+ cpu_on = <2>;
};
memory@80000000 {
diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index 748fc347ed18..14fb2e609bab 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -136,5 +136,12 @@
clock-names = "cpu_1x";
clock-ranges;
};
+ scutimer: scutimer@f8f00600 {
+ interrupt-parent = <&intc>;
+ interrupts = < 1 13 0x301 >;
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = < 0xf8f00600 0x20 >;
+ clocks = <&cpu_clk 1>;
+ } ;
};
};
diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c
index 9d2d3ba339ff..ddc740769601 100644
--- a/arch/arm/common/timer-sp.c
+++ b/arch/arm/common/timer-sp.c
@@ -25,33 +25,29 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <asm/sched_clock.h>
#include <asm/hardware/arm_timer.h>
+#include <asm/hardware/timer-sp.h>
-static long __init sp804_get_clock_rate(const char *name)
+static long __init sp804_get_clock_rate(struct clk *clk)
{
- struct clk *clk;
long rate;
int err;
- clk = clk_get_sys("sp804", name);
- if (IS_ERR(clk)) {
- pr_err("sp804: %s clock not found: %d\n", name,
- (int)PTR_ERR(clk));
- return PTR_ERR(clk);
- }
-
err = clk_prepare(clk);
if (err) {
- pr_err("sp804: %s clock failed to prepare: %d\n", name, err);
+ pr_err("sp804: clock failed to prepare: %d\n", err);
clk_put(clk);
return err;
}
err = clk_enable(clk);
if (err) {
- pr_err("sp804: %s clock failed to enable: %d\n", name, err);
+ pr_err("sp804: clock failed to enable: %d\n", err);
clk_unprepare(clk);
clk_put(clk);
return err;
@@ -59,7 +55,7 @@ static long __init sp804_get_clock_rate(const char *name)
rate = clk_get_rate(clk);
if (rate < 0) {
- pr_err("sp804: %s clock failed to get rate: %ld\n", name, rate);
+ pr_err("sp804: clock failed to get rate: %ld\n", rate);
clk_disable(clk);
clk_unprepare(clk);
clk_put(clk);
@@ -77,9 +73,21 @@ static u32 sp804_read(void)
void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
const char *name,
+ struct clk *clk,
int use_sched_clock)
{
- long rate = sp804_get_clock_rate(name);
+ long rate;
+
+ if (!clk) {
+ clk = clk_get_sys("sp804", name);
+ if (IS_ERR(clk)) {
+ pr_err("sp804: clock not found: %d\n",
+ (int)PTR_ERR(clk));
+ return;
+ }
+ }
+
+ rate = sp804_get_clock_rate(clk);
if (rate < 0)
return;
@@ -171,12 +179,20 @@ static struct irqaction sp804_timer_irq = {
.dev_id = &sp804_clockevent,
};
-void __init sp804_clockevents_init(void __iomem *base, unsigned int irq,
- const char *name)
+void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name)
{
struct clock_event_device *evt = &sp804_clockevent;
- long rate = sp804_get_clock_rate(name);
+ long rate;
+ if (!clk)
+ clk = clk_get_sys("sp804", name);
+ if (IS_ERR(clk)) {
+ pr_err("sp804: %s clock not found: %d\n", name,
+ (int)PTR_ERR(clk));
+ return;
+ }
+
+ rate = sp804_get_clock_rate(clk);
if (rate < 0)
return;
@@ -186,6 +202,98 @@ void __init sp804_clockevents_init(void __iomem *base, unsigned int irq,
evt->irq = irq;
evt->cpumask = cpu_possible_mask;
+ writel(0, base + TIMER_CTRL);
+
setup_irq(irq, &sp804_timer_irq);
clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
}
+
+static void __init sp804_of_init(struct device_node *np)
+{
+ static bool initialized = false;
+ void __iomem *base;
+ int irq;
+ u32 irq_num = 0;
+ struct clk *clk1, *clk2;
+ const char *name = of_get_property(np, "compatible", NULL);
+
+ base = of_iomap(np, 0);
+ if (WARN_ON(!base))
+ return;
+
+ /* Ensure timers are disabled */
+ writel(0, base + TIMER_CTRL);
+ writel(0, base + TIMER_2_BASE + TIMER_CTRL);
+
+ if (initialized || !of_device_is_available(np))
+ goto err;
+
+ clk1 = of_clk_get(np, 0);
+ if (IS_ERR(clk1))
+ clk1 = NULL;
+
+ /* Get the 2nd clock if the timer has 2 timer clocks */
+ if (of_count_phandle_with_args(np, "clocks", "#clock-cells") == 3) {
+ clk2 = of_clk_get(np, 1);
+ if (IS_ERR(clk2)) {
+ pr_err("sp804: %s clock not found: %d\n", np->name,
+ (int)PTR_ERR(clk2));
+ goto err;
+ }
+ } else
+ clk2 = clk1;
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq <= 0)
+ goto err;
+
+ of_property_read_u32(np, "arm,sp804-has-irq", &irq_num);
+ if (irq_num == 2) {
+ __sp804_clockevents_init(base + TIMER_2_BASE, irq, clk2, name);
+ __sp804_clocksource_and_sched_clock_init(base, name, clk1, 1);
+ } else {
+ __sp804_clockevents_init(base, irq, clk1 , name);
+ __sp804_clocksource_and_sched_clock_init(base + TIMER_2_BASE,
+ name, clk2, 1);
+ }
+ initialized = true;
+
+ return;
+err:
+ iounmap(base);
+}
+CLOCKSOURCE_OF_DECLARE(sp804, "arm,sp804", sp804_of_init);
+
+static void __init integrator_cp_of_init(struct device_node *np)
+{
+ static int init_count = 0;
+ void __iomem *base;
+ int irq;
+ const char *name = of_get_property(np, "compatible", NULL);
+
+ base = of_iomap(np, 0);
+ if (WARN_ON(!base))
+ return;
+
+ /* Ensure timer is disabled */
+ writel(0, base + TIMER_CTRL);
+
+ if (init_count == 2 || !of_device_is_available(np))
+ goto err;
+
+ if (!init_count)
+ sp804_clocksource_init(base, name);
+ else {
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq <= 0)
+ goto err;
+
+ sp804_clockevents_init(base, irq, name);
+ }
+
+ init_count++;
+ return;
+err:
+ iounmap(base);
+}
+CLOCKSOURCE_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
diff --git a/arch/arm/configs/bockw_defconfig b/arch/arm/configs/bockw_defconfig
new file mode 100644
index 000000000000..6524cdf3b08d
--- /dev/null
+++ b/arch/arm/configs/bockw_defconfig
@@ -0,0 +1,94 @@
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_KERNEL_LZMA=y
+CONFIG_NO_HZ=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_SHMOBILE=y
+CONFIG_ARCH_R8A7778=y
+CONFIG_MACH_BOCKW=y
+CONFIG_MEMORY_START=0x60000000
+CONFIG_MEMORY_SIZE=0x10000000
+CONFIG_SHMOBILE_TIMER_HZ=1024
+# CONFIG_SH_TIMER_CMT is not set
+# CONFIG_EM_TIMER_STI is not set
+CONFIG_ARM_ERRATA_430973=y
+CONFIG_ARM_ERRATA_458693=y
+CONFIG_ARM_ERRATA_460075=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_CMDLINE="console=ttySC0,115200 ignore_loglevel root=/dev/nfs ip=dhcp"
+CONFIG_CMDLINE_FORCE=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+CONFIG_SMSC911X=y
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=6
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_UIO=y
+CONFIG_UIO_PDRV_GENIRQ=y
+# CONFIG_IOMMU_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_NFS_V4_1=y
+CONFIG_ROOT_NFS=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_FTRACE is not set
+# CONFIG_ARM_UNWIND is not set
+CONFIG_AVERAGE=y
diff --git a/arch/arm/configs/imx_v4_v5_defconfig b/arch/arm/configs/imx_v4_v5_defconfig
index 02c657af4005..f07a847b00c9 100644
--- a/arch/arm/configs/imx_v4_v5_defconfig
+++ b/arch/arm/configs/imx_v4_v5_defconfig
@@ -109,6 +109,7 @@ CONFIG_I2C_IMX=y
CONFIG_SPI=y
CONFIG_SPI_IMX=y
CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
CONFIG_W1=y
CONFIG_W1_MASTER_MXC=y
CONFIG_W1_SLAVE_THERM=y
diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig
index 088d6c11a0fa..6ec010f248b5 100644
--- a/arch/arm/configs/imx_v6_v7_defconfig
+++ b/arch/arm/configs/imx_v6_v7_defconfig
@@ -9,6 +9,7 @@ CONFIG_CGROUPS=y
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_EXPERT=y
+CONFIG_PERF_EVENTS=y
# CONFIG_SLUB_DEBUG is not set
# CONFIG_COMPAT_BRK is not set
CONFIG_MODULES=y
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h
index 7ade91d8cc6f..7c1bfc0aea0c 100644
--- a/arch/arm/include/asm/arch_timer.h
+++ b/arch/arm/include/asm/arch_timer.h
@@ -10,8 +10,7 @@
#include <clocksource/arm_arch_timer.h>
#ifdef CONFIG_ARM_ARCH_TIMER
-int arch_timer_of_register(void);
-int arch_timer_sched_clock_init(void);
+int arch_timer_arch_init(void);
/*
* These register accessors are marked inline so the compiler can
@@ -110,16 +109,6 @@ static inline void __cpuinit arch_counter_set_user_access(void)
asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
}
-#else
-static inline int arch_timer_of_register(void)
-{
- return -ENXIO;
-}
-
-static inline int arch_timer_sched_clock_init(void)
-{
- return -ENXIO;
-}
#endif
#endif
diff --git a/arch/arm/include/asm/hardware/timer-sp.h b/arch/arm/include/asm/hardware/timer-sp.h
index 2dd9d3f83f29..bb28af7c32de 100644
--- a/arch/arm/include/asm/hardware/timer-sp.h
+++ b/arch/arm/include/asm/hardware/timer-sp.h
@@ -1,15 +1,23 @@
+struct clk;
+
void __sp804_clocksource_and_sched_clock_init(void __iomem *,
- const char *, int);
+ const char *, struct clk *, int);
+void __sp804_clockevents_init(void __iomem *, unsigned int,
+ struct clk *, const char *);
static inline void sp804_clocksource_init(void __iomem *base, const char *name)
{
- __sp804_clocksource_and_sched_clock_init(base, name, 0);
+ __sp804_clocksource_and_sched_clock_init(base, name, NULL, 0);
}
static inline void sp804_clocksource_and_sched_clock_init(void __iomem *base,
const char *name)
{
- __sp804_clocksource_and_sched_clock_init(base, name, 1);
+ __sp804_clocksource_and_sched_clock_init(base, name, NULL, 1);
}
-void sp804_clockevents_init(void __iomem *, unsigned int, const char *);
+static inline void sp804_clockevents_init(void __iomem *base, unsigned int irq, const char *name)
+{
+ __sp804_clockevents_init(base, irq, NULL, name);
+
+}
diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h
index e3f757263438..3d520ddca61b 100644
--- a/arch/arm/include/asm/sched_clock.h
+++ b/arch/arm/include/asm/sched_clock.h
@@ -11,4 +11,6 @@
extern void sched_clock_postinit(void);
extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate);
+extern unsigned long long (*sched_clock_func)(void);
+
#endif
diff --git a/arch/arm/include/asm/xen/hypercall.h b/arch/arm/include/asm/xen/hypercall.h
index 8a823253d775..799f42ecca63 100644
--- a/arch/arm/include/asm/xen/hypercall.h
+++ b/arch/arm/include/asm/xen/hypercall.h
@@ -46,6 +46,7 @@ int HYPERVISOR_event_channel_op(int cmd, void *arg);
unsigned long HYPERVISOR_hvm_op(int op, void *arg);
int HYPERVISOR_memory_op(unsigned int cmd, void *arg);
int HYPERVISOR_physdev_op(int cmd, void *arg);
+int HYPERVISOR_vcpu_op(int cmd, int vcpuid, void *extra_args);
static inline void
MULTI_update_va_mapping(struct multicall_entry *mcl, unsigned long va,
diff --git a/arch/arm/include/debug/mvebu.S b/arch/arm/include/debug/mvebu.S
index 865c6d02b332..df191afa3be1 100644
--- a/arch/arm/include/debug/mvebu.S
+++ b/arch/arm/include/debug/mvebu.S
@@ -12,7 +12,7 @@
*/
#define ARMADA_370_XP_REGS_PHYS_BASE 0xd0000000
-#define ARMADA_370_XP_REGS_VIRT_BASE 0xfeb00000
+#define ARMADA_370_XP_REGS_VIRT_BASE 0xfec00000
.macro addruart, rp, rv, tmp
ldr \rp, =ARMADA_370_XP_REGS_PHYS_BASE
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
index d957a51435d8..59dcdced6e30 100644
--- a/arch/arm/kernel/arch_timer.c
+++ b/arch/arm/kernel/arch_timer.c
@@ -22,9 +22,11 @@ static unsigned long arch_timer_read_counter_long(void)
return arch_timer_read_counter();
}
-static u32 arch_timer_read_counter_u32(void)
+static u32 sched_clock_mult __read_mostly;
+
+static unsigned long long notrace arch_timer_sched_clock(void)
{
- return arch_timer_read_counter();
+ return arch_timer_read_counter() * sched_clock_mult;
}
static struct delay_timer arch_delay_timer;
@@ -37,25 +39,20 @@ static void __init arch_timer_delay_timer_register(void)
register_current_timer_delay(&arch_delay_timer);
}
-int __init arch_timer_of_register(void)
+int __init arch_timer_arch_init(void)
{
- int ret;
+ u32 arch_timer_rate = arch_timer_get_rate();
- ret = arch_timer_init();
- if (ret)
- return ret;
+ if (arch_timer_rate == 0)
+ return -ENXIO;
arch_timer_delay_timer_register();
- return 0;
-}
-
-int __init arch_timer_sched_clock_init(void)
-{
- if (arch_timer_get_rate() == 0)
- return -ENXIO;
+ /* Cache the sched_clock multiplier to save a divide in the hot path. */
+ sched_clock_mult = NSEC_PER_SEC / arch_timer_rate;
+ sched_clock_func = arch_timer_sched_clock;
+ pr_info("sched_clock: ARM arch timer >56 bits at %ukHz, resolution %uns\n",
+ arch_timer_rate / 1000, sched_clock_mult);
- setup_sched_clock(arch_timer_read_counter_u32,
- 32, arch_timer_get_rate());
return 0;
}
diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c
index 59d2adb764a9..e8edcaa0e432 100644
--- a/arch/arm/kernel/sched_clock.c
+++ b/arch/arm/kernel/sched_clock.c
@@ -20,6 +20,7 @@ struct clock_data {
u64 epoch_ns;
u32 epoch_cyc;
u32 epoch_cyc_copy;
+ unsigned long rate;
u32 mult;
u32 shift;
bool suspended;
@@ -113,11 +114,14 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
u64 res, wrap;
char r_unit;
+ if (cd.rate > rate)
+ return;
+
BUG_ON(bits > 32);
WARN_ON(!irqs_disabled());
- WARN_ON(read_sched_clock != jiffy_sched_clock_read);
read_sched_clock = read;
sched_clock_mask = (1 << bits) - 1;
+ cd.rate = rate;
/* calculate the mult/shift to convert counter ticks to ns. */
clocks_calc_mult_shift(&cd.mult, &cd.shift, rate, NSEC_PER_SEC, 0);
@@ -161,12 +165,19 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
pr_debug("Registered %pF as sched_clock source\n", read);
}
-unsigned long long notrace sched_clock(void)
+static unsigned long long notrace sched_clock_32(void)
{
u32 cyc = read_sched_clock();
return cyc_to_sched_clock(cyc, sched_clock_mask);
}
+unsigned long long __read_mostly (*sched_clock_func)(void) = sched_clock_32;
+
+unsigned long long notrace sched_clock(void)
+{
+ return sched_clock_func();
+}
+
void __init sched_clock_postinit(void)
{
/*
diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c
index 955d92d265e5..abff4e9aaee0 100644
--- a/arch/arm/kernel/time.c
+++ b/arch/arm/kernel/time.c
@@ -22,6 +22,7 @@
#include <linux/errno.h>
#include <linux/profile.h>
#include <linux/timer.h>
+#include <linux/clocksource.h>
#include <linux/irq.h>
#include <asm/thread_info.h>
@@ -115,6 +116,10 @@ int __init register_persistent_clock(clock_access_fn read_boot,
void __init time_init(void)
{
- machine_desc->init_time();
+ if (machine_desc->init_time)
+ machine_desc->init_time();
+ else
+ clocksource_of_init();
+
sched_clock_postinit();
}
diff --git a/arch/arm/mach-at91/cpuidle.c b/arch/arm/mach-at91/cpuidle.c
index 48f1228c611c..69f9e3bbf4e5 100644
--- a/arch/arm/mach-at91/cpuidle.c
+++ b/arch/arm/mach-at91/cpuidle.c
@@ -36,6 +36,8 @@ static int at91_enter_idle(struct cpuidle_device *dev,
at91rm9200_standby();
else if (cpu_is_at91sam9g45())
at91sam9g45_standby();
+ else if (cpu_is_at91sam9263())
+ at91sam9263_standby();
else
at91sam9_standby();
diff --git a/arch/arm/mach-at91/include/mach/cpu.h b/arch/arm/mach-at91/include/mach/cpu.h
index 0f3379fe645f..d3d7b993846b 100644
--- a/arch/arm/mach-at91/include/mach/cpu.h
+++ b/arch/arm/mach-at91/include/mach/cpu.h
@@ -86,7 +86,7 @@ enum at91_soc_type {
AT91_SOC_SAMA5D3,
/* Unknown type */
- AT91_SOC_NONE
+ AT91_SOC_UNKNOWN,
};
enum at91_soc_subtype {
@@ -107,8 +107,11 @@ enum at91_soc_subtype {
AT91_SOC_SAMA5D31, AT91_SOC_SAMA5D33, AT91_SOC_SAMA5D34,
AT91_SOC_SAMA5D35,
+ /* No subtype for this SoC */
+ AT91_SOC_SUBTYPE_NONE,
+
/* Unknown subtype */
- AT91_SOC_SUBTYPE_NONE
+ AT91_SOC_SUBTYPE_UNKNOWN,
};
struct at91_socinfo {
@@ -122,7 +125,7 @@ const char *at91_get_soc_subtype(struct at91_socinfo *c);
static inline int at91_soc_is_detected(void)
{
- return at91_soc_initdata.type != AT91_SOC_NONE;
+ return at91_soc_initdata.type != AT91_SOC_UNKNOWN;
}
#ifdef CONFIG_SOC_AT91RM9200
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 73f1f250403a..530db304ec5e 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -270,6 +270,8 @@ static int at91_pm_enter(suspend_state_t state)
at91rm9200_standby();
else if (cpu_is_at91sam9g45())
at91sam9g45_standby();
+ else if (cpu_is_at91sam9263())
+ at91sam9263_standby();
else
at91sam9_standby();
break;
diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
index 38f467c6b710..2f5908f0b8c5 100644
--- a/arch/arm/mach-at91/pm.h
+++ b/arch/arm/mach-at91/pm.h
@@ -70,13 +70,31 @@ static inline void at91sam9g45_standby(void)
at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1);
}
-#ifdef CONFIG_SOC_AT91SAM9263
-/*
- * FIXME either or both the SDRAM controllers (EB0, EB1) might be in use;
- * handle those cases both here and in the Suspend-To-RAM support.
+/* We manage both DDRAM/SDRAM controllers, we need more than one value to
+ * remember.
*/
-#warning Assuming EB1 SDRAM controller is *NOT* used
-#endif
+static inline void at91sam9263_standby(void)
+{
+ u32 lpr0, lpr1;
+ u32 saved_lpr0, saved_lpr1;
+
+ saved_lpr1 = at91_ramc_read(1, AT91_SDRAMC_LPR);
+ lpr1 = saved_lpr1 & ~AT91_SDRAMC_LPCB;
+ lpr1 |= AT91_SDRAMC_LPCB_SELF_REFRESH;
+
+ saved_lpr0 = at91_ramc_read(0, AT91_SDRAMC_LPR);
+ lpr0 = saved_lpr0 & ~AT91_SDRAMC_LPCB;
+ lpr0 |= AT91_SDRAMC_LPCB_SELF_REFRESH;
+
+ /* self-refresh mode now */
+ at91_ramc_write(0, AT91_SDRAMC_LPR, lpr0);
+ at91_ramc_write(1, AT91_SDRAMC_LPR, lpr1);
+
+ cpu_do_idle();
+
+ at91_ramc_write(0, AT91_SDRAMC_LPR, saved_lpr0);
+ at91_ramc_write(1, AT91_SDRAMC_LPR, saved_lpr1);
+}
static inline void at91sam9_standby(void)
{
diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
index e8491e77b1f7..e2f4bdd146d6 100644
--- a/arch/arm/mach-at91/setup.c
+++ b/arch/arm/mach-at91/setup.c
@@ -105,28 +105,32 @@ static void __init soc_detect(u32 dbgu_base)
switch (socid) {
case ARCH_ID_AT91RM9200:
at91_soc_initdata.type = AT91_SOC_RM9200;
- if (at91_soc_initdata.subtype == AT91_SOC_SUBTYPE_NONE)
+ if (at91_soc_initdata.subtype == AT91_SOC_SUBTYPE_UNKNOWN)
at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA;
at91_boot_soc = at91rm9200_soc;
break;
case ARCH_ID_AT91SAM9260:
at91_soc_initdata.type = AT91_SOC_SAM9260;
+ at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_NONE;
at91_boot_soc = at91sam9260_soc;
break;
case ARCH_ID_AT91SAM9261:
at91_soc_initdata.type = AT91_SOC_SAM9261;
+ at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_NONE;
at91_boot_soc = at91sam9261_soc;
break;
case ARCH_ID_AT91SAM9263:
at91_soc_initdata.type = AT91_SOC_SAM9263;
+ at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_NONE;
at91_boot_soc = at91sam9263_soc;
break;
case ARCH_ID_AT91SAM9G20:
at91_soc_initdata.type = AT91_SOC_SAM9G20;
+ at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_NONE;
at91_boot_soc = at91sam9260_soc;
break;
@@ -139,6 +143,7 @@ static void __init soc_detect(u32 dbgu_base)
case ARCH_ID_AT91SAM9RL64:
at91_soc_initdata.type = AT91_SOC_SAM9RL;
+ at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_NONE;
at91_boot_soc = at91sam9rl_soc;
break;
@@ -161,6 +166,7 @@ static void __init soc_detect(u32 dbgu_base)
/* at91sam9g10 */
if ((socid & ~AT91_CIDR_EXT) == ARCH_ID_AT91SAM9G10) {
at91_soc_initdata.type = AT91_SOC_SAM9G10;
+ at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_NONE;
at91_boot_soc = at91sam9261_soc;
}
/* at91sam9xe */
@@ -242,7 +248,7 @@ static const char *soc_name[] = {
[AT91_SOC_SAM9X5] = "at91sam9x5",
[AT91_SOC_SAM9N12] = "at91sam9n12",
[AT91_SOC_SAMA5D3] = "sama5d3",
- [AT91_SOC_NONE] = "Unknown"
+ [AT91_SOC_UNKNOWN] = "Unknown",
};
const char *at91_get_soc_type(struct at91_socinfo *c)
@@ -268,7 +274,8 @@ static const char *soc_subtype_name[] = {
[AT91_SOC_SAMA5D33] = "sama5d33",
[AT91_SOC_SAMA5D34] = "sama5d34",
[AT91_SOC_SAMA5D35] = "sama5d35",
- [AT91_SOC_SUBTYPE_NONE] = "Unknown"
+ [AT91_SOC_SUBTYPE_NONE] = "None",
+ [AT91_SOC_SUBTYPE_UNKNOWN] = "Unknown",
};
const char *at91_get_soc_subtype(struct at91_socinfo *c)
@@ -282,8 +289,8 @@ void __init at91_map_io(void)
/* Map peripherals */
iotable_init(&at91_io_desc, 1);
- at91_soc_initdata.type = AT91_SOC_NONE;
- at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_NONE;
+ at91_soc_initdata.type = AT91_SOC_UNKNOWN;
+ at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_UNKNOWN;
soc_detect(AT91_BASE_DBGU0);
if (!at91_soc_is_detected())
@@ -294,8 +301,9 @@ void __init at91_map_io(void)
pr_info("AT91: Detected soc type: %s\n",
at91_get_soc_type(&at91_soc_initdata));
- pr_info("AT91: Detected soc subtype: %s\n",
- at91_get_soc_subtype(&at91_soc_initdata));
+ if (at91_soc_initdata.subtype != AT91_SOC_SUBTYPE_NONE)
+ pr_info("AT91: Detected soc subtype: %s\n",
+ at91_get_soc_subtype(&at91_soc_initdata));
if (!at91_soc_is_enabled())
panic("AT91: Soc not enabled");
diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c
index b1c0a5958275..961aea8bbad5 100644
--- a/arch/arm/mach-davinci/da8xx-dt.c
+++ b/arch/arm/mach-davinci/da8xx-dt.c
@@ -41,6 +41,12 @@ static struct of_dev_auxdata da850_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("ti,davinci-i2c", 0x01c22000, "i2c_davinci.1", NULL),
OF_DEV_AUXDATA("ti,davinci-wdt", 0x01c21000, "watchdog", NULL),
OF_DEV_AUXDATA("ti,da830-mmc", 0x01c40000, "da830-mmc.0", NULL),
+ OF_DEV_AUXDATA("ti,da850-ehrpwm", 0x01f00000, "ehrpwm", NULL),
+ OF_DEV_AUXDATA("ti,da850-ehrpwm", 0x01f02000, "ehrpwm", NULL),
+ OF_DEV_AUXDATA("ti,da850-ecap", 0x01f06000, "ecap", NULL),
+ OF_DEV_AUXDATA("ti,da850-ecap", 0x01f07000, "ecap", NULL),
+ OF_DEV_AUXDATA("ti,da850-ecap", 0x01f08000, "ecap", NULL),
+ OF_DEV_AUXDATA("ti,da830-spi", 0x01f0e000, "spi_davinci.1", NULL),
{}
};
diff --git a/arch/arm/mach-dove/Makefile b/arch/arm/mach-dove/Makefile
index 3f0a858fb597..4d9d2ffc4535 100644
--- a/arch/arm/mach-dove/Makefile
+++ b/arch/arm/mach-dove/Makefile
@@ -1,4 +1,4 @@
-obj-y += common.o addr-map.o irq.o
+obj-y += common.o irq.o
obj-$(CONFIG_DOVE_LEGACY) += mpp.o
obj-$(CONFIG_PCI) += pcie.o
obj-$(CONFIG_MACH_DOVE_DB) += dove-db-setup.o
diff --git a/arch/arm/mach-dove/addr-map.c b/arch/arm/mach-dove/addr-map.c
deleted file mode 100644
index 2a06c0163418..000000000000
--- a/arch/arm/mach-dove/addr-map.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * arch/arm/mach-dove/addr-map.c
- *
- * Address map functions for Marvell Dove 88AP510 SoC
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/mbus.h>
-#include <linux/io.h>
-#include <asm/mach/arch.h>
-#include <asm/setup.h>
-#include <mach/dove.h>
-#include <plat/addr-map.h>
-#include "common.h"
-
-/*
- * Generic Address Decode Windows bit settings
- */
-#define TARGET_DDR 0x0
-#define TARGET_BOOTROM 0x1
-#define TARGET_CESA 0x3
-#define TARGET_PCIE0 0x4
-#define TARGET_PCIE1 0x8
-#define TARGET_SCRATCHPAD 0xd
-
-#define ATTR_CESA 0x01
-#define ATTR_BOOTROM 0xfd
-#define ATTR_DEV_SPI0_ROM 0xfe
-#define ATTR_DEV_SPI1_ROM 0xfb
-#define ATTR_PCIE_IO 0xe0
-#define ATTR_PCIE_MEM 0xe8
-#define ATTR_SCRATCHPAD 0x0
-
-static inline void __iomem *ddr_map_sc(int i)
-{
- return (void __iomem *)(DOVE_MC_VIRT_BASE + 0x100 + ((i) << 4));
-}
-
-/*
- * Description of the windows needed by the platform code
- */
-static struct __initdata orion_addr_map_cfg addr_map_cfg = {
- .num_wins = 8,
- .remappable_wins = 4,
- .bridge_virt_base = BRIDGE_VIRT_BASE,
-};
-
-static const struct __initdata orion_addr_map_info addr_map_info[] = {
- /*
- * Windows for PCIe IO+MEM space.
- */
- { 0, DOVE_PCIE0_IO_PHYS_BASE, DOVE_PCIE0_IO_SIZE,
- TARGET_PCIE0, ATTR_PCIE_IO, DOVE_PCIE0_IO_BUS_BASE
- },
- { 1, DOVE_PCIE1_IO_PHYS_BASE, DOVE_PCIE1_IO_SIZE,
- TARGET_PCIE1, ATTR_PCIE_IO, DOVE_PCIE1_IO_BUS_BASE
- },
- { 2, DOVE_PCIE0_MEM_PHYS_BASE, DOVE_PCIE0_MEM_SIZE,
- TARGET_PCIE0, ATTR_PCIE_MEM, -1
- },
- { 3, DOVE_PCIE1_MEM_PHYS_BASE, DOVE_PCIE1_MEM_SIZE,
- TARGET_PCIE1, ATTR_PCIE_MEM, -1
- },
- /*
- * Window for CESA engine.
- */
- { 4, DOVE_CESA_PHYS_BASE, DOVE_CESA_SIZE,
- TARGET_CESA, ATTR_CESA, -1
- },
- /*
- * Window to the BootROM for Standby and Sleep Resume
- */
- { 5, DOVE_BOOTROM_PHYS_BASE, DOVE_BOOTROM_SIZE,
- TARGET_BOOTROM, ATTR_BOOTROM, -1
- },
- /*
- * Window to the PMU Scratch Pad space
- */
- { 6, DOVE_SCRATCHPAD_PHYS_BASE, DOVE_SCRATCHPAD_SIZE,
- TARGET_SCRATCHPAD, ATTR_SCRATCHPAD, -1
- },
- /* End marker */
- { -1, 0, 0, 0, 0, 0 }
-};
-
-void __init dove_setup_cpu_mbus(void)
-{
- int i;
- int cs;
-
- /*
- * Disable, clear and configure windows.
- */
- orion_config_wins(&addr_map_cfg, addr_map_info);
-
- /*
- * Setup MBUS dram target info.
- */
- orion_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
-
- for (i = 0, cs = 0; i < 2; i++) {
- u32 map = readl(ddr_map_sc(i));
-
- /*
- * Chip select enabled?
- */
- if (map & 1) {
- struct mbus_dram_window *w;
-
- w = &orion_mbus_dram_info.cs[cs++];
- w->cs_index = i;
- w->mbus_attr = 0; /* CS address decoding done inside */
- /* the DDR controller, no need to */
- /* provide attributes */
- w->base = map & 0xff800000;
- w->size = 0x100000 << (((map & 0x000f0000) >> 16) - 4);
- }
- }
- orion_mbus_dram_info.num_cs = cs;
-}
diff --git a/arch/arm/mach-dove/board-dt.c b/arch/arm/mach-dove/board-dt.c
index fbde1dd67113..0b142803b2e1 100644
--- a/arch/arm/mach-dove/board-dt.c
+++ b/arch/arm/mach-dove/board-dt.c
@@ -64,7 +64,7 @@ static void __init dove_dt_init(void)
#ifdef CONFIG_CACHE_TAUROS2
tauros2_init(0);
#endif
- dove_setup_cpu_mbus();
+ dove_setup_cpu_wins();
/* Setup root of clk tree */
dove_of_clk_init();
diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c
index c6b3b2bb50e7..e2b5da031f96 100644
--- a/arch/arm/mach-dove/common.c
+++ b/arch/arm/mach-dove/common.c
@@ -224,6 +224,9 @@ void __init dove_i2c_init(void)
void __init dove_init_early(void)
{
orion_time_set_base(TIMER_VIRT_BASE);
+ mvebu_mbus_init("marvell,dove-mbus",
+ BRIDGE_WINS_BASE, BRIDGE_WINS_SZ,
+ DOVE_MC_WINS_BASE, DOVE_MC_WINS_SZ);
}
static int __init dove_find_tclk(void)
@@ -326,6 +329,40 @@ void __init dove_sdio1_init(void)
platform_device_register(&dove_sdio1);
}
+void __init dove_setup_cpu_wins(void)
+{
+ /*
+ * The PCIe windows will no longer be statically allocated
+ * here once Dove is migrated to the pci-mvebu driver.
+ */
+ mvebu_mbus_add_window_remap_flags("pcie0.0",
+ DOVE_PCIE0_IO_PHYS_BASE,
+ DOVE_PCIE0_IO_SIZE,
+ DOVE_PCIE0_IO_BUS_BASE,
+ MVEBU_MBUS_PCI_IO);
+ mvebu_mbus_add_window_remap_flags("pcie1.0",
+ DOVE_PCIE1_IO_PHYS_BASE,
+ DOVE_PCIE1_IO_SIZE,
+ DOVE_PCIE1_IO_BUS_BASE,
+ MVEBU_MBUS_PCI_IO);
+ mvebu_mbus_add_window_remap_flags("pcie0.0",
+ DOVE_PCIE0_MEM_PHYS_BASE,
+ DOVE_PCIE0_MEM_SIZE,
+ MVEBU_MBUS_NO_REMAP,
+ MVEBU_MBUS_PCI_MEM);
+ mvebu_mbus_add_window_remap_flags("pcie1.0",
+ DOVE_PCIE1_MEM_PHYS_BASE,
+ DOVE_PCIE1_MEM_SIZE,
+ MVEBU_MBUS_NO_REMAP,
+ MVEBU_MBUS_PCI_MEM);
+ mvebu_mbus_add_window("cesa", DOVE_CESA_PHYS_BASE,
+ DOVE_CESA_SIZE);
+ mvebu_mbus_add_window("bootrom", DOVE_BOOTROM_PHYS_BASE,
+ DOVE_BOOTROM_SIZE);
+ mvebu_mbus_add_window("scratchpad", DOVE_SCRATCHPAD_PHYS_BASE,
+ DOVE_SCRATCHPAD_SIZE);
+}
+
void __init dove_init(void)
{
pr_info("Dove 88AP510 SoC, TCLK = %d MHz.\n",
@@ -334,7 +371,7 @@ void __init dove_init(void)
#ifdef CONFIG_CACHE_TAUROS2
tauros2_init(0);
#endif
- dove_setup_cpu_mbus();
+ dove_setup_cpu_wins();
/* Setup root of clk tree */
dove_clk_init();
diff --git a/arch/arm/mach-dove/common.h b/arch/arm/mach-dove/common.h
index ee59fba4c6d1..e86347928b67 100644
--- a/arch/arm/mach-dove/common.h
+++ b/arch/arm/mach-dove/common.h
@@ -23,7 +23,7 @@ void dove_map_io(void);
void dove_init(void);
void dove_init_early(void);
void dove_init_irq(void);
-void dove_setup_cpu_mbus(void);
+void dove_setup_cpu_wins(void);
void dove_ge00_init(struct mv643xx_eth_platform_data *eth_data);
void dove_sata_init(struct mv_sata_platform_data *sata_data);
#ifdef CONFIG_PCI
diff --git a/arch/arm/mach-dove/include/mach/dove.h b/arch/arm/mach-dove/include/mach/dove.h
index 661725e3115a..0c4b35f4ee5b 100644
--- a/arch/arm/mach-dove/include/mach/dove.h
+++ b/arch/arm/mach-dove/include/mach/dove.h
@@ -77,6 +77,8 @@
/* North-South Bridge */
#define BRIDGE_VIRT_BASE (DOVE_SB_REGS_VIRT_BASE + 0x20000)
#define BRIDGE_PHYS_BASE (DOVE_SB_REGS_PHYS_BASE + 0x20000)
+#define BRIDGE_WINS_BASE (BRIDGE_PHYS_BASE)
+#define BRIDGE_WINS_SZ (0x80)
/* Cryptographic Engine */
#define DOVE_CRYPT_PHYS_BASE (DOVE_SB_REGS_PHYS_BASE + 0x30000)
@@ -168,6 +170,9 @@
#define DOVE_SSP_CLOCK_ENABLE (1 << 1)
#define DOVE_SSP_BPB_CLOCK_SRC_SSP (1 << 11)
/* Memory Controller */
+#define DOVE_MC_PHYS_BASE (DOVE_NB_REGS_PHYS_BASE + 0x00000)
+#define DOVE_MC_WINS_BASE (DOVE_MC_PHYS_BASE + 0x100)
+#define DOVE_MC_WINS_SZ (0x8)
#define DOVE_MC_VIRT_BASE (DOVE_NB_REGS_VIRT_BASE + 0x00000)
/* LCD Controller */
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c
index 46089fe24705..745e304ad0de 100644
--- a/arch/arm/mach-exynos/common.c
+++ b/arch/arm/mach-exynos/common.c
@@ -120,17 +120,6 @@ static struct map_desc exynos_iodesc[] __initdata = {
},
};
-#ifdef CONFIG_ARCH_EXYNOS5
-static struct map_desc exynos5440_iodesc[] __initdata = {
- {
- .virtual = (unsigned long)S5P_VA_CHIPID,
- .pfn = __phys_to_pfn(EXYNOS5440_PA_CHIPID),
- .length = SZ_4K,
- .type = MT_DEVICE,
- },
-};
-#endif
-
static struct map_desc exynos4_iodesc[] __initdata = {
{
.virtual = (unsigned long)S3C_VA_SYS,
@@ -348,6 +337,31 @@ void __init exynos_init_late(void)
exynos_pm_late_initcall();
}
+#ifdef CONFIG_OF
+int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ struct map_desc iodesc;
+ __be32 *reg;
+ unsigned long len;
+
+ if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid") &&
+ !of_flat_dt_is_compatible(node, "samsung,exynos5440-clock"))
+ return 0;
+
+ reg = of_get_flat_dt_prop(node, "reg", &len);
+ if (reg == NULL || len != (sizeof(unsigned long) * 2))
+ return 0;
+
+ iodesc.pfn = __phys_to_pfn(be32_to_cpu(reg[0]));
+ iodesc.length = be32_to_cpu(reg[1]) - 1;
+ iodesc.virtual = (unsigned long)S5P_VA_CHIPID;
+ iodesc.type = MT_DEVICE;
+ iotable_init(&iodesc, 1);
+ return 1;
+}
+#endif
+
/*
* exynos_map_io
*
@@ -356,19 +370,12 @@ void __init exynos_init_late(void)
void __init exynos_init_io(struct map_desc *mach_desc, int size)
{
- struct map_desc *iodesc = exynos_iodesc;
- int iodesc_sz = ARRAY_SIZE(exynos_iodesc);
-#if defined(CONFIG_OF) && defined(CONFIG_ARCH_EXYNOS5)
- unsigned long root = of_get_flat_dt_root();
-
- /* initialize the io descriptors we need for initialization */
- if (of_flat_dt_is_compatible(root, "samsung,exynos5440")) {
- iodesc = exynos5440_iodesc;
- iodesc_sz = ARRAY_SIZE(exynos5440_iodesc);
- }
+#ifdef CONFIG_OF
+ if (initial_boot_params)
+ of_scan_flat_dt(exynos_fdt_map_chipid, NULL);
+ else
#endif
-
- iotable_init(iodesc, iodesc_sz);
+ iotable_init(exynos_iodesc, ARRAY_SIZE(exynos_iodesc));
if (mach_desc)
iotable_init(mach_desc, size);
@@ -445,13 +452,26 @@ void __init exynos_init_time(void)
} else {
/* todo: remove after migrating legacy E4 platforms to dt */
#ifdef CONFIG_ARCH_EXYNOS4
- exynos4_clk_init(NULL);
+ exynos4_clk_init(NULL, !soc_is_exynos4210(), S5P_VA_CMU, readl(S5P_VA_CHIPID + 8) & 1);
exynos4_clk_register_fixed_ext(xxti_f, xusbxti_f);
#endif
- mct_init();
+ mct_init(S5P_VA_SYSTIMER, EXYNOS4_IRQ_MCT_G0, EXYNOS4_IRQ_MCT_L0, EXYNOS4_IRQ_MCT_L1);
}
}
+static unsigned int max_combiner_nr(void)
+{
+ if (soc_is_exynos5250())
+ return EXYNOS5_MAX_COMBINER_NR;
+ else if (soc_is_exynos4412())
+ return EXYNOS4412_MAX_COMBINER_NR;
+ else if (soc_is_exynos4212())
+ return EXYNOS4212_MAX_COMBINER_NR;
+ else
+ return EXYNOS4210_MAX_COMBINER_NR;
+}
+
+
void __init exynos4_init_irq(void)
{
unsigned int gic_bank_offset;
@@ -466,14 +486,8 @@ void __init exynos4_init_irq(void)
#endif
if (!of_have_populated_dt())
- combiner_init(S5P_VA_COMBINER_BASE, NULL);
-
- /*
- * The parameters of s5p_init_irq() are for VIC init.
- * Theses parameters should be NULL and 0 because EXYNOS4
- * uses GIC instead of VIC.
- */
- s5p_init_irq(NULL, 0);
+ combiner_init(S5P_VA_COMBINER_BASE, NULL,
+ max_combiner_nr(), COMBINER_IRQ(0, 0));
gic_arch_extn.irq_set_wake = s3c_irq_wake;
}
@@ -483,14 +497,6 @@ void __init exynos5_init_irq(void)
#ifdef CONFIG_OF
irqchip_init();
#endif
- /*
- * The parameters of s5p_init_irq() are for VIC init.
- * Theses parameters should be NULL and 0 because EXYNOS4
- * uses GIC instead of VIC.
- */
- if (!of_machine_is_compatible("samsung,exynos5440"))
- s5p_init_irq(NULL, 0);
-
gic_arch_extn.irq_set_wake = s3c_irq_wake;
}
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index b17448c1a164..60dd35cc01a6 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -14,7 +14,7 @@
#include <linux/of.h>
-extern void mct_init(void);
+void mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1);
void exynos_init_time(void);
extern unsigned long xxti_f, xusbxti_f;
@@ -27,7 +27,7 @@ void exynos5_restart(char mode, const char *cmd);
void exynos_init_late(void);
/* ToDo: remove these after migrating legacy exynos4 platforms to dt */
-void exynos4_clk_init(struct device_node *np);
+void exynos4_clk_init(struct device_node *np, int is_exynos4210, void __iomem *reg_base, unsigned long xom);
void exynos4_clk_register_fixed_ext(unsigned long, unsigned long);
void exynos_firmware_init(void);
@@ -71,7 +71,8 @@ void exynos4212_register_clocks(void);
#endif
struct device_node;
-void combiner_init(void __iomem *combiner_base, struct device_node *np);
+void combiner_init(void __iomem *combiner_base, struct device_node *np,
+ unsigned int max_nr, int irq_base);
extern struct smp_operations exynos_smp_ops;
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index 99e0a79f3b1f..92b29bb583cb 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -56,7 +56,6 @@
#define EXYNOS4_PA_ONENAND_DMA 0x0C600000
#define EXYNOS_PA_CHIPID 0x10000000
-#define EXYNOS5440_PA_CHIPID 0x00160000
#define EXYNOS4_PA_SYSCON 0x10010000
#define EXYNOS5_PA_SYSCON 0x10050100
diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c
index 76c1170b3528..e7df2dd43a40 100644
--- a/arch/arm/mach-highbank/highbank.c
+++ b/arch/arm/mach-highbank/highbank.c
@@ -15,6 +15,7 @@
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
+#include <linux/clocksource.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/irq.h>
@@ -28,12 +29,9 @@
#include <linux/amba/bus.h>
#include <linux/clk-provider.h>
-#include <asm/arch_timer.h>
#include <asm/cacheflush.h>
#include <asm/cputype.h>
#include <asm/smp_plat.h>
-#include <asm/hardware/arm_timer.h>
-#include <asm/hardware/timer-sp.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -90,36 +88,16 @@ static void __init highbank_init_irq(void)
#endif
}
-static struct clk_lookup lookup = {
- .dev_id = "sp804",
- .con_id = NULL,
-};
-
static void __init highbank_timer_init(void)
{
- int irq;
struct device_node *np;
- void __iomem *timer_base;
/* Map system registers */
np = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs");
sregs_base = of_iomap(np, 0);
WARN_ON(!sregs_base);
- np = of_find_compatible_node(NULL, NULL, "arm,sp804");
- timer_base = of_iomap(np, 0);
- WARN_ON(!timer_base);
- irq = irq_of_parse_and_map(np, 0);
-
of_clk_init(NULL);
- lookup.clk = of_clk_get(np, 0);
- clkdev_add(&lookup);
-
- sp804_clocksource_and_sched_clock_init(timer_base + 0x20, "timer1");
- sp804_clockevents_init(timer_base, irq, "timer0");
-
- arch_timer_of_register();
- arch_timer_sched_clock_init();
clocksource_of_init();
}
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 2ebc97e16b91..78f795d73cb6 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -65,6 +65,9 @@ config IRAM_ALLOC
bool
select GENERIC_ALLOCATOR
+config HAVE_IMX_ANATOP
+ bool
+
config HAVE_IMX_GPC
bool
@@ -73,6 +76,7 @@ config HAVE_IMX_MMDC
config HAVE_IMX_SRC
def_bool y if SMP
+ select ARCH_HAS_RESET_CONTROLLER
config IMX_HAVE_IOMUX_V1
bool
@@ -115,6 +119,8 @@ config SOC_IMX25
config SOC_IMX27
bool
+ select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_OPP
select COMMON_CLK
select CPU_ARM926T
select IMX_HAVE_IOMUX_V1
@@ -142,6 +148,7 @@ config SOC_IMX35
config SOC_IMX5
bool
select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_OPP
select ARCH_MXC_IOMUX_V3
select COMMON_CLK
select CPU_V7
@@ -783,7 +790,7 @@ config SOC_IMX53
This enables support for Freescale i.MX53 processor.
config SOC_IMX6Q
- bool "i.MX6 Quad support"
+ bool "i.MX6 Quad/DualLite support"
select ARCH_HAS_CPUFREQ
select ARCH_HAS_OPP
select ARM_CPU_SUSPEND if PM
@@ -796,6 +803,7 @@ config SOC_IMX6Q
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_CAN_FLEXCAN if CAN
+ select HAVE_IMX_ANATOP
select HAVE_IMX_GPC
select HAVE_IMX_MMDC
select HAVE_IMX_SRC
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index fbe60a145344..930958973f81 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_MACH_EUKREA_CPUIMX35SD) += mach-cpuimx35.o
obj-$(CONFIG_MACH_EUKREA_MBIMXSD35_BASEBOARD) += eukrea_mbimxsd35-baseboard.o
obj-$(CONFIG_MACH_VPR200) += mach-vpr200.o
+obj-$(CONFIG_HAVE_IMX_ANATOP) += anatop.o
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_HAVE_IMX_MMDC) += mmdc.o
obj-$(CONFIG_HAVE_IMX_SRC) += src.o
diff --git a/arch/arm/mach-imx/anatop.c b/arch/arm/mach-imx/anatop.c
new file mode 100644
index 000000000000..0cfa07dd9aa4
--- /dev/null
+++ b/arch/arm/mach-imx/anatop.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include "common.h"
+
+#define REG_SET 0x4
+#define REG_CLR 0x8
+
+#define ANADIG_REG_2P5 0x130
+#define ANADIG_REG_CORE 0x140
+#define ANADIG_ANA_MISC0 0x150
+#define ANADIG_USB1_CHRG_DETECT 0x1b0
+#define ANADIG_USB2_CHRG_DETECT 0x210
+#define ANADIG_DIGPROG 0x260
+
+#define BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG 0x40000
+#define BM_ANADIG_REG_CORE_FET_ODRIVE 0x20000000
+#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG 0x1000
+#define BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B 0x80000
+#define BM_ANADIG_USB_CHRG_DETECT_EN_B 0x100000
+
+static struct regmap *anatop;
+
+static void imx_anatop_enable_weak2p5(bool enable)
+{
+ u32 reg, val;
+
+ regmap_read(anatop, ANADIG_ANA_MISC0, &val);
+
+ /* can only be enabled when stop_mode_config is clear. */
+ reg = ANADIG_REG_2P5;
+ reg += (enable && (val & BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG) == 0) ?
+ REG_SET : REG_CLR;
+ regmap_write(anatop, reg, BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG);
+}
+
+static void imx_anatop_enable_fet_odrive(bool enable)
+{
+ regmap_write(anatop, ANADIG_REG_CORE + (enable ? REG_SET : REG_CLR),
+ BM_ANADIG_REG_CORE_FET_ODRIVE);
+}
+
+void imx_anatop_pre_suspend(void)
+{
+ imx_anatop_enable_weak2p5(true);
+ imx_anatop_enable_fet_odrive(true);
+}
+
+void imx_anatop_post_resume(void)
+{
+ imx_anatop_enable_fet_odrive(false);
+ imx_anatop_enable_weak2p5(false);
+}
+
+void imx_anatop_usb_chrg_detect_disable(void)
+{
+ regmap_write(anatop, ANADIG_USB1_CHRG_DETECT,
+ BM_ANADIG_USB_CHRG_DETECT_EN_B
+ | BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B);
+ regmap_write(anatop, ANADIG_USB2_CHRG_DETECT,
+ BM_ANADIG_USB_CHRG_DETECT_EN_B |
+ BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B);
+}
+
+u32 imx_anatop_get_digprog(void)
+{
+ struct device_node *np;
+ void __iomem *anatop_base;
+ static u32 digprog;
+
+ if (digprog)
+ return digprog;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
+ anatop_base = of_iomap(np, 0);
+ WARN_ON(!anatop_base);
+ digprog = readl_relaxed(anatop_base + ANADIG_DIGPROG);
+
+ return digprog;
+}
+
+void __init imx_anatop_init(void)
+{
+ anatop = syscon_regmap_lookup_by_compatible("fsl,imx6q-anatop");
+ if (IS_ERR(anatop)) {
+ pr_err("%s: failed to find imx6q-anatop regmap!\n", __func__);
+ return;
+ }
+}
diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c
index 2bc623b414c1..6fc486b6a3c6 100644
--- a/arch/arm/mach-imx/clk-imx51-imx53.c
+++ b/arch/arm/mach-imx/clk-imx51-imx53.c
@@ -45,16 +45,40 @@ static const char *mx53_ipu_di1_sel[] = { "di_pred", "osc", "ckih1", "tve_di", "
static const char *mx53_ldb_di1_sel[] = { "pll3_sw", "pll4_sw", };
static const char *mx51_tve_ext_sel[] = { "osc", "ckih1", };
static const char *mx53_tve_ext_sel[] = { "pll4_sw", "ckih1", };
-static const char *tve_sel[] = { "tve_pred", "tve_ext_sel", };
+static const char *mx51_tve_sel[] = { "tve_pred", "tve_ext_sel", };
static const char *ipu_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb", };
+static const char *gpu3d_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb" };
+static const char *gpu2d_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb" };
static const char *vpu_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb", };
static const char *mx53_can_sel[] = { "ipg", "ckih1", "ckih2", "lp_apm", };
+static const char *mx53_cko1_sel[] = {
+ "cpu_podf", "pll1_sw", "pll2_sw", "pll3_sw",
+ "emi_slow_podf", "pll4_sw", "nfc_podf", "dummy",
+ "di_pred", "dummy", "dummy", "ahb",
+ "ipg", "per_root", "ckil", "dummy",};
+static const char *mx53_cko2_sel[] = {
+ "dummy"/* dptc_core */, "dummy"/* dptc_perich */,
+ "dummy", "esdhc_a_podf",
+ "usboh3_podf", "dummy"/* wrck_clk_root */,
+ "ecspi_podf", "dummy"/* pll1_ref_clk */,
+ "esdhc_b_podf", "dummy"/* ddr_clk_root */,
+ "dummy"/* arm_axi_clk_root */, "dummy"/* usb_phy_out */,
+ "vpu_sel", "ipu_sel",
+ "osc", "ckih1",
+ "dummy", "esdhc_c_sel",
+ "ssi1_root_podf", "ssi2_root_podf",
+ "dummy", "dummy",
+ "dummy"/* lpsr_clk_root */, "dummy"/* pgc_clk_root */,
+ "dummy"/* tve_out */, "usb_phy_sel",
+ "tve_sel", "lp_apm",
+ "uart_root", "dummy"/* spdif0_clk_root */,
+ "dummy", "dummy", };
enum imx5_clks {
dummy, ckil, osc, ckih1, ckih2, ahb, ipg, axi_a, axi_b, uart_pred,
uart_root, esdhc_a_pred, esdhc_b_pred, esdhc_c_s, esdhc_d_s,
emi_sel, emi_slow_podf, nfc_podf, ecspi_pred, ecspi_podf, usboh3_pred,
- usboh3_podf, usb_phy_pred, usb_phy_podf, cpu_podf, di_pred, tve_di,
+ usboh3_podf, usb_phy_pred, usb_phy_podf, cpu_podf, di_pred, tve_di_unused,
tve_s, uart1_ipg_gate, uart1_per_gate, uart2_ipg_gate,
uart2_per_gate, uart3_ipg_gate, uart3_per_gate, i2c1_gate, i2c2_gate,
gpt_ipg_gate, pwm1_ipg_gate, pwm1_hf_gate, pwm2_ipg_gate, pwm2_hf_gate,
@@ -83,7 +107,10 @@ enum imx5_clks {
ssi2_root_gate, ssi3_root_gate, ssi_ext1_gate, ssi_ext2_gate,
epit1_ipg_gate, epit1_hf_gate, epit2_ipg_gate, epit2_hf_gate,
can_sel, can1_serial_gate, can1_ipg_gate,
- owire_gate,
+ owire_gate, gpu3d_s, gpu2d_s, gpu3d_gate, gpu2d_gate, garb_gate,
+ cko1_sel, cko1_podf, cko1,
+ cko2_sel, cko2_podf, cko2,
+ srtc_gate, pata_gate,
clk_max
};
@@ -160,8 +187,6 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
usb_phy_sel_str, ARRAY_SIZE(usb_phy_sel_str));
clk[cpu_podf] = imx_clk_divider("cpu_podf", "pll1_sw", MXC_CCM_CACRR, 0, 3);
clk[di_pred] = imx_clk_divider("di_pred", "pll3_sw", MXC_CCM_CDCDR, 6, 3);
- clk[tve_di] = imx_clk_fixed("tve_di", 65000000); /* FIXME */
- clk[tve_s] = imx_clk_mux("tve_sel", MXC_CCM_CSCMR1, 7, 1, tve_sel, ARRAY_SIZE(tve_sel));
clk[iim_gate] = imx_clk_gate2("iim_gate", "ipg", MXC_CCM_CCGR0, 30);
clk[uart1_ipg_gate] = imx_clk_gate2("uart1_ipg_gate", "ipg", MXC_CCM_CCGR1, 6);
clk[uart1_per_gate] = imx_clk_gate2("uart1_per_gate", "uart_root", MXC_CCM_CCGR1, 8);
@@ -200,6 +225,11 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
clk[nfc_gate] = imx_clk_gate2("nfc_gate", "nfc_podf", MXC_CCM_CCGR5, 20);
clk[ipu_di0_gate] = imx_clk_gate2("ipu_di0_gate", "ipu_di0_sel", MXC_CCM_CCGR6, 10);
clk[ipu_di1_gate] = imx_clk_gate2("ipu_di1_gate", "ipu_di1_sel", MXC_CCM_CCGR6, 12);
+ clk[gpu3d_s] = imx_clk_mux("gpu3d_sel", MXC_CCM_CBCMR, 4, 2, gpu3d_sel, ARRAY_SIZE(gpu3d_sel));
+ clk[gpu2d_s] = imx_clk_mux("gpu2d_sel", MXC_CCM_CBCMR, 16, 2, gpu2d_sel, ARRAY_SIZE(gpu2d_sel));
+ clk[gpu3d_gate] = imx_clk_gate2("gpu3d_gate", "gpu3d_sel", MXC_CCM_CCGR5, 2);
+ clk[garb_gate] = imx_clk_gate2("garb_gate", "axi_a", MXC_CCM_CCGR5, 4);
+ clk[gpu2d_gate] = imx_clk_gate2("gpu2d_gate", "gpu2d_sel", MXC_CCM_CCGR6, 14);
clk[vpu_s] = imx_clk_mux("vpu_sel", MXC_CCM_CBCMR, 14, 2, vpu_sel, ARRAY_SIZE(vpu_sel));
clk[vpu_gate] = imx_clk_gate2("vpu_gate", "vpu_sel", MXC_CCM_CCGR5, 6);
clk[vpu_reference_gate] = imx_clk_gate2("vpu_reference_gate", "osc", MXC_CCM_CCGR5, 8);
@@ -235,6 +265,8 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
clk[epit2_ipg_gate] = imx_clk_gate2("epit2_ipg_gate", "ipg", MXC_CCM_CCGR2, 6);
clk[epit2_hf_gate] = imx_clk_gate2("epit2_hf_gate", "per_root", MXC_CCM_CCGR2, 8);
clk[owire_gate] = imx_clk_gate2("owire_gate", "per_root", MXC_CCM_CCGR2, 22);
+ clk[srtc_gate] = imx_clk_gate2("srtc_gate", "per_root", MXC_CCM_CCGR4, 28);
+ clk[pata_gate] = imx_clk_gate2("pata_gate", "ipg", MXC_CCM_CCGR4, 0);
for (i = 0; i < ARRAY_SIZE(clk); i++)
if (IS_ERR(clk[i]))
@@ -286,7 +318,6 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
clk_register_clkdev(clk[dummy], NULL, "imx2-wdt.0");
clk_register_clkdev(clk[dummy], NULL, "imx2-wdt.1");
clk_register_clkdev(clk[dummy], NULL, "imx-keypad");
- clk_register_clkdev(clk[tve_gate], NULL, "imx-tve.0");
clk_register_clkdev(clk[ipu_di1_gate], "di1", "imx-tve.0");
clk_register_clkdev(clk[gpc_dvfs], "gpc_dvfs", NULL);
clk_register_clkdev(clk[epit1_ipg_gate], "ipg", "imx-epit.0");
@@ -331,8 +362,10 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
mx51_ipu_di0_sel, ARRAY_SIZE(mx51_ipu_di0_sel));
clk[ipu_di1_sel] = imx_clk_mux("ipu_di1_sel", MXC_CCM_CSCMR2, 29, 3,
mx51_ipu_di1_sel, ARRAY_SIZE(mx51_ipu_di1_sel));
- clk[tve_ext_sel] = imx_clk_mux("tve_ext_sel", MXC_CCM_CSCMR1, 6, 1,
- mx51_tve_ext_sel, ARRAY_SIZE(mx51_tve_ext_sel));
+ clk[tve_ext_sel] = imx_clk_mux_flags("tve_ext_sel", MXC_CCM_CSCMR1, 6, 1,
+ mx51_tve_ext_sel, ARRAY_SIZE(mx51_tve_ext_sel), CLK_SET_RATE_PARENT);
+ clk[tve_s] = imx_clk_mux("tve_sel", MXC_CCM_CSCMR1, 7, 1,
+ mx51_tve_sel, ARRAY_SIZE(mx51_tve_sel));
clk[tve_gate] = imx_clk_gate2("tve_gate", "tve_sel", MXC_CCM_CCGR2, 30);
clk[tve_pred] = imx_clk_divider("tve_pred", "pll3_sw", MXC_CCM_CDCDR, 28, 3);
clk[esdhc1_per_gate] = imx_clk_gate2("esdhc1_per_gate", "esdhc_a_podf", MXC_CCM_CCGR3, 2);
@@ -420,23 +453,23 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
clk[pll3_sw] = imx_clk_pllv2("pll3_sw", "osc", MX53_DPLL3_BASE);
clk[pll4_sw] = imx_clk_pllv2("pll4_sw", "osc", MX53_DPLL4_BASE);
- clk[ldb_di1_sel] = imx_clk_mux("ldb_di1_sel", MXC_CCM_CSCMR2, 9, 1,
- mx53_ldb_di1_sel, ARRAY_SIZE(mx53_ldb_di1_sel));
clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
- clk[ldb_di1_div] = imx_clk_divider("ldb_di1_div", "ldb_di1_div_3_5", MXC_CCM_CSCMR2, 11, 1);
+ clk[ldb_di1_div] = imx_clk_divider_flags("ldb_di1_div", "ldb_di1_div_3_5", MXC_CCM_CSCMR2, 11, 1, 0);
+ clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", MXC_CCM_CSCMR2, 9, 1,
+ mx53_ldb_di1_sel, ARRAY_SIZE(mx53_ldb_di1_sel), CLK_SET_RATE_PARENT);
clk[di_pll4_podf] = imx_clk_divider("di_pll4_podf", "pll4_sw", MXC_CCM_CDCDR, 16, 3);
- clk[ldb_di0_sel] = imx_clk_mux("ldb_di0_sel", MXC_CCM_CSCMR2, 8, 1,
- mx53_ldb_di0_sel, ARRAY_SIZE(mx53_ldb_di0_sel));
clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
- clk[ldb_di0_div] = imx_clk_divider("ldb_di0_div", "ldb_di0_div_3_5", MXC_CCM_CSCMR2, 10, 1);
+ clk[ldb_di0_div] = imx_clk_divider_flags("ldb_di0_div", "ldb_di0_div_3_5", MXC_CCM_CSCMR2, 10, 1, 0);
+ clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", MXC_CCM_CSCMR2, 8, 1,
+ mx53_ldb_di0_sel, ARRAY_SIZE(mx53_ldb_di0_sel), CLK_SET_RATE_PARENT);
clk[ldb_di0_gate] = imx_clk_gate2("ldb_di0_gate", "ldb_di0_div", MXC_CCM_CCGR6, 28);
clk[ldb_di1_gate] = imx_clk_gate2("ldb_di1_gate", "ldb_di1_div", MXC_CCM_CCGR6, 30);
clk[ipu_di0_sel] = imx_clk_mux("ipu_di0_sel", MXC_CCM_CSCMR2, 26, 3,
mx53_ipu_di0_sel, ARRAY_SIZE(mx53_ipu_di0_sel));
clk[ipu_di1_sel] = imx_clk_mux("ipu_di1_sel", MXC_CCM_CSCMR2, 29, 3,
mx53_ipu_di1_sel, ARRAY_SIZE(mx53_ipu_di1_sel));
- clk[tve_ext_sel] = imx_clk_mux("tve_ext_sel", MXC_CCM_CSCMR1, 6, 1,
- mx53_tve_ext_sel, ARRAY_SIZE(mx53_tve_ext_sel));
+ clk[tve_ext_sel] = imx_clk_mux_flags("tve_ext_sel", MXC_CCM_CSCMR1, 6, 1,
+ mx53_tve_ext_sel, ARRAY_SIZE(mx53_tve_ext_sel), CLK_SET_RATE_PARENT);
clk[tve_gate] = imx_clk_gate2("tve_gate", "tve_pred", MXC_CCM_CCGR2, 30);
clk[tve_pred] = imx_clk_divider("tve_pred", "tve_ext_sel", MXC_CCM_CDCDR, 28, 3);
clk[esdhc1_per_gate] = imx_clk_gate2("esdhc1_per_gate", "esdhc_a_podf", MXC_CCM_CCGR3, 2);
@@ -453,6 +486,16 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
clk[can2_ipg_gate] = imx_clk_gate2("can2_ipg_gate", "ipg", MXC_CCM_CCGR4, 6);
clk[i2c3_gate] = imx_clk_gate2("i2c3_gate", "per_root", MXC_CCM_CCGR1, 22);
+ clk[cko1_sel] = imx_clk_mux("cko1_sel", MXC_CCM_CCOSR, 0, 4,
+ mx53_cko1_sel, ARRAY_SIZE(mx53_cko1_sel));
+ clk[cko1_podf] = imx_clk_divider("cko1_podf", "cko1_sel", MXC_CCM_CCOSR, 4, 3);
+ clk[cko1] = imx_clk_gate2("cko1", "cko1_podf", MXC_CCM_CCOSR, 7);
+
+ clk[cko2_sel] = imx_clk_mux("cko2_sel", MXC_CCM_CCOSR, 16, 5,
+ mx53_cko2_sel, ARRAY_SIZE(mx53_cko2_sel));
+ clk[cko2_podf] = imx_clk_divider("cko2_podf", "cko2_sel", MXC_CCM_CCOSR, 21, 3);
+ clk[cko2] = imx_clk_gate2("cko2", "cko2_podf", MXC_CCM_CCOSR, 24);
+
for (i = 0; i < ARRAY_SIZE(clk); i++)
if (IS_ERR(clk[i]))
pr_err("i.MX53 clk %d: register failed with %ld\n",
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index d38e54f5b6d7..151259003086 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011-2013 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
@@ -22,6 +23,12 @@
#include "clk.h"
#include "common.h"
+#include "hardware.h"
+
+#define CCR 0x0
+#define BM_CCR_WB_COUNT (0x7 << 16)
+#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
+#define BM_CCR_RBC_EN (0x1 << 27)
#define CCGR0 0x68
#define CCGR1 0x6c
@@ -67,6 +74,67 @@ void imx6q_set_chicken_bit(void)
writel_relaxed(val, ccm_base + CGPR);
}
+static void imx6q_enable_rbc(bool enable)
+{
+ u32 val;
+ static bool last_rbc_mode;
+
+ if (last_rbc_mode == enable)
+ return;
+ /*
+ * need to mask all interrupts in GPC before
+ * operating RBC configurations
+ */
+ imx_gpc_mask_all();
+
+ /* configure RBC enable bit */
+ val = readl_relaxed(ccm_base + CCR);
+ val &= ~BM_CCR_RBC_EN;
+ val |= enable ? BM_CCR_RBC_EN : 0;
+ writel_relaxed(val, ccm_base + CCR);
+
+ /* configure RBC count */
+ val = readl_relaxed(ccm_base + CCR);
+ val &= ~BM_CCR_RBC_BYPASS_COUNT;
+ val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
+ writel(val, ccm_base + CCR);
+
+ /*
+ * need to delay at least 2 cycles of CKIL(32K)
+ * due to hardware design requirement, which is
+ * ~61us, here we use 65us for safe
+ */
+ udelay(65);
+
+ /* restore GPC interrupt mask settings */
+ imx_gpc_restore_all();
+
+ last_rbc_mode = enable;
+}
+
+static void imx6q_enable_wb(bool enable)
+{
+ u32 val;
+ static bool last_wb_mode;
+
+ if (last_wb_mode == enable)
+ return;
+
+ /* configure well bias enable bit */
+ val = readl_relaxed(ccm_base + CLPCR);
+ val &= ~BM_CLPCR_WB_PER_AT_LPM;
+ val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
+ writel_relaxed(val, ccm_base + CLPCR);
+
+ /* configure well bias count */
+ val = readl_relaxed(ccm_base + CCR);
+ val &= ~BM_CCR_WB_COUNT;
+ val |= enable ? BM_CCR_WB_COUNT : 0;
+ writel_relaxed(val, ccm_base + CCR);
+
+ last_wb_mode = enable;
+}
+
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
{
u32 val = readl_relaxed(ccm_base + CLPCR);
@@ -74,6 +142,8 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
val &= ~BM_CLPCR_LPM;
switch (mode) {
case WAIT_CLOCKED:
+ imx6q_enable_wb(false);
+ imx6q_enable_rbc(false);
break;
case WAIT_UNCLOCKED:
val |= 0x1 << BP_CLPCR_LPM;
@@ -92,6 +162,8 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
val |= 0x3 << BP_CLPCR_STBY_COUNT;
val |= BM_CLPCR_VSTBY;
val |= BM_CLPCR_SBYOS;
+ imx6q_enable_wb(true);
+ imx6q_enable_rbc(true);
break;
default:
return -EINVAL;
@@ -109,29 +181,29 @@ static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", };
static const char *periph_sels[] = { "periph_pre", "periph_clk2", };
static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", };
static const char *axi_sels[] = { "periph", "pll2_pfd2_396m", "pll3_pfd1_540m", };
-static const char *audio_sels[] = { "pll4_audio", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", };
+static const char *audio_sels[] = { "pll4_post_div", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", };
static const char *gpu_axi_sels[] = { "axi", "ahb", };
static const char *gpu2d_core_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", };
static const char *gpu3d_core_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd2_396m", };
static const char *gpu3d_shader_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd9_720m", };
static const char *ipu_sels[] = { "mmdc_ch0_axi", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", };
static const char *ldb_di_sels[] = { "pll5_video", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_usb_otg", };
-static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", };
+static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", };
static const char *ipu1_di0_sels[] = { "ipu1_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
static const char *ipu1_di1_sels[] = { "ipu1_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
static const char *ipu2_di0_sels[] = { "ipu2_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
static const char *ipu2_di1_sels[] = { "ipu2_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", };
static const char *hsi_tx_sels[] = { "pll3_120m", "pll2_pfd2_396m", };
static const char *pcie_axi_sels[] = { "axi", "ahb", };
-static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_audio", };
+static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_post_div", };
static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
static const char *enfc_sels[] = { "pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", };
static const char *emi_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd2_396m", "pll2_pfd0_352m", };
static const char *vdo_axi_sels[] = { "axi", "ahb", };
static const char *vpu_axi_sels[] = { "axi", "pll2_pfd2_396m", "pll2_pfd0_352m", };
-static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video",
+static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div",
"dummy", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0",
- "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_audio", };
+ "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_post_div", };
enum mx6q_clks {
dummy, ckil, ckih, osc, pll2_pfd0_352m, pll2_pfd1_594m, pll2_pfd2_396m,
@@ -165,7 +237,7 @@ enum mx6q_clks {
pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg,
ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5,
sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate,
- usbphy2_gate, clk_max
+ usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, clk_max
};
static struct clk *clk[clk_max];
@@ -182,6 +254,21 @@ static struct clk_div_table clk_enet_ref_table[] = {
{ .val = 3, .div = 4, },
};
+static struct clk_div_table post_div_table[] = {
+ { .val = 2, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 0, .div = 4, },
+ { }
+};
+
+static struct clk_div_table video_div_table[] = {
+ { .val = 0, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 2, .div = 1, },
+ { .val = 3, .div = 4, },
+ { }
+};
+
int __init mx6q_clocks_init(void)
{
struct device_node *np;
@@ -208,6 +295,14 @@ int __init mx6q_clocks_init(void)
base = of_iomap(np, 0);
WARN_ON(!base);
+ /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */
+ if (cpu_is_imx6q() && imx6q_revision() == IMX_CHIP_REVISION_1_0) {
+ post_div_table[1].div = 1;
+ post_div_table[2].div = 1;
+ video_div_table[1].div = 1;
+ video_div_table[2].div = 1;
+ };
+
/* type name parent_name base div_mask */
clk[pll1_sys] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f);
clk[pll2_bus] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1);
@@ -260,6 +355,10 @@ int __init mx6q_clocks_init(void)
clk[pll3_60m] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8);
clk[twd] = imx_clk_fixed_factor("twd", "arm", 1, 2);
+ clk[pll4_post_div] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock);
+ clk[pll5_post_div] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock);
+ clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock);
+
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ccm");
base = of_iomap(np, 0);
WARN_ON(!base);
@@ -283,8 +382,8 @@ int __init mx6q_clocks_init(void)
clk[gpu3d_shader_sel] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels));
clk[ipu1_sel] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clk[ipu2_sel] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
- clk[ldb_di0_sel] = imx_clk_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
- clk[ldb_di1_sel] = imx_clk_mux("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
+ clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
+ clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
clk[ipu1_di0_pre_sel] = imx_clk_mux("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
clk[ipu1_di1_pre_sel] = imx_clk_mux("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
clk[ipu2_di0_pre_sel] = imx_clk_mux("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels));
@@ -332,9 +431,9 @@ int __init mx6q_clocks_init(void)
clk[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3);
clk[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3);
clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
- clk[ldb_di0_podf] = imx_clk_divider("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1);
+ clk[ldb_di0_podf] = imx_clk_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0);
clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
- clk[ldb_di1_podf] = imx_clk_divider("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1);
+ clk[ldb_di1_podf] = imx_clk_divider_flags("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1, 0);
clk[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3);
clk[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3);
clk[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3);
@@ -448,6 +547,11 @@ int __init mx6q_clocks_init(void)
clk_register_clkdev(clk[cko1], "cko1", NULL);
clk_register_clkdev(clk[arm], NULL, "cpu0");
+ if (imx6q_revision() != IMX_CHIP_REVISION_1_0) {
+ clk_set_parent(clk[ldb_di0_sel], clk[pll5_video_div]);
+ clk_set_parent(clk[ldb_di1_sel], clk[pll5_video_div]);
+ }
+
/*
* The gpmi needs 100MHz frequency in the EDO/Sync mode,
* We can not get the 100MHz from the pll2_pfd0_352m.
diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h
index 9d1f3b99d1d3..d9d9d9c66dff 100644
--- a/arch/arm/mach-imx/clk.h
+++ b/arch/arm/mach-imx/clk.h
@@ -59,6 +59,14 @@ static inline struct clk *imx_clk_divider(const char *name, const char *parent,
reg, shift, width, 0, &imx_ccm_lock);
}
+static inline struct clk *imx_clk_divider_flags(const char *name,
+ const char *parent, void __iomem *reg, u8 shift, u8 width,
+ unsigned long flags)
+{
+ return clk_register_divider(NULL, name, parent, flags,
+ reg, shift, width, 0, &imx_ccm_lock);
+}
+
static inline struct clk *imx_clk_gate(const char *name, const char *parent,
void __iomem *reg, u8 shift)
{
@@ -73,6 +81,15 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
width, 0, &imx_ccm_lock);
}
+static inline struct clk *imx_clk_mux_flags(const char *name,
+ void __iomem *reg, u8 shift, u8 width, const char **parents,
+ int num_parents, unsigned long flags)
+{
+ return clk_register_mux(NULL, name, parents, num_parents,
+ flags, reg, shift, width, 0,
+ &imx_ccm_lock);
+}
+
static inline struct clk *imx_clk_fixed_factor(const char *name,
const char *parent, unsigned int mult, unsigned int div)
{
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 9fea2522d7a3..4cba7dbb079f 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -74,6 +74,7 @@ extern void mxc_set_cpu_type(unsigned int type);
extern void mxc_restart(char, const char *);
extern void mxc_arch_reset_init(void __iomem *);
extern int mx53_revision(void);
+extern int imx6q_revision(void);
extern int mx53_display_revision(void);
extern void imx_set_aips(void __iomem *);
extern int mxc_device_init(void);
@@ -128,6 +129,13 @@ extern void imx_src_prepare_restart(void);
extern void imx_gpc_init(void);
extern void imx_gpc_pre_suspend(void);
extern void imx_gpc_post_resume(void);
+extern void imx_gpc_mask_all(void);
+extern void imx_gpc_restore_all(void);
+extern void imx_anatop_init(void);
+extern void imx_anatop_pre_suspend(void);
+extern void imx_anatop_post_resume(void);
+extern void imx_anatop_usb_chrg_detect_disable(void);
+extern u32 imx_anatop_get_digprog(void);
extern int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode);
extern void imx6q_set_chicken_bit(void);
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 02b61cdf39b9..44a65e9ff1fc 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011-2013 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
@@ -69,6 +69,27 @@ static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on)
return 0;
}
+void imx_gpc_mask_all(void)
+{
+ void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
+ int i;
+
+ for (i = 0; i < IMR_NUM; i++) {
+ gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4);
+ writel_relaxed(~0, reg_imr1 + i * 4);
+ }
+
+}
+
+void imx_gpc_restore_all(void)
+{
+ void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
+ int i;
+
+ for (i = 0; i < IMR_NUM; i++)
+ writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4);
+}
+
static void imx_gpc_irq_unmask(struct irq_data *d)
{
void __iomem *reg;
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 99502eeefdf7..5536fd81379a 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011-2013 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
@@ -38,38 +38,32 @@
#include "cpuidle.h"
#include "hardware.h"
-#define IMX6Q_ANALOG_DIGPROG 0x260
+static u32 chip_revision;
-static int imx6q_revision(void)
+int imx6q_revision(void)
{
- struct device_node *np;
- void __iomem *base;
- static u32 rev;
-
- if (!rev) {
- np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
- if (!np)
- return IMX_CHIP_REVISION_UNKNOWN;
- base = of_iomap(np, 0);
- if (!base) {
- of_node_put(np);
- return IMX_CHIP_REVISION_UNKNOWN;
- }
- rev = readl_relaxed(base + IMX6Q_ANALOG_DIGPROG);
- iounmap(base);
- of_node_put(np);
- }
+ return chip_revision;
+}
+
+static void __init imx6q_init_revision(void)
+{
+ u32 rev = imx_anatop_get_digprog();
switch (rev & 0xff) {
case 0:
- return IMX_CHIP_REVISION_1_0;
+ chip_revision = IMX_CHIP_REVISION_1_0;
+ break;
case 1:
- return IMX_CHIP_REVISION_1_1;
+ chip_revision = IMX_CHIP_REVISION_1_1;
+ break;
case 2:
- return IMX_CHIP_REVISION_1_2;
+ chip_revision = IMX_CHIP_REVISION_1_2;
+ break;
default:
- return IMX_CHIP_REVISION_UNKNOWN;
+ chip_revision = IMX_CHIP_REVISION_UNKNOWN;
}
+
+ mxc_set_cpu_type(rev >> 16 & 0xff);
}
static void imx6q_restart(char mode, const char *cmd)
@@ -164,29 +158,7 @@ static void __init imx6q_1588_init(void)
}
static void __init imx6q_usb_init(void)
{
- struct regmap *anatop;
-
-#define HW_ANADIG_USB1_CHRG_DETECT 0x000001b0
-#define HW_ANADIG_USB2_CHRG_DETECT 0x00000210
-
-#define BM_ANADIG_USB_CHRG_DETECT_EN_B 0x00100000
-#define BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B 0x00080000
-
- anatop = syscon_regmap_lookup_by_compatible("fsl,imx6q-anatop");
- if (!IS_ERR(anatop)) {
- /*
- * The external charger detector needs to be disabled,
- * or the signal at DP will be poor
- */
- regmap_write(anatop, HW_ANADIG_USB1_CHRG_DETECT,
- BM_ANADIG_USB_CHRG_DETECT_EN_B
- | BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B);
- regmap_write(anatop, HW_ANADIG_USB2_CHRG_DETECT,
- BM_ANADIG_USB_CHRG_DETECT_EN_B |
- BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B);
- } else {
- pr_warn("failed to find fsl,imx6q-anatop regmap\n");
- }
+ imx_anatop_usb_chrg_detect_disable();
}
static void __init imx6q_init_machine(void)
@@ -196,6 +168,7 @@ static void __init imx6q_init_machine(void)
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+ imx_anatop_init();
imx6q_pm_init();
imx6q_usb_init();
imx6q_1588_init();
@@ -282,6 +255,7 @@ static void __init imx6q_map_io(void)
static void __init imx6q_init_irq(void)
{
+ imx6q_init_revision();
l2x0_of_init(0, ~0UL);
imx_src_init();
imx_gpc_init();
@@ -292,15 +266,17 @@ static void __init imx6q_timer_init(void)
{
mx6q_clocks_init();
clocksource_of_init();
- imx_print_silicon_rev("i.MX6Q", imx6q_revision());
+ imx_print_silicon_rev(cpu_is_imx6dl() ? "i.MX6DL" : "i.MX6Q",
+ imx6q_revision());
}
static const char *imx6q_dt_compat[] __initdata = {
+ "fsl,imx6dl",
"fsl,imx6q",
NULL,
};
-DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad (Device Tree)")
+DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)")
.smp = smp_ops(imx_smp_ops),
.map_io = imx6q_map_io,
.init_irq = imx6q_init_irq,
diff --git a/arch/arm/mach-imx/mm-imx1.c b/arch/arm/mach-imx/mm-imx1.c
index 7a146671e65a..3c609c52d3eb 100644
--- a/arch/arm/mach-imx/mm-imx1.c
+++ b/arch/arm/mach-imx/mm-imx1.c
@@ -51,6 +51,8 @@ void __init mx1_init_irq(void)
void __init imx1_soc_init(void)
{
+ mxc_device_init();
+
mxc_register_gpio("imx1-gpio", 0, MX1_GPIO1_BASE_ADDR, SZ_256,
MX1_GPIO_INT_PORTA, 0);
mxc_register_gpio("imx1-gpio", 1, MX1_GPIO2_BASE_ADDR, SZ_256,
diff --git a/arch/arm/mach-imx/mxc.h b/arch/arm/mach-imx/mxc.h
index 7dce17a9fe6c..8629e5be7ecd 100644
--- a/arch/arm/mach-imx/mxc.h
+++ b/arch/arm/mach-imx/mxc.h
@@ -34,6 +34,8 @@
#define MXC_CPU_MX35 35
#define MXC_CPU_MX51 51
#define MXC_CPU_MX53 53
+#define MXC_CPU_IMX6DL 0x61
+#define MXC_CPU_IMX6Q 0x63
#define IMX_CHIP_REVISION_1_0 0x10
#define IMX_CHIP_REVISION_1_1 0x11
@@ -150,6 +152,15 @@ extern unsigned int __mxc_cpu_type;
#endif
#ifndef __ASSEMBLY__
+static inline bool cpu_is_imx6dl(void)
+{
+ return __mxc_cpu_type == MXC_CPU_IMX6DL;
+}
+
+static inline bool cpu_is_imx6q(void)
+{
+ return __mxc_cpu_type == MXC_CPU_IMX6Q;
+}
struct cpu_op {
u32 cpu_rate;
diff --git a/arch/arm/mach-imx/platsmp.c b/arch/arm/mach-imx/platsmp.c
index 77e9a25ed0f6..4a69305db65e 100644
--- a/arch/arm/mach-imx/platsmp.c
+++ b/arch/arm/mach-imx/platsmp.c
@@ -68,8 +68,8 @@ static void __init imx_smp_init_cpus(void)
ncores = scu_get_core_count(scu_base);
- for (i = 0; i < ncores; i++)
- set_cpu_possible(i, true);
+ for (i = ncores; i < NR_CPUS; i++)
+ set_cpu_possible(i, false);
}
void imx_smp_prepare(void)
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c
index 5faba7a3c95f..204942749e21 100644
--- a/arch/arm/mach-imx/pm-imx6q.c
+++ b/arch/arm/mach-imx/pm-imx6q.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011-2013 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
@@ -34,10 +34,12 @@ static int imx6q_pm_enter(suspend_state_t state)
case PM_SUSPEND_MEM:
imx6q_set_lpm(STOP_POWER_OFF);
imx_gpc_pre_suspend();
+ imx_anatop_pre_suspend();
imx_set_cpu_jump(0, v7_cpu_resume);
/* Zzz ... */
cpu_suspend(0, imx6q_suspend_finish);
imx_smp_prepare();
+ imx_anatop_post_resume();
imx_gpc_post_resume();
imx6q_set_lpm(WAIT_CLOCKED);
break;
diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c
index 97d086889481..10a6b1a8c5ac 100644
--- a/arch/arm/mach-imx/src.c
+++ b/arch/arm/mach-imx/src.c
@@ -14,6 +14,7 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/reset-controller.h>
#include <linux/smp.h>
#include <asm/smp_plat.h>
#include "common.h"
@@ -21,10 +22,65 @@
#define SRC_SCR 0x000
#define SRC_GPR1 0x020
#define BP_SRC_SCR_WARM_RESET_ENABLE 0
+#define BP_SRC_SCR_SW_GPU_RST 1
+#define BP_SRC_SCR_SW_VPU_RST 2
+#define BP_SRC_SCR_SW_IPU1_RST 3
+#define BP_SRC_SCR_SW_OPEN_VG_RST 4
+#define BP_SRC_SCR_SW_IPU2_RST 12
#define BP_SRC_SCR_CORE1_RST 14
#define BP_SRC_SCR_CORE1_ENABLE 22
static void __iomem *src_base;
+static DEFINE_SPINLOCK(scr_lock);
+
+static const int sw_reset_bits[5] = {
+ BP_SRC_SCR_SW_GPU_RST,
+ BP_SRC_SCR_SW_VPU_RST,
+ BP_SRC_SCR_SW_IPU1_RST,
+ BP_SRC_SCR_SW_OPEN_VG_RST,
+ BP_SRC_SCR_SW_IPU2_RST
+};
+
+static int imx_src_reset_module(struct reset_controller_dev *rcdev,
+ unsigned long sw_reset_idx)
+{
+ unsigned long timeout;
+ unsigned long flags;
+ int bit;
+ u32 val;
+
+ if (!src_base)
+ return -ENODEV;
+
+ if (sw_reset_idx >= ARRAY_SIZE(sw_reset_bits))
+ return -EINVAL;
+
+ bit = 1 << sw_reset_bits[sw_reset_idx];
+
+ spin_lock_irqsave(&scr_lock, flags);
+ val = readl_relaxed(src_base + SRC_SCR);
+ val |= bit;
+ writel_relaxed(val, src_base + SRC_SCR);
+ spin_unlock_irqrestore(&scr_lock, flags);
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (readl(src_base + SRC_SCR) & bit) {
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static struct reset_control_ops imx_src_ops = {
+ .reset = imx_src_reset_module,
+};
+
+static struct reset_controller_dev imx_reset_controller = {
+ .ops = &imx_src_ops,
+ .nr_resets = ARRAY_SIZE(sw_reset_bits),
+};
void imx_enable_cpu(int cpu, bool enable)
{
@@ -32,9 +88,11 @@ void imx_enable_cpu(int cpu, bool enable)
cpu = cpu_logical_map(cpu);
mask = 1 << (BP_SRC_SCR_CORE1_ENABLE + cpu - 1);
+ spin_lock(&scr_lock);
val = readl_relaxed(src_base + SRC_SCR);
val = enable ? val | mask : val & ~mask;
writel_relaxed(val, src_base + SRC_SCR);
+ spin_unlock(&scr_lock);
}
void imx_set_cpu_jump(int cpu, void *jump_addr)
@@ -61,9 +119,11 @@ void imx_src_prepare_restart(void)
u32 val;
/* clear enable bits of secondary cores */
+ spin_lock(&scr_lock);
val = readl_relaxed(src_base + SRC_SCR);
val &= ~(0x7 << BP_SRC_SCR_CORE1_ENABLE);
writel_relaxed(val, src_base + SRC_SCR);
+ spin_unlock(&scr_lock);
/* clear persistent entry register of primary core */
writel_relaxed(0, src_base + SRC_GPR1);
@@ -80,11 +140,17 @@ void __init imx_src_init(void)
src_base = of_iomap(np, 0);
WARN_ON(!src_base);
+ imx_reset_controller.of_node = np;
+ if (IS_ENABLED(CONFIG_RESET_CONTROLLER))
+ reset_controller_register(&imx_reset_controller);
+
/*
* force warm reset sources to generate cold reset
* for a more reliable restart
*/
+ spin_lock(&scr_lock);
val = readl_relaxed(src_base + SRC_SCR);
val &= ~(1 << BP_SRC_SCR_WARM_RESET_ENABLE);
writel_relaxed(val, src_base + SRC_SCR);
+ spin_unlock(&scr_lock);
}
diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c
index da1091be0887..8c60fcb08a98 100644
--- a/arch/arm/mach-integrator/integrator_cp.c
+++ b/arch/arm/mach-integrator/integrator_cp.c
@@ -250,39 +250,6 @@ static void __init intcp_init_early(void)
}
#ifdef CONFIG_OF
-
-static void __init cp_of_timer_init(void)
-{
- struct device_node *node;
- const char *path;
- void __iomem *base;
- int err;
- int irq;
-
- err = of_property_read_string(of_aliases,
- "arm,timer-primary", &path);
- if (WARN_ON(err))
- return;
- node = of_find_node_by_path(path);
- base = of_iomap(node, 0);
- if (WARN_ON(!base))
- return;
- writel(0, base + TIMER_CTRL);
- sp804_clocksource_init(base, node->name);
-
- err = of_property_read_string(of_aliases,
- "arm,timer-secondary", &path);
- if (WARN_ON(err))
- return;
- node = of_find_node_by_path(path);
- base = of_iomap(node, 0);
- if (WARN_ON(!base))
- return;
- irq = irq_of_parse_and_map(node, 0);
- writel(0, base + TIMER_CTRL);
- sp804_clockevents_init(base, irq, node->name);
-}
-
static const struct of_device_id fpga_irq_of_match[] __initconst = {
{ .compatible = "arm,versatile-fpga-irq", .data = fpga_irq_of_init, },
{ /* Sentinel */ }
@@ -383,7 +350,6 @@ DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)")
.init_early = intcp_init_early,
.init_irq = intcp_init_irq_of,
.handle_irq = fpga_handle_irq,
- .init_time = cp_of_timer_init,
.init_machine = intcp_init_of,
.restart = integrator_restart,
.dt_compat = intcp_dt_board_compat,
diff --git a/arch/arm/mach-kirkwood/Makefile b/arch/arm/mach-kirkwood/Makefile
index cdbca328a412..e1f3735d3415 100644
--- a/arch/arm/mach-kirkwood/Makefile
+++ b/arch/arm/mach-kirkwood/Makefile
@@ -1,4 +1,4 @@
-obj-y += common.o addr-map.o irq.o pcie.o mpp.o
+obj-y += common.o irq.o pcie.o mpp.o
obj-$(CONFIG_MACH_D2NET_V2) += d2net_v2-setup.o lacie_v2-common.o
obj-$(CONFIG_MACH_DB88F6281_BP) += db88f6281-bp-setup.o
diff --git a/arch/arm/mach-kirkwood/addr-map.c b/arch/arm/mach-kirkwood/addr-map.c
deleted file mode 100644
index 8f0d162a1e1d..000000000000
--- a/arch/arm/mach-kirkwood/addr-map.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * arch/arm/mach-kirkwood/addr-map.c
- *
- * Address map functions for Marvell Kirkwood SoCs
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/mbus.h>
-#include <linux/io.h>
-#include <mach/hardware.h>
-#include <plat/addr-map.h>
-#include "common.h"
-
-/*
- * Generic Address Decode Windows bit settings
- */
-#define TARGET_DEV_BUS 1
-#define TARGET_SRAM 3
-#define TARGET_PCIE 4
-#define ATTR_DEV_SPI_ROM 0x1e
-#define ATTR_DEV_BOOT 0x1d
-#define ATTR_DEV_NAND 0x2f
-#define ATTR_DEV_CS3 0x37
-#define ATTR_DEV_CS2 0x3b
-#define ATTR_DEV_CS1 0x3d
-#define ATTR_DEV_CS0 0x3e
-#define ATTR_PCIE_IO 0xe0
-#define ATTR_PCIE_MEM 0xe8
-#define ATTR_PCIE1_IO 0xd0
-#define ATTR_PCIE1_MEM 0xd8
-#define ATTR_SRAM 0x01
-
-/*
- * Description of the windows needed by the platform code
- */
-static struct __initdata orion_addr_map_cfg addr_map_cfg = {
- .num_wins = 8,
- .remappable_wins = 4,
- .bridge_virt_base = BRIDGE_VIRT_BASE,
-};
-
-static const struct __initdata orion_addr_map_info addr_map_info[] = {
- /*
- * Windows for PCIe IO+MEM space.
- */
- { 0, KIRKWOOD_PCIE_IO_PHYS_BASE, KIRKWOOD_PCIE_IO_SIZE,
- TARGET_PCIE, ATTR_PCIE_IO, KIRKWOOD_PCIE_IO_BUS_BASE
- },
- { 1, KIRKWOOD_PCIE_MEM_PHYS_BASE, KIRKWOOD_PCIE_MEM_SIZE,
- TARGET_PCIE, ATTR_PCIE_MEM, KIRKWOOD_PCIE_MEM_BUS_BASE
- },
- { 2, KIRKWOOD_PCIE1_IO_PHYS_BASE, KIRKWOOD_PCIE1_IO_SIZE,
- TARGET_PCIE, ATTR_PCIE1_IO, KIRKWOOD_PCIE1_IO_BUS_BASE
- },
- { 3, KIRKWOOD_PCIE1_MEM_PHYS_BASE, KIRKWOOD_PCIE1_MEM_SIZE,
- TARGET_PCIE, ATTR_PCIE1_MEM, KIRKWOOD_PCIE1_MEM_BUS_BASE
- },
- /*
- * Window for NAND controller.
- */
- { 4, KIRKWOOD_NAND_MEM_PHYS_BASE, KIRKWOOD_NAND_MEM_SIZE,
- TARGET_DEV_BUS, ATTR_DEV_NAND, -1
- },
- /*
- * Window for SRAM.
- */
- { 5, KIRKWOOD_SRAM_PHYS_BASE, KIRKWOOD_SRAM_SIZE,
- TARGET_SRAM, ATTR_SRAM, -1
- },
- /* End marker */
- { -1, 0, 0, 0, 0, 0 }
-};
-
-void __init kirkwood_setup_cpu_mbus(void)
-{
- /*
- * Disable, clear and configure windows.
- */
- orion_config_wins(&addr_map_cfg, addr_map_info);
-
- /*
- * Setup MBUS dram target info.
- */
- orion_setup_cpu_mbus_target(&addr_map_cfg,
- (void __iomem *) DDR_WINDOW_CPU_BASE);
-}
diff --git a/arch/arm/mach-kirkwood/board-dt.c b/arch/arm/mach-kirkwood/board-dt.c
index 7904758e771f..e9647b80cb59 100644
--- a/arch/arm/mach-kirkwood/board-dt.c
+++ b/arch/arm/mach-kirkwood/board-dt.c
@@ -93,7 +93,7 @@ static void __init kirkwood_dt_init(void)
*/
writel(readl(CPU_CONFIG) & ~CPU_CONFIG_ERROR_PROP, CPU_CONFIG);
- kirkwood_setup_cpu_mbus();
+ kirkwood_setup_wins();
kirkwood_l2_init();
diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c
index 49792a0cd2d3..c2cae69e6d2b 100644
--- a/arch/arm/mach-kirkwood/common.c
+++ b/arch/arm/mach-kirkwood/common.c
@@ -33,7 +33,6 @@
#include <linux/platform_data/usb-ehci-orion.h>
#include <plat/common.h>
#include <plat/time.h>
-#include <plat/addr-map.h>
#include <linux/platform_data/dma-mv_xor.h>
#include "common.h"
@@ -535,6 +534,9 @@ void __init kirkwood_init_early(void)
* the allocations won't fail.
*/
init_dma_coherent_pool_size(SZ_1M);
+ mvebu_mbus_init("marvell,kirkwood-mbus",
+ BRIDGE_WINS_BASE, BRIDGE_WINS_SZ,
+ DDR_WINDOW_CPU_BASE, DDR_WINDOW_CPU_SZ);
}
int kirkwood_tclk;
@@ -650,6 +652,38 @@ char * __init kirkwood_id(void)
}
}
+void __init kirkwood_setup_wins(void)
+{
+ /*
+ * The PCIe windows will no longer be statically allocated
+ * here once Kirkwood is migrated to the pci-mvebu driver.
+ */
+ mvebu_mbus_add_window_remap_flags("pcie0.0",
+ KIRKWOOD_PCIE_IO_PHYS_BASE,
+ KIRKWOOD_PCIE_IO_SIZE,
+ KIRKWOOD_PCIE_IO_BUS_BASE,
+ MVEBU_MBUS_PCI_IO);
+ mvebu_mbus_add_window_remap_flags("pcie0.0",
+ KIRKWOOD_PCIE_MEM_PHYS_BASE,
+ KIRKWOOD_PCIE_MEM_SIZE,
+ MVEBU_MBUS_NO_REMAP,
+ MVEBU_MBUS_PCI_MEM);
+ mvebu_mbus_add_window_remap_flags("pcie1.0",
+ KIRKWOOD_PCIE1_IO_PHYS_BASE,
+ KIRKWOOD_PCIE1_IO_SIZE,
+ KIRKWOOD_PCIE1_IO_BUS_BASE,
+ MVEBU_MBUS_PCI_IO);
+ mvebu_mbus_add_window_remap_flags("pcie1.0",
+ KIRKWOOD_PCIE1_MEM_PHYS_BASE,
+ KIRKWOOD_PCIE1_MEM_SIZE,
+ MVEBU_MBUS_NO_REMAP,
+ MVEBU_MBUS_PCI_MEM);
+ mvebu_mbus_add_window("nand", KIRKWOOD_NAND_MEM_PHYS_BASE,
+ KIRKWOOD_NAND_MEM_SIZE);
+ mvebu_mbus_add_window("sram", KIRKWOOD_SRAM_PHYS_BASE,
+ KIRKWOOD_SRAM_SIZE);
+}
+
void __init kirkwood_l2_init(void)
{
#ifdef CONFIG_CACHE_FEROCEON_L2
@@ -675,7 +709,7 @@ void __init kirkwood_init(void)
*/
writel(readl(CPU_CONFIG) & ~CPU_CONFIG_ERROR_PROP, CPU_CONFIG);
- kirkwood_setup_cpu_mbus();
+ kirkwood_setup_wins();
kirkwood_l2_init();
diff --git a/arch/arm/mach-kirkwood/common.h b/arch/arm/mach-kirkwood/common.h
index 3147be2f34da..21da3b1ebd7b 100644
--- a/arch/arm/mach-kirkwood/common.h
+++ b/arch/arm/mach-kirkwood/common.h
@@ -30,7 +30,7 @@ void kirkwood_init(void);
void kirkwood_init_early(void);
void kirkwood_init_irq(void);
-void kirkwood_setup_cpu_mbus(void);
+void kirkwood_setup_wins(void);
void kirkwood_enable_pcie(void);
void kirkwood_pcie_id(u32 *dev, u32 *rev);
diff --git a/arch/arm/mach-kirkwood/include/mach/kirkwood.h b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
index a05563a31c95..92976cef3910 100644
--- a/arch/arm/mach-kirkwood/include/mach/kirkwood.h
+++ b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
@@ -60,8 +60,9 @@
* Register Map
*/
#define DDR_VIRT_BASE (KIRKWOOD_REGS_VIRT_BASE + 0x00000)
-#define DDR_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE + 0x00000)
-#define DDR_WINDOW_CPU_BASE (DDR_VIRT_BASE + 0x1500)
+#define DDR_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE + 0x00000)
+#define DDR_WINDOW_CPU_BASE (DDR_PHYS_BASE + 0x1500)
+#define DDR_WINDOW_CPU_SZ (0x20)
#define DDR_OPERATION_BASE (DDR_PHYS_BASE + 0x1418)
#define DEV_BUS_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE + 0x10000)
@@ -80,6 +81,8 @@
#define BRIDGE_VIRT_BASE (KIRKWOOD_REGS_VIRT_BASE + 0x20000)
#define BRIDGE_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE + 0x20000)
+#define BRIDGE_WINS_BASE (BRIDGE_PHYS_BASE)
+#define BRIDGE_WINS_SZ (0x80)
#define CRYPTO_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE + 0x30000)
diff --git a/arch/arm/mach-kirkwood/pcie.c b/arch/arm/mach-kirkwood/pcie.c
index d96ad4c09972..7f43e6c2f8c0 100644
--- a/arch/arm/mach-kirkwood/pcie.c
+++ b/arch/arm/mach-kirkwood/pcie.c
@@ -17,7 +17,6 @@
#include <asm/mach/pci.h>
#include <plat/pcie.h>
#include <mach/bridge-regs.h>
-#include <plat/addr-map.h>
#include "common.h"
static void kirkwood_enable_pcie_clk(const char *port)
diff --git a/arch/arm/mach-msm/last_radio_log.c b/arch/arm/mach-msm/last_radio_log.c
index 7777767ee89a..9c392a29fc7e 100644
--- a/arch/arm/mach-msm/last_radio_log.c
+++ b/arch/arm/mach-msm/last_radio_log.c
@@ -66,6 +66,6 @@ void msm_init_last_radio_log(struct module *owner)
pr_err("%s: last radio log is %d bytes long\n", __func__,
radio_log_size);
last_radio_log_fops.owner = owner;
- entry->size = radio_log_size;
+ proc_set_size(entry, radio_log_size);
}
EXPORT_SYMBOL(msm_init_last_radio_log);
diff --git a/arch/arm/mach-mv78xx0/Makefile b/arch/arm/mach-mv78xx0/Makefile
index 67a13f9bfe64..7cd04634d302 100644
--- a/arch/arm/mach-mv78xx0/Makefile
+++ b/arch/arm/mach-mv78xx0/Makefile
@@ -1,4 +1,4 @@
-obj-y += common.o addr-map.o mpp.o irq.o pcie.o
+obj-y += common.o mpp.o irq.o pcie.o
obj-$(CONFIG_MACH_DB78X00_BP) += db78x00-bp-setup.o
obj-$(CONFIG_MACH_RD78X00_MASA) += rd78x00-masa-setup.o
obj-$(CONFIG_MACH_TERASTATION_WXL) += buffalo-wxl-setup.o
diff --git a/arch/arm/mach-mv78xx0/addr-map.c b/arch/arm/mach-mv78xx0/addr-map.c
deleted file mode 100644
index 26e9876b50e9..000000000000
--- a/arch/arm/mach-mv78xx0/addr-map.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * arch/arm/mach-mv78xx0/addr-map.c
- *
- * Address map functions for Marvell MV78xx0 SoCs
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/mbus.h>
-#include <linux/io.h>
-#include <plat/addr-map.h>
-#include <mach/mv78xx0.h>
-#include "common.h"
-
-/*
- * Generic Address Decode Windows bit settings
- */
-#define TARGET_DEV_BUS 1
-#define TARGET_PCIE0 4
-#define TARGET_PCIE1 8
-#define TARGET_PCIE(i) ((i) ? TARGET_PCIE1 : TARGET_PCIE0)
-#define ATTR_DEV_SPI_ROM 0x1f
-#define ATTR_DEV_BOOT 0x2f
-#define ATTR_DEV_CS3 0x37
-#define ATTR_DEV_CS2 0x3b
-#define ATTR_DEV_CS1 0x3d
-#define ATTR_DEV_CS0 0x3e
-#define ATTR_PCIE_IO(l) (0xf0 & ~(0x10 << (l)))
-#define ATTR_PCIE_MEM(l) (0xf8 & ~(0x10 << (l)))
-
-/*
- * CPU Address Decode Windows registers
- */
-#define WIN0_OFF(n) (BRIDGE_VIRT_BASE + 0x0000 + ((n) << 4))
-#define WIN8_OFF(n) (BRIDGE_VIRT_BASE + 0x0900 + (((n) - 8) << 4))
-
-static void __init __iomem *win_cfg_base(const struct orion_addr_map_cfg *cfg, int win)
-{
- /*
- * Find the control register base address for this window.
- *
- * BRIDGE_VIRT_BASE points to the right (CPU0's or CPU1's)
- * MBUS bridge depending on which CPU core we're running on,
- * so we don't need to take that into account here.
- */
-
- return (win < 8) ? WIN0_OFF(win) : WIN8_OFF(win);
-}
-
-/*
- * Description of the windows needed by the platform code
- */
-static struct orion_addr_map_cfg addr_map_cfg __initdata = {
- .num_wins = 14,
- .remappable_wins = 8,
- .win_cfg_base = win_cfg_base,
-};
-
-void __init mv78xx0_setup_cpu_mbus(void)
-{
- /*
- * Disable, clear and configure windows.
- */
- orion_config_wins(&addr_map_cfg, NULL);
-
- /*
- * Setup MBUS dram target info.
- */
- if (mv78xx0_core_index() == 0)
- orion_setup_cpu_mbus_target(&addr_map_cfg,
- (void __iomem *) DDR_WINDOW_CPU0_BASE);
- else
- orion_setup_cpu_mbus_target(&addr_map_cfg,
- (void __iomem *) DDR_WINDOW_CPU1_BASE);
-}
-
-void __init mv78xx0_setup_pcie_io_win(int window, u32 base, u32 size,
- int maj, int min)
-{
- orion_setup_cpu_win(&addr_map_cfg, window, base, size,
- TARGET_PCIE(maj), ATTR_PCIE_IO(min), 0);
-}
-
-void __init mv78xx0_setup_pcie_mem_win(int window, u32 base, u32 size,
- int maj, int min)
-{
- orion_setup_cpu_win(&addr_map_cfg, window, base, size,
- TARGET_PCIE(maj), ATTR_PCIE_MEM(min), -1);
-}
diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c
index 0efa14498ebc..749a7f8c4992 100644
--- a/arch/arm/mach-mv78xx0/common.c
+++ b/arch/arm/mach-mv78xx0/common.c
@@ -334,6 +334,14 @@ void __init mv78xx0_uart3_init(void)
void __init mv78xx0_init_early(void)
{
orion_time_set_base(TIMER_VIRT_BASE);
+ if (mv78xx0_core_index() == 0)
+ mvebu_mbus_init("marvell,mv78xx0-mbus",
+ BRIDGE_WINS_CPU0_BASE, BRIDGE_WINS_SZ,
+ DDR_WINDOW_CPU0_BASE, DDR_WINDOW_CPU_SZ);
+ else
+ mvebu_mbus_init("marvell,mv78xx0-mbus",
+ BRIDGE_WINS_CPU1_BASE, BRIDGE_WINS_SZ,
+ DDR_WINDOW_CPU1_BASE, DDR_WINDOW_CPU_SZ);
}
void __init_refok mv78xx0_timer_init(void)
@@ -397,8 +405,6 @@ void __init mv78xx0_init(void)
printk("HCLK = %dMHz, ", (hclk + 499999) / 1000000);
printk("TCLK = %dMHz\n", (get_tclk() + 499999) / 1000000);
- mv78xx0_setup_cpu_mbus();
-
#ifdef CONFIG_CACHE_FEROCEON_L2
feroceon_l2_init(is_l2_writethrough());
#endif
diff --git a/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h b/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h
index 46200a183cf2..723748d8ba7d 100644
--- a/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h
+++ b/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h
@@ -60,13 +60,18 @@
*/
#define BRIDGE_VIRT_BASE (MV78XX0_CORE_REGS_VIRT_BASE)
#define BRIDGE_PHYS_BASE (MV78XX0_CORE_REGS_PHYS_BASE)
+#define BRIDGE_WINS_CPU0_BASE (MV78XX0_CORE0_REGS_PHYS_BASE)
+#define BRIDGE_WINS_CPU1_BASE (MV78XX0_CORE1_REGS_PHYS_BASE)
+#define BRIDGE_WINS_SZ (0xA000)
/*
* Register Map
*/
#define DDR_VIRT_BASE (MV78XX0_REGS_VIRT_BASE + 0x00000)
-#define DDR_WINDOW_CPU0_BASE (DDR_VIRT_BASE + 0x1500)
-#define DDR_WINDOW_CPU1_BASE (DDR_VIRT_BASE + 0x1570)
+#define DDR_PHYS_BASE (MV78XX0_REGS_PHYS_BASE + 0x00000)
+#define DDR_WINDOW_CPU0_BASE (DDR_PHYS_BASE + 0x1500)
+#define DDR_WINDOW_CPU1_BASE (DDR_PHYS_BASE + 0x1570)
+#define DDR_WINDOW_CPU_SZ (0x20)
#define DEV_BUS_PHYS_BASE (MV78XX0_REGS_PHYS_BASE + 0x10000)
#define DEV_BUS_VIRT_BASE (MV78XX0_REGS_VIRT_BASE + 0x10000)
diff --git a/arch/arm/mach-mv78xx0/pcie.c b/arch/arm/mach-mv78xx0/pcie.c
index ee8c0b51df2c..dc26a654c496 100644
--- a/arch/arm/mach-mv78xx0/pcie.c
+++ b/arch/arm/mach-mv78xx0/pcie.c
@@ -10,11 +10,11 @@
#include <linux/kernel.h>
#include <linux/pci.h>
+#include <linux/mbus.h>
#include <video/vga.h>
#include <asm/irq.h>
#include <asm/mach/pci.h>
#include <plat/pcie.h>
-#include <plat/addr-map.h>
#include <mach/mv78xx0.h>
#include "common.h"
@@ -54,7 +54,6 @@ static void __init mv78xx0_pcie_preinit(void)
int i;
u32 size_each;
u32 start;
- int win = 0;
pcie_io_space.name = "PCIe I/O Space";
pcie_io_space.start = MV78XX0_PCIE_IO_PHYS_BASE(0);
@@ -72,6 +71,7 @@ static void __init mv78xx0_pcie_preinit(void)
start = MV78XX0_PCIE_MEM_PHYS_BASE;
for (i = 0; i < num_pcie_ports; i++) {
struct pcie_port *pp = pcie_port + i;
+ char winname[MVEBU_MBUS_MAX_WINNAME_SZ];
snprintf(pp->mem_space_name, sizeof(pp->mem_space_name),
"PCIe %d.%d MEM", pp->maj, pp->min);
@@ -85,12 +85,17 @@ static void __init mv78xx0_pcie_preinit(void)
if (request_resource(&iomem_resource, &pp->res))
panic("can't allocate PCIe MEM sub-space");
- mv78xx0_setup_pcie_mem_win(win + i + 8, pp->res.start,
- resource_size(&pp->res),
- pp->maj, pp->min);
-
- mv78xx0_setup_pcie_io_win(win + i, i * SZ_64K, SZ_64K,
- pp->maj, pp->min);
+ snprintf(winname, sizeof(winname), "pcie%d.%d",
+ pp->maj, pp->min);
+
+ mvebu_mbus_add_window_remap_flags(winname,
+ pp->res.start,
+ resource_size(&pp->res),
+ MVEBU_MBUS_NO_REMAP,
+ MVEBU_MBUS_PCI_MEM);
+ mvebu_mbus_add_window_remap_flags(winname,
+ i * SZ_64K, SZ_64K,
+ 0, MVEBU_MBUS_PCI_IO);
}
}
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index 440b13ef1fed..e11acbb0a46d 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -13,6 +13,8 @@ config ARCH_MVEBU
select MVEBU_CLK_CORE
select MVEBU_CLK_CPU
select MVEBU_CLK_GATING
+ select MVEBU_MBUS
+ select ZONE_DMA if ARM_LPAE
if ARCH_MVEBU
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile
index da93bcbc74c1..2d04f0e21870 100644
--- a/arch/arm/mach-mvebu/Makefile
+++ b/arch/arm/mach-mvebu/Makefile
@@ -5,6 +5,6 @@ AFLAGS_coherency_ll.o := -Wa,-march=armv7-a
obj-y += system-controller.o
obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o
-obj-$(CONFIG_ARCH_MVEBU) += addr-map.o coherency.o coherency_ll.o pmsu.o irq-armada-370-xp.o
+obj-$(CONFIG_ARCH_MVEBU) += coherency.o coherency_ll.o pmsu.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-mvebu/addr-map.c b/arch/arm/mach-mvebu/addr-map.c
deleted file mode 100644
index ab9b3bd4fef5..000000000000
--- a/arch/arm/mach-mvebu/addr-map.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Address map functions for Marvell 370 / XP SoCs
- *
- * Copyright (C) 2012 Marvell
- *
- * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/mbus.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <plat/addr-map.h>
-
-/*
- * Generic Address Decode Windows bit settings
- */
-#define ARMADA_XP_TARGET_DEV_BUS 1
-#define ARMADA_XP_ATTR_DEV_BOOTROM 0x1D
-#define ARMADA_XP_TARGET_ETH1 3
-#define ARMADA_XP_TARGET_PCIE_0_2 4
-#define ARMADA_XP_TARGET_ETH0 7
-#define ARMADA_XP_TARGET_PCIE_1_3 8
-
-#define ARMADA_370_TARGET_DEV_BUS 1
-#define ARMADA_370_ATTR_DEV_BOOTROM 0x1D
-#define ARMADA_370_TARGET_PCIE_0 4
-#define ARMADA_370_TARGET_PCIE_1 8
-
-#define ARMADA_WINDOW_8_PLUS_OFFSET 0x90
-#define ARMADA_SDRAM_ADDR_DECODING_OFFSET 0x180
-
-static const struct __initdata orion_addr_map_info
-armada_xp_addr_map_info[] = {
- /*
- * Window for the BootROM, needed for SMP on Armada XP
- */
- { 0, 0xfff00000, SZ_1M, ARMADA_XP_TARGET_DEV_BUS,
- ARMADA_XP_ATTR_DEV_BOOTROM, -1 },
- /* End marker */
- { -1, 0, 0, 0, 0, 0 },
-};
-
-static const struct __initdata orion_addr_map_info
-armada_370_addr_map_info[] = {
- /* End marker */
- { -1, 0, 0, 0, 0, 0 },
-};
-
-static struct of_device_id of_addr_decoding_controller_table[] = {
- { .compatible = "marvell,armada-addr-decoding-controller" },
- { /* end of list */ },
-};
-
-static void __iomem *
-armada_cfg_base(const struct orion_addr_map_cfg *cfg, int win)
-{
- unsigned int offset;
-
- /* The register layout is a bit annoying and the below code
- * tries to cope with it.
- * - At offset 0x0, there are the registers for the first 8
- * windows, with 4 registers of 32 bits per window (ctrl,
- * base, remap low, remap high)
- * - Then at offset 0x80, there is a hole of 0x10 bytes for
- * the internal registers base address and internal units
- * sync barrier register.
- * - Then at offset 0x90, there the registers for 12
- * windows, with only 2 registers of 32 bits per window
- * (ctrl, base).
- */
- if (win < 8)
- offset = (win << 4);
- else
- offset = ARMADA_WINDOW_8_PLUS_OFFSET + ((win - 8) << 3);
-
- return cfg->bridge_virt_base + offset;
-}
-
-static struct __initdata orion_addr_map_cfg addr_map_cfg = {
- .num_wins = 20,
- .remappable_wins = 8,
- .win_cfg_base = armada_cfg_base,
-};
-
-static int __init armada_setup_cpu_mbus(void)
-{
- struct device_node *np;
- void __iomem *mbus_unit_addr_decoding_base;
- void __iomem *sdram_addr_decoding_base;
-
- np = of_find_matching_node(NULL, of_addr_decoding_controller_table);
- if (!np)
- return -ENODEV;
-
- mbus_unit_addr_decoding_base = of_iomap(np, 0);
- BUG_ON(!mbus_unit_addr_decoding_base);
-
- sdram_addr_decoding_base =
- mbus_unit_addr_decoding_base +
- ARMADA_SDRAM_ADDR_DECODING_OFFSET;
-
- addr_map_cfg.bridge_virt_base = mbus_unit_addr_decoding_base;
-
- if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"))
- addr_map_cfg.hw_io_coherency = 1;
-
- /*
- * Disable, clear and configure windows.
- */
- if (of_machine_is_compatible("marvell,armadaxp"))
- orion_config_wins(&addr_map_cfg, armada_xp_addr_map_info);
- else if (of_machine_is_compatible("marvell,armada370"))
- orion_config_wins(&addr_map_cfg, armada_370_addr_map_info);
- else {
- pr_err("Unsupported SoC\n");
- return -EINVAL;
- }
-
- /*
- * Setup MBUS dram target info.
- */
- orion_setup_cpu_mbus_target(&addr_map_cfg,
- sdram_addr_decoding_base);
- return 0;
-}
-
-/* Using a early_initcall is needed so that this initialization gets
- * done before the SMP initialization, which requires the BootROM to
- * be remapped. */
-early_initcall(armada_setup_cpu_mbus);
diff --git a/arch/arm/mach-mvebu/armada-370-xp.c b/arch/arm/mach-mvebu/armada-370-xp.c
index a5ea616d6d12..42a4cb3087e2 100644
--- a/arch/arm/mach-mvebu/armada-370-xp.c
+++ b/arch/arm/mach-mvebu/armada-370-xp.c
@@ -19,6 +19,9 @@
#include <linux/time-armada-370-xp.h>
#include <linux/clk/mvebu.h>
#include <linux/dma-mapping.h>
+#include <linux/mbus.h>
+#include <linux/irqchip.h>
+#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
@@ -48,12 +51,33 @@ void __init armada_370_xp_timer_and_clk_init(void)
void __init armada_370_xp_init_early(void)
{
+ char *mbus_soc_name;
+
/*
* Some Armada 370/XP devices allocate their coherent buffers
* from atomic context. Increase size of atomic coherent pool
* to make sure such the allocations won't fail.
*/
init_dma_coherent_pool_size(SZ_1M);
+
+ /*
+ * This initialization will be replaced by a DT-based
+ * initialization once the mvebu-mbus driver gains DT support.
+ */
+ if (of_machine_is_compatible("marvell,armada370"))
+ mbus_soc_name = "marvell,armada370-mbus";
+ else
+ mbus_soc_name = "marvell,armadaxp-mbus";
+
+ mvebu_mbus_init(mbus_soc_name,
+ ARMADA_370_XP_MBUS_WINS_BASE,
+ ARMADA_370_XP_MBUS_WINS_SIZE,
+ ARMADA_370_XP_SDRAM_WINS_BASE,
+ ARMADA_370_XP_SDRAM_WINS_SIZE);
+
+#ifdef CONFIG_CACHE_L2X0
+ l2x0_of_init(0, ~0UL);
+#endif
}
static void __init armada_370_xp_dt_init(void)
@@ -72,8 +96,7 @@ DT_MACHINE_START(ARMADA_XP_DT, "Marvell Armada 370/XP (Device Tree)")
.init_machine = armada_370_xp_dt_init,
.map_io = armada_370_xp_map_io,
.init_early = armada_370_xp_init_early,
- .init_irq = armada_370_xp_init_irq,
- .handle_irq = armada_370_xp_handle_irq,
+ .init_irq = irqchip_init,
.init_time = armada_370_xp_timer_and_clk_init,
.restart = mvebu_restart,
.dt_compat = armada_370_xp_dt_compat,
diff --git a/arch/arm/mach-mvebu/armada-370-xp.h b/arch/arm/mach-mvebu/armada-370-xp.h
index c6a7d74fddfe..2070e1b4f342 100644
--- a/arch/arm/mach-mvebu/armada-370-xp.h
+++ b/arch/arm/mach-mvebu/armada-370-xp.h
@@ -16,9 +16,15 @@
#define __MACH_ARMADA_370_XP_H
#define ARMADA_370_XP_REGS_PHYS_BASE 0xd0000000
-#define ARMADA_370_XP_REGS_VIRT_BASE IOMEM(0xfeb00000)
+#define ARMADA_370_XP_REGS_VIRT_BASE IOMEM(0xfec00000)
#define ARMADA_370_XP_REGS_SIZE SZ_1M
+/* These defines can go away once mvebu-mbus has a DT binding */
+#define ARMADA_370_XP_MBUS_WINS_BASE (ARMADA_370_XP_REGS_PHYS_BASE + 0x20000)
+#define ARMADA_370_XP_MBUS_WINS_SIZE 0x100
+#define ARMADA_370_XP_SDRAM_WINS_BASE (ARMADA_370_XP_REGS_PHYS_BASE + 0x20180)
+#define ARMADA_370_XP_SDRAM_WINS_SIZE 0x20
+
#ifdef CONFIG_SMP
#include <linux/cpumask.h>
diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c
index fe16aaf7c19c..875ea748391c 100644
--- a/arch/arm/mach-mvebu/platsmp.c
+++ b/arch/arm/mach-mvebu/platsmp.c
@@ -21,6 +21,7 @@
#include <linux/smp.h>
#include <linux/clk.h>
#include <linux/of.h>
+#include <linux/mbus.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include "common.h"
@@ -109,6 +110,7 @@ void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
set_secondary_cpus_clock();
flush_cache_all();
set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0);
+ mvebu_mbus_add_window("bootrom", 0xfff00000, SZ_1M);
}
struct smp_operations armada_xp_smp_ops __initdata = {
diff --git a/arch/arm/mach-mxs/mach-mxs.c b/arch/arm/mach-mxs/mach-mxs.c
index b5c1bdd3dcdf..5b62b6489d4b 100644
--- a/arch/arm/mach-mxs/mach-mxs.c
+++ b/arch/arm/mach-mxs/mach-mxs.c
@@ -22,7 +22,6 @@
#include <linux/irqchip.h>
#include <linux/irqchip/mxs.h>
#include <linux/micrel_phy.h>
-#include <linux/mxsfb.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
@@ -61,106 +60,6 @@ static inline void __mxs_togl(u32 mask, void __iomem *reg)
__raw_writel(mask, reg + MXS_TOG_ADDR);
}
-static struct fb_videomode mx23evk_video_modes[] = {
- {
- .name = "Samsung-LMS430HF02",
- .refresh = 60,
- .xres = 480,
- .yres = 272,
- .pixclock = 108096, /* picosecond (9.2 MHz) */
- .left_margin = 15,
- .right_margin = 8,
- .upper_margin = 12,
- .lower_margin = 4,
- .hsync_len = 1,
- .vsync_len = 1,
- },
-};
-
-static struct fb_videomode mx28evk_video_modes[] = {
- {
- .name = "Seiko-43WVF1G",
- .refresh = 60,
- .xres = 800,
- .yres = 480,
- .pixclock = 29851, /* picosecond (33.5 MHz) */
- .left_margin = 89,
- .right_margin = 164,
- .upper_margin = 23,
- .lower_margin = 10,
- .hsync_len = 10,
- .vsync_len = 10,
- },
-};
-
-static struct fb_videomode m28evk_video_modes[] = {
- {
- .name = "Ampire AM-800480R2TMQW-T01H",
- .refresh = 60,
- .xres = 800,
- .yres = 480,
- .pixclock = 30066, /* picosecond (33.26 MHz) */
- .left_margin = 0,
- .right_margin = 256,
- .upper_margin = 0,
- .lower_margin = 45,
- .hsync_len = 1,
- .vsync_len = 1,
- },
-};
-
-static struct fb_videomode apx4devkit_video_modes[] = {
- {
- .name = "HannStar PJ70112A",
- .refresh = 60,
- .xres = 800,
- .yres = 480,
- .pixclock = 33333, /* picosecond (30.00 MHz) */
- .left_margin = 88,
- .right_margin = 40,
- .upper_margin = 32,
- .lower_margin = 13,
- .hsync_len = 48,
- .vsync_len = 3,
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- },
-};
-
-static struct fb_videomode apf28dev_video_modes[] = {
- {
- .name = "LW700",
- .refresh = 60,
- .xres = 800,
- .yres = 480,
- .pixclock = 30303, /* picosecond */
- .left_margin = 96,
- .right_margin = 96, /* at least 3 & 1 */
- .upper_margin = 0x14,
- .lower_margin = 0x15,
- .hsync_len = 64,
- .vsync_len = 4,
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- },
-};
-
-static struct fb_videomode cfa10049_video_modes[] = {
- {
- .name = "Himax HX8357-B",
- .refresh = 60,
- .xres = 320,
- .yres = 480,
- .pixclock = 108506, /* picosecond (9.216 MHz) */
- .left_margin = 2,
- .right_margin = 2,
- .upper_margin = 2,
- .lower_margin = 2,
- .hsync_len = 15,
- .vsync_len = 15,
- },
-};
-
-static struct mxsfb_platform_data mxsfb_pdata __initdata;
-
/*
* MX28EVK_FLEXCAN_SWITCH is shared between both flexcan controllers
*/
@@ -191,8 +90,6 @@ static void mx28evk_flexcan1_switch(int enable)
static struct flexcan_platform_data flexcan_pdata[2];
static struct of_dev_auxdata mxs_auxdata_lookup[] __initdata = {
- OF_DEV_AUXDATA("fsl,imx23-lcdif", 0x80030000, NULL, &mxsfb_pdata),
- OF_DEV_AUXDATA("fsl,imx28-lcdif", 0x80030000, NULL, &mxsfb_pdata),
OF_DEV_AUXDATA("fsl,imx28-flexcan", 0x80032000, NULL, &flexcan_pdata[0]),
OF_DEV_AUXDATA("fsl,imx28-flexcan", 0x80034000, NULL, &flexcan_pdata[1]),
{ /* sentinel */ }
@@ -342,16 +239,6 @@ static void __init update_fec_mac_prop(enum mac_oui oui)
}
}
-static void __init imx23_evk_init(void)
-{
- mxsfb_pdata.mode_list = mx23evk_video_modes;
- mxsfb_pdata.mode_count = ARRAY_SIZE(mx23evk_video_modes);
- mxsfb_pdata.default_bpp = 32;
- mxsfb_pdata.ld_intf_width = STMLCDIF_24BIT;
- mxsfb_pdata.sync = MXSFB_SYNC_DATA_ENABLE_HIGH_ACT |
- MXSFB_SYNC_DOTCLK_FAILING_ACT;
-}
-
static inline void enable_clk_enet_out(void)
{
struct clk *clk = clk_get_sys("enet_out", NULL);
@@ -362,16 +249,8 @@ static inline void enable_clk_enet_out(void)
static void __init imx28_evk_init(void)
{
- enable_clk_enet_out();
update_fec_mac_prop(OUI_FSL);
- mxsfb_pdata.mode_list = mx28evk_video_modes;
- mxsfb_pdata.mode_count = ARRAY_SIZE(mx28evk_video_modes);
- mxsfb_pdata.default_bpp = 32;
- mxsfb_pdata.ld_intf_width = STMLCDIF_24BIT;
- mxsfb_pdata.sync = MXSFB_SYNC_DATA_ENABLE_HIGH_ACT |
- MXSFB_SYNC_DOTCLK_FAILING_ACT;
-
mxs_saif_clkmux_select(MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0);
}
@@ -384,20 +263,6 @@ static void __init imx28_evk_post_init(void)
}
}
-static void __init m28evk_init(void)
-{
- mxsfb_pdata.mode_list = m28evk_video_modes;
- mxsfb_pdata.mode_count = ARRAY_SIZE(m28evk_video_modes);
- mxsfb_pdata.default_bpp = 16;
- mxsfb_pdata.ld_intf_width = STMLCDIF_18BIT;
- mxsfb_pdata.sync = MXSFB_SYNC_DATA_ENABLE_HIGH_ACT;
-}
-
-static void __init sc_sps1_init(void)
-{
- enable_clk_enet_out();
-}
-
static int apx4devkit_phy_fixup(struct phy_device *phy)
{
phy->dev_flags |= MICREL_PHY_50MHZ_CLK;
@@ -411,13 +276,6 @@ static void __init apx4devkit_init(void)
if (IS_BUILTIN(CONFIG_PHYLIB))
phy_register_fixup_for_uid(PHY_ID_KSZ8051, MICREL_PHY_ID_MASK,
apx4devkit_phy_fixup);
-
- mxsfb_pdata.mode_list = apx4devkit_video_modes;
- mxsfb_pdata.mode_count = ARRAY_SIZE(apx4devkit_video_modes);
- mxsfb_pdata.default_bpp = 32;
- mxsfb_pdata.ld_intf_width = STMLCDIF_24BIT;
- mxsfb_pdata.sync = MXSFB_SYNC_DATA_ENABLE_HIGH_ACT |
- MXSFB_SYNC_DOTCLK_FAILING_ACT;
}
#define ENET0_MDC__GPIO_4_0 MXS_GPIO_NR(4, 0)
@@ -496,52 +354,24 @@ static void __init tx28_post_init(void)
static void __init cfa10049_init(void)
{
- enable_clk_enet_out();
update_fec_mac_prop(OUI_CRYSTALFONTZ);
-
- mxsfb_pdata.mode_list = cfa10049_video_modes;
- mxsfb_pdata.mode_count = ARRAY_SIZE(cfa10049_video_modes);
- mxsfb_pdata.default_bpp = 32;
- mxsfb_pdata.ld_intf_width = STMLCDIF_18BIT;
- mxsfb_pdata.sync = MXSFB_SYNC_DATA_ENABLE_HIGH_ACT;
}
static void __init cfa10037_init(void)
{
- enable_clk_enet_out();
update_fec_mac_prop(OUI_CRYSTALFONTZ);
}
-static void __init apf28_init(void)
-{
- enable_clk_enet_out();
-
- mxsfb_pdata.mode_list = apf28dev_video_modes;
- mxsfb_pdata.mode_count = ARRAY_SIZE(apf28dev_video_modes);
- mxsfb_pdata.default_bpp = 16;
- mxsfb_pdata.ld_intf_width = STMLCDIF_16BIT;
- mxsfb_pdata.sync = MXSFB_SYNC_DATA_ENABLE_HIGH_ACT |
- MXSFB_SYNC_DOTCLK_FAILING_ACT;
-}
-
static void __init mxs_machine_init(void)
{
if (of_machine_is_compatible("fsl,imx28-evk"))
imx28_evk_init();
- else if (of_machine_is_compatible("fsl,imx23-evk"))
- imx23_evk_init();
- else if (of_machine_is_compatible("denx,m28evk"))
- m28evk_init();
else if (of_machine_is_compatible("bluegiga,apx4devkit"))
apx4devkit_init();
else if (of_machine_is_compatible("crystalfontz,cfa10037"))
cfa10037_init();
else if (of_machine_is_compatible("crystalfontz,cfa10049"))
cfa10049_init();
- else if (of_machine_is_compatible("armadeus,imx28-apf28"))
- apf28_init();
- else if (of_machine_is_compatible("schulercontrol,imx28-sps1"))
- sc_sps1_init();
of_platform_populate(NULL, of_default_bus_match_table,
mxs_auxdata_lookup, NULL);
diff --git a/arch/arm/mach-omap2/board-2430sdp.c b/arch/arm/mach-omap2/board-2430sdp.c
index 5b86423c89fa..244d8a5aa54b 100644
--- a/arch/arm/mach-omap2/board-2430sdp.c
+++ b/arch/arm/mach-omap2/board-2430sdp.c
@@ -108,24 +108,13 @@ static struct platform_device *sdp2430_devices[] __initdata = {
#define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91
#define SDP2430_LCD_PANEL_ENABLE_GPIO 154
-static int sdp2430_panel_enable_lcd(struct omap_dss_device *dssdev)
-{
- gpio_direction_output(SDP2430_LCD_PANEL_ENABLE_GPIO, 1);
- gpio_direction_output(SDP2430_LCD_PANEL_BACKLIGHT_GPIO, 1);
-
- return 0;
-}
-
-static void sdp2430_panel_disable_lcd(struct omap_dss_device *dssdev)
-{
- gpio_direction_output(SDP2430_LCD_PANEL_ENABLE_GPIO, 0);
- gpio_direction_output(SDP2430_LCD_PANEL_BACKLIGHT_GPIO, 0);
-}
-
static struct panel_generic_dpi_data sdp2430_panel_data = {
.name = "nec_nl2432dr22-11b",
- .platform_enable = sdp2430_panel_enable_lcd,
- .platform_disable = sdp2430_panel_disable_lcd,
+ .num_gpios = 2,
+ .gpios = {
+ SDP2430_LCD_PANEL_ENABLE_GPIO,
+ SDP2430_LCD_PANEL_BACKLIGHT_GPIO,
+ },
};
static struct omap_dss_device sdp2430_lcd_device = {
@@ -146,26 +135,6 @@ static struct omap_dss_board_info sdp2430_dss_data = {
.default_device = &sdp2430_lcd_device,
};
-static void __init sdp2430_display_init(void)
-{
- int r;
-
- static struct gpio gpios[] __initdata = {
- { SDP2430_LCD_PANEL_ENABLE_GPIO, GPIOF_OUT_INIT_LOW,
- "LCD reset" },
- { SDP2430_LCD_PANEL_BACKLIGHT_GPIO, GPIOF_OUT_INIT_LOW,
- "LCD Backlight" },
- };
-
- r = gpio_request_array(gpios, ARRAY_SIZE(gpios));
- if (r) {
- pr_err("Cannot request LCD GPIOs, error %d\n", r);
- return;
- }
-
- omap_display_init(&sdp2430_dss_data);
-}
-
#if IS_ENABLED(CONFIG_SMC91X)
static struct omap_smc91x_platform_data board_smc91x_data = {
@@ -273,7 +242,7 @@ static void __init omap_2430sdp_init(void)
gpio_request_one(SECONDARY_LCD_GPIO, GPIOF_OUT_INIT_LOW,
"Secondary LCD backlight");
- sdp2430_display_init();
+ omap_display_init(&sdp2430_dss_data);
}
MACHINE_START(OMAP_2430SDP, "OMAP2430 sdp2430 board")
diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c
index a4d4664894e1..23b004afa3f8 100644
--- a/arch/arm/mach-omap2/board-3430sdp.c
+++ b/arch/arm/mach-omap2/board-3430sdp.c
@@ -108,53 +108,38 @@ static struct twl4030_keypad_data sdp3430_kp_data = {
#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 8
#define SDP3430_LCD_PANEL_ENABLE_GPIO 5
-static struct gpio sdp3430_dss_gpios[] __initdata = {
- {SDP3430_LCD_PANEL_ENABLE_GPIO, GPIOF_OUT_INIT_LOW, "LCD reset" },
- {SDP3430_LCD_PANEL_BACKLIGHT_GPIO, GPIOF_OUT_INIT_LOW, "LCD Backlight"},
-};
-
static void __init sdp3430_display_init(void)
{
int r;
- r = gpio_request_array(sdp3430_dss_gpios,
- ARRAY_SIZE(sdp3430_dss_gpios));
+ /*
+ * the backlight GPIO doesn't directly go to the panel, it enables
+ * an internal circuit on 3430sdp to create the signal V_BKL_28V,
+ * this is connected to LED+ pin of the sharp panel. This GPIO
+ * is left enabled in the board file, and not passed to the panel
+ * as platform_data.
+ */
+ r = gpio_request_one(SDP3430_LCD_PANEL_BACKLIGHT_GPIO,
+ GPIOF_OUT_INIT_HIGH, "LCD Backlight");
if (r)
- printk(KERN_ERR "failed to get LCD control GPIOs\n");
-
-}
+ pr_err("failed to get LCD Backlight GPIO\n");
-static int sdp3430_panel_enable_lcd(struct omap_dss_device *dssdev)
-{
- gpio_direction_output(SDP3430_LCD_PANEL_ENABLE_GPIO, 1);
- gpio_direction_output(SDP3430_LCD_PANEL_BACKLIGHT_GPIO, 1);
-
- return 0;
-}
-
-static void sdp3430_panel_disable_lcd(struct omap_dss_device *dssdev)
-{
- gpio_direction_output(SDP3430_LCD_PANEL_ENABLE_GPIO, 0);
- gpio_direction_output(SDP3430_LCD_PANEL_BACKLIGHT_GPIO, 0);
-}
-
-static int sdp3430_panel_enable_tv(struct omap_dss_device *dssdev)
-{
- return 0;
-}
-
-static void sdp3430_panel_disable_tv(struct omap_dss_device *dssdev)
-{
}
+static struct panel_sharp_ls037v7dw01_data sdp3430_lcd_data = {
+ .resb_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO,
+ .ini_gpio = -1,
+ .mo_gpio = -1,
+ .lr_gpio = -1,
+ .ud_gpio = -1,
+};
static struct omap_dss_device sdp3430_lcd_device = {
.name = "lcd",
.driver_name = "sharp_ls_panel",
.type = OMAP_DISPLAY_TYPE_DPI,
.phy.dpi.data_lines = 16,
- .platform_enable = sdp3430_panel_enable_lcd,
- .platform_disable = sdp3430_panel_disable_lcd,
+ .data = &sdp3430_lcd_data,
};
static struct tfp410_platform_data dvi_panel = {
@@ -175,8 +160,6 @@ static struct omap_dss_device sdp3430_tv_device = {
.driver_name = "venc",
.type = OMAP_DISPLAY_TYPE_VENC,
.phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO,
- .platform_enable = sdp3430_panel_enable_tv,
- .platform_disable = sdp3430_panel_disable_tv,
};
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index 00d72902ef4f..56a9a4f855c7 100644
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -730,7 +730,7 @@ static void __init omap_4430sdp_init(void)
omap4_sdp4430_wifi_init();
omap4_twl6030_hsmmc_init(mmc);
- usb_bind_phy("musb-hdrc.0.auto", 0, "omap-usb2.1.auto");
+ usb_bind_phy("musb-hdrc.2.auto", 0, "omap-usb2.3.auto");
usb_musb_init(&musb_board_data);
status = omap_ethernet_init();
diff --git a/arch/arm/mach-omap2/board-am3517evm.c b/arch/arm/mach-omap2/board-am3517evm.c
index c29d2e743688..d63f14b534b5 100644
--- a/arch/arm/mach-omap2/board-am3517evm.c
+++ b/arch/arm/mach-omap2/board-am3517evm.c
@@ -120,63 +120,14 @@ static int __init am3517_evm_i2c_init(void)
return 0;
}
-static int lcd_enabled;
-static int dvi_enabled;
-
-#if defined(CONFIG_PANEL_SHARP_LQ043T1DG01) || \
- defined(CONFIG_PANEL_SHARP_LQ043T1DG01_MODULE)
-static struct gpio am3517_evm_dss_gpios[] __initdata = {
- /* GPIO 182 = LCD Backlight Power */
- { LCD_PANEL_BKLIGHT_PWR, GPIOF_OUT_INIT_HIGH, "lcd_backlight_pwr" },
- /* GPIO 181 = LCD Panel PWM */
- { LCD_PANEL_PWM, GPIOF_OUT_INIT_HIGH, "lcd bl enable" },
- /* GPIO 176 = LCD Panel Power enable pin */
- { LCD_PANEL_PWR, GPIOF_OUT_INIT_HIGH, "dvi enable" },
-};
-
-static void __init am3517_evm_display_init(void)
-{
- int r;
-
- omap_mux_init_gpio(LCD_PANEL_PWR, OMAP_PIN_INPUT_PULLUP);
- omap_mux_init_gpio(LCD_PANEL_BKLIGHT_PWR, OMAP_PIN_INPUT_PULLDOWN);
- omap_mux_init_gpio(LCD_PANEL_PWM, OMAP_PIN_INPUT_PULLDOWN);
-
- r = gpio_request_array(am3517_evm_dss_gpios,
- ARRAY_SIZE(am3517_evm_dss_gpios));
- if (r) {
- printk(KERN_ERR "failed to get DSS panel control GPIOs\n");
- return;
- }
-
- printk(KERN_INFO "Display initialized successfully\n");
-}
-#else
-static void __init am3517_evm_display_init(void) {}
-#endif
-
-static int am3517_evm_panel_enable_lcd(struct omap_dss_device *dssdev)
-{
- if (dvi_enabled) {
- printk(KERN_ERR "cannot enable LCD, DVI is enabled\n");
- return -EINVAL;
- }
- gpio_set_value(LCD_PANEL_PWR, 1);
- lcd_enabled = 1;
-
- return 0;
-}
-
-static void am3517_evm_panel_disable_lcd(struct omap_dss_device *dssdev)
-{
- gpio_set_value(LCD_PANEL_PWR, 0);
- lcd_enabled = 0;
-}
-
static struct panel_generic_dpi_data lcd_panel = {
.name = "sharp_lq",
- .platform_enable = am3517_evm_panel_enable_lcd,
- .platform_disable = am3517_evm_panel_disable_lcd,
+ .num_gpios = 3,
+ .gpios = {
+ LCD_PANEL_PWR,
+ LCD_PANEL_BKLIGHT_PWR,
+ LCD_PANEL_PWM,
+ },
};
static struct omap_dss_device am3517_evm_lcd_device = {
@@ -187,22 +138,11 @@ static struct omap_dss_device am3517_evm_lcd_device = {
.phy.dpi.data_lines = 16,
};
-static int am3517_evm_panel_enable_tv(struct omap_dss_device *dssdev)
-{
- return 0;
-}
-
-static void am3517_evm_panel_disable_tv(struct omap_dss_device *dssdev)
-{
-}
-
static struct omap_dss_device am3517_evm_tv_device = {
.type = OMAP_DISPLAY_TYPE_VENC,
.name = "tv",
.driver_name = "venc",
.phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO,
- .platform_enable = am3517_evm_panel_enable_tv,
- .platform_disable = am3517_evm_panel_disable_tv,
};
static struct tfp410_platform_data dvi_panel = {
@@ -365,8 +305,6 @@ static void __init am3517_evm_init(void)
usbhs_init_phys(phy_data, ARRAY_SIZE(phy_data));
usbhs_init(&usbhs_bdata);
am3517_evm_hecc_init(&am3517_evm_hecc_pdata);
- /* DSS */
- am3517_evm_display_init();
/* RTC - S35390A */
am3517_evm_rtc_init();
diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c
index e0ed8c07fc54..ee6218c74807 100644
--- a/arch/arm/mach-omap2/board-cm-t35.c
+++ b/arch/arm/mach-omap2/board-cm-t35.c
@@ -190,45 +190,12 @@ static inline void cm_t35_init_nand(void) {}
#define CM_T35_LCD_BL_GPIO 58
#define CM_T35_DVI_EN_GPIO 54
-static int lcd_enabled;
-static int dvi_enabled;
-
-static int cm_t35_panel_enable_lcd(struct omap_dss_device *dssdev)
-{
- if (dvi_enabled) {
- printk(KERN_ERR "cannot enable LCD, DVI is enabled\n");
- return -EINVAL;
- }
-
- gpio_set_value(CM_T35_LCD_EN_GPIO, 1);
- gpio_set_value(CM_T35_LCD_BL_GPIO, 1);
-
- lcd_enabled = 1;
-
- return 0;
-}
-
-static void cm_t35_panel_disable_lcd(struct omap_dss_device *dssdev)
-{
- lcd_enabled = 0;
-
- gpio_set_value(CM_T35_LCD_BL_GPIO, 0);
- gpio_set_value(CM_T35_LCD_EN_GPIO, 0);
-}
-
-static int cm_t35_panel_enable_tv(struct omap_dss_device *dssdev)
-{
- return 0;
-}
-
-static void cm_t35_panel_disable_tv(struct omap_dss_device *dssdev)
-{
-}
-
static struct panel_generic_dpi_data lcd_panel = {
.name = "toppoly_tdo35s",
- .platform_enable = cm_t35_panel_enable_lcd,
- .platform_disable = cm_t35_panel_disable_lcd,
+ .num_gpios = 1,
+ .gpios = {
+ CM_T35_LCD_BL_GPIO,
+ },
};
static struct omap_dss_device cm_t35_lcd_device = {
@@ -257,8 +224,6 @@ static struct omap_dss_device cm_t35_tv_device = {
.driver_name = "venc",
.type = OMAP_DISPLAY_TYPE_VENC,
.phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO,
- .platform_enable = cm_t35_panel_enable_tv,
- .platform_disable = cm_t35_panel_disable_tv,
};
static struct omap_dss_device *cm_t35_dss_devices[] = {
@@ -292,11 +257,6 @@ static struct spi_board_info cm_t35_lcd_spi_board_info[] __initdata = {
},
};
-static struct gpio cm_t35_dss_gpios[] __initdata = {
- { CM_T35_LCD_EN_GPIO, GPIOF_OUT_INIT_LOW, "lcd enable" },
- { CM_T35_LCD_BL_GPIO, GPIOF_OUT_INIT_LOW, "lcd bl enable" },
-};
-
static void __init cm_t35_init_display(void)
{
int err;
@@ -304,23 +264,21 @@ static void __init cm_t35_init_display(void)
spi_register_board_info(cm_t35_lcd_spi_board_info,
ARRAY_SIZE(cm_t35_lcd_spi_board_info));
- err = gpio_request_array(cm_t35_dss_gpios,
- ARRAY_SIZE(cm_t35_dss_gpios));
+
+ err = gpio_request_one(CM_T35_LCD_EN_GPIO, GPIOF_OUT_INIT_LOW,
+ "lcd bl enable");
if (err) {
- pr_err("CM-T35: failed to request DSS control GPIOs\n");
+ pr_err("CM-T35: failed to request LCD EN GPIO\n");
return;
}
- gpio_export(CM_T35_LCD_EN_GPIO, 0);
- gpio_export(CM_T35_LCD_BL_GPIO, 0);
-
msleep(50);
gpio_set_value(CM_T35_LCD_EN_GPIO, 1);
err = omap_display_init(&cm_t35_dss_data);
if (err) {
pr_err("CM-T35: failed to register DSS device\n");
- gpio_free_array(cm_t35_dss_gpios, ARRAY_SIZE(cm_t35_dss_gpios));
+ gpio_free(CM_T35_LCD_EN_GPIO);
}
}
diff --git a/arch/arm/mach-omap2/board-devkit8000.c b/arch/arm/mach-omap2/board-devkit8000.c
index e44b804f75ae..576420544178 100644
--- a/arch/arm/mach-omap2/board-devkit8000.c
+++ b/arch/arm/mach-omap2/board-devkit8000.c
@@ -103,19 +103,6 @@ static struct omap2_hsmmc_info mmc[] = {
{} /* Terminator */
};
-static int devkit8000_panel_enable_lcd(struct omap_dss_device *dssdev)
-{
- if (gpio_is_valid(dssdev->reset_gpio))
- gpio_set_value_cansleep(dssdev->reset_gpio, 1);
- return 0;
-}
-
-static void devkit8000_panel_disable_lcd(struct omap_dss_device *dssdev)
-{
- if (gpio_is_valid(dssdev->reset_gpio))
- gpio_set_value_cansleep(dssdev->reset_gpio, 0);
-}
-
static struct regulator_consumer_supply devkit8000_vmmc1_supply[] = {
REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0"),
};
@@ -127,8 +114,7 @@ static struct regulator_consumer_supply devkit8000_vio_supply[] = {
static struct panel_generic_dpi_data lcd_panel = {
.name = "innolux_at070tn83",
- .platform_enable = devkit8000_panel_enable_lcd,
- .platform_disable = devkit8000_panel_disable_lcd,
+ /* gpios filled in code */
};
static struct omap_dss_device devkit8000_lcd_device = {
@@ -210,8 +196,6 @@ static struct gpio_led gpio_leds[];
static int devkit8000_twl_gpio_setup(struct device *dev,
unsigned gpio, unsigned ngpio)
{
- int ret;
-
/* gpio + 0 is "mmc0_cd" (input/IRQ) */
mmc[0].gpio_cd = gpio + 0;
omap_hsmmc_late_init(mmc);
@@ -220,13 +204,8 @@ static int devkit8000_twl_gpio_setup(struct device *dev,
gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
/* TWL4030_GPIO_MAX + 0 is "LCD_PWREN" (out, active high) */
- devkit8000_lcd_device.reset_gpio = gpio + TWL4030_GPIO_MAX + 0;
- ret = gpio_request_one(devkit8000_lcd_device.reset_gpio,
- GPIOF_OUT_INIT_LOW, "LCD_PWREN");
- if (ret < 0) {
- devkit8000_lcd_device.reset_gpio = -EINVAL;
- printk(KERN_ERR "Failed to request GPIO for LCD_PWRN\n");
- }
+ lcd_panel.num_gpios = 1;
+ lcd_panel.gpios[0] = gpio + TWL4030_GPIO_MAX + 0;
/* gpio + 7 is "DVI_PD" (out, active low) */
dvi_panel.power_down_gpio = gpio + 7;
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 78813b397209..88aa6b1835c3 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -110,6 +110,7 @@ MACHINE_END
static const char *omap3_gp_boards_compat[] __initdata = {
"ti,omap3-beagle",
+ "timll,omap3-devkit8000",
NULL,
};
diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c
index 8a8e505a0e90..d0d17bc58d9b 100644
--- a/arch/arm/mach-omap2/board-ldp.c
+++ b/arch/arm/mach-omap2/board-ldp.c
@@ -181,34 +181,13 @@ static inline void __init ldp_init_smsc911x(void)
/* LCD */
-static int ldp_backlight_gpio;
-static int ldp_lcd_enable_gpio;
-
#define LCD_PANEL_RESET_GPIO 55
#define LCD_PANEL_QVGA_GPIO 56
-static int ldp_panel_enable_lcd(struct omap_dss_device *dssdev)
-{
- if (gpio_is_valid(ldp_lcd_enable_gpio))
- gpio_direction_output(ldp_lcd_enable_gpio, 1);
- if (gpio_is_valid(ldp_backlight_gpio))
- gpio_direction_output(ldp_backlight_gpio, 1);
-
- return 0;
-}
-
-static void ldp_panel_disable_lcd(struct omap_dss_device *dssdev)
-{
- if (gpio_is_valid(ldp_lcd_enable_gpio))
- gpio_direction_output(ldp_lcd_enable_gpio, 0);
- if (gpio_is_valid(ldp_backlight_gpio))
- gpio_direction_output(ldp_backlight_gpio, 0);
-}
-
static struct panel_generic_dpi_data ldp_panel_data = {
.name = "nec_nl2432dr22-11b",
- .platform_enable = ldp_panel_enable_lcd,
- .platform_disable = ldp_panel_disable_lcd,
+ .num_gpios = 4,
+ /* gpios filled in code */
};
static struct omap_dss_device ldp_lcd_device = {
@@ -231,41 +210,19 @@ static struct omap_dss_board_info ldp_dss_data = {
static void __init ldp_display_init(void)
{
- int r;
-
- static struct gpio gpios[] __initdata = {
- {LCD_PANEL_RESET_GPIO, GPIOF_OUT_INIT_HIGH, "LCD RESET"},
- {LCD_PANEL_QVGA_GPIO, GPIOF_OUT_INIT_HIGH, "LCD QVGA"},
- };
-
- r = gpio_request_array(gpios, ARRAY_SIZE(gpios));
- if (r) {
- pr_err("Cannot request LCD GPIOs, error %d\n", r);
- return;
- }
+ ldp_panel_data.gpios[2] = LCD_PANEL_RESET_GPIO;
+ ldp_panel_data.gpios[3] = LCD_PANEL_QVGA_GPIO;
omap_display_init(&ldp_dss_data);
}
static int ldp_twl_gpio_setup(struct device *dev, unsigned gpio, unsigned ngpio)
{
- int r;
-
- struct gpio gpios[] = {
- {gpio + 7 , GPIOF_OUT_INIT_LOW, "LCD ENABLE"},
- {gpio + 15, GPIOF_OUT_INIT_LOW, "LCD BACKLIGHT"},
- };
-
- r = gpio_request_array(gpios, ARRAY_SIZE(gpios));
- if (r) {
- pr_err("Cannot request LCD GPIOs, error %d\n", r);
- ldp_backlight_gpio = -EINVAL;
- ldp_lcd_enable_gpio = -EINVAL;
- return r;
- }
-
- ldp_backlight_gpio = gpio + 15;
- ldp_lcd_enable_gpio = gpio + 7;
+ ldp_panel_data.gpios[0] = gpio + 7;
+ ldp_panel_data.gpio_invert[0] = true;
+
+ ldp_panel_data.gpios[1] = gpio + 15;
+ ldp_panel_data.gpio_invert[1] = true;
return 0;
}
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index 4f1bbc3cc29b..f76d0de7b406 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -155,61 +155,43 @@ static inline void __init omap3evm_init_smsc911x(void) { return; }
#define OMAP3EVM_LCD_PANEL_LR 2
#define OMAP3EVM_LCD_PANEL_UD 3
#define OMAP3EVM_LCD_PANEL_INI 152
-#define OMAP3EVM_LCD_PANEL_ENVDD 153
#define OMAP3EVM_LCD_PANEL_QVGA 154
#define OMAP3EVM_LCD_PANEL_RESB 155
+
+#define OMAP3EVM_LCD_PANEL_ENVDD 153
#define OMAP3EVM_LCD_PANEL_BKLIGHT_GPIO 210
+
+/*
+ * OMAP3EVM DVI control signals
+ */
#define OMAP3EVM_DVI_PANEL_EN_GPIO 199
-static struct gpio omap3_evm_dss_gpios[] __initdata = {
- { OMAP3EVM_LCD_PANEL_RESB, GPIOF_OUT_INIT_HIGH, "lcd_panel_resb" },
- { OMAP3EVM_LCD_PANEL_INI, GPIOF_OUT_INIT_HIGH, "lcd_panel_ini" },
- { OMAP3EVM_LCD_PANEL_QVGA, GPIOF_OUT_INIT_LOW, "lcd_panel_qvga" },
- { OMAP3EVM_LCD_PANEL_LR, GPIOF_OUT_INIT_HIGH, "lcd_panel_lr" },
- { OMAP3EVM_LCD_PANEL_UD, GPIOF_OUT_INIT_HIGH, "lcd_panel_ud" },
- { OMAP3EVM_LCD_PANEL_ENVDD, GPIOF_OUT_INIT_LOW, "lcd_panel_envdd" },
+static struct panel_sharp_ls037v7dw01_data omap3_evm_lcd_data = {
+ .resb_gpio = OMAP3EVM_LCD_PANEL_RESB,
+ .ini_gpio = OMAP3EVM_LCD_PANEL_INI,
+ .mo_gpio = OMAP3EVM_LCD_PANEL_QVGA,
+ .lr_gpio = OMAP3EVM_LCD_PANEL_LR,
+ .ud_gpio = OMAP3EVM_LCD_PANEL_UD,
};
-static int lcd_enabled;
-static int dvi_enabled;
-
static void __init omap3_evm_display_init(void)
{
int r;
- r = gpio_request_array(omap3_evm_dss_gpios,
- ARRAY_SIZE(omap3_evm_dss_gpios));
+ r = gpio_request_one(OMAP3EVM_LCD_PANEL_ENVDD, GPIOF_OUT_INIT_LOW,
+ "lcd_panel_envdd");
if (r)
- printk(KERN_ERR "failed to get lcd_panel_* gpios\n");
-}
+ pr_err("failed to get lcd_panel_envdd GPIO\n");
-static int omap3_evm_enable_lcd(struct omap_dss_device *dssdev)
-{
- if (dvi_enabled) {
- printk(KERN_ERR "cannot enable LCD, DVI is enabled\n");
- return -EINVAL;
- }
- gpio_set_value(OMAP3EVM_LCD_PANEL_ENVDD, 0);
+ r = gpio_request_one(OMAP3EVM_LCD_PANEL_BKLIGHT_GPIO,
+ GPIOF_OUT_INIT_LOW, "lcd_panel_bklight");
+ if (r)
+ pr_err("failed to get lcd_panel_bklight GPIO\n");
if (get_omap3_evm_rev() >= OMAP3EVM_BOARD_GEN_2)
gpio_set_value_cansleep(OMAP3EVM_LCD_PANEL_BKLIGHT_GPIO, 0);
else
gpio_set_value_cansleep(OMAP3EVM_LCD_PANEL_BKLIGHT_GPIO, 1);
-
- lcd_enabled = 1;
- return 0;
-}
-
-static void omap3_evm_disable_lcd(struct omap_dss_device *dssdev)
-{
- gpio_set_value(OMAP3EVM_LCD_PANEL_ENVDD, 1);
-
- if (get_omap3_evm_rev() >= OMAP3EVM_BOARD_GEN_2)
- gpio_set_value_cansleep(OMAP3EVM_LCD_PANEL_BKLIGHT_GPIO, 1);
- else
- gpio_set_value_cansleep(OMAP3EVM_LCD_PANEL_BKLIGHT_GPIO, 0);
-
- lcd_enabled = 0;
}
static struct omap_dss_device omap3_evm_lcd_device = {
@@ -217,26 +199,14 @@ static struct omap_dss_device omap3_evm_lcd_device = {
.driver_name = "sharp_ls_panel",
.type = OMAP_DISPLAY_TYPE_DPI,
.phy.dpi.data_lines = 18,
- .platform_enable = omap3_evm_enable_lcd,
- .platform_disable = omap3_evm_disable_lcd,
+ .data = &omap3_evm_lcd_data,
};
-static int omap3_evm_enable_tv(struct omap_dss_device *dssdev)
-{
- return 0;
-}
-
-static void omap3_evm_disable_tv(struct omap_dss_device *dssdev)
-{
-}
-
static struct omap_dss_device omap3_evm_tv_device = {
.name = "tv",
.driver_name = "venc",
.type = OMAP_DISPLAY_TYPE_VENC,
.phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO,
- .platform_enable = omap3_evm_enable_tv,
- .platform_disable = omap3_evm_disable_tv,
};
static struct tfp410_platform_data dvi_panel = {
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index 1004d2aaa68f..28133d5b4fed 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -44,6 +44,7 @@
#include "common.h"
#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
#include <linux/platform_data/mtd-nand-omap2.h>
#include "mux.h"
@@ -230,12 +231,16 @@ static struct twl4030_keypad_data pandora_kp_data = {
.rep = 1,
};
+static struct panel_tpo_td043_data lcd_data = {
+ .nreset_gpio = 157,
+};
+
static struct omap_dss_device pandora_lcd_device = {
.name = "lcd",
.driver_name = "tpo_td043mtea1_panel",
.type = OMAP_DISPLAY_TYPE_DPI,
.phy.dpi.data_lines = 24,
- .reset_gpio = 157,
+ .data = &lcd_data,
};
static struct omap_dss_device pandora_tv_device = {
diff --git a/arch/arm/mach-omap2/board-omap3stalker.c b/arch/arm/mach-omap2/board-omap3stalker.c
index 8afbba0923d6..d37e6b187ae4 100644
--- a/arch/arm/mach-omap2/board-omap3stalker.c
+++ b/arch/arm/mach-omap2/board-omap3stalker.c
@@ -94,15 +94,6 @@ static void __init omap3_stalker_display_init(void)
return;
}
-static int omap3_stalker_enable_tv(struct omap_dss_device *dssdev)
-{
- return 0;
-}
-
-static void omap3_stalker_disable_tv(struct omap_dss_device *dssdev)
-{
-}
-
static struct omap_dss_device omap3_stalker_tv_device = {
.name = "tv",
.driver_name = "venc",
@@ -112,8 +103,6 @@ static struct omap_dss_device omap3_stalker_tv_device = {
#elif defined(CONFIG_OMAP2_VENC_OUT_TYPE_COMPOSITE)
.u.venc.type = OMAP_DSS_VENC_TYPE_COMPOSITE,
#endif
- .platform_enable = omap3_stalker_enable_tv,
- .platform_disable = omap3_stalker_disable_tv,
};
static struct tfp410_platform_data dvi_panel = {
diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c
index a71ad345f20d..1e2c75eee912 100644
--- a/arch/arm/mach-omap2/board-omap4panda.c
+++ b/arch/arm/mach-omap2/board-omap4panda.c
@@ -435,7 +435,7 @@ static void __init omap4_panda_init(void)
omap_sdrc_init(NULL, NULL);
omap4_twl6030_hsmmc_init(mmc);
omap4_ehci_init();
- usb_bind_phy("musb-hdrc.0.auto", 0, "omap-usb2.1.auto");
+ usb_bind_phy("musb-hdrc.2.auto", 0, "omap-usb2.3.auto");
usb_musb_init(&musb_board_data);
omap4_panda_display_init();
}
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c
index f9101407cd56..4ca6b680aa72 100644
--- a/arch/arm/mach-omap2/board-overo.c
+++ b/arch/arm/mach-omap2/board-overo.c
@@ -145,28 +145,9 @@ static inline void __init overo_init_smsc911x(void) { return; }
#endif
/* DSS */
-static int lcd_enabled;
-static int dvi_enabled;
-
#define OVERO_GPIO_LCD_EN 144
#define OVERO_GPIO_LCD_BL 145
-static struct gpio overo_dss_gpios[] __initdata = {
- { OVERO_GPIO_LCD_EN, GPIOF_OUT_INIT_HIGH, "OVERO_GPIO_LCD_EN" },
- { OVERO_GPIO_LCD_BL, GPIOF_OUT_INIT_HIGH, "OVERO_GPIO_LCD_BL" },
-};
-
-static void __init overo_display_init(void)
-{
- if (gpio_request_array(overo_dss_gpios, ARRAY_SIZE(overo_dss_gpios))) {
- printk(KERN_ERR "could not obtain DSS control GPIOs\n");
- return;
- }
-
- gpio_export(OVERO_GPIO_LCD_EN, 0);
- gpio_export(OVERO_GPIO_LCD_BL, 0);
-}
-
static struct tfp410_platform_data dvi_panel = {
.i2c_bus_num = 3,
.power_down_gpio = -1,
@@ -187,30 +168,13 @@ static struct omap_dss_device overo_tv_device = {
.phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO,
};
-static int overo_panel_enable_lcd(struct omap_dss_device *dssdev)
-{
- if (dvi_enabled) {
- printk(KERN_ERR "cannot enable LCD, DVI is enabled\n");
- return -EINVAL;
- }
-
- gpio_set_value(OVERO_GPIO_LCD_EN, 1);
- gpio_set_value(OVERO_GPIO_LCD_BL, 1);
- lcd_enabled = 1;
- return 0;
-}
-
-static void overo_panel_disable_lcd(struct omap_dss_device *dssdev)
-{
- gpio_set_value(OVERO_GPIO_LCD_EN, 0);
- gpio_set_value(OVERO_GPIO_LCD_BL, 0);
- lcd_enabled = 0;
-}
-
static struct panel_generic_dpi_data lcd43_panel = {
.name = "samsung_lte430wq_f0c",
- .platform_enable = overo_panel_enable_lcd,
- .platform_disable = overo_panel_disable_lcd,
+ .num_gpios = 2,
+ .gpios = {
+ OVERO_GPIO_LCD_EN,
+ OVERO_GPIO_LCD_BL
+ },
};
static struct omap_dss_device overo_lcd43_device = {
@@ -223,13 +187,20 @@ static struct omap_dss_device overo_lcd43_device = {
#if defined(CONFIG_PANEL_LGPHILIPS_LB035Q02) || \
defined(CONFIG_PANEL_LGPHILIPS_LB035Q02_MODULE)
+static struct panel_generic_dpi_data lcd35_panel = {
+ .num_gpios = 2,
+ .gpios = {
+ OVERO_GPIO_LCD_EN,
+ OVERO_GPIO_LCD_BL
+ },
+};
+
static struct omap_dss_device overo_lcd35_device = {
.type = OMAP_DISPLAY_TYPE_DPI,
.name = "lcd35",
.driver_name = "lgphilips_lb035q02_panel",
.phy.dpi.data_lines = 24,
- .platform_enable = overo_panel_enable_lcd,
- .platform_disable = overo_panel_disable_lcd,
+ .data = &lcd35_panel,
};
#endif
@@ -508,7 +479,6 @@ static void __init overo_init(void)
usbhs_init(&usbhs_bdata);
overo_spi_init();
overo_init_smsc911x();
- overo_display_init();
overo_init_led();
overo_init_keys();
omap_twl4030_audio_init("overo", NULL);
diff --git a/arch/arm/mach-omap2/board-rx51-video.c b/arch/arm/mach-omap2/board-rx51-video.c
index eb667261df08..bd74f9f6063b 100644
--- a/arch/arm/mach-omap2/board-rx51-video.c
+++ b/arch/arm/mach-omap2/board-rx51-video.c
@@ -16,6 +16,8 @@
#include <linux/mm.h>
#include <asm/mach-types.h>
#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
#include <linux/platform_data/spi-omap2-mcspi.h>
#include "soc.h"
@@ -27,25 +29,16 @@
#if defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE)
-static int rx51_lcd_enable(struct omap_dss_device *dssdev)
-{
- gpio_set_value(dssdev->reset_gpio, 1);
- return 0;
-}
-
-static void rx51_lcd_disable(struct omap_dss_device *dssdev)
-{
- gpio_set_value(dssdev->reset_gpio, 0);
-}
+static struct panel_acx565akm_data lcd_data = {
+ .reset_gpio = RX51_LCD_RESET_GPIO,
+};
static struct omap_dss_device rx51_lcd_device = {
.name = "lcd",
.driver_name = "panel-acx565akm",
.type = OMAP_DISPLAY_TYPE_SDI,
.phy.sdi.datapairs = 2,
- .reset_gpio = RX51_LCD_RESET_GPIO,
- .platform_enable = rx51_lcd_enable,
- .platform_disable = rx51_lcd_disable,
+ .data = &lcd_data,
};
static struct omap_dss_device rx51_tv_device = {
@@ -76,13 +69,8 @@ static int __init rx51_video_init(void)
return 0;
}
- if (gpio_request_one(RX51_LCD_RESET_GPIO, GPIOF_OUT_INIT_HIGH,
- "LCD ACX565AKM reset")) {
- pr_err("%s failed to get LCD Reset GPIO\n", __func__);
- return 0;
- }
-
omap_display_init(&rx51_dss_board_info);
+
return 0;
}
diff --git a/arch/arm/mach-omap2/board-zoom-display.c b/arch/arm/mach-omap2/board-zoom-display.c
index 9a7174faac51..c2a079cb76fc 100644
--- a/arch/arm/mach-omap2/board-zoom-display.c
+++ b/arch/arm/mach-omap2/board-zoom-display.c
@@ -15,8 +15,9 @@
#include <linux/spi/spi.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <video/omapdss.h>
-#include "board-zoom.h"
+#include <video/omap-panel-data.h>
+#include "board-zoom.h"
#include "soc.h"
#include "common.h"
@@ -24,37 +25,17 @@
#define LCD_PANEL_RESET_GPIO_PILOT 55
#define LCD_PANEL_QVGA_GPIO 56
-static struct gpio zoom_lcd_gpios[] __initdata = {
- { -EINVAL, GPIOF_OUT_INIT_HIGH, "lcd reset" },
- { LCD_PANEL_QVGA_GPIO, GPIOF_OUT_INIT_HIGH, "lcd qvga" },
+static struct panel_nec_nl8048_data zoom_lcd_data = {
+ /* res_gpio filled in code */
+ .qvga_gpio = LCD_PANEL_QVGA_GPIO,
};
-static void __init zoom_lcd_panel_init(void)
-{
- zoom_lcd_gpios[0].gpio = (omap_rev() > OMAP3430_REV_ES3_0) ?
- LCD_PANEL_RESET_GPIO_PROD :
- LCD_PANEL_RESET_GPIO_PILOT;
-
- if (gpio_request_array(zoom_lcd_gpios, ARRAY_SIZE(zoom_lcd_gpios)))
- pr_err("%s: Failed to get LCD GPIOs.\n", __func__);
-}
-
-static int zoom_panel_enable_lcd(struct omap_dss_device *dssdev)
-{
- return 0;
-}
-
-static void zoom_panel_disable_lcd(struct omap_dss_device *dssdev)
-{
-}
-
static struct omap_dss_device zoom_lcd_device = {
.name = "lcd",
.driver_name = "NEC_8048_panel",
.type = OMAP_DISPLAY_TYPE_DPI,
.phy.dpi.data_lines = 24,
- .platform_enable = zoom_panel_enable_lcd,
- .platform_disable = zoom_panel_disable_lcd,
+ .data = &zoom_lcd_data,
};
static struct omap_dss_device *zoom_dss_devices[] = {
@@ -67,6 +48,13 @@ static struct omap_dss_board_info zoom_dss_data = {
.default_device = &zoom_lcd_device,
};
+static void __init zoom_lcd_panel_init(void)
+{
+ zoom_lcd_data.res_gpio = (omap_rev() > OMAP3430_REV_ES3_0) ?
+ LCD_PANEL_RESET_GPIO_PROD :
+ LCD_PANEL_RESET_GPIO_PILOT;
+}
+
static struct omap2_mcspi_device_config dss_lcd_mcspi_config = {
.turbo_mode = 1,
};
diff --git a/arch/arm/mach-omap2/dss-common.c b/arch/arm/mach-omap2/dss-common.c
index 9c49bbe825f7..393aeefaebb0 100644
--- a/arch/arm/mach-omap2/dss-common.c
+++ b/arch/arm/mach-omap2/dss-common.c
@@ -52,7 +52,6 @@ static struct omap_dss_device omap4_panda_dvi_device = {
.driver_name = "tfp410",
.data = &omap4_dvi_panel,
.phy.dpi.data_lines = 24,
- .reset_gpio = PANDA_DVI_TFP410_POWER_DOWN_GPIO,
.channel = OMAP_DSS_CHANNEL_LCD2,
};
@@ -177,45 +176,12 @@ static struct picodlp_panel_data sdp4430_picodlp_pdata = {
.pwrgood_gpio = 45,
};
-static void sdp4430_picodlp_init(void)
-{
- int r;
- const struct gpio picodlp_gpios[] = {
- {DLP_POWER_ON_GPIO, GPIOF_OUT_INIT_LOW,
- "DLP POWER ON"},
- {sdp4430_picodlp_pdata.emu_done_gpio, GPIOF_IN,
- "DLP EMU DONE"},
- {sdp4430_picodlp_pdata.pwrgood_gpio, GPIOF_OUT_INIT_LOW,
- "DLP PWRGOOD"},
- };
-
- r = gpio_request_array(picodlp_gpios, ARRAY_SIZE(picodlp_gpios));
- if (r)
- pr_err("Cannot request PicoDLP GPIOs, error %d\n", r);
-}
-
-static int sdp4430_panel_enable_picodlp(struct omap_dss_device *dssdev)
-{
- gpio_set_value(DISPLAY_SEL_GPIO, 0);
- gpio_set_value(DLP_POWER_ON_GPIO, 1);
-
- return 0;
-}
-
-static void sdp4430_panel_disable_picodlp(struct omap_dss_device *dssdev)
-{
- gpio_set_value(DLP_POWER_ON_GPIO, 0);
- gpio_set_value(DISPLAY_SEL_GPIO, 1);
-}
-
static struct omap_dss_device sdp4430_picodlp_device = {
.name = "picodlp",
.driver_name = "picodlp_panel",
.type = OMAP_DISPLAY_TYPE_DPI,
.phy.dpi.data_lines = 24,
.channel = OMAP_DSS_CHANNEL_LCD2,
- .platform_enable = sdp4430_panel_enable_picodlp,
- .platform_disable = sdp4430_panel_disable_picodlp,
.data = &sdp4430_picodlp_pdata,
};
@@ -232,17 +198,26 @@ static struct omap_dss_board_info sdp4430_dss_data = {
.default_device = &sdp4430_lcd_device,
};
+/*
+ * we select LCD2 by default (instead of Pico DLP) by setting DISPLAY_SEL_GPIO.
+ * Setting DLP_POWER_ON gpio enables the VDLP_2V5 VDLP_1V8 and VDLP_1V0 rails
+ * used by picodlp on the 4430sdp platform. Keep this gpio disabled as LCD2 is
+ * selected by default
+ */
void __init omap_4430sdp_display_init(void)
{
int r;
- /* Enable LCD2 by default (instead of Pico DLP) */
r = gpio_request_one(DISPLAY_SEL_GPIO, GPIOF_OUT_INIT_HIGH,
"display_sel");
if (r)
pr_err("%s: Could not get display_sel GPIO\n", __func__);
- sdp4430_picodlp_init();
+ r = gpio_request_one(DLP_POWER_ON_GPIO, GPIOF_OUT_INIT_LOW,
+ "DLP POWER ON");
+ if (r)
+ pr_err("%s: Could not get DLP POWER ON GPIO\n", __func__);
+
omap_display_init(&sdp4430_dss_data);
/*
* OMAP4460SDP/Blaze and OMAP4430 ES2.3 SDP/Blaze boards and
@@ -262,12 +237,15 @@ void __init omap_4430sdp_display_init_of(void)
{
int r;
- /* Enable LCD2 by default (instead of Pico DLP) */
r = gpio_request_one(DISPLAY_SEL_GPIO, GPIOF_OUT_INIT_HIGH,
"display_sel");
if (r)
pr_err("%s: Could not get display_sel GPIO\n", __func__);
- sdp4430_picodlp_init();
+ r = gpio_request_one(DLP_POWER_ON_GPIO, GPIOF_OUT_INIT_LOW,
+ "DLP POWER ON");
+ if (r)
+ pr_err("%s: Could not get DLP POWER ON GPIO\n", __func__);
+
omap_display_init(&sdp4430_dss_data);
}
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 93f213b6a784..d25a95fe9921 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -139,6 +139,8 @@
#include <linux/slab.h>
#include <linux/bootmem.h>
#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
#include <asm/system_misc.h>
@@ -2350,6 +2352,34 @@ static int _shutdown(struct omap_hwmod *oh)
}
/**
+ * of_dev_hwmod_lookup - look up needed hwmod from dt blob
+ * @np: struct device_node *
+ * @oh: struct omap_hwmod *
+ *
+ * Parse the dt blob and find out needed hwmod. Recursive function is
+ * implemented to take care hierarchical dt blob parsing.
+ * Return: The device node on success or NULL on failure.
+ */
+static struct device_node *of_dev_hwmod_lookup(struct device_node *np,
+ struct omap_hwmod *oh)
+{
+ struct device_node *np0 = NULL, *np1 = NULL;
+ const char *p;
+
+ for_each_child_of_node(np, np0) {
+ if (of_find_property(np0, "ti,hwmods", NULL)) {
+ p = of_get_property(np0, "ti,hwmods", NULL);
+ if (!strcmp(p, oh->name))
+ return np0;
+ np1 = of_dev_hwmod_lookup(np0, oh);
+ if (np1)
+ return np1;
+ }
+ }
+ return NULL;
+}
+
+/**
* _init_mpu_rt_base - populate the virtual address for a hwmod
* @oh: struct omap_hwmod * to locate the virtual address
*
@@ -2361,7 +2391,8 @@ static int _shutdown(struct omap_hwmod *oh)
static void __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data)
{
struct omap_hwmod_addr_space *mem;
- void __iomem *va_start;
+ void __iomem *va_start = NULL;
+ struct device_node *np;
if (!oh)
return;
@@ -2375,10 +2406,18 @@ static void __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data)
if (!mem) {
pr_debug("omap_hwmod: %s: no MPU register target found\n",
oh->name);
- return;
+
+ /* Extract the IO space from device tree blob */
+ if (!of_have_populated_dt())
+ return;
+
+ np = of_dev_hwmod_lookup(of_find_node_by_name(NULL, "ocp"), oh);
+ if (np)
+ va_start = of_iomap(np, 0);
+ } else {
+ va_start = ioremap(mem->pa_start, mem->pa_end - mem->pa_start);
}
- va_start = ioremap(mem->pa_start, mem->pa_end - mem->pa_start);
if (!va_start) {
pr_err("omap_hwmod: %s: Could not ioremap\n", oh->name);
return;
@@ -2410,7 +2449,8 @@ static int __init _init(struct omap_hwmod *oh, void *data)
if (oh->_state != _HWMOD_STATE_REGISTERED)
return 0;
- _init_mpu_rt_base(oh, NULL);
+ if (oh->class->sysc)
+ _init_mpu_rt_base(oh, NULL);
r = _init_clocks(oh, NULL);
if (r < 0) {
diff --git a/arch/arm/mach-omap2/pmu.c b/arch/arm/mach-omap2/pmu.c
index 9debf822687c..9ace8eae7ee8 100644
--- a/arch/arm/mach-omap2/pmu.c
+++ b/arch/arm/mach-omap2/pmu.c
@@ -11,6 +11,8 @@
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
+#include <linux/of.h>
+
#include <asm/pmu.h>
#include "soc.h"
@@ -63,6 +65,15 @@ static int __init omap_init_pmu(void)
unsigned oh_num;
char **oh_names;
+ /* XXX Remove this check when the CTI driver is available */
+ if (cpu_is_omap443x()) {
+ pr_info("ARM PMU: not yet supported on OMAP4430 due to missing CTI driver\n");
+ return 0;
+ }
+
+ if (of_have_populated_dt())
+ return 0;
+
/*
* To create an ARM-PMU device the following HWMODs
* are required for the various OMAP2+ devices.
@@ -75,9 +86,6 @@ static int __init omap_init_pmu(void)
if (cpu_is_omap443x()) {
oh_num = ARRAY_SIZE(omap4430_pmu_oh_names);
oh_names = omap4430_pmu_oh_names;
- /* XXX Remove the next two lines when CTI driver available */
- pr_info("ARM PMU: not yet supported on OMAP4430 due to missing CTI driver\n");
- return 0;
} else if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
oh_num = ARRAY_SIZE(omap3_pmu_oh_names);
oh_names = omap3_pmu_oh_names;
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index f12aa6c15da4..f8b23b8040d9 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -46,7 +46,6 @@
#include <asm/smp_twd.h>
#include <asm/sched_clock.h>
-#include <asm/arch_timer.h>
#include "omap_hwmod.h"
#include "omap_device.h"
#include <plat/counter-32k.h>
@@ -133,7 +132,12 @@ static struct property device_disabled = {
};
static struct of_device_id omap_timer_match[] __initdata = {
- { .compatible = "ti,omap2-timer", },
+ { .compatible = "ti,omap2420-timer", },
+ { .compatible = "ti,omap3430-timer", },
+ { .compatible = "ti,omap4430-timer", },
+ { .compatible = "ti,omap5430-timer", },
+ { .compatible = "ti,am335x-timer", },
+ { .compatible = "ti,am335x-timer-1ms", },
{ }
};
@@ -549,6 +553,8 @@ static inline void __init realtime_counter_init(void)
clksrc_nr, clksrc_src, clksrc_prop) \
void __init omap##name##_gptimer_timer_init(void) \
{ \
+ if (omap_clk_init) \
+ omap_clk_init(); \
omap_dmtimer_init(); \
omap2_gp_clockevent_init((clkev_nr), clkev_src, clkev_prop); \
omap2_gptimer_clocksource_init((clksrc_nr), clksrc_src, \
@@ -559,6 +565,8 @@ void __init omap##name##_gptimer_timer_init(void) \
clksrc_nr, clksrc_src, clksrc_prop) \
void __init omap##name##_sync32k_timer_init(void) \
{ \
+ if (omap_clk_init) \
+ omap_clk_init(); \
omap_dmtimer_init(); \
omap2_gp_clockevent_init((clkev_nr), clkev_src, clkev_prop); \
/* Enable the use of clocksource="gp_timer" kernel parameter */ \
@@ -622,14 +630,10 @@ void __init omap4_local_timer_init(void)
#ifdef CONFIG_SOC_OMAP5
void __init omap5_realtime_timer_init(void)
{
- int err;
-
omap4_sync32k_timer_init();
realtime_counter_init();
- err = arch_timer_of_register();
- if (err)
- pr_err("%s: arch_timer_register failed %d\n", __func__, err);
+ clocksource_of_init();
}
#endif /* CONFIG_SOC_OMAP5 */
diff --git a/arch/arm/mach-orion5x/Makefile b/arch/arm/mach-orion5x/Makefile
index 9e809a7c05c0..45da805fb236 100644
--- a/arch/arm/mach-orion5x/Makefile
+++ b/arch/arm/mach-orion5x/Makefile
@@ -1,4 +1,4 @@
-obj-y += common.o addr-map.o pci.o irq.o mpp.o
+obj-y += common.o pci.o irq.o mpp.o
obj-$(CONFIG_MACH_DB88F5281) += db88f5281-setup.o
obj-$(CONFIG_MACH_RD88F5182) += rd88f5182-setup.o
obj-$(CONFIG_MACH_KUROBOX_PRO) += kurobox_pro-setup.o
diff --git a/arch/arm/mach-orion5x/addr-map.c b/arch/arm/mach-orion5x/addr-map.c
deleted file mode 100644
index b5efc0fd31cb..000000000000
--- a/arch/arm/mach-orion5x/addr-map.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * arch/arm/mach-orion5x/addr-map.c
- *
- * Address map functions for Marvell Orion 5x SoCs
- *
- * Maintainer: Tzachi Perelstein <tzachi@marvell.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/mbus.h>
-#include <linux/io.h>
-#include <mach/hardware.h>
-#include <plat/addr-map.h>
-#include "common.h"
-
-/*
- * The Orion has fully programmable address map. There's a separate address
- * map for each of the device _master_ interfaces, e.g. CPU, PCI, PCIe, USB,
- * Gigabit Ethernet, DMA/XOR engines, etc. Each interface has its own
- * address decode windows that allow it to access any of the Orion resources.
- *
- * CPU address decoding --
- * Linux assumes that it is the boot loader that already setup the access to
- * DDR and internal registers.
- * Setup access to PCI and PCIe IO/MEM space is issued by this file.
- * Setup access to various devices located on the device bus interface (e.g.
- * flashes, RTC, etc) should be issued by machine-setup.c according to
- * specific board population (by using orion5x_setup_*_win()).
- *
- * Non-CPU Masters address decoding --
- * Unlike the CPU, we setup the access from Orion's master interfaces to DDR
- * banks only (the typical use case).
- * Setup access for each master to DDR is issued by platform device setup.
- */
-
-/*
- * Generic Address Decode Windows bit settings
- */
-#define TARGET_DEV_BUS 1
-#define TARGET_PCI 3
-#define TARGET_PCIE 4
-#define TARGET_SRAM 9
-#define ATTR_PCIE_MEM 0x59
-#define ATTR_PCIE_IO 0x51
-#define ATTR_PCIE_WA 0x79
-#define ATTR_PCI_MEM 0x59
-#define ATTR_PCI_IO 0x51
-#define ATTR_DEV_CS0 0x1e
-#define ATTR_DEV_CS1 0x1d
-#define ATTR_DEV_CS2 0x1b
-#define ATTR_DEV_BOOT 0xf
-#define ATTR_SRAM 0x0
-
-static int __initdata win_alloc_count;
-
-static int __init cpu_win_can_remap(const struct orion_addr_map_cfg *cfg,
- const int win)
-{
- u32 dev, rev;
-
- orion5x_pcie_id(&dev, &rev);
- if ((dev == MV88F5281_DEV_ID && win < 4)
- || (dev == MV88F5182_DEV_ID && win < 2)
- || (dev == MV88F5181_DEV_ID && win < 2)
- || (dev == MV88F6183_DEV_ID && win < 4))
- return 1;
-
- return 0;
-}
-
-/*
- * Description of the windows needed by the platform code
- */
-static struct orion_addr_map_cfg addr_map_cfg __initdata = {
- .num_wins = 8,
- .cpu_win_can_remap = cpu_win_can_remap,
- .bridge_virt_base = ORION5X_BRIDGE_VIRT_BASE,
-};
-
-static const struct __initdata orion_addr_map_info addr_map_info[] = {
- /*
- * Setup windows for PCI+PCIe IO+MEM space.
- */
- { 0, ORION5X_PCIE_IO_PHYS_BASE, ORION5X_PCIE_IO_SIZE,
- TARGET_PCIE, ATTR_PCIE_IO, ORION5X_PCIE_IO_BUS_BASE
- },
- { 1, ORION5X_PCI_IO_PHYS_BASE, ORION5X_PCI_IO_SIZE,
- TARGET_PCI, ATTR_PCI_IO, ORION5X_PCI_IO_BUS_BASE
- },
- { 2, ORION5X_PCIE_MEM_PHYS_BASE, ORION5X_PCIE_MEM_SIZE,
- TARGET_PCIE, ATTR_PCIE_MEM, -1
- },
- { 3, ORION5X_PCI_MEM_PHYS_BASE, ORION5X_PCI_MEM_SIZE,
- TARGET_PCI, ATTR_PCI_MEM, -1
- },
- /* End marker */
- { -1, 0, 0, 0, 0, 0 }
-};
-
-void __init orion5x_setup_cpu_mbus_bridge(void)
-{
- /*
- * Disable, clear and configure windows.
- */
- orion_config_wins(&addr_map_cfg, addr_map_info);
- win_alloc_count = 4;
-
- /*
- * Setup MBUS dram target info.
- */
- orion_setup_cpu_mbus_target(&addr_map_cfg,
- (void __iomem *) ORION5X_DDR_WINDOW_CPU_BASE);
-}
-
-void __init orion5x_setup_dev_boot_win(u32 base, u32 size)
-{
- orion_setup_cpu_win(&addr_map_cfg, win_alloc_count++, base, size,
- TARGET_DEV_BUS, ATTR_DEV_BOOT, -1);
-}
-
-void __init orion5x_setup_dev0_win(u32 base, u32 size)
-{
- orion_setup_cpu_win(&addr_map_cfg, win_alloc_count++, base, size,
- TARGET_DEV_BUS, ATTR_DEV_CS0, -1);
-}
-
-void __init orion5x_setup_dev1_win(u32 base, u32 size)
-{
- orion_setup_cpu_win(&addr_map_cfg, win_alloc_count++, base, size,
- TARGET_DEV_BUS, ATTR_DEV_CS1, -1);
-}
-
-void __init orion5x_setup_dev2_win(u32 base, u32 size)
-{
- orion_setup_cpu_win(&addr_map_cfg, win_alloc_count++, base, size,
- TARGET_DEV_BUS, ATTR_DEV_CS2, -1);
-}
-
-void __init orion5x_setup_pcie_wa_win(u32 base, u32 size)
-{
- orion_setup_cpu_win(&addr_map_cfg, win_alloc_count++, base, size,
- TARGET_PCIE, ATTR_PCIE_WA, -1);
-}
-
-void __init orion5x_setup_sram_win(void)
-{
- orion_setup_cpu_win(&addr_map_cfg, win_alloc_count++,
- ORION5X_SRAM_PHYS_BASE, ORION5X_SRAM_SIZE,
- TARGET_SRAM, ATTR_SRAM, -1);
-}
diff --git a/arch/arm/mach-orion5x/board-dt.c b/arch/arm/mach-orion5x/board-dt.c
index 94fbb815680c..b91002ca92f3 100644
--- a/arch/arm/mach-orion5x/board-dt.c
+++ b/arch/arm/mach-orion5x/board-dt.c
@@ -42,7 +42,7 @@ static void __init orion5x_dt_init(void)
/*
* Setup Orion address map
*/
- orion5x_setup_cpu_mbus_bridge();
+ orion5x_setup_wins();
/* Setup root of clk tree */
clk_init();
diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c
index 2075bf8e3d90..b97fd672e89d 100644
--- a/arch/arm/mach-orion5x/common.c
+++ b/arch/arm/mach-orion5x/common.c
@@ -35,7 +35,6 @@
#include <linux/platform_data/usb-ehci-orion.h>
#include <plat/time.h>
#include <plat/common.h>
-#include <plat/addr-map.h>
#include "common.h"
/*****************************************************************************
@@ -175,7 +174,8 @@ void __init orion5x_xor_init(void)
****************************************************************************/
static void __init orion5x_crypto_init(void)
{
- orion5x_setup_sram_win();
+ mvebu_mbus_add_window("sram", ORION5X_SRAM_PHYS_BASE,
+ ORION5X_SRAM_SIZE);
orion_crypto_init(ORION5X_CRYPTO_PHYS_BASE, ORION5X_SRAM_PHYS_BASE,
SZ_8K, IRQ_ORION5X_CESA);
}
@@ -194,6 +194,9 @@ void __init orion5x_wdt_init(void)
****************************************************************************/
void __init orion5x_init_early(void)
{
+ u32 rev, dev;
+ const char *mbus_soc_name;
+
orion_time_set_base(TIMER_VIRT_BASE);
/*
@@ -202,6 +205,46 @@ void __init orion5x_init_early(void)
* the allocations won't fail.
*/
init_dma_coherent_pool_size(SZ_1M);
+
+ /* Initialize the MBUS driver */
+ orion5x_pcie_id(&dev, &rev);
+ if (dev == MV88F5281_DEV_ID)
+ mbus_soc_name = "marvell,orion5x-88f5281-mbus";
+ else if (dev == MV88F5182_DEV_ID)
+ mbus_soc_name = "marvell,orion5x-88f5182-mbus";
+ else if (dev == MV88F5181_DEV_ID)
+ mbus_soc_name = "marvell,orion5x-88f5181-mbus";
+ else if (dev == MV88F6183_DEV_ID)
+ mbus_soc_name = "marvell,orion5x-88f6183-mbus";
+ else
+ mbus_soc_name = NULL;
+ mvebu_mbus_init(mbus_soc_name, ORION5X_BRIDGE_WINS_BASE,
+ ORION5X_BRIDGE_WINS_SZ,
+ ORION5X_DDR_WINS_BASE, ORION5X_DDR_WINS_SZ);
+}
+
+void orion5x_setup_wins(void)
+{
+ /*
+ * The PCIe windows will no longer be statically allocated
+ * here once Orion5x is migrated to the pci-mvebu driver.
+ */
+ mvebu_mbus_add_window_remap_flags("pcie0.0", ORION5X_PCIE_IO_PHYS_BASE,
+ ORION5X_PCIE_IO_SIZE,
+ ORION5X_PCIE_IO_BUS_BASE,
+ MVEBU_MBUS_PCI_IO);
+ mvebu_mbus_add_window_remap_flags("pcie0.0", ORION5X_PCIE_MEM_PHYS_BASE,
+ ORION5X_PCIE_MEM_SIZE,
+ MVEBU_MBUS_NO_REMAP,
+ MVEBU_MBUS_PCI_MEM);
+ mvebu_mbus_add_window_remap_flags("pci0.0", ORION5X_PCI_IO_PHYS_BASE,
+ ORION5X_PCI_IO_SIZE,
+ ORION5X_PCI_IO_BUS_BASE,
+ MVEBU_MBUS_PCI_IO);
+ mvebu_mbus_add_window_remap_flags("pci0.0", ORION5X_PCI_MEM_PHYS_BASE,
+ ORION5X_PCI_MEM_SIZE,
+ MVEBU_MBUS_NO_REMAP,
+ MVEBU_MBUS_PCI_MEM);
}
int orion5x_tclk;
@@ -283,7 +326,7 @@ void __init orion5x_init(void)
/*
* Setup Orion address map
*/
- orion5x_setup_cpu_mbus_bridge();
+ orion5x_setup_wins();
/* Setup root of clk tree */
clk_init();
diff --git a/arch/arm/mach-orion5x/common.h b/arch/arm/mach-orion5x/common.h
index e60345760283..cdaa01f3d186 100644
--- a/arch/arm/mach-orion5x/common.h
+++ b/arch/arm/mach-orion5x/common.h
@@ -17,18 +17,7 @@ void clk_init(void);
extern int orion5x_tclk;
extern void orion5x_timer_init(void);
-/*
- * Enumerations and functions for Orion windows mapping. Used by Orion core
- * functions to map its interfaces and by the machine-setup to map its on-
- * board devices. Details in /mach-orion/addr-map.c
- */
-void orion5x_setup_cpu_mbus_bridge(void);
-void orion5x_setup_dev_boot_win(u32 base, u32 size);
-void orion5x_setup_dev0_win(u32 base, u32 size);
-void orion5x_setup_dev1_win(u32 base, u32 size);
-void orion5x_setup_dev2_win(u32 base, u32 size);
-void orion5x_setup_pcie_wa_win(u32 base, u32 size);
-void orion5x_setup_sram_win(void);
+void orion5x_setup_wins(void);
void orion5x_ehci0_init(void);
void orion5x_ehci1_init(void);
diff --git a/arch/arm/mach-orion5x/d2net-setup.c b/arch/arm/mach-orion5x/d2net-setup.c
index 57d0af74874d..16c88bbabc98 100644
--- a/arch/arm/mach-orion5x/d2net-setup.c
+++ b/arch/arm/mach-orion5x/d2net-setup.c
@@ -317,8 +317,8 @@ static void __init d2net_init(void)
d2net_sata_power_init();
orion5x_sata_init(&d2net_sata_data);
- orion5x_setup_dev_boot_win(D2NET_NOR_BOOT_BASE,
- D2NET_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", D2NET_NOR_BOOT_BASE,
+ D2NET_NOR_BOOT_SIZE);
platform_device_register(&d2net_nor_flash);
platform_device_register(&d2net_gpio_buttons);
diff --git a/arch/arm/mach-orion5x/db88f5281-setup.c b/arch/arm/mach-orion5x/db88f5281-setup.c
index 76665640087b..4e1263da38bb 100644
--- a/arch/arm/mach-orion5x/db88f5281-setup.c
+++ b/arch/arm/mach-orion5x/db88f5281-setup.c
@@ -340,16 +340,19 @@ static void __init db88f5281_init(void)
orion5x_uart0_init();
orion5x_uart1_init();
- orion5x_setup_dev_boot_win(DB88F5281_NOR_BOOT_BASE,
- DB88F5281_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", DB88F5281_NOR_BOOT_BASE,
+ DB88F5281_NOR_BOOT_SIZE);
platform_device_register(&db88f5281_boot_flash);
- orion5x_setup_dev0_win(DB88F5281_7SEG_BASE, DB88F5281_7SEG_SIZE);
+ mvebu_mbus_add_window("devbus-cs0", DB88F5281_7SEG_BASE,
+ DB88F5281_7SEG_SIZE);
- orion5x_setup_dev1_win(DB88F5281_NOR_BASE, DB88F5281_NOR_SIZE);
+ mvebu_mbus_add_window("devbus-cs1", DB88F5281_NOR_BASE,
+ DB88F5281_NOR_SIZE);
platform_device_register(&db88f5281_nor_flash);
- orion5x_setup_dev2_win(DB88F5281_NAND_BASE, DB88F5281_NAND_SIZE);
+ mvebu_mbus_add_window("devbus-cs2", DB88F5281_NAND_BASE,
+ DB88F5281_NAND_SIZE);
platform_device_register(&db88f5281_nand_flash);
i2c_register_board_info(0, &db88f5281_i2c_rtc, 1);
diff --git a/arch/arm/mach-orion5x/dns323-setup.c b/arch/arm/mach-orion5x/dns323-setup.c
index 6eb1732757fd..9e6baf581ed3 100644
--- a/arch/arm/mach-orion5x/dns323-setup.c
+++ b/arch/arm/mach-orion5x/dns323-setup.c
@@ -611,7 +611,8 @@ static void __init dns323_init(void)
/* setup flash mapping
* CS3 holds a 8 MB Spansion S29GL064M90TFIR4
*/
- orion5x_setup_dev_boot_win(DNS323_NOR_BOOT_BASE, DNS323_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", DNS323_NOR_BOOT_BASE,
+ DNS323_NOR_BOOT_SIZE);
platform_device_register(&dns323_nor_flash);
/* Sort out LEDs, Buttons and i2c devices */
diff --git a/arch/arm/mach-orion5x/edmini_v2-setup.c b/arch/arm/mach-orion5x/edmini_v2-setup.c
index d675e727803d..147615510dd0 100644
--- a/arch/arm/mach-orion5x/edmini_v2-setup.c
+++ b/arch/arm/mach-orion5x/edmini_v2-setup.c
@@ -154,8 +154,8 @@ void __init edmini_v2_init(void)
orion5x_ehci0_init();
orion5x_eth_init(&edmini_v2_eth_data);
- orion5x_setup_dev_boot_win(EDMINI_V2_NOR_BOOT_BASE,
- EDMINI_V2_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", EDMINI_V2_NOR_BOOT_BASE,
+ EDMINI_V2_NOR_BOOT_SIZE);
platform_device_register(&edmini_v2_nor_flash);
pr_notice("edmini_v2: USB device port, flash write and power-off "
diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h
index d265f5484a8e..b78ff3248868 100644
--- a/arch/arm/mach-orion5x/include/mach/orion5x.h
+++ b/arch/arm/mach-orion5x/include/mach/orion5x.h
@@ -66,8 +66,10 @@
* Orion Registers Map
******************************************************************************/
+#define ORION5X_DDR_PHYS_BASE (ORION5X_REGS_PHYS_BASE + 0x00000)
+#define ORION5X_DDR_WINS_BASE (ORION5X_DDR_PHYS_BASE + 0x1500)
+#define ORION5X_DDR_WINS_SZ (0x10)
#define ORION5X_DDR_VIRT_BASE (ORION5X_REGS_VIRT_BASE + 0x00000)
-#define ORION5X_DDR_WINDOW_CPU_BASE (ORION5X_DDR_VIRT_BASE + 0x1500)
#define ORION5X_DEV_BUS_PHYS_BASE (ORION5X_REGS_PHYS_BASE + 0x10000)
#define ORION5X_DEV_BUS_VIRT_BASE (ORION5X_REGS_VIRT_BASE + 0x10000)
#define ORION5X_DEV_BUS_REG(x) (ORION5X_DEV_BUS_VIRT_BASE + (x))
@@ -81,6 +83,8 @@
#define ORION5X_BRIDGE_VIRT_BASE (ORION5X_REGS_VIRT_BASE + 0x20000)
#define ORION5X_BRIDGE_PHYS_BASE (ORION5X_REGS_PHYS_BASE + 0x20000)
+#define ORION5X_BRIDGE_WINS_BASE (ORION5X_BRIDGE_PHYS_BASE)
+#define ORION5X_BRIDGE_WINS_SZ (0x80)
#define ORION5X_PCI_VIRT_BASE (ORION5X_REGS_VIRT_BASE + 0x30000)
diff --git a/arch/arm/mach-orion5x/kurobox_pro-setup.c b/arch/arm/mach-orion5x/kurobox_pro-setup.c
index b98403526218..aae10e4a917c 100644
--- a/arch/arm/mach-orion5x/kurobox_pro-setup.c
+++ b/arch/arm/mach-orion5x/kurobox_pro-setup.c
@@ -359,13 +359,13 @@ static void __init kurobox_pro_init(void)
orion5x_uart1_init();
orion5x_xor_init();
- orion5x_setup_dev_boot_win(KUROBOX_PRO_NOR_BOOT_BASE,
- KUROBOX_PRO_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", KUROBOX_PRO_NOR_BOOT_BASE,
+ KUROBOX_PRO_NOR_BOOT_SIZE);
platform_device_register(&kurobox_pro_nor_flash);
if (machine_is_kurobox_pro()) {
- orion5x_setup_dev0_win(KUROBOX_PRO_NAND_BASE,
- KUROBOX_PRO_NAND_SIZE);
+ mvebu_mbus_add_window("devbus-cs0", KUROBOX_PRO_NAND_BASE,
+ KUROBOX_PRO_NAND_SIZE);
platform_device_register(&kurobox_pro_nand_flash);
}
diff --git a/arch/arm/mach-orion5x/ls-chl-setup.c b/arch/arm/mach-orion5x/ls-chl-setup.c
index 044da5b6a6ae..24f4e14e5893 100644
--- a/arch/arm/mach-orion5x/ls-chl-setup.c
+++ b/arch/arm/mach-orion5x/ls-chl-setup.c
@@ -294,8 +294,8 @@ static void __init lschl_init(void)
orion5x_uart0_init();
orion5x_xor_init();
- orion5x_setup_dev_boot_win(LSCHL_NOR_BOOT_BASE,
- LSCHL_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", LSCHL_NOR_BOOT_BASE,
+ LSCHL_NOR_BOOT_SIZE);
platform_device_register(&lschl_nor_flash);
platform_device_register(&lschl_leds);
diff --git a/arch/arm/mach-orion5x/ls_hgl-setup.c b/arch/arm/mach-orion5x/ls_hgl-setup.c
index d49f93423f52..fc653bb41e78 100644
--- a/arch/arm/mach-orion5x/ls_hgl-setup.c
+++ b/arch/arm/mach-orion5x/ls_hgl-setup.c
@@ -243,8 +243,8 @@ static void __init ls_hgl_init(void)
orion5x_uart0_init();
orion5x_xor_init();
- orion5x_setup_dev_boot_win(LS_HGL_NOR_BOOT_BASE,
- LS_HGL_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", LS_HGL_NOR_BOOT_BASE,
+ LS_HGL_NOR_BOOT_SIZE);
platform_device_register(&ls_hgl_nor_flash);
platform_device_register(&ls_hgl_button_device);
diff --git a/arch/arm/mach-orion5x/lsmini-setup.c b/arch/arm/mach-orion5x/lsmini-setup.c
index 8e3965c6c0fe..18e66e617dc2 100644
--- a/arch/arm/mach-orion5x/lsmini-setup.c
+++ b/arch/arm/mach-orion5x/lsmini-setup.c
@@ -244,8 +244,8 @@ static void __init lsmini_init(void)
orion5x_uart0_init();
orion5x_xor_init();
- orion5x_setup_dev_boot_win(LSMINI_NOR_BOOT_BASE,
- LSMINI_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", LSMINI_NOR_BOOT_BASE,
+ LSMINI_NOR_BOOT_SIZE);
platform_device_register(&lsmini_nor_flash);
platform_device_register(&lsmini_button_device);
diff --git a/arch/arm/mach-orion5x/mss2-setup.c b/arch/arm/mach-orion5x/mss2-setup.c
index 0ec94a1f2b16..827acbafc9dc 100644
--- a/arch/arm/mach-orion5x/mss2-setup.c
+++ b/arch/arm/mach-orion5x/mss2-setup.c
@@ -241,7 +241,8 @@ static void __init mss2_init(void)
orion5x_uart0_init();
orion5x_xor_init();
- orion5x_setup_dev_boot_win(MSS2_NOR_BOOT_BASE, MSS2_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", MSS2_NOR_BOOT_BASE,
+ MSS2_NOR_BOOT_SIZE);
platform_device_register(&mss2_nor_flash);
platform_device_register(&mss2_button_device);
diff --git a/arch/arm/mach-orion5x/mv2120-setup.c b/arch/arm/mach-orion5x/mv2120-setup.c
index 18143f2a9093..92600ae2b4b6 100644
--- a/arch/arm/mach-orion5x/mv2120-setup.c
+++ b/arch/arm/mach-orion5x/mv2120-setup.c
@@ -204,7 +204,8 @@ static void __init mv2120_init(void)
orion5x_uart0_init();
orion5x_xor_init();
- orion5x_setup_dev_boot_win(MV2120_NOR_BOOT_BASE, MV2120_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", MV2120_NOR_BOOT_BASE,
+ MV2120_NOR_BOOT_SIZE);
platform_device_register(&mv2120_nor_flash);
platform_device_register(&mv2120_button_device);
diff --git a/arch/arm/mach-orion5x/net2big-setup.c b/arch/arm/mach-orion5x/net2big-setup.c
index 282e503b003e..dd0641a0d074 100644
--- a/arch/arm/mach-orion5x/net2big-setup.c
+++ b/arch/arm/mach-orion5x/net2big-setup.c
@@ -397,8 +397,8 @@ static void __init net2big_init(void)
net2big_sata_power_init();
orion5x_sata_init(&net2big_sata_data);
- orion5x_setup_dev_boot_win(NET2BIG_NOR_BOOT_BASE,
- NET2BIG_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", NET2BIG_NOR_BOOT_BASE,
+ NET2BIG_NOR_BOOT_SIZE);
platform_device_register(&net2big_nor_flash);
platform_device_register(&net2big_gpio_buttons);
diff --git a/arch/arm/mach-orion5x/pci.c b/arch/arm/mach-orion5x/pci.c
index 973db98a3c27..503368023bb1 100644
--- a/arch/arm/mach-orion5x/pci.c
+++ b/arch/arm/mach-orion5x/pci.c
@@ -157,8 +157,11 @@ static int __init pcie_setup(struct pci_sys_data *sys)
if (dev == MV88F5181_DEV_ID || dev == MV88F5182_DEV_ID) {
printk(KERN_NOTICE "Applying Orion-1/Orion-NAS PCIe config "
"read transaction workaround\n");
- orion5x_setup_pcie_wa_win(ORION5X_PCIE_WA_PHYS_BASE,
- ORION5X_PCIE_WA_SIZE);
+ mvebu_mbus_add_window_remap_flags("pcie0.0",
+ ORION5X_PCIE_WA_PHYS_BASE,
+ ORION5X_PCIE_WA_SIZE,
+ MVEBU_MBUS_NO_REMAP,
+ MVEBU_MBUS_PCI_WA);
pcie_ops.read = pcie_rd_conf_wa;
}
diff --git a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
index d6e72f672afb..1c4498bf650a 100644
--- a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
+++ b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
@@ -123,8 +123,8 @@ static void __init rd88f5181l_fxo_init(void)
orion5x_eth_switch_init(&rd88f5181l_fxo_switch_plat_data, NO_IRQ);
orion5x_uart0_init();
- orion5x_setup_dev_boot_win(RD88F5181L_FXO_NOR_BOOT_BASE,
- RD88F5181L_FXO_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", RD88F5181L_FXO_NOR_BOOT_BASE,
+ RD88F5181L_FXO_NOR_BOOT_SIZE);
platform_device_register(&rd88f5181l_fxo_nor_boot_flash);
}
diff --git a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
index c8b7913310e5..adabe34c4fc6 100644
--- a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
+++ b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
@@ -130,8 +130,8 @@ static void __init rd88f5181l_ge_init(void)
orion5x_i2c_init();
orion5x_uart0_init();
- orion5x_setup_dev_boot_win(RD88F5181L_GE_NOR_BOOT_BASE,
- RD88F5181L_GE_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", RD88F5181L_GE_NOR_BOOT_BASE,
+ RD88F5181L_GE_NOR_BOOT_SIZE);
platform_device_register(&rd88f5181l_ge_nor_boot_flash);
i2c_register_board_info(0, &rd88f5181l_ge_i2c_rtc, 1);
diff --git a/arch/arm/mach-orion5x/rd88f5182-setup.c b/arch/arm/mach-orion5x/rd88f5182-setup.c
index f9e156725d7c..66e77ec91532 100644
--- a/arch/arm/mach-orion5x/rd88f5182-setup.c
+++ b/arch/arm/mach-orion5x/rd88f5182-setup.c
@@ -264,10 +264,11 @@ static void __init rd88f5182_init(void)
orion5x_uart0_init();
orion5x_xor_init();
- orion5x_setup_dev_boot_win(RD88F5182_NOR_BOOT_BASE,
- RD88F5182_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", RD88F5182_NOR_BOOT_BASE,
+ RD88F5182_NOR_BOOT_SIZE);
- orion5x_setup_dev1_win(RD88F5182_NOR_BASE, RD88F5182_NOR_SIZE);
+ mvebu_mbus_add_window("devbus-cs1", RD88F5182_NOR_BASE,
+ RD88F5182_NOR_SIZE);
platform_device_register(&rd88f5182_nor_flash);
platform_device_register(&rd88f5182_gpio_leds);
diff --git a/arch/arm/mach-orion5x/terastation_pro2-setup.c b/arch/arm/mach-orion5x/terastation_pro2-setup.c
index acc0877ec1c9..a0bfa53e7556 100644
--- a/arch/arm/mach-orion5x/terastation_pro2-setup.c
+++ b/arch/arm/mach-orion5x/terastation_pro2-setup.c
@@ -329,8 +329,8 @@ static void __init tsp2_init(void)
/*
* Configure peripherals.
*/
- orion5x_setup_dev_boot_win(TSP2_NOR_BOOT_BASE,
- TSP2_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", TSP2_NOR_BOOT_BASE,
+ TSP2_NOR_BOOT_SIZE);
platform_device_register(&tsp2_nor_flash);
orion5x_ehci0_init();
diff --git a/arch/arm/mach-orion5x/ts209-setup.c b/arch/arm/mach-orion5x/ts209-setup.c
index 9c17f0c2b488..80174f0f168e 100644
--- a/arch/arm/mach-orion5x/ts209-setup.c
+++ b/arch/arm/mach-orion5x/ts209-setup.c
@@ -286,8 +286,8 @@ static void __init qnap_ts209_init(void)
/*
* Configure peripherals.
*/
- orion5x_setup_dev_boot_win(QNAP_TS209_NOR_BOOT_BASE,
- QNAP_TS209_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", QNAP_TS209_NOR_BOOT_BASE,
+ QNAP_TS209_NOR_BOOT_SIZE);
platform_device_register(&qnap_ts209_nor_flash);
orion5x_ehci0_init();
diff --git a/arch/arm/mach-orion5x/ts409-setup.c b/arch/arm/mach-orion5x/ts409-setup.c
index 8cc5ab6c503e..92592790d6da 100644
--- a/arch/arm/mach-orion5x/ts409-setup.c
+++ b/arch/arm/mach-orion5x/ts409-setup.c
@@ -277,8 +277,8 @@ static void __init qnap_ts409_init(void)
/*
* Configure peripherals.
*/
- orion5x_setup_dev_boot_win(QNAP_TS409_NOR_BOOT_BASE,
- QNAP_TS409_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", QNAP_TS409_NOR_BOOT_BASE,
+ QNAP_TS409_NOR_BOOT_SIZE);
platform_device_register(&qnap_ts409_nor_flash);
orion5x_ehci0_init();
diff --git a/arch/arm/mach-orion5x/wnr854t-setup.c b/arch/arm/mach-orion5x/wnr854t-setup.c
index 66552ca7e05d..6b84863c018d 100644
--- a/arch/arm/mach-orion5x/wnr854t-setup.c
+++ b/arch/arm/mach-orion5x/wnr854t-setup.c
@@ -127,8 +127,8 @@ static void __init wnr854t_init(void)
orion5x_eth_switch_init(&wnr854t_switch_plat_data, NO_IRQ);
orion5x_uart0_init();
- orion5x_setup_dev_boot_win(WNR854T_NOR_BOOT_BASE,
- WNR854T_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", WNR854T_NOR_BOOT_BASE,
+ WNR854T_NOR_BOOT_SIZE);
platform_device_register(&wnr854t_nor_flash);
}
diff --git a/arch/arm/mach-orion5x/wrt350n-v2-setup.c b/arch/arm/mach-orion5x/wrt350n-v2-setup.c
index 2c5408e2e689..fae684bc54f2 100644
--- a/arch/arm/mach-orion5x/wrt350n-v2-setup.c
+++ b/arch/arm/mach-orion5x/wrt350n-v2-setup.c
@@ -213,8 +213,8 @@ static void __init wrt350n_v2_init(void)
orion5x_eth_switch_init(&wrt350n_v2_switch_plat_data, NO_IRQ);
orion5x_uart0_init();
- orion5x_setup_dev_boot_win(WRT350N_V2_NOR_BOOT_BASE,
- WRT350N_V2_NOR_BOOT_SIZE);
+ mvebu_mbus_add_window("devbus-boot", WRT350N_V2_NOR_BOOT_BASE,
+ WRT350N_V2_NOR_BOOT_SIZE);
platform_device_register(&wrt350n_v2_nor_flash);
platform_device_register(&wrt350n_v2_leds);
platform_device_register(&wrt350n_v2_button_device);
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
index eb3a7ff19e72..1a517e2fe449 100644
--- a/arch/arm/mach-shmobile/Kconfig
+++ b/arch/arm/mach-shmobile/Kconfig
@@ -18,11 +18,28 @@ config ARCH_SH73A0
select SH_CLK_CPG
select RENESAS_INTC_IRQPIN
+config ARCH_R8A73A4
+ bool "R-Mobile APE6 (R8A73A40)"
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARM_GIC
+ select CPU_V7
+ select ARM_ARCH_TIMER
+ select SH_CLK_CPG
+ select RENESAS_IRQC
+
config ARCH_R8A7740
bool "R-Mobile A1 (R8A77400)"
select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARM_GIC
select CPU_V7
select SH_CLK_CPG
+ select RENESAS_INTC_IRQPIN
+
+config ARCH_R8A7778
+ bool "R-Car M1 (R8A77780)"
+ select CPU_V7
+ select SH_CLK_CPG
+ select ARM_GIC
config ARCH_R8A7779
bool "R-Car H1 (R8A77790)"
@@ -34,6 +51,15 @@ config ARCH_R8A7779
select USB_ARCH_HAS_OHCI
select RENESAS_INTC_IRQPIN
+config ARCH_R8A7790
+ bool "R-Car H2 (R8A77900)"
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARM_GIC
+ select CPU_V7
+ select ARM_ARCH_TIMER
+ select SH_CLK_CPG
+ select RENESAS_IRQC
+
config ARCH_EMEV2
bool "Emma Mobile EV2"
select ARCH_WANT_OPTIONAL_GPIOLIB
@@ -70,6 +96,11 @@ config MACH_AG5EVM
select REGULATOR_FIXED_VOLTAGE if REGULATOR
select SH_LCD_MIPI_DSI
+config MACH_APE6EVM
+ bool "APE6EVM board"
+ depends on ARCH_R8A73A4
+ select USE_OF
+
config MACH_MACKEREL
bool "mackerel board"
depends on ARCH_SH7372
@@ -98,6 +129,13 @@ config MACH_ARMADILLO800EVA
select SND_SOC_WM8978 if SND_SIMPLE_CARD
select USE_OF
+config MACH_BOCKW
+ bool "BOCK-W platform"
+ depends on ARCH_R8A7778
+ select ARCH_REQUIRE_GPIOLIB
+ select RENESAS_INTC_IRQPIN
+ select USE_OF
+
config MACH_MARZEN
bool "MARZEN board"
depends on ARCH_R8A7779
@@ -117,6 +155,11 @@ config MACH_MARZEN_REFERENCE
This is intended to aid developers
+config MACH_LAGER
+ bool "Lager board"
+ depends on ARCH_R8A7790
+ select USE_OF
+
config MACH_KZM9D
bool "KZM9D board"
depends on ARCH_EMEV2
@@ -157,7 +200,8 @@ config MEMORY_START
hex "Physical memory start address"
default "0x40000000" if MACH_AP4EVB || MACH_AG5EVM || \
MACH_MACKEREL || MACH_BONITO || \
- MACH_ARMADILLO800EVA
+ MACH_ARMADILLO800EVA || MACH_APE6EVM || \
+ MACH_LAGER
default "0x41000000" if MACH_KOTA2
default "0x00000000"
---help---
@@ -167,6 +211,8 @@ config MEMORY_START
config MEMORY_SIZE
hex "Physical memory size"
+ default "0x80000000" if MACH_LAGER
+ default "0x40000000" if MACH_APE6EVM
default "0x20000000" if MACH_AG5EVM || MACH_BONITO || \
MACH_ARMADILLO800EVA
default "0x1e000000" if MACH_KOTA2
diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
index c621edfa6ead..068f1dadc46b 100644
--- a/arch/arm/mach-shmobile/Makefile
+++ b/arch/arm/mach-shmobile/Makefile
@@ -8,8 +8,11 @@ obj-y := timer.o console.o clock.o
# CPU objects
obj-$(CONFIG_ARCH_SH7372) += setup-sh7372.o clock-sh7372.o intc-sh7372.o
obj-$(CONFIG_ARCH_SH73A0) += setup-sh73a0.o clock-sh73a0.o intc-sh73a0.o
+obj-$(CONFIG_ARCH_R8A73A4) += setup-r8a73a4.o clock-r8a73a4.o
obj-$(CONFIG_ARCH_R8A7740) += setup-r8a7740.o clock-r8a7740.o intc-r8a7740.o
+obj-$(CONFIG_ARCH_R8A7778) += setup-r8a7778.o clock-r8a7778.o
obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o clock-r8a7779.o intc-r8a7779.o
+obj-$(CONFIG_ARCH_R8A7790) += setup-r8a7790.o clock-r8a7790.o
obj-$(CONFIG_ARCH_EMEV2) += setup-emev2.o clock-emev2.o
# SMP objects
@@ -34,11 +37,14 @@ obj-$(CONFIG_ARCH_SH73A0) += pm-sh73a0.o
# Board objects
obj-$(CONFIG_MACH_AP4EVB) += board-ap4evb.o
obj-$(CONFIG_MACH_AG5EVM) += board-ag5evm.o
+obj-$(CONFIG_MACH_APE6EVM) += board-ape6evm.o
obj-$(CONFIG_MACH_MACKEREL) += board-mackerel.o
obj-$(CONFIG_MACH_KOTA2) += board-kota2.o
obj-$(CONFIG_MACH_BONITO) += board-bonito.o
+obj-$(CONFIG_MACH_BOCKW) += board-bockw.o
obj-$(CONFIG_MACH_MARZEN) += board-marzen.o
obj-$(CONFIG_MACH_MARZEN_REFERENCE) += board-marzen-reference.o
+obj-$(CONFIG_MACH_LAGER) += board-lager.o
obj-$(CONFIG_MACH_ARMADILLO800EVA) += board-armadillo800eva.o
obj-$(CONFIG_MACH_KZM9D) += board-kzm9d.o
obj-$(CONFIG_MACH_KZM9G) += board-kzm9g.o
diff --git a/arch/arm/mach-shmobile/board-ape6evm.c b/arch/arm/mach-shmobile/board-ape6evm.c
new file mode 100644
index 000000000000..55b8c9fef954
--- /dev/null
+++ b/arch/arm/mach-shmobile/board-ape6evm.c
@@ -0,0 +1,94 @@
+/*
+ * APE6EVM board support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
+#include <linux/kernel.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
+#include <linux/smsc911x.h>
+#include <mach/common.h>
+#include <mach/irqs.h>
+#include <mach/r8a73a4.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+ REGULATOR_SUPPLY("vddvario", "smsc911x"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
+/* SMSC LAN9220 */
+static const struct resource lan9220_res[] = {
+ DEFINE_RES_MEM(0x08000000, 0x1000),
+ {
+ .start = irq_pin(40), /* IRQ40 */
+ .flags = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,
+ },
+};
+
+static const struct smsc911x_platform_config lan9220_data = {
+ .flags = SMSC911X_USE_32BIT,
+ .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL,
+ .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH,
+};
+
+static const struct pinctrl_map ape6evm_pinctrl_map[] = {
+ /* SCIFA0 console */
+ PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.0", "pfc-r8a73a4",
+ "scifa0_data", "scifa0"),
+ /* SMSC */
+ PIN_MAP_MUX_GROUP_DEFAULT("smsc911x", "pfc-r8a73a4",
+ "irqc_irq40", "irqc"),
+};
+
+static void __init ape6evm_add_standard_devices(void)
+{
+ r8a73a4_clock_init();
+ pinctrl_register_mappings(ape6evm_pinctrl_map,
+ ARRAY_SIZE(ape6evm_pinctrl_map));
+ r8a73a4_pinmux_init();
+ r8a73a4_add_standard_devices();
+
+ /* LAN9220 ethernet */
+ gpio_request_one(270, GPIOF_OUT_INIT_HIGH, NULL); /* smsc9220 RESET */
+
+ regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
+ platform_device_register_resndata(&platform_bus, "smsc911x", -1,
+ lan9220_res, ARRAY_SIZE(lan9220_res),
+ &lan9220_data, sizeof(lan9220_data));
+}
+
+static const char *ape6evm_boards_compat_dt[] __initdata = {
+ "renesas,ape6evm",
+ NULL,
+};
+
+DT_MACHINE_START(APE6EVM_DT, "ape6evm")
+ .init_irq = irqchip_init,
+ .init_time = shmobile_timer_init,
+ .init_machine = ape6evm_add_standard_devices,
+ .dt_compat = ape6evm_boards_compat_dt,
+MACHINE_END
diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c
index 4dfe32262eb7..b85b2882dbd0 100644
--- a/arch/arm/mach-shmobile/board-armadillo800eva.c
+++ b/arch/arm/mach-shmobile/board-armadillo800eva.c
@@ -149,7 +149,7 @@
* see
* usbhsf_power_ctrl()
*/
-#define IRQ7 evt2irq(0x02e0)
+#define IRQ7 irq_pin(7)
#define USBCR1 IOMEM(0xe605810a)
#define USBH 0xC6700000
#define USBH_USBCTR 0x10834
@@ -338,7 +338,7 @@ static struct resource usbhsf_resources[] = {
.flags = IORESOURCE_MEM,
},
{
- .start = evt2irq(0x0A20),
+ .start = gic_spi(51),
.flags = IORESOURCE_IRQ,
},
};
@@ -371,7 +371,7 @@ static struct resource sh_eth_resources[] = {
.end = 0xe9a02000 - 1,
.flags = IORESOURCE_MEM,
}, {
- .start = evt2irq(0x0500),
+ .start = gic_spi(110),
.flags = IORESOURCE_IRQ,
},
};
@@ -425,7 +425,7 @@ static struct resource lcdc0_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = intcs_evt2irq(0x580),
+ .start = gic_spi(177),
.flags = IORESOURCE_IRQ,
},
};
@@ -460,7 +460,7 @@ static struct resource hdmi_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = evt2irq(0x1700),
+ .start = gic_spi(131),
.flags = IORESOURCE_IRQ,
},
[2] = {
@@ -522,7 +522,7 @@ static struct resource hdmi_lcdc_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = intcs_evt2irq(0x1780),
+ .start = gic_spi(178),
.flags = IORESOURCE_IRQ,
},
};
@@ -686,7 +686,7 @@ static struct platform_device vcc_sdhi1 = {
* We can use IRQ31 as card detect irq,
* but it needs chattering removal operation
*/
-#define IRQ31 evt2irq(0x33E0)
+#define IRQ31 irq_pin(31)
static struct sh_mobile_sdhi_info sdhi0_info = {
.dma_slave_tx = SHDMA_SLAVE_SDHI0_TX,
.dma_slave_rx = SHDMA_SLAVE_SDHI0_RX,
@@ -708,12 +708,12 @@ static struct resource sdhi0_resources[] = {
*/
{
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
- .start = evt2irq(0x0E20),
+ .start = gic_spi(118),
.flags = IORESOURCE_IRQ,
},
{
.name = SH_MOBILE_SDHI_IRQ_SDIO,
- .start = evt2irq(0x0E40),
+ .start = gic_spi(119),
.flags = IORESOURCE_IRQ,
},
};
@@ -747,15 +747,15 @@ static struct resource sdhi1_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = evt2irq(0x0E80),
+ .start = gic_spi(121),
.flags = IORESOURCE_IRQ,
},
[2] = {
- .start = evt2irq(0x0EA0),
+ .start = gic_spi(122),
.flags = IORESOURCE_IRQ,
},
[3] = {
- .start = evt2irq(0x0EC0),
+ .start = gic_spi(123),
.flags = IORESOURCE_IRQ,
},
};
@@ -798,12 +798,12 @@ static struct resource sh_mmcif_resources[] = {
},
[1] = {
/* MMC ERR */
- .start = evt2irq(0x1AC0),
+ .start = gic_spi(56),
.flags = IORESOURCE_IRQ,
},
[2] = {
/* MMC NOR */
- .start = evt2irq(0x1AE0),
+ .start = gic_spi(57),
.flags = IORESOURCE_IRQ,
},
};
@@ -880,7 +880,7 @@ static struct resource ceu0_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = intcs_evt2irq(0x0500),
+ .start = gic_spi(160),
.flags = IORESOURCE_IRQ,
},
[2] = {
@@ -922,7 +922,7 @@ static struct resource fsi_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = evt2irq(0x1840),
+ .start = gic_spi(9),
.flags = IORESOURCE_IRQ,
},
};
@@ -1009,7 +1009,7 @@ static struct st1232_pdata st1232_i2c0_pdata = {
static struct i2c_board_info i2c0_devices[] = {
{
I2C_BOARD_INFO("st1232-ts", 0x55),
- .irq = evt2irq(0x0340),
+ .irq = irq_pin(10),
.platform_data = &st1232_i2c0_pdata,
},
{
@@ -1292,7 +1292,6 @@ DT_MACHINE_START(ARMADILLO800EVA_DT, "armadillo800eva")
.map_io = r8a7740_map_io,
.init_early = eva_add_early_devices,
.init_irq = r8a7740_init_irq,
- .handle_irq = shmobile_handle_irq_intc,
.init_machine = eva_init,
.init_late = shmobile_init_late,
.init_time = eva_earlytimer_init,
diff --git a/arch/arm/mach-shmobile/board-bockw.c b/arch/arm/mach-shmobile/board-bockw.c
new file mode 100644
index 000000000000..38e5e50fb318
--- /dev/null
+++ b/arch/arm/mach-shmobile/board-bockw.c
@@ -0,0 +1,81 @@
+/*
+ * Bock-W board support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/platform_device.h>
+#include <linux/smsc911x.h>
+#include <mach/common.h>
+#include <mach/irqs.h>
+#include <mach/r8a7778.h>
+#include <asm/mach/arch.h>
+
+static struct smsc911x_platform_config smsc911x_data = {
+ .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
+ .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL,
+ .flags = SMSC911X_USE_32BIT,
+ .phy_interface = PHY_INTERFACE_MODE_MII,
+};
+
+static struct resource smsc911x_resources[] = {
+ DEFINE_RES_MEM(0x18300000, 0x1000),
+ DEFINE_RES_IRQ(irq_pin(0)), /* IRQ 0 */
+};
+
+#define IRQ0MR 0x30
+static void __init bockw_init(void)
+{
+ void __iomem *fpga;
+
+ r8a7778_clock_init();
+ r8a7778_init_irq_extpin(1);
+ r8a7778_add_standard_devices();
+
+ fpga = ioremap_nocache(0x18200000, SZ_1M);
+ if (fpga) {
+ /*
+ * CAUTION
+ *
+ * IRQ0/1 is cascaded interrupt from FPGA.
+ * it should be cared in the future
+ * Now, it is assuming IRQ0 was used only from SMSC.
+ */
+ u16 val = ioread16(fpga + IRQ0MR);
+ val &= ~(1 << 4); /* enable SMSC911x */
+ iowrite16(val, fpga + IRQ0MR);
+ iounmap(fpga);
+
+ platform_device_register_resndata(
+ &platform_bus, "smsc911x", -1,
+ smsc911x_resources, ARRAY_SIZE(smsc911x_resources),
+ &smsc911x_data, sizeof(smsc911x_data));
+ }
+}
+
+static const char *bockw_boards_compat_dt[] __initdata = {
+ "renesas,bockw",
+ NULL,
+};
+
+DT_MACHINE_START(BOCKW_DT, "bockw")
+ .init_early = r8a7778_init_delay,
+ .init_irq = r8a7778_init_irq_dt,
+ .init_machine = bockw_init,
+ .init_time = shmobile_timer_init,
+ .dt_compat = bockw_boards_compat_dt,
+MACHINE_END
diff --git a/arch/arm/mach-shmobile/board-kzm9d.c b/arch/arm/mach-shmobile/board-kzm9d.c
index c254782aa727..c016ccd92433 100644
--- a/arch/arm/mach-shmobile/board-kzm9d.c
+++ b/arch/arm/mach-shmobile/board-kzm9d.c
@@ -90,6 +90,5 @@ DT_MACHINE_START(KZM9D_DT, "kzm9d")
.init_irq = emev2_init_irq,
.init_machine = kzm9d_add_standard_devices,
.init_late = shmobile_init_late,
- .init_time = shmobile_timer_init,
.dt_compat = kzm9d_boards_compat_dt,
MACHINE_END
diff --git a/arch/arm/mach-shmobile/board-lager.c b/arch/arm/mach-shmobile/board-lager.c
new file mode 100644
index 000000000000..f587187a8603
--- /dev/null
+++ b/arch/arm/mach-shmobile/board-lager.c
@@ -0,0 +1,46 @@
+/*
+ * Lager board support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <mach/common.h>
+#include <mach/r8a7790.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+static void __init lager_add_standard_devices(void)
+{
+ r8a7790_clock_init();
+ r8a7790_add_standard_devices();
+}
+
+static const char *lager_boards_compat_dt[] __initdata = {
+ "renesas,lager",
+ NULL,
+};
+
+DT_MACHINE_START(LAGER_DT, "lager")
+ .init_irq = irqchip_init,
+ .init_time = r8a7790_timer_init,
+ .init_machine = lager_add_standard_devices,
+ .dt_compat = lager_boards_compat_dt,
+MACHINE_END
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 2b60f2bf1fbb..fa3407da682a 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -969,15 +969,6 @@ static struct platform_device nand_flash_device = {
},
};
-/*
- * The card detect pin of the top SD/MMC slot (CN7) is active low and is
- * connected to GPIO A22 of SH7372 (GPIO 41).
- */
-static int slot_cn7_get_cd(struct platform_device *pdev)
-{
- return !gpio_get_value(41);
-}
-
/* SDHI0 */
static struct sh_mobile_sdhi_info sdhi0_info = {
.dma_slave_tx = SHDMA_SLAVE_SDHI0_TX,
@@ -988,21 +979,17 @@ static struct sh_mobile_sdhi_info sdhi0_info = {
};
static struct resource sdhi0_resources[] = {
- [0] = {
+ {
.name = "SDHI0",
.start = 0xe6850000,
.end = 0xe68500ff,
.flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0x0e00) /* SDHI0_SDHI0I0 */,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
+ }, {
+ .name = SH_MOBILE_SDHI_IRQ_SDCARD,
.start = evt2irq(0x0e20) /* SDHI0_SDHI0I1 */,
.flags = IORESOURCE_IRQ,
- },
- [3] = {
+ }, {
+ .name = SH_MOBILE_SDHI_IRQ_SDIO,
.start = evt2irq(0x0e40) /* SDHI0_SDHI0I2 */,
.flags = IORESOURCE_IRQ,
},
@@ -1018,36 +1005,30 @@ static struct platform_device sdhi0_device = {
},
};
-#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
+#if !IS_ENABLED(CONFIG_MMC_SH_MMCIF)
/* SDHI1 */
+
+/* GPIO 41 can trigger IRQ8, but it is used by USBHS1, we have to poll */
static struct sh_mobile_sdhi_info sdhi1_info = {
.dma_slave_tx = SHDMA_SLAVE_SDHI1_TX,
.dma_slave_rx = SHDMA_SLAVE_SDHI1_RX,
- .tmio_ocr_mask = MMC_VDD_165_195,
- .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE,
+ .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_USE_GPIO_CD,
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_NEEDS_POLL,
- .get_cd = slot_cn7_get_cd,
+ .cd_gpio = 41,
};
static struct resource sdhi1_resources[] = {
- [0] = {
+ {
.name = "SDHI1",
.start = 0xe6860000,
.end = 0xe68600ff,
.flags = IORESOURCE_MEM,
- },
- [1] = {
- .name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
- .start = evt2irq(0x0e80), /* SDHI1_SDHI1I0 */
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
+ }, {
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
.start = evt2irq(0x0ea0), /* SDHI1_SDHI1I1 */
.flags = IORESOURCE_IRQ,
- },
- [3] = {
+ }, {
.name = SH_MOBILE_SDHI_IRQ_SDIO,
.start = evt2irq(0x0ec0), /* SDHI1_SDHI1I2 */
.flags = IORESOURCE_IRQ,
@@ -1065,43 +1046,32 @@ static struct platform_device sdhi1_device = {
};
#endif
+/* SDHI2 */
+
/*
* The card detect pin of the top SD/MMC slot (CN23) is active low and is
- * connected to GPIO SCIFB_SCK of SH7372 (162).
+ * connected to GPIO SCIFB_SCK of SH7372 (GPIO 162).
*/
-static int slot_cn23_get_cd(struct platform_device *pdev)
-{
- return !gpio_get_value(162);
-}
-
-/* SDHI2 */
static struct sh_mobile_sdhi_info sdhi2_info = {
.dma_slave_tx = SHDMA_SLAVE_SDHI2_TX,
.dma_slave_rx = SHDMA_SLAVE_SDHI2_RX,
- .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE,
+ .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_USE_GPIO_CD,
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_NEEDS_POLL,
- .get_cd = slot_cn23_get_cd,
+ .cd_gpio = 162,
};
static struct resource sdhi2_resources[] = {
- [0] = {
+ {
.name = "SDHI2",
.start = 0xe6870000,
.end = 0xe68700ff,
.flags = IORESOURCE_MEM,
- },
- [1] = {
- .name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
- .start = evt2irq(0x1200), /* SDHI2_SDHI2I0 */
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
+ }, {
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
.start = evt2irq(0x1220), /* SDHI2_SDHI2I1 */
.flags = IORESOURCE_IRQ,
- },
- [3] = {
+ }, {
.name = SH_MOBILE_SDHI_IRQ_SDIO,
.start = evt2irq(0x1240), /* SDHI2_SDHI2I2 */
.flags = IORESOURCE_IRQ,
@@ -1119,6 +1089,7 @@ static struct platform_device sdhi2_device = {
};
/* SH_MMCIF */
+#if IS_ENABLED(CONFIG_MMC_SH_MMCIF)
static struct resource sh_mmcif_resources[] = {
[0] = {
.name = "MMCIF",
@@ -1140,16 +1111,17 @@ static struct resource sh_mmcif_resources[] = {
static struct sh_mmcif_plat_data sh_mmcif_plat = {
.sup_pclk = 0,
- .ocr = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
.caps = MMC_CAP_4_BIT_DATA |
MMC_CAP_8_BIT_DATA |
MMC_CAP_NEEDS_POLL,
- .get_cd = slot_cn7_get_cd,
+ .use_cd_gpio = true,
+ /* card detect pin for SD/MMC slot (CN7) */
+ .cd_gpio = 41,
.slave_id_tx = SHDMA_SLAVE_MMCIF_TX,
.slave_id_rx = SHDMA_SLAVE_MMCIF_RX,
};
-static struct platform_device sh_mmcif_device __maybe_unused = {
+static struct platform_device sh_mmcif_device = {
.name = "sh_mmcif",
.id = 0,
.dev = {
@@ -1160,7 +1132,7 @@ static struct platform_device sh_mmcif_device __maybe_unused = {
.num_resources = ARRAY_SIZE(sh_mmcif_resources),
.resource = sh_mmcif_resources,
};
-
+#endif
static int mackerel_camera_add(struct soc_camera_device *icd);
static void mackerel_camera_del(struct soc_camera_device *icd);
@@ -1267,11 +1239,12 @@ static struct platform_device *mackerel_devices[] __initdata = {
&fsi_hdmi_device,
&nand_flash_device,
&sdhi0_device,
-#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
+#if !IS_ENABLED(CONFIG_MMC_SH_MMCIF)
&sdhi1_device,
+#else
+ &sh_mmcif_device,
#endif
&sdhi2_device,
- &sh_mmcif_device,
&ceu_device,
&mackerel_camera,
&hdmi_device,
@@ -1336,11 +1309,6 @@ static struct i2c_board_info i2c1_devices[] = {
};
static const struct pinctrl_map mackerel_pinctrl_map[] = {
- /* MMCIF */
- PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.0", "pfc-sh7372",
- "mmc0_data8_0", "mmc0"),
- PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.0", "pfc-sh7372",
- "mmc0_ctrl_0", "mmc0"),
/* SDHI0 */
PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-sh7372",
"sdhi0_data4", "sdhi0"),
@@ -1349,11 +1317,17 @@ static const struct pinctrl_map mackerel_pinctrl_map[] = {
PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-sh7372",
"sdhi0_wp", "sdhi0"),
/* SDHI1 */
-#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
+#if !IS_ENABLED(CONFIG_MMC_SH_MMCIF)
PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-sh7372",
"sdhi1_data4", "sdhi1"),
PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-sh7372",
"sdhi1_ctrl", "sdhi1"),
+#else
+ /* MMCIF */
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.0", "pfc-sh7372",
+ "mmc0_data8_0", "mmc0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.0", "pfc-sh7372",
+ "mmc0_ctrl_0", "mmc0"),
#endif
/* SDHI2 */
PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.2", "pfc-sh7372",
@@ -1378,10 +1352,11 @@ static void __init mackerel_init(void)
{ "A3SP", &usbhs0_device, },
{ "A3SP", &usbhs1_device, },
{ "A3SP", &nand_flash_device, },
- { "A3SP", &sh_mmcif_device, },
{ "A3SP", &sdhi0_device, },
-#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
+#if !IS_ENABLED(CONFIG_MMC_SH_MMCIF)
{ "A3SP", &sdhi1_device, },
+#else
+ { "A3SP", &sh_mmcif_device, },
#endif
{ "A3SP", &sdhi2_device, },
{ "A4R", &ceu_device, },
@@ -1492,12 +1467,6 @@ static void __init mackerel_init(void)
/* SDHI0 PORT172 card-detect IRQ26 */
gpio_request(GPIO_FN_IRQ26_172, NULL);
- /* card detect pin for MMC slot (CN7) */
- gpio_request_one(41, GPIOF_IN, NULL);
-
- /* card detect pin for microSD slot (CN23) */
- gpio_request_one(162, GPIOF_IN, NULL);
-
/* FLCTL */
gpio_request(GPIO_FN_D0_NAF0, NULL);
gpio_request(GPIO_FN_D1_NAF1, NULL);
diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c
index 2333a2d7c937..91052855cc12 100644
--- a/arch/arm/mach-shmobile/board-marzen.c
+++ b/arch/arm/mach-shmobile/board-marzen.c
@@ -25,6 +25,7 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/leds.h>
#include <linux/dma-mapping.h>
#include <linux/pinctrl/machine.h>
#include <linux/regulator/fixed.h>
@@ -168,12 +169,43 @@ static struct platform_device usb_phy_device = {
.num_resources = ARRAY_SIZE(usb_phy_resources),
};
+/* LEDS */
+static struct gpio_led marzen_leds[] = {
+ {
+ .name = "led2",
+ .gpio = 157,
+ .default_state = LEDS_GPIO_DEFSTATE_ON,
+ }, {
+ .name = "led3",
+ .gpio = 158,
+ .default_state = LEDS_GPIO_DEFSTATE_ON,
+ }, {
+ .name = "led4",
+ .gpio = 159,
+ .default_state = LEDS_GPIO_DEFSTATE_ON,
+ },
+};
+
+static struct gpio_led_platform_data marzen_leds_pdata = {
+ .leds = marzen_leds,
+ .num_leds = ARRAY_SIZE(marzen_leds),
+};
+
+static struct platform_device leds_device = {
+ .name = "leds-gpio",
+ .id = 0,
+ .dev = {
+ .platform_data = &marzen_leds_pdata,
+ },
+};
+
static struct platform_device *marzen_devices[] __initdata = {
&eth_device,
&sdhi0_device,
&thermal_device,
&hspi_device,
&usb_phy_device,
+ &leds_device,
};
/* USB */
diff --git a/arch/arm/mach-shmobile/clock-r8a73a4.c b/arch/arm/mach-shmobile/clock-r8a73a4.c
new file mode 100644
index 000000000000..e710c00c3822
--- /dev/null
+++ b/arch/arm/mach-shmobile/clock-r8a73a4.c
@@ -0,0 +1,115 @@
+/*
+ * r8a73a4 clock framework support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/sh_clk.h>
+#include <linux/clkdev.h>
+#include <mach/common.h>
+
+#define CPG_BASE 0xe6150000
+#define CPG_LEN 0x270
+
+#define MPCKCR 0xe6150080
+#define SMSTPCR2 0xe6150138
+#define SMSTPCR5 0xe6150144
+
+static struct clk_mapping cpg_mapping = {
+ .phys = CPG_BASE,
+ .len = CPG_LEN,
+};
+
+static struct clk extalr_clk = {
+ .rate = 32768,
+ .mapping = &cpg_mapping,
+};
+
+static struct clk extal1_clk = {
+ .rate = 26000000,
+ .mapping = &cpg_mapping,
+};
+
+static struct clk extal2_clk = {
+ .rate = 48000000,
+ .mapping = &cpg_mapping,
+};
+
+static struct clk *main_clks[] = {
+ &extalr_clk,
+ &extal1_clk,
+ &extal2_clk,
+};
+
+enum {
+ MSTP217, MSTP216, MSTP207, MSTP206, MSTP204, MSTP203,
+ MSTP522,
+ MSTP_NR
+};
+
+static struct clk mstp_clks[MSTP_NR] = {
+ [MSTP204] = SH_CLK_MSTP32(&extal2_clk, SMSTPCR2, 4, 0), /* SCIFA0 */
+ [MSTP203] = SH_CLK_MSTP32(&extal2_clk, SMSTPCR2, 3, 0), /* SCIFA1 */
+ [MSTP206] = SH_CLK_MSTP32(&extal2_clk, SMSTPCR2, 6, 0), /* SCIFB0 */
+ [MSTP207] = SH_CLK_MSTP32(&extal2_clk, SMSTPCR2, 7, 0), /* SCIFB1 */
+ [MSTP216] = SH_CLK_MSTP32(&extal2_clk, SMSTPCR2, 16, 0), /* SCIFB2 */
+ [MSTP217] = SH_CLK_MSTP32(&extal2_clk, SMSTPCR2, 17, 0), /* SCIFB3 */
+ [MSTP522] = SH_CLK_MSTP32(&extal2_clk, SMSTPCR5, 22, 0), /* Thermal */
+};
+
+static struct clk_lookup lookups[] = {
+ CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]),
+ CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]),
+ CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP206]),
+ CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP207]),
+ CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP216]),
+ CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP217]),
+ CLKDEV_DEV_ID("rcar_thermal", &mstp_clks[MSTP522]),
+
+ /* for DT */
+ CLKDEV_DEV_ID("e61f0000.thermal", &mstp_clks[MSTP522]),
+};
+
+void __init r8a73a4_clock_init(void)
+{
+ void __iomem *cpg_base, *reg;
+ int k, ret = 0;
+
+ /* fix MPCLK to EXTAL2 for now.
+ * this is needed until more detailed clock topology is supported
+ */
+ cpg_base = ioremap_nocache(CPG_BASE, CPG_LEN);
+ BUG_ON(!cpg_base);
+ reg = cpg_base + (MPCKCR - CPG_BASE);
+ iowrite32(ioread32(reg) | 1 << 7 | 0x0c, reg); /* set CKSEL */
+ iounmap(cpg_base);
+
+ for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
+ ret = clk_register(main_clks[k]);
+
+ if (!ret)
+ ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
+
+ clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+ if (!ret)
+ shmobile_clk_init();
+ else
+ panic("failed to setup r8a73a4 clocks\n");
+}
diff --git a/arch/arm/mach-shmobile/clock-r8a7740.c b/arch/arm/mach-shmobile/clock-r8a7740.c
index 1feb9a2286a8..c0d39aa6de50 100644
--- a/arch/arm/mach-shmobile/clock-r8a7740.c
+++ b/arch/arm/mach-shmobile/clock-r8a7740.c
@@ -22,6 +22,7 @@
#include <linux/io.h>
#include <linux/sh_clk.h>
#include <linux/clkdev.h>
+#include <mach/clock.h>
#include <mach/common.h>
#include <mach/r8a7740.h>
@@ -97,42 +98,13 @@ static struct clk dv_clk = {
.rate = 27000000,
};
-static unsigned long div_recalc(struct clk *clk)
-{
- return clk->parent->rate / (int)(clk->priv);
-}
-
-static struct sh_clk_ops div_clk_ops = {
- .recalc = div_recalc,
-};
-
-/* extal1 / 2 */
-static struct clk extal1_div2_clk = {
- .ops = &div_clk_ops,
- .priv = (void *)2,
- .parent = &extal1_clk,
-};
-
-/* extal1 / 1024 */
-static struct clk extal1_div1024_clk = {
- .ops = &div_clk_ops,
- .priv = (void *)1024,
- .parent = &extal1_clk,
-};
-
-/* extal1 / 2 / 1024 */
-static struct clk extal1_div2048_clk = {
- .ops = &div_clk_ops,
- .priv = (void *)1024,
- .parent = &extal1_div2_clk,
-};
+SH_CLK_RATIO(div2, 1, 2);
+SH_CLK_RATIO(div1k, 1, 1024);
-/* extal2 / 2 */
-static struct clk extal2_div2_clk = {
- .ops = &div_clk_ops,
- .priv = (void *)2,
- .parent = &extal2_clk,
-};
+SH_FIXED_RATIO_CLK(extal1_div2_clk, extal1_clk, div2);
+SH_FIXED_RATIO_CLK(extal1_div1024_clk, extal1_clk, div1k);
+SH_FIXED_RATIO_CLK(extal1_div2048_clk, extal1_div2_clk, div1k);
+SH_FIXED_RATIO_CLK(extal2_div2_clk, extal2_clk, div2);
static struct sh_clk_ops followparent_clk_ops = {
.recalc = followparent_recalc,
@@ -143,11 +115,7 @@ static struct clk system_clk = {
.ops = &followparent_clk_ops,
};
-static struct clk system_div2_clk = {
- .ops = &div_clk_ops,
- .priv = (void *)2,
- .parent = &system_clk,
-};
+SH_FIXED_RATIO_CLK(system_div2_clk, system_clk, div2);
/* r_clk */
static struct clk r_clk = {
@@ -184,11 +152,7 @@ static struct clk pllc1_clk = {
};
/* PLLC1 / 2 */
-static struct clk pllc1_div2_clk = {
- .ops = &div_clk_ops,
- .priv = (void *)2,
- .parent = &pllc1_clk,
-};
+SH_FIXED_RATIO_CLK(pllc1_div2_clk, pllc1_clk, div2);
/* USB clock */
/*
@@ -323,6 +287,7 @@ struct clk *main_clks[] = {
&fsibck_clk,
};
+/* DIV4 clocks */
static void div4_kick(struct clk *clk)
{
unsigned long value;
@@ -346,6 +311,26 @@ static struct clk_div4_table div4_table = {
.kick = div4_kick,
};
+enum {
+ DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1, DIV4_HP,
+ DIV4_HPP, DIV4_USBP, DIV4_S, DIV4_ZB, DIV4_M3, DIV4_CP,
+ DIV4_NR
+};
+
+struct clk div4_clks[DIV4_NR] = {
+ [DIV4_I] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 20, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_ZG] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 16, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_B] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 8, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_M1] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 4, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_HP] = SH_CLK_DIV4(&pllc1_clk, FRQCRB, 4, 0x6fff, 0),
+ [DIV4_HPP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 20, 0x6fff, 0),
+ [DIV4_USBP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 16, 0x6fff, 0),
+ [DIV4_S] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 12, 0x6fff, 0),
+ [DIV4_ZB] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 8, 0x6fff, 0),
+ [DIV4_M3] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 4, 0x6fff, 0),
+ [DIV4_CP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 0, 0x6fff, 0),
+};
+
/* DIV6 reparent */
enum {
DIV6_HDMI,
@@ -391,6 +376,16 @@ static struct clk div6_reparent_clks[DIV6_REPARENT_NR] = {
fsib_parents, ARRAY_SIZE(fsib_parents), 6, 2),
};
+/* DIV6 clocks */
+enum {
+ DIV6_SUB,
+ DIV6_NR
+};
+
+static struct clk div6_clks[DIV6_NR] = {
+ [DIV6_SUB] = SH_CLK_DIV6(&pllc1_div2_clk, SUBCKCR, 0),
+};
+
/* HDMI1/2 clock */
static unsigned long hdmi12_recalc(struct clk *clk)
{
@@ -456,35 +451,6 @@ static struct clk fsidivs[] = {
/* MSTP */
enum {
- DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1, DIV4_HP,
- DIV4_HPP, DIV4_USBP, DIV4_S, DIV4_ZB, DIV4_M3, DIV4_CP,
- DIV4_NR
-};
-
-struct clk div4_clks[DIV4_NR] = {
- [DIV4_I] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 20, 0x6fff, CLK_ENABLE_ON_INIT),
- [DIV4_ZG] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 16, 0x6fff, CLK_ENABLE_ON_INIT),
- [DIV4_B] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 8, 0x6fff, CLK_ENABLE_ON_INIT),
- [DIV4_M1] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 4, 0x6fff, CLK_ENABLE_ON_INIT),
- [DIV4_HP] = SH_CLK_DIV4(&pllc1_clk, FRQCRB, 4, 0x6fff, 0),
- [DIV4_HPP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 20, 0x6fff, 0),
- [DIV4_USBP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 16, 0x6fff, 0),
- [DIV4_S] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 12, 0x6fff, 0),
- [DIV4_ZB] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 8, 0x6fff, 0),
- [DIV4_M3] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 4, 0x6fff, 0),
- [DIV4_CP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 0, 0x6fff, 0),
-};
-
-enum {
- DIV6_SUB,
- DIV6_NR
-};
-
-static struct clk div6_clks[DIV6_NR] = {
- [DIV6_SUB] = SH_CLK_DIV6(&pllc1_div2_clk, SUBCKCR, 0),
-};
-
-enum {
MSTP128, MSTP127, MSTP125,
MSTP116, MSTP111, MSTP100, MSTP117,
diff --git a/arch/arm/mach-shmobile/clock-r8a7778.c b/arch/arm/mach-shmobile/clock-r8a7778.c
new file mode 100644
index 000000000000..cd6855290b1f
--- /dev/null
+++ b/arch/arm/mach-shmobile/clock-r8a7778.c
@@ -0,0 +1,107 @@
+/*
+ * r8a7778 clock framework support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on r8a7779
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Copyright (C) 2011 Magnus Damm
+ *
+ * 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
+ * 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/io.h>
+#include <linux/sh_clk.h>
+#include <linux/clkdev.h>
+#include <mach/common.h>
+
+#define MSTPCR0 IOMEM(0xffc80030)
+#define MSTPCR1 IOMEM(0xffc80034)
+#define MSTPCR3 IOMEM(0xffc8003c)
+#define MSTPSR1 IOMEM(0xffc80044)
+#define MSTPSR4 IOMEM(0xffc80048)
+#define MSTPSR6 IOMEM(0xffc8004c)
+#define MSTPCR4 IOMEM(0xffc80050)
+#define MSTPCR5 IOMEM(0xffc80054)
+#define MSTPCR6 IOMEM(0xffc80058)
+
+/* ioremap() through clock mapping mandatory to avoid
+ * collision with ARM coherent DMA virtual memory range.
+ */
+
+static struct clk_mapping cpg_mapping = {
+ .phys = 0xffc80000,
+ .len = 0x80,
+};
+
+static struct clk clkp = {
+ .rate = 62500000, /* FIXME: shortcut */
+ .flags = CLK_ENABLE_ON_INIT,
+ .mapping = &cpg_mapping,
+};
+
+static struct clk *main_clks[] = {
+ &clkp,
+};
+
+enum {
+ MSTP114,
+ MSTP026, MSTP025, MSTP024, MSTP023, MSTP022, MSTP021,
+ MSTP016, MSTP015,
+ MSTP_NR };
+
+static struct clk mstp_clks[MSTP_NR] = {
+ [MSTP114] = SH_CLK_MSTP32(&clkp, MSTPCR1, 14, 0), /* Ether */
+ [MSTP026] = SH_CLK_MSTP32(&clkp, MSTPCR0, 26, 0), /* SCIF0 */
+ [MSTP025] = SH_CLK_MSTP32(&clkp, MSTPCR0, 25, 0), /* SCIF1 */
+ [MSTP024] = SH_CLK_MSTP32(&clkp, MSTPCR0, 24, 0), /* SCIF2 */
+ [MSTP023] = SH_CLK_MSTP32(&clkp, MSTPCR0, 23, 0), /* SCIF3 */
+ [MSTP022] = SH_CLK_MSTP32(&clkp, MSTPCR0, 22, 0), /* SCIF4 */
+ [MSTP021] = SH_CLK_MSTP32(&clkp, MSTPCR0, 21, 0), /* SCIF5 */
+ [MSTP016] = SH_CLK_MSTP32(&clkp, MSTPCR0, 16, 0), /* TMU0 */
+ [MSTP015] = SH_CLK_MSTP32(&clkp, MSTPCR0, 15, 0), /* TMU1 */
+};
+
+static struct clk_lookup lookups[] = {
+ /* MSTP32 clocks */
+ CLKDEV_DEV_ID("sh-eth", &mstp_clks[MSTP114]), /* Ether */
+ CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP026]), /* SCIF0 */
+ CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP025]), /* SCIF1 */
+ CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP024]), /* SCIF2 */
+ CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP023]), /* SCIF3 */
+ CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP022]), /* SCIF4 */
+ CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP021]), /* SCIF6 */
+ CLKDEV_DEV_ID("sh_tmu.0", &mstp_clks[MSTP016]), /* TMU00 */
+ CLKDEV_DEV_ID("sh_tmu.1", &mstp_clks[MSTP015]), /* TMU01 */
+};
+
+void __init r8a7778_clock_init(void)
+{
+ int k, ret = 0;
+
+ for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
+ ret = clk_register(main_clks[k]);
+
+ if (!ret)
+ ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
+
+ clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+ if (!ret)
+ shmobile_clk_init();
+ else
+ panic("failed to setup r8a7778 clocks\n");
+}
diff --git a/arch/arm/mach-shmobile/clock-r8a7779.c b/arch/arm/mach-shmobile/clock-r8a7779.c
index d9edeaf66007..31d5cd4d9787 100644
--- a/arch/arm/mach-shmobile/clock-r8a7779.c
+++ b/arch/arm/mach-shmobile/clock-r8a7779.c
@@ -17,13 +17,36 @@
* 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/bitops.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/sh_clk.h>
#include <linux/clkdev.h>
+#include <mach/clock.h>
#include <mach/common.h>
+/*
+ * MD1 = 1 MD1 = 0
+ * (PLLA = 1500) (PLLA = 1600)
+ * (MHz) (MHz)
+ *------------------------------------------------+--------------------
+ * clkz 1000 (2/3) 800 (1/2)
+ * clkzs 250 (1/6) 200 (1/8)
+ * clki 750 (1/2) 800 (1/2)
+ * clks 250 (1/6) 200 (1/8)
+ * clks1 125 (1/12) 100 (1/16)
+ * clks3 187.5 (1/8) 200 (1/8)
+ * clks4 93.7 (1/16) 100 (1/16)
+ * clkp 62.5 (1/24) 50 (1/32)
+ * clkg 62.5 (1/24) 66.6 (1/24)
+ * clkb, CLKOUT
+ * (MD2 = 0) 62.5 (1/24) 66.6 (1/24)
+ * (MD2 = 1) 41.6 (1/36) 50 (1/32)
+*/
+
+#define MD(nr) BIT(nr)
+
#define FRQMR IOMEM(0xffc80014)
#define MSTPCR0 IOMEM(0xffc80030)
#define MSTPCR1 IOMEM(0xffc80034)
@@ -36,6 +59,9 @@
#define MSTPCR6 IOMEM(0xffc80058)
#define MSTPCR7 IOMEM(0xffc80040)
+#define MODEMR 0xffcc0020
+
+
/* ioremap() through clock mapping mandatory to avoid
* collision with ARM coherent DMA virtual memory range.
*/
@@ -50,44 +76,43 @@ static struct clk_mapping cpg_mapping = {
* from the platform code.
*/
static struct clk plla_clk = {
- .rate = 1500000000,
+ /* .rate will be updated on r8a7779_clock_init() */
.mapping = &cpg_mapping,
};
+/*
+ * clock ratio of these clock will be updated
+ * on r8a7779_clock_init()
+ */
+SH_FIXED_RATIO_CLK_SET(clkz_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clkzs_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clki_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clks_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clks1_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clks3_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clks4_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clkb_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clkout_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clkp_clk, plla_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(clkg_clk, plla_clk, 1, 1);
+
static struct clk *main_clks[] = {
&plla_clk,
-};
-
-static int divisors[] = { 0, 0, 0, 6, 8, 12, 16, 0, 24, 32, 36, 0, 0, 0, 0, 0 };
-
-static struct clk_div_mult_table div4_div_mult_table = {
- .divisors = divisors,
- .nr_divisors = ARRAY_SIZE(divisors),
-};
-
-static struct clk_div4_table div4_table = {
- .div_mult_table = &div4_div_mult_table,
-};
-
-enum { DIV4_S, DIV4_OUT, DIV4_S4, DIV4_S3, DIV4_S1, DIV4_P, DIV4_NR };
-
-static struct clk div4_clks[DIV4_NR] = {
- [DIV4_S] = SH_CLK_DIV4(&plla_clk, FRQMR, 20,
- 0x0018, CLK_ENABLE_ON_INIT),
- [DIV4_OUT] = SH_CLK_DIV4(&plla_clk, FRQMR, 16,
- 0x0700, CLK_ENABLE_ON_INIT),
- [DIV4_S4] = SH_CLK_DIV4(&plla_clk, FRQMR, 12,
- 0x0040, CLK_ENABLE_ON_INIT),
- [DIV4_S3] = SH_CLK_DIV4(&plla_clk, FRQMR, 8,
- 0x0010, CLK_ENABLE_ON_INIT),
- [DIV4_S1] = SH_CLK_DIV4(&plla_clk, FRQMR, 4,
- 0x0060, CLK_ENABLE_ON_INIT),
- [DIV4_P] = SH_CLK_DIV4(&plla_clk, FRQMR, 0,
- 0x0300, CLK_ENABLE_ON_INIT),
+ &clkz_clk,
+ &clkzs_clk,
+ &clki_clk,
+ &clks_clk,
+ &clks1_clk,
+ &clks3_clk,
+ &clks4_clk,
+ &clkb_clk,
+ &clkout_clk,
+ &clkp_clk,
+ &clkg_clk,
};
enum { MSTP323, MSTP322, MSTP321, MSTP320,
- MSTP115,
+ MSTP115, MSTP114,
MSTP103, MSTP101, MSTP100,
MSTP030,
MSTP029, MSTP028, MSTP027, MSTP026, MSTP025, MSTP024, MSTP023, MSTP022, MSTP021,
@@ -96,52 +121,29 @@ enum { MSTP323, MSTP322, MSTP321, MSTP320,
MSTP_NR };
static struct clk mstp_clks[MSTP_NR] = {
- [MSTP323] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 23, 0), /* SDHI0 */
- [MSTP322] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 22, 0), /* SDHI1 */
- [MSTP321] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 21, 0), /* SDHI2 */
- [MSTP320] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 20, 0), /* SDHI3 */
- [MSTP115] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 15, 0), /* SATA */
- [MSTP103] = SH_CLK_MSTP32(&div4_clks[DIV4_S], MSTPCR1, 3, 0), /* DU */
- [MSTP101] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 1, 0), /* USB2 */
- [MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 0, 0), /* USB0/1 */
- [MSTP030] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 30, 0), /* I2C0 */
- [MSTP029] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 29, 0), /* I2C1 */
- [MSTP028] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 28, 0), /* I2C2 */
- [MSTP027] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 27, 0), /* I2C3 */
- [MSTP026] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 26, 0), /* SCIF0 */
- [MSTP025] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 25, 0), /* SCIF1 */
- [MSTP024] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 24, 0), /* SCIF2 */
- [MSTP023] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 23, 0), /* SCIF3 */
- [MSTP022] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 22, 0), /* SCIF4 */
- [MSTP021] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 21, 0), /* SCIF5 */
- [MSTP016] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 16, 0), /* TMU0 */
- [MSTP015] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 15, 0), /* TMU1 */
- [MSTP014] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 14, 0), /* TMU2 */
- [MSTP007] = SH_CLK_MSTP32(&div4_clks[DIV4_S], MSTPCR0, 7, 0), /* HSPI */
-};
-
-static unsigned long mul4_recalc(struct clk *clk)
-{
- return clk->parent->rate * 4;
-}
-
-static struct sh_clk_ops mul4_clk_ops = {
- .recalc = mul4_recalc,
-};
-
-struct clk clkz_clk = {
- .ops = &mul4_clk_ops,
- .parent = &div4_clks[DIV4_S],
-};
-
-struct clk clkzs_clk = {
- /* clks x 4 / 4 = clks */
- .parent = &div4_clks[DIV4_S],
-};
-
-static struct clk *late_main_clks[] = {
- &clkz_clk,
- &clkzs_clk,
+ [MSTP323] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 23, 0), /* SDHI0 */
+ [MSTP322] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 22, 0), /* SDHI1 */
+ [MSTP321] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 21, 0), /* SDHI2 */
+ [MSTP320] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 20, 0), /* SDHI3 */
+ [MSTP115] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 15, 0), /* SATA */
+ [MSTP114] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 14, 0), /* Ether */
+ [MSTP103] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 3, 0), /* DU */
+ [MSTP101] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 1, 0), /* USB2 */
+ [MSTP100] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 0, 0), /* USB0/1 */
+ [MSTP030] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 30, 0), /* I2C0 */
+ [MSTP029] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 29, 0), /* I2C1 */
+ [MSTP028] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 28, 0), /* I2C2 */
+ [MSTP027] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 27, 0), /* I2C3 */
+ [MSTP026] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 26, 0), /* SCIF0 */
+ [MSTP025] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 25, 0), /* SCIF1 */
+ [MSTP024] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 24, 0), /* SCIF2 */
+ [MSTP023] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 23, 0), /* SCIF3 */
+ [MSTP022] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 22, 0), /* SCIF4 */
+ [MSTP021] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 21, 0), /* SCIF5 */
+ [MSTP016] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 16, 0), /* TMU0 */
+ [MSTP015] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 15, 0), /* TMU1 */
+ [MSTP014] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 14, 0), /* TMU2 */
+ [MSTP007] = SH_CLK_MSTP32(&clks_clk, MSTPCR0, 7, 0), /* HSPI */
};
static struct clk_lookup lookups[] = {
@@ -151,16 +153,17 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("clkzs_clk", &clkzs_clk),
/* DIV4 clocks */
- CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_S]),
- CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_OUT]),
- CLKDEV_CON_ID("shyway4_clk", &div4_clks[DIV4_S4]),
- CLKDEV_CON_ID("shyway3_clk", &div4_clks[DIV4_S3]),
- CLKDEV_CON_ID("shyway1_clk", &div4_clks[DIV4_S1]),
- CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]),
+ CLKDEV_CON_ID("shyway_clk", &clks_clk),
+ CLKDEV_CON_ID("bus_clk", &clkout_clk),
+ CLKDEV_CON_ID("shyway4_clk", &clks4_clk),
+ CLKDEV_CON_ID("shyway3_clk", &clks3_clk),
+ CLKDEV_CON_ID("shyway1_clk", &clks1_clk),
+ CLKDEV_CON_ID("peripheral_clk", &clkp_clk),
/* MSTP32 clocks */
CLKDEV_DEV_ID("sata_rcar", &mstp_clks[MSTP115]), /* SATA */
CLKDEV_DEV_ID("fc600000.sata", &mstp_clks[MSTP115]), /* SATA w/DT */
+ CLKDEV_DEV_ID("sh-eth", &mstp_clks[MSTP114]), /* Ether */
CLKDEV_DEV_ID("ehci-platform.1", &mstp_clks[MSTP101]), /* USB EHCI port2 */
CLKDEV_DEV_ID("ohci-platform.1", &mstp_clks[MSTP101]), /* USB OHCI port2 */
CLKDEV_DEV_ID("ehci-platform.0", &mstp_clks[MSTP100]), /* USB EHCI port0/1 */
@@ -190,20 +193,60 @@ static struct clk_lookup lookups[] = {
void __init r8a7779_clock_init(void)
{
+ void __iomem *modemr = ioremap_nocache(MODEMR, PAGE_SIZE);
+ u32 mode;
int k, ret = 0;
+ BUG_ON(!modemr);
+ mode = ioread32(modemr);
+ iounmap(modemr);
+
+ if (mode & MD(1)) {
+ plla_clk.rate = 1500000000;
+
+ SH_CLK_SET_RATIO(&clkz_clk_ratio, 2, 3);
+ SH_CLK_SET_RATIO(&clkzs_clk_ratio, 1, 6);
+ SH_CLK_SET_RATIO(&clki_clk_ratio, 1, 2);
+ SH_CLK_SET_RATIO(&clks_clk_ratio, 1, 6);
+ SH_CLK_SET_RATIO(&clks1_clk_ratio, 1, 12);
+ SH_CLK_SET_RATIO(&clks3_clk_ratio, 1, 8);
+ SH_CLK_SET_RATIO(&clks4_clk_ratio, 1, 16);
+ SH_CLK_SET_RATIO(&clkp_clk_ratio, 1, 24);
+ SH_CLK_SET_RATIO(&clkg_clk_ratio, 1, 24);
+ if (mode & MD(2)) {
+ SH_CLK_SET_RATIO(&clkb_clk_ratio, 1, 36);
+ SH_CLK_SET_RATIO(&clkout_clk_ratio, 1, 36);
+ } else {
+ SH_CLK_SET_RATIO(&clkb_clk_ratio, 1, 24);
+ SH_CLK_SET_RATIO(&clkout_clk_ratio, 1, 24);
+ }
+ } else {
+ plla_clk.rate = 1600000000;
+
+ SH_CLK_SET_RATIO(&clkz_clk_ratio, 1, 2);
+ SH_CLK_SET_RATIO(&clkzs_clk_ratio, 1, 8);
+ SH_CLK_SET_RATIO(&clki_clk_ratio, 1, 2);
+ SH_CLK_SET_RATIO(&clks_clk_ratio, 1, 8);
+ SH_CLK_SET_RATIO(&clks1_clk_ratio, 1, 16);
+ SH_CLK_SET_RATIO(&clks3_clk_ratio, 1, 8);
+ SH_CLK_SET_RATIO(&clks4_clk_ratio, 1, 16);
+ SH_CLK_SET_RATIO(&clkp_clk_ratio, 1, 32);
+ SH_CLK_SET_RATIO(&clkg_clk_ratio, 1, 24);
+ if (mode & MD(2)) {
+ SH_CLK_SET_RATIO(&clkb_clk_ratio, 1, 32);
+ SH_CLK_SET_RATIO(&clkout_clk_ratio, 1, 32);
+ } else {
+ SH_CLK_SET_RATIO(&clkb_clk_ratio, 1, 24);
+ SH_CLK_SET_RATIO(&clkout_clk_ratio, 1, 24);
+ }
+ }
+
for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
ret = clk_register(main_clks[k]);
if (!ret)
- ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
-
- if (!ret)
ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
- for (k = 0; !ret && (k < ARRAY_SIZE(late_main_clks)); k++)
- ret = clk_register(late_main_clks[k]);
-
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
if (!ret)
diff --git a/arch/arm/mach-shmobile/clock-r8a7790.c b/arch/arm/mach-shmobile/clock-r8a7790.c
new file mode 100644
index 000000000000..bad9bf2e34d6
--- /dev/null
+++ b/arch/arm/mach-shmobile/clock-r8a7790.c
@@ -0,0 +1,93 @@
+/*
+ * r8a7790 clock framework support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/sh_clk.h>
+#include <linux/clkdev.h>
+#include <mach/common.h>
+
+#define CPG_BASE 0xe6150000
+#define CPG_LEN 0x1000
+
+#define SMSTPCR2 0xe6150138
+#define SMSTPCR7 0xe615014c
+
+static struct clk_mapping cpg_mapping = {
+ .phys = CPG_BASE,
+ .len = CPG_LEN,
+};
+
+static struct clk p_clk = {
+ .rate = 65000000, /* shortcut for now */
+ .mapping = &cpg_mapping,
+};
+
+static struct clk mp_clk = {
+ .rate = 52000000, /* shortcut for now */
+ .mapping = &cpg_mapping,
+};
+
+static struct clk *main_clks[] = {
+ &p_clk,
+ &mp_clk,
+};
+
+enum { MSTP721, MSTP720,
+ MSTP216, MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP_NR };
+static struct clk mstp_clks[MSTP_NR] = {
+ [MSTP721] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 21, 0), /* SCIF0 */
+ [MSTP720] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 20, 0), /* SCIF1 */
+ [MSTP216] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 16, 0), /* SCIFB2 */
+ [MSTP207] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 7, 0), /* SCIFB1 */
+ [MSTP206] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 6, 0), /* SCIFB0 */
+ [MSTP204] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 4, 0), /* SCIFA0 */
+ [MSTP203] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 3, 0), /* SCIFA1 */
+ [MSTP202] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 2, 0), /* SCIFA2 */
+};
+
+static struct clk_lookup lookups[] = {
+ CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]),
+ CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]),
+ CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP206]),
+ CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP207]),
+ CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP216]),
+ CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP202]),
+ CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP721]),
+ CLKDEV_DEV_ID("sh-sci.7", &mstp_clks[MSTP720]),
+};
+
+void __init r8a7790_clock_init(void)
+{
+ int k, ret = 0;
+
+ for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
+ ret = clk_register(main_clks[k]);
+
+ if (!ret)
+ ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
+
+ clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+ if (!ret)
+ shmobile_clk_init();
+ else
+ panic("failed to setup r8a7790 clocks\n");
+}
diff --git a/arch/arm/mach-shmobile/clock-sh7372.c b/arch/arm/mach-shmobile/clock-sh7372.c
index 45d21fe317f4..7e105932c09d 100644
--- a/arch/arm/mach-shmobile/clock-sh7372.c
+++ b/arch/arm/mach-shmobile/clock-sh7372.c
@@ -21,6 +21,7 @@
#include <linux/io.h>
#include <linux/sh_clk.h>
#include <linux/clkdev.h>
+#include <mach/clock.h>
#include <mach/common.h>
/* SH7372 registers */
@@ -83,39 +84,12 @@ struct clk sh7372_extal2_clk = {
.rate = 48000000,
};
-/* A fixed divide-by-2 block */
-static unsigned long div2_recalc(struct clk *clk)
-{
- return clk->parent->rate / 2;
-}
-
-static struct sh_clk_ops div2_clk_ops = {
- .recalc = div2_recalc,
-};
+SH_CLK_RATIO(div2, 1, 2);
-/* Divide dv_clki by two */
-struct clk sh7372_dv_clki_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &sh7372_dv_clki_clk,
-};
-
-/* Divide extal1 by two */
-static struct clk extal1_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &sh7372_extal1_clk,
-};
-
-/* Divide extal2 by two */
-static struct clk extal2_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &sh7372_extal2_clk,
-};
-
-/* Divide extal2 by four */
-static struct clk extal2_div4_clk = {
- .ops = &div2_clk_ops,
- .parent = &extal2_div2_clk,
-};
+SH_FIXED_RATIO_CLKg(sh7372_dv_clki_div2_clk, sh7372_dv_clki_clk, div2);
+SH_FIXED_RATIO_CLK(extal1_div2_clk, sh7372_extal1_clk, div2);
+SH_FIXED_RATIO_CLK(extal2_div2_clk, sh7372_extal2_clk, div2);
+SH_FIXED_RATIO_CLK(extal2_div4_clk, extal2_div2_clk, div2);
/* PLLC0 and PLLC1 */
static unsigned long pllc01_recalc(struct clk *clk)
@@ -147,10 +121,7 @@ static struct clk pllc1_clk = {
};
/* Divide PLLC1 by two */
-static struct clk pllc1_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &pllc1_clk,
-};
+SH_FIXED_RATIO_CLK(pllc1_div2_clk, pllc1_clk, div2);
/* PLLC2 */
@@ -342,7 +313,7 @@ static struct clk_div4_table div4_table = {
};
enum { DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1, DIV4_CSIR,
- DIV4_ZTR, DIV4_ZT, DIV4_ZX, DIV4_HP,
+ DIV4_ZX, DIV4_HP,
DIV4_ISPB, DIV4_S, DIV4_ZB, DIV4_ZB3, DIV4_CP,
DIV4_DDRP, DIV4_NR };
@@ -355,8 +326,6 @@ static struct clk div4_clks[DIV4_NR] = {
[DIV4_B] = DIV4(FRQCRA, 8, 0x6fff, CLK_ENABLE_ON_INIT),
[DIV4_M1] = DIV4(FRQCRA, 4, 0x6fff, CLK_ENABLE_ON_INIT),
[DIV4_CSIR] = DIV4(FRQCRA, 0, 0x6fff, 0),
- [DIV4_ZTR] = DIV4(FRQCRB, 20, 0x6fff, 0),
- [DIV4_ZT] = DIV4(FRQCRB, 16, 0x6fff, 0),
[DIV4_ZX] = DIV4(FRQCRB, 12, 0x6fff, 0),
[DIV4_HP] = DIV4(FRQCRB, 4, 0x6fff, 0),
[DIV4_ISPB] = DIV4(FRQCRC, 20, 0x6fff, 0),
@@ -516,8 +485,6 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("b_clk", &div4_clks[DIV4_B]),
CLKDEV_CON_ID("m1_clk", &div4_clks[DIV4_M1]),
CLKDEV_CON_ID("csir_clk", &div4_clks[DIV4_CSIR]),
- CLKDEV_CON_ID("ztr_clk", &div4_clks[DIV4_ZTR]),
- CLKDEV_CON_ID("zt_clk", &div4_clks[DIV4_ZT]),
CLKDEV_CON_ID("zx_clk", &div4_clks[DIV4_ZX]),
CLKDEV_CON_ID("hp_clk", &div4_clks[DIV4_HP]),
CLKDEV_CON_ID("ispb_clk", &div4_clks[DIV4_ISPB]),
@@ -654,5 +621,4 @@ void __init sh7372_clock_init(void)
shmobile_clk_init();
else
panic("failed to setup sh7372 clocks\n");
-
}
diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c
index 71843dd39e16..784fbaa4cc55 100644
--- a/arch/arm/mach-shmobile/clock-sh73a0.c
+++ b/arch/arm/mach-shmobile/clock-sh73a0.c
@@ -21,6 +21,8 @@
#include <linux/io.h>
#include <linux/sh_clk.h>
#include <linux/clkdev.h>
+#include <asm/processor.h>
+#include <mach/clock.h>
#include <mach/common.h>
#define FRQCRA IOMEM(0xe6150000)
@@ -82,61 +84,16 @@ struct clk sh73a0_extal2_clk = {
.rate = 48000000,
};
-/* A fixed divide-by-2 block */
-static unsigned long div2_recalc(struct clk *clk)
-{
- return clk->parent->rate / 2;
-}
-
-static struct sh_clk_ops div2_clk_ops = {
- .recalc = div2_recalc,
-};
-
-static unsigned long div7_recalc(struct clk *clk)
-{
- return clk->parent->rate / 7;
-}
-
-static struct sh_clk_ops div7_clk_ops = {
- .recalc = div7_recalc,
-};
-
-static unsigned long div13_recalc(struct clk *clk)
-{
- return clk->parent->rate / 13;
-}
-
-static struct sh_clk_ops div13_clk_ops = {
- .recalc = div13_recalc,
-};
-
-/* Divide extal1 by two */
-static struct clk extal1_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &sh73a0_extal1_clk,
-};
-
-/* Divide extal2 by two */
-static struct clk extal2_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &sh73a0_extal2_clk,
-};
-
static struct sh_clk_ops main_clk_ops = {
.recalc = followparent_recalc,
};
/* Main clock */
static struct clk main_clk = {
+ /* .parent wll be set on sh73a0_clock_init() */
.ops = &main_clk_ops,
};
-/* Divide Main clock by two */
-static struct clk main_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &main_clk,
-};
-
/* PLL0, PLL1, PLL2, PLL3 */
static unsigned long pll_recalc(struct clk *clk)
{
@@ -192,21 +149,17 @@ static struct clk pll3_clk = {
.enable_bit = 3,
};
-/* Divide PLL */
-static struct clk pll1_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &pll1_clk,
-};
-
-static struct clk pll1_div7_clk = {
- .ops = &div7_clk_ops,
- .parent = &pll1_clk,
-};
+/* A fixed divide block */
+SH_CLK_RATIO(div2, 1, 2);
+SH_CLK_RATIO(div7, 1, 7);
+SH_CLK_RATIO(div13, 1, 13);
-static struct clk pll1_div13_clk = {
- .ops = &div13_clk_ops,
- .parent = &pll1_clk,
-};
+SH_FIXED_RATIO_CLK(extal1_div2_clk, sh73a0_extal1_clk, div2);
+SH_FIXED_RATIO_CLK(extal2_div2_clk, sh73a0_extal2_clk, div2);
+SH_FIXED_RATIO_CLK(main_div2_clk, main_clk, div2);
+SH_FIXED_RATIO_CLK(pll1_div2_clk, pll1_clk, div2);
+SH_FIXED_RATIO_CLK(pll1_div7_clk, pll1_clk, div7);
+SH_FIXED_RATIO_CLK(pll1_div13_clk, pll1_clk, div13);
/* External input clock */
struct clk sh73a0_extcki_clk = {
@@ -234,14 +187,24 @@ static struct clk *main_clks[] = {
&sh73a0_extalr_clk,
};
-static void div4_kick(struct clk *clk)
+static int frqcr_kick(void)
{
- unsigned long value;
+ int i;
+
+ /* set KICK bit in FRQCRB to update hardware setting, check success */
+ __raw_writel(__raw_readl(FRQCRB) | (1 << 31), FRQCRB);
+ for (i = 1000; i; i--)
+ if (__raw_readl(FRQCRB) & (1 << 31))
+ cpu_relax();
+ else
+ return i;
- /* set KICK bit in FRQCRB to update hardware setting */
- value = __raw_readl(FRQCRB);
- value |= (1 << 31);
- __raw_writel(value, FRQCRB);
+ return -ETIMEDOUT;
+}
+
+static void div4_kick(struct clk *clk)
+{
+ frqcr_kick();
}
static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18,
@@ -258,7 +221,7 @@ static struct clk_div4_table div4_table = {
};
enum { DIV4_I, DIV4_ZG, DIV4_M3, DIV4_B, DIV4_M1, DIV4_M2,
- DIV4_Z, DIV4_ZTR, DIV4_ZT, DIV4_ZX, DIV4_HP, DIV4_NR };
+ DIV4_Z, DIV4_ZX, DIV4_HP, DIV4_NR };
#define DIV4(_reg, _bit, _mask, _flags) \
SH_CLK_DIV4(&pll1_clk, _reg, _bit, _mask, _flags)
@@ -271,12 +234,24 @@ static struct clk div4_clks[DIV4_NR] = {
[DIV4_M1] = DIV4(FRQCRA, 4, 0x1dff, 0),
[DIV4_M2] = DIV4(FRQCRA, 0, 0x1dff, 0),
[DIV4_Z] = SH_CLK_DIV4(&pll0_clk, FRQCRB, 24, 0x97f, 0),
- [DIV4_ZTR] = DIV4(FRQCRB, 20, 0xdff, 0),
- [DIV4_ZT] = DIV4(FRQCRB, 16, 0xdff, 0),
[DIV4_ZX] = DIV4(FRQCRB, 12, 0xdff, 0),
[DIV4_HP] = DIV4(FRQCRB, 4, 0xdff, 0),
};
+static unsigned long twd_recalc(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) / 4;
+}
+
+static struct sh_clk_ops twd_clk_ops = {
+ .recalc = twd_recalc,
+};
+
+static struct clk twd_clk = {
+ .parent = &div4_clks[DIV4_Z],
+ .ops = &twd_clk_ops,
+};
+
enum { DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_ZB1,
DIV6_FLCTL, DIV6_SDHI0, DIV6_SDHI1, DIV6_SDHI2,
DIV6_FSIA, DIV6_FSIB, DIV6_SUB,
@@ -471,6 +446,7 @@ static struct clk dsi1phy_clk = {
static struct clk *late_main_clks[] = {
&dsi0phy_clk,
&dsi1phy_clk,
+ &twd_clk,
};
enum { MSTP001,
@@ -535,6 +511,7 @@ static struct clk mstp_clks[MSTP_NR] = {
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("r_clk", &r_clk),
+ CLKDEV_DEV_ID("smp_twd", &twd_clk), /* smp_twd */
/* DIV6 clocks */
CLKDEV_CON_ID("vck1_clk", &div6_clks[DIV6_VCK1]),
diff --git a/arch/arm/mach-shmobile/clock.c b/arch/arm/mach-shmobile/clock.c
index e816ca9bd213..ad7df629d995 100644
--- a/arch/arm/mach-shmobile/clock.c
+++ b/arch/arm/mach-shmobile/clock.c
@@ -23,6 +23,19 @@
#include <linux/init.h>
#include <linux/sh_clk.h>
#include <linux/export.h>
+#include <mach/clock.h>
+#include <mach/common.h>
+
+unsigned long shmobile_fixed_ratio_clk_recalc(struct clk *clk)
+{
+ struct clk_ratio *p = clk->priv;
+
+ return clk->parent->rate / p->div * p->mul;
+};
+
+struct sh_clk_ops shmobile_fixed_ratio_clk_ops = {
+ .recalc = shmobile_fixed_ratio_clk_recalc,
+};
int __init shmobile_clk_init(void)
{
diff --git a/arch/arm/mach-shmobile/include/mach/clock.h b/arch/arm/mach-shmobile/include/mach/clock.h
new file mode 100644
index 000000000000..76ac61292e48
--- /dev/null
+++ b/arch/arm/mach-shmobile/include/mach/clock.h
@@ -0,0 +1,39 @@
+#ifndef CLOCK_H
+#define CLOCK_H
+
+unsigned long shmobile_fixed_ratio_clk_recalc(struct clk *clk);
+extern struct sh_clk_ops shmobile_fixed_ratio_clk_ops;
+
+/* clock ratio */
+struct clk_ratio {
+ int mul;
+ int div;
+};
+
+#define SH_CLK_RATIO(name, m, d) \
+static struct clk_ratio name ##_ratio = { \
+ .mul = m, \
+ .div = d, \
+}
+
+#define SH_FIXED_RATIO_CLKg(name, p, r) \
+struct clk name = { \
+ .parent = &p, \
+ .ops = &shmobile_fixed_ratio_clk_ops,\
+ .priv = &r ## _ratio, \
+}
+
+#define SH_FIXED_RATIO_CLK(name, p, r) \
+static SH_FIXED_RATIO_CLKg(name, p, r);
+
+#define SH_FIXED_RATIO_CLK_SET(name, p, m, d) \
+ SH_CLK_RATIO(name, m, d); \
+ SH_FIXED_RATIO_CLK(name, p, name);
+
+#define SH_CLK_SET_RATIO(p, m, d) \
+{ \
+ (p)->mul = m; \
+ (p)->div = d; \
+}
+
+#endif
diff --git a/arch/arm/mach-shmobile/include/mach/common.h b/arch/arm/mach-shmobile/include/mach/common.h
index 1fef737a4c1a..4634a5d4b63f 100644
--- a/arch/arm/mach-shmobile/include/mach/common.h
+++ b/arch/arm/mach-shmobile/include/mach/common.h
@@ -16,60 +16,6 @@ extern struct platform_suspend_ops shmobile_suspend_ops;
struct cpuidle_driver;
extern void shmobile_cpuidle_set_driver(struct cpuidle_driver *drv);
-extern void sh7372_init_irq(void);
-extern void sh7372_map_io(void);
-extern void sh7372_earlytimer_init(void);
-extern void sh7372_add_early_devices(void);
-extern void sh7372_add_standard_devices(void);
-extern void sh7372_add_early_devices_dt(void);
-extern void sh7372_add_standard_devices_dt(void);
-extern void sh7372_clock_init(void);
-extern void sh7372_pinmux_init(void);
-extern void sh7372_pm_init(void);
-extern void sh7372_resume_core_standby_sysc(void);
-extern int sh7372_do_idle_sysc(unsigned long sleep_mode);
-extern struct clk sh7372_extal1_clk;
-extern struct clk sh7372_extal2_clk;
-
-extern void sh73a0_init_delay(void);
-extern void sh73a0_init_irq(void);
-extern void sh73a0_init_irq_dt(void);
-extern void sh73a0_map_io(void);
-extern void sh73a0_earlytimer_init(void);
-extern void sh73a0_add_early_devices(void);
-extern void sh73a0_add_standard_devices(void);
-extern void sh73a0_add_standard_devices_dt(void);
-extern void sh73a0_clock_init(void);
-extern void sh73a0_pinmux_init(void);
-extern void sh73a0_pm_init(void);
-extern struct clk sh73a0_extal1_clk;
-extern struct clk sh73a0_extal2_clk;
-extern struct clk sh73a0_extcki_clk;
-extern struct clk sh73a0_extalr_clk;
-
-extern void r8a7740_meram_workaround(void);
-extern void r8a7740_init_irq(void);
-extern void r8a7740_map_io(void);
-extern void r8a7740_add_early_devices(void);
-extern void r8a7740_add_standard_devices(void);
-extern void r8a7740_clock_init(u8 md_ck);
-extern void r8a7740_pinmux_init(void);
-extern void r8a7740_pm_init(void);
-
-extern void r8a7779_init_delay(void);
-extern void r8a7779_init_irq(void);
-extern void r8a7779_init_irq_extpin(int irlm);
-extern void r8a7779_init_irq_dt(void);
-extern void r8a7779_map_io(void);
-extern void r8a7779_earlytimer_init(void);
-extern void r8a7779_add_early_devices(void);
-extern void r8a7779_add_standard_devices(void);
-extern void r8a7779_add_standard_devices_dt(void);
-extern void r8a7779_clock_init(void);
-extern void r8a7779_pinmux_init(void);
-extern void r8a7779_pm_init(void);
-extern void r8a7779_register_twd(void);
-
#ifdef CONFIG_SUSPEND
int shmobile_suspend_init(void);
#else
diff --git a/arch/arm/mach-shmobile/include/mach/r8a73a4.h b/arch/arm/mach-shmobile/include/mach/r8a73a4.h
new file mode 100644
index 000000000000..f043103e32c9
--- /dev/null
+++ b/arch/arm/mach-shmobile/include/mach/r8a73a4.h
@@ -0,0 +1,8 @@
+#ifndef __ASM_R8A73A4_H__
+#define __ASM_R8A73A4_H__
+
+void r8a73a4_add_standard_devices(void);
+void r8a73a4_clock_init(void);
+void r8a73a4_pinmux_init(void);
+
+#endif /* __ASM_R8A73A4_H__ */
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7740.h b/arch/arm/mach-shmobile/include/mach/r8a7740.h
index c2583610ad36..abdc4d4efa28 100644
--- a/arch/arm/mach-shmobile/include/mach/r8a7740.h
+++ b/arch/arm/mach-shmobile/include/mach/r8a7740.h
@@ -532,6 +532,15 @@ enum {
SHDMA_SLAVE_USBHS_RX,
};
+extern void r8a7740_meram_workaround(void);
+extern void r8a7740_init_irq(void);
+extern void r8a7740_map_io(void);
+extern void r8a7740_add_early_devices(void);
+extern void r8a7740_add_standard_devices(void);
+extern void r8a7740_clock_init(u8 md_ck);
+extern void r8a7740_pinmux_init(void);
+extern void r8a7740_pm_init(void);
+
#ifdef CONFIG_PM
extern void __init r8a7740_init_pm_domains(void);
#else
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7778.h b/arch/arm/mach-shmobile/include/mach/r8a7778.h
new file mode 100644
index 000000000000..951149e6bcca
--- /dev/null
+++ b/arch/arm/mach-shmobile/include/mach/r8a7778.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __ASM_R8A7778_H__
+#define __ASM_R8A7778_H__
+
+#include <linux/sh_eth.h>
+
+extern void r8a7778_add_standard_devices(void);
+extern void r8a7778_add_standard_devices_dt(void);
+extern void r8a7778_add_ether_device(struct sh_eth_plat_data *pdata);
+extern void r8a7778_init_delay(void);
+extern void r8a7778_init_irq(void);
+extern void r8a7778_init_irq_dt(void);
+extern void r8a7778_clock_init(void);
+extern void r8a7778_init_irq_extpin(int irlm);
+
+#endif /* __ASM_R8A7778_H__ */
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7779.h b/arch/arm/mach-shmobile/include/mach/r8a7779.h
index 8ea0ad18cdff..188b295938a5 100644
--- a/arch/arm/mach-shmobile/include/mach/r8a7779.h
+++ b/arch/arm/mach-shmobile/include/mach/r8a7779.h
@@ -3,323 +3,7 @@
#include <linux/sh_clk.h>
#include <linux/pm_domain.h>
-
-/* Pin Function Controller:
- * GPIO_FN_xx - GPIO used to select pin function
- * GPIO_GP_x_x - GPIO mapped to real I/O pin on CPU
- */
-enum {
- GPIO_GP_0_0, GPIO_GP_0_1, GPIO_GP_0_2, GPIO_GP_0_3,
- GPIO_GP_0_4, GPIO_GP_0_5, GPIO_GP_0_6, GPIO_GP_0_7,
- GPIO_GP_0_8, GPIO_GP_0_9, GPIO_GP_0_10, GPIO_GP_0_11,
- GPIO_GP_0_12, GPIO_GP_0_13, GPIO_GP_0_14, GPIO_GP_0_15,
- GPIO_GP_0_16, GPIO_GP_0_17, GPIO_GP_0_18, GPIO_GP_0_19,
- GPIO_GP_0_20, GPIO_GP_0_21, GPIO_GP_0_22, GPIO_GP_0_23,
- GPIO_GP_0_24, GPIO_GP_0_25, GPIO_GP_0_26, GPIO_GP_0_27,
- GPIO_GP_0_28, GPIO_GP_0_29, GPIO_GP_0_30, GPIO_GP_0_31,
-
- GPIO_GP_1_0, GPIO_GP_1_1, GPIO_GP_1_2, GPIO_GP_1_3,
- GPIO_GP_1_4, GPIO_GP_1_5, GPIO_GP_1_6, GPIO_GP_1_7,
- GPIO_GP_1_8, GPIO_GP_1_9, GPIO_GP_1_10, GPIO_GP_1_11,
- GPIO_GP_1_12, GPIO_GP_1_13, GPIO_GP_1_14, GPIO_GP_1_15,
- GPIO_GP_1_16, GPIO_GP_1_17, GPIO_GP_1_18, GPIO_GP_1_19,
- GPIO_GP_1_20, GPIO_GP_1_21, GPIO_GP_1_22, GPIO_GP_1_23,
- GPIO_GP_1_24, GPIO_GP_1_25, GPIO_GP_1_26, GPIO_GP_1_27,
- GPIO_GP_1_28, GPIO_GP_1_29, GPIO_GP_1_30, GPIO_GP_1_31,
-
- GPIO_GP_2_0, GPIO_GP_2_1, GPIO_GP_2_2, GPIO_GP_2_3,
- GPIO_GP_2_4, GPIO_GP_2_5, GPIO_GP_2_6, GPIO_GP_2_7,
- GPIO_GP_2_8, GPIO_GP_2_9, GPIO_GP_2_10, GPIO_GP_2_11,
- GPIO_GP_2_12, GPIO_GP_2_13, GPIO_GP_2_14, GPIO_GP_2_15,
- GPIO_GP_2_16, GPIO_GP_2_17, GPIO_GP_2_18, GPIO_GP_2_19,
- GPIO_GP_2_20, GPIO_GP_2_21, GPIO_GP_2_22, GPIO_GP_2_23,
- GPIO_GP_2_24, GPIO_GP_2_25, GPIO_GP_2_26, GPIO_GP_2_27,
- GPIO_GP_2_28, GPIO_GP_2_29, GPIO_GP_2_30, GPIO_GP_2_31,
-
- GPIO_GP_3_0, GPIO_GP_3_1, GPIO_GP_3_2, GPIO_GP_3_3,
- GPIO_GP_3_4, GPIO_GP_3_5, GPIO_GP_3_6, GPIO_GP_3_7,
- GPIO_GP_3_8, GPIO_GP_3_9, GPIO_GP_3_10, GPIO_GP_3_11,
- GPIO_GP_3_12, GPIO_GP_3_13, GPIO_GP_3_14, GPIO_GP_3_15,
- GPIO_GP_3_16, GPIO_GP_3_17, GPIO_GP_3_18, GPIO_GP_3_19,
- GPIO_GP_3_20, GPIO_GP_3_21, GPIO_GP_3_22, GPIO_GP_3_23,
- GPIO_GP_3_24, GPIO_GP_3_25, GPIO_GP_3_26, GPIO_GP_3_27,
- GPIO_GP_3_28, GPIO_GP_3_29, GPIO_GP_3_30, GPIO_GP_3_31,
-
- GPIO_GP_4_0, GPIO_GP_4_1, GPIO_GP_4_2, GPIO_GP_4_3,
- GPIO_GP_4_4, GPIO_GP_4_5, GPIO_GP_4_6, GPIO_GP_4_7,
- GPIO_GP_4_8, GPIO_GP_4_9, GPIO_GP_4_10, GPIO_GP_4_11,
- GPIO_GP_4_12, GPIO_GP_4_13, GPIO_GP_4_14, GPIO_GP_4_15,
- GPIO_GP_4_16, GPIO_GP_4_17, GPIO_GP_4_18, GPIO_GP_4_19,
- GPIO_GP_4_20, GPIO_GP_4_21, GPIO_GP_4_22, GPIO_GP_4_23,
- GPIO_GP_4_24, GPIO_GP_4_25, GPIO_GP_4_26, GPIO_GP_4_27,
- GPIO_GP_4_28, GPIO_GP_4_29, GPIO_GP_4_30, GPIO_GP_4_31,
-
- GPIO_GP_5_0, GPIO_GP_5_1, GPIO_GP_5_2, GPIO_GP_5_3,
- GPIO_GP_5_4, GPIO_GP_5_5, GPIO_GP_5_6, GPIO_GP_5_7,
- GPIO_GP_5_8, GPIO_GP_5_9, GPIO_GP_5_10, GPIO_GP_5_11,
- GPIO_GP_5_12, GPIO_GP_5_13, GPIO_GP_5_14, GPIO_GP_5_15,
- GPIO_GP_5_16, GPIO_GP_5_17, GPIO_GP_5_18, GPIO_GP_5_19,
- GPIO_GP_5_20, GPIO_GP_5_21, GPIO_GP_5_22, GPIO_GP_5_23,
- GPIO_GP_5_24, GPIO_GP_5_25, GPIO_GP_5_26, GPIO_GP_5_27,
- GPIO_GP_5_28, GPIO_GP_5_29, GPIO_GP_5_30, GPIO_GP_5_31,
-
- GPIO_GP_6_0, GPIO_GP_6_1, GPIO_GP_6_2, GPIO_GP_6_3,
- GPIO_GP_6_4, GPIO_GP_6_5, GPIO_GP_6_6, GPIO_GP_6_7,
- GPIO_GP_6_8,
-
- GPIO_FN_AVS1, GPIO_FN_AVS2, GPIO_FN_A17, GPIO_FN_A18,
- GPIO_FN_A19,
-
- /* IPSR0 */
- GPIO_FN_PWM1, GPIO_FN_PWMFSW0,
- GPIO_FN_SCIF_CLK, GPIO_FN_TCLK0_C, GPIO_FN_BS,
- GPIO_FN_FD2, GPIO_FN_ATADIR0, GPIO_FN_SDSELF,
- GPIO_FN_HCTS1, GPIO_FN_A0,
- GPIO_FN_FD3, GPIO_FN_A20,
- GPIO_FN_A21,
- GPIO_FN_A22, GPIO_FN_VI1_R0,
- GPIO_FN_A23, GPIO_FN_FCLE, GPIO_FN_VI1_R1,
- GPIO_FN_A24, GPIO_FN_FD4,
- GPIO_FN_VI1_R2, GPIO_FN_SSI_WS78_B, GPIO_FN_A25,
- GPIO_FN_FD5,
- GPIO_FN_VI1_R3, GPIO_FN_SSI_SDATA7_B,
- GPIO_FN_CLKOUT, GPIO_FN_PWM0_B,
- GPIO_FN_SDSELF_B, GPIO_FN_RD_WR, GPIO_FN_FWE, GPIO_FN_ATAG0,
- GPIO_FN_VI1_R7, GPIO_FN_HRTS1,
-
- /* IPSR1 */
- GPIO_FN_FD6, GPIO_FN_FD7,
- GPIO_FN_FALE,
- GPIO_FN_ATACS00,
- GPIO_FN_FRE, GPIO_FN_ATACS10, GPIO_FN_VI1_R4,
- GPIO_FN_HSCK1, GPIO_FN_SSI_SDATA8_B,
- GPIO_FN_SSI_SDATA9,
- GPIO_FN_FD0, GPIO_FN_ATARD0, GPIO_FN_VI1_R5,
- GPIO_FN_HTX1, GPIO_FN_SSI_SCK9,
- GPIO_FN_FD1,
- GPIO_FN_ATAWR0, GPIO_FN_VI1_R6, GPIO_FN_HRX1,
- GPIO_FN_SSI_WS9, GPIO_FN_MLB_CLK, GPIO_FN_PWM2,
- GPIO_FN_MLB_SIG, GPIO_FN_PWM3,
- GPIO_FN_MLB_DAT, GPIO_FN_PWM4, GPIO_FN_HTX0,
- GPIO_FN_SDATA, GPIO_FN_SUB_TCK,
- GPIO_FN_CC5_STATE2, GPIO_FN_CC5_STATE10, GPIO_FN_CC5_STATE18,
- GPIO_FN_CC5_STATE26, GPIO_FN_CC5_STATE34,
-
- /* IPSR2 */
- GPIO_FN_HRX0, GPIO_FN_SCKZ,
- GPIO_FN_SUB_TDI, GPIO_FN_CC5_STATE3, GPIO_FN_CC5_STATE11,
- GPIO_FN_CC5_STATE19, GPIO_FN_CC5_STATE27, GPIO_FN_CC5_STATE35,
- GPIO_FN_HSCK0, GPIO_FN_MTS, GPIO_FN_PWM5,
- GPIO_FN_SSI_SDATA9_B, GPIO_FN_SUB_TDO,
- GPIO_FN_CC5_STATE0, GPIO_FN_CC5_STATE8, GPIO_FN_CC5_STATE16,
- GPIO_FN_CC5_STATE24, GPIO_FN_CC5_STATE32, GPIO_FN_HCTS0,
- GPIO_FN_STM, GPIO_FN_PWM0_D, GPIO_FN_SCIF_CLK_C,
- GPIO_FN_SUB_TRST, GPIO_FN_TCLK1_B, GPIO_FN_CC5_OSCOUT, GPIO_FN_HRTS0,
- GPIO_FN_MDATA, GPIO_FN_SUB_TMS,
- GPIO_FN_CC5_STATE1, GPIO_FN_CC5_STATE9, GPIO_FN_CC5_STATE17,
- GPIO_FN_CC5_STATE25, GPIO_FN_CC5_STATE33,
- GPIO_FN_LCDOUT0, GPIO_FN_DREQ0, GPIO_FN_GPS_CLK_B, GPIO_FN_AUDATA0,
- GPIO_FN_LCDOUT1, GPIO_FN_DACK0,
- GPIO_FN_DRACK0, GPIO_FN_GPS_SIGN_B, GPIO_FN_AUDATA1,
- GPIO_FN_LCDOUT2, GPIO_FN_LCDOUT3,
- GPIO_FN_LCDOUT4, GPIO_FN_LCDOUT5,
- GPIO_FN_LCDOUT6, GPIO_FN_LCDOUT7,
- GPIO_FN_LCDOUT8, GPIO_FN_DREQ1, GPIO_FN_SCL2,
- GPIO_FN_AUDATA2,
-
- /* IPSR3 */
- GPIO_FN_LCDOUT9, GPIO_FN_DACK1, GPIO_FN_SDA2,
- GPIO_FN_AUDATA3, GPIO_FN_LCDOUT10,
- GPIO_FN_LCDOUT11, GPIO_FN_LCDOUT12,
- GPIO_FN_LCDOUT13, GPIO_FN_LCDOUT14,
- GPIO_FN_LCDOUT15, GPIO_FN_LCDOUT16, GPIO_FN_EX_WAIT1,
- GPIO_FN_SCL1, GPIO_FN_TCLK1, GPIO_FN_AUDATA4,
- GPIO_FN_LCDOUT17, GPIO_FN_EX_WAIT2, GPIO_FN_SDA1, GPIO_FN_GPS_MAG_B,
- GPIO_FN_AUDATA5, GPIO_FN_LCDOUT18,
- GPIO_FN_LCDOUT19, GPIO_FN_LCDOUT20,
- GPIO_FN_LCDOUT21, GPIO_FN_LCDOUT22,
- GPIO_FN_LCDOUT23,
- GPIO_FN_QSTVA_QVS, GPIO_FN_SCL3_B,
- GPIO_FN_QCLK,
- GPIO_FN_QSTVB_QVE, GPIO_FN_SDA3_B,
- GPIO_FN_SDA2_C, GPIO_FN_DACK0_B, GPIO_FN_DRACK0_B,
- GPIO_FN_QSTH_QHS,
- GPIO_FN_QSTB_QHE,
- GPIO_FN_QCPV_QDE,
- GPIO_FN_CAN1_TX, GPIO_FN_SCL2_C, GPIO_FN_REMOCON,
-
- /* IPSR4 */
- GPIO_FN_QPOLA, GPIO_FN_CAN_CLK_C,
- GPIO_FN_QPOLB, GPIO_FN_CAN1_RX,
- GPIO_FN_DREQ0_B, GPIO_FN_SSI_SCK78_B,
- GPIO_FN_VI2_DATA0_VI2_B0, GPIO_FN_PWM6,
- GPIO_FN_AUDCK, GPIO_FN_PWMFSW0_B,
- GPIO_FN_VI2_DATA1_VI2_B1, GPIO_FN_PWM0,
- GPIO_FN_AUDSYNC,
- GPIO_FN_VI2_G0,
- GPIO_FN_VI2_G1, GPIO_FN_VI2_G2,
- GPIO_FN_VI2_G3, GPIO_FN_VI2_G4,
- GPIO_FN_VI2_G5, GPIO_FN_VI2_DATA2_VI2_B2,
- GPIO_FN_SCL1_B, GPIO_FN_AUDATA6,
- GPIO_FN_VI2_DATA3_VI2_B3,
- GPIO_FN_SDA1_B, GPIO_FN_AUDATA7,
- GPIO_FN_VI2_G6,
- GPIO_FN_VI2_G7, GPIO_FN_VI2_R0,
- GPIO_FN_VI2_R1, GPIO_FN_VI2_R2,
- GPIO_FN_VI2_R3, GPIO_FN_VI2_DATA4_VI2_B4,
- GPIO_FN_SCL2_B,
-
- /* IPSR5 */
- GPIO_FN_VI2_DATA5_VI2_B5, GPIO_FN_SDA2_B,
- GPIO_FN_VI2_R4, GPIO_FN_VI2_R5,
- GPIO_FN_VI2_R6, GPIO_FN_VI2_R7,
- GPIO_FN_SCL2_D, GPIO_FN_SDA2_D,
- GPIO_FN_VI2_CLKENB,
- GPIO_FN_SCL1_D, GPIO_FN_VI2_FIELD,
- GPIO_FN_SDA1_D, GPIO_FN_VI2_HSYNC,
- GPIO_FN_VI3_HSYNC, GPIO_FN_VI2_VSYNC,
- GPIO_FN_VI3_VSYNC,
- GPIO_FN_VI2_CLK,
- GPIO_FN_VI1_CLKENB, GPIO_FN_VI3_CLKENB,
- GPIO_FN_AUDIO_CLKC, GPIO_FN_SPEEDIN,
- GPIO_FN_GPS_SIGN_D, GPIO_FN_VI2_DATA6_VI2_B6,
- GPIO_FN_TCLK0, GPIO_FN_QSTVA_B_QVS_B,
- GPIO_FN_AUDIO_CLKOUT_B, GPIO_FN_GPS_MAG_D,
- GPIO_FN_VI2_DATA7_VI2_B7,
- GPIO_FN_VI1_FIELD, GPIO_FN_VI3_FIELD,
- GPIO_FN_AUDIO_CLKOUT, GPIO_FN_GPS_CLK_C,
- GPIO_FN_GPS_CLK_D, GPIO_FN_AUDIO_CLKA, GPIO_FN_CAN_TXCLK,
- GPIO_FN_AUDIO_CLKB, GPIO_FN_CAN_DEBUGOUT0,
- GPIO_FN_MOUT0,
-
- /* IPSR6 */
- GPIO_FN_SSI_SCK0129, GPIO_FN_CAN_DEBUGOUT1, GPIO_FN_MOUT1,
- GPIO_FN_SSI_WS0129, GPIO_FN_CAN_DEBUGOUT2, GPIO_FN_MOUT2,
- GPIO_FN_SSI_SDATA0, GPIO_FN_CAN_DEBUGOUT3, GPIO_FN_MOUT5,
- GPIO_FN_SSI_SDATA1, GPIO_FN_CAN_DEBUGOUT4, GPIO_FN_MOUT6,
- GPIO_FN_SSI_SDATA2, GPIO_FN_CAN_DEBUGOUT5, GPIO_FN_SSI_SCK34,
- GPIO_FN_CAN_DEBUGOUT6, GPIO_FN_CAN0_TX_B, GPIO_FN_IERX,
- GPIO_FN_SSI_SCK9_C, GPIO_FN_SSI_WS34, GPIO_FN_CAN_DEBUGOUT7,
- GPIO_FN_CAN0_RX_B, GPIO_FN_IETX, GPIO_FN_SSI_WS9_C,
- GPIO_FN_SSI_SDATA3, GPIO_FN_PWM0_C, GPIO_FN_CAN_DEBUGOUT8,
- GPIO_FN_CAN_CLK_B, GPIO_FN_IECLK, GPIO_FN_SCIF_CLK_B, GPIO_FN_TCLK0_B,
- GPIO_FN_SSI_SDATA4, GPIO_FN_CAN_DEBUGOUT9, GPIO_FN_SSI_SDATA9_C,
- GPIO_FN_SSI_SCK5, GPIO_FN_ADICLK, GPIO_FN_CAN_DEBUGOUT10,
- GPIO_FN_TCLK0_D, GPIO_FN_SSI_WS5, GPIO_FN_ADICS_SAMP,
- GPIO_FN_CAN_DEBUGOUT11, GPIO_FN_SSI_SDATA5,
- GPIO_FN_ADIDATA, GPIO_FN_CAN_DEBUGOUT12,
- GPIO_FN_SSI_SCK6, GPIO_FN_ADICHS0, GPIO_FN_CAN0_TX, GPIO_FN_IERX_B,
-
- /* IPSR7 */
- GPIO_FN_SSI_WS6, GPIO_FN_ADICHS1, GPIO_FN_CAN0_RX, GPIO_FN_IETX_B,
- GPIO_FN_SSI_SDATA6, GPIO_FN_ADICHS2, GPIO_FN_CAN_CLK, GPIO_FN_IECLK_B,
- GPIO_FN_SSI_SCK78, GPIO_FN_CAN_DEBUGOUT13,
- GPIO_FN_SSI_SCK9_B, GPIO_FN_SSI_WS78,
- GPIO_FN_CAN_DEBUGOUT14, GPIO_FN_SSI_WS9_B,
- GPIO_FN_SSI_SDATA7, GPIO_FN_CAN_DEBUGOUT15,
- GPIO_FN_TCLK1_C,
- GPIO_FN_SSI_SDATA8, GPIO_FN_VSP,
- GPIO_FN_ATACS01,
- GPIO_FN_ATACS11, GPIO_FN_CC5_TDO,
- GPIO_FN_ATADIR1, GPIO_FN_CC5_TRST,
- GPIO_FN_ATAG1, GPIO_FN_CC5_TMS,
- GPIO_FN_ATARD1, GPIO_FN_CC5_TCK,
- GPIO_FN_ATAWR1, GPIO_FN_CC5_TDI,
- GPIO_FN_DREQ2, GPIO_FN_DACK2,
-
- /* IPSR8 */
- GPIO_FN_AD_CLK,
- GPIO_FN_CC5_STATE4, GPIO_FN_CC5_STATE12, GPIO_FN_CC5_STATE20,
- GPIO_FN_CC5_STATE28, GPIO_FN_CC5_STATE36,
- GPIO_FN_AD_DI,
- GPIO_FN_CC5_STATE5, GPIO_FN_CC5_STATE13, GPIO_FN_CC5_STATE21,
- GPIO_FN_CC5_STATE29, GPIO_FN_CC5_STATE37,
- GPIO_FN_CAN_DEBUG_HW_TRIGGER, GPIO_FN_AD_DO,
- GPIO_FN_CC5_STATE6, GPIO_FN_CC5_STATE14, GPIO_FN_CC5_STATE22,
- GPIO_FN_CC5_STATE30, GPIO_FN_CC5_STATE38,
- GPIO_FN_CAN_STEP0, GPIO_FN_AD_NCS, GPIO_FN_CC5_STATE7,
- GPIO_FN_CC5_STATE15, GPIO_FN_CC5_STATE23, GPIO_FN_CC5_STATE31,
- GPIO_FN_CC5_STATE39, GPIO_FN_FMCLK, GPIO_FN_RDS_CLK, GPIO_FN_PCMOE,
- GPIO_FN_BPFCLK, GPIO_FN_PCMWE, GPIO_FN_FMIN, GPIO_FN_RDS_DATA,
- GPIO_FN_VI0_CLK, GPIO_FN_VI0_CLKENB,
- GPIO_FN_HTX1_B, GPIO_FN_MT1_SYNC, GPIO_FN_VI0_FIELD,
- GPIO_FN_HRX1_B, GPIO_FN_VI0_HSYNC, GPIO_FN_VI0_DATA0_B_VI0_B0_B,
- GPIO_FN_HSCK1_B,
- GPIO_FN_VI0_VSYNC, GPIO_FN_VI0_DATA1_B_VI0_B1_B,
- GPIO_FN_PWMFSW0_C,
-
- /* IPSR9 */
- GPIO_FN_VI0_DATA0_VI0_B0, GPIO_FN_HRTS1_B, GPIO_FN_MT1_VCXO,
- GPIO_FN_VI0_DATA1_VI0_B1, GPIO_FN_HCTS1_B, GPIO_FN_MT1_PWM,
- GPIO_FN_VI0_DATA2_VI0_B2, GPIO_FN_VI0_DATA3_VI0_B3,
- GPIO_FN_VI0_DATA4_VI0_B4,
- GPIO_FN_VI0_DATA5_VI0_B5, GPIO_FN_VI0_DATA6_VI0_B6,
- GPIO_FN_ARM_TRACEDATA_0, GPIO_FN_VI0_DATA7_VI0_B7,
- GPIO_FN_ARM_TRACEDATA_1, GPIO_FN_VI0_G0,
- GPIO_FN_SSI_SCK78_C, GPIO_FN_ARM_TRACEDATA_2,
- GPIO_FN_VI0_G1, GPIO_FN_SSI_WS78_C,
- GPIO_FN_ARM_TRACEDATA_3, GPIO_FN_VI0_G2, GPIO_FN_ETH_TXD1,
- GPIO_FN_ARM_TRACEDATA_4, GPIO_FN_TS_SPSYNC0,
- GPIO_FN_VI0_G3, GPIO_FN_ETH_CRS_DV,
- GPIO_FN_ARM_TRACEDATA_5, GPIO_FN_TS_SDAT0, GPIO_FN_VI0_G4,
- GPIO_FN_ETH_TX_EN, GPIO_FN_ARM_TRACEDATA_6,
- GPIO_FN_VI0_G5, GPIO_FN_ETH_RX_ER,
- GPIO_FN_ARM_TRACEDATA_7, GPIO_FN_VI0_G6, GPIO_FN_ETH_RXD0,
- GPIO_FN_ARM_TRACEDATA_8, GPIO_FN_VI0_G7,
- GPIO_FN_ETH_RXD1, GPIO_FN_ARM_TRACEDATA_9,
-
- /* IPSR10 */
- GPIO_FN_VI0_R0, GPIO_FN_SSI_SDATA7_C, GPIO_FN_DREQ1_B,
- GPIO_FN_ARM_TRACEDATA_10, GPIO_FN_DREQ0_C, GPIO_FN_VI0_R1,
- GPIO_FN_SSI_SDATA8_C, GPIO_FN_DACK1_B, GPIO_FN_ARM_TRACEDATA_11,
- GPIO_FN_DACK0_C, GPIO_FN_DRACK0_C, GPIO_FN_VI0_R2, GPIO_FN_ETH_LINK,
- GPIO_FN_ARM_TRACEDATA_12,
- GPIO_FN_VI0_R3, GPIO_FN_ETH_MAGIC,
- GPIO_FN_ARM_TRACEDATA_13, GPIO_FN_VI0_R4, GPIO_FN_ETH_REFCLK,
- GPIO_FN_ARM_TRACEDATA_14,
- GPIO_FN_MT1_CLK, GPIO_FN_TS_SCK0, GPIO_FN_VI0_R5, GPIO_FN_ETH_TXD0,
- GPIO_FN_ARM_TRACEDATA_15,
- GPIO_FN_MT1_D, GPIO_FN_TS_SDEN0, GPIO_FN_VI0_R6, GPIO_FN_ETH_MDC,
- GPIO_FN_DREQ2_C, GPIO_FN_TRACECLK,
- GPIO_FN_MT1_BEN, GPIO_FN_PWMFSW0_D, GPIO_FN_VI0_R7, GPIO_FN_ETH_MDIO,
- GPIO_FN_DACK2_C, GPIO_FN_SCIF_CLK_D,
- GPIO_FN_TRACECTL, GPIO_FN_MT1_PEN, GPIO_FN_VI1_CLK, GPIO_FN_SIM_D,
- GPIO_FN_SDA3, GPIO_FN_VI1_HSYNC, GPIO_FN_VI3_CLK, GPIO_FN_SSI_SCK4,
- GPIO_FN_GPS_SIGN_C, GPIO_FN_PWMFSW0_E, GPIO_FN_VI1_VSYNC,
- GPIO_FN_AUDIO_CLKOUT_C, GPIO_FN_SSI_WS4, GPIO_FN_SIM_CLK,
- GPIO_FN_GPS_MAG_C, GPIO_FN_SPV_TRST, GPIO_FN_SCL3,
-
- /* IPSR11 */
- GPIO_FN_VI1_DATA0_VI1_B0, GPIO_FN_SIM_RST,
- GPIO_FN_SPV_TCK, GPIO_FN_ADICLK_B, GPIO_FN_VI1_DATA1_VI1_B1,
- GPIO_FN_MT0_CLK, GPIO_FN_SPV_TMS,
- GPIO_FN_ADICS_B_SAMP_B, GPIO_FN_VI1_DATA2_VI1_B2,
- GPIO_FN_MT0_D, GPIO_FN_SPVTDI, GPIO_FN_ADIDATA_B,
- GPIO_FN_VI1_DATA3_VI1_B3, GPIO_FN_MT0_BEN,
- GPIO_FN_SPV_TDO, GPIO_FN_ADICHS0_B, GPIO_FN_VI1_DATA4_VI1_B4,
- GPIO_FN_MT0_PEN, GPIO_FN_SPA_TRST,
- GPIO_FN_ADICHS1_B, GPIO_FN_VI1_DATA5_VI1_B5,
- GPIO_FN_MT0_SYNC, GPIO_FN_SPA_TCK,
- GPIO_FN_ADICHS2_B, GPIO_FN_VI1_DATA6_VI1_B6,
- GPIO_FN_MT0_VCXO, GPIO_FN_SPA_TMS,
- GPIO_FN_VI1_DATA7_VI1_B7, GPIO_FN_MT0_PWM,
- GPIO_FN_SPA_TDI, GPIO_FN_VI1_G0, GPIO_FN_VI3_DATA0,
- GPIO_FN_TS_SCK1, GPIO_FN_DREQ2_B,
- GPIO_FN_SPA_TDO, GPIO_FN_HCTS0_B, GPIO_FN_VI1_G1, GPIO_FN_VI3_DATA1,
- GPIO_FN_SSI_SCK1, GPIO_FN_TS_SDEN1, GPIO_FN_DACK2_B,
- GPIO_FN_HRTS0_B,
-
- /* IPSR12 */
- GPIO_FN_VI1_G2, GPIO_FN_VI3_DATA2, GPIO_FN_SSI_WS1, GPIO_FN_TS_SPSYNC1,
- GPIO_FN_HSCK0_B, GPIO_FN_VI1_G3, GPIO_FN_VI3_DATA3,
- GPIO_FN_SSI_SCK2, GPIO_FN_TS_SDAT1, GPIO_FN_SCL1_C, GPIO_FN_HTX0_B,
- GPIO_FN_VI1_G4, GPIO_FN_VI3_DATA4, GPIO_FN_SSI_WS2, GPIO_FN_SDA1_C,
- GPIO_FN_SIM_RST_B, GPIO_FN_HRX0_B, GPIO_FN_VI1_G5, GPIO_FN_VI3_DATA5,
- GPIO_FN_GPS_CLK, GPIO_FN_FSE, GPIO_FN_SIM_D_B,
- GPIO_FN_VI1_G6, GPIO_FN_VI3_DATA6, GPIO_FN_GPS_SIGN, GPIO_FN_FRB,
- GPIO_FN_SIM_CLK_B, GPIO_FN_VI1_G7, GPIO_FN_VI3_DATA7,
- GPIO_FN_GPS_MAG, GPIO_FN_FCE,
-};
+#include <linux/sh_eth.h>
struct platform_device;
@@ -339,6 +23,20 @@ static inline struct r8a7779_pm_ch *to_r8a7779_ch(struct generic_pm_domain *d)
return &container_of(d, struct r8a7779_pm_domain, genpd)->ch;
}
+extern void r8a7779_init_delay(void);
+extern void r8a7779_init_irq(void);
+extern void r8a7779_init_irq_extpin(int irlm);
+extern void r8a7779_init_irq_dt(void);
+extern void r8a7779_map_io(void);
+extern void r8a7779_earlytimer_init(void);
+extern void r8a7779_add_early_devices(void);
+extern void r8a7779_add_standard_devices(void);
+extern void r8a7779_add_standard_devices_dt(void);
+extern void r8a7779_add_ether_device(struct sh_eth_plat_data *pdata);
+extern void r8a7779_clock_init(void);
+extern void r8a7779_pinmux_init(void);
+extern void r8a7779_pm_init(void);
+extern void r8a7779_register_twd(void);
extern int r8a7779_sysc_power_down(struct r8a7779_pm_ch *r8a7779_ch);
extern int r8a7779_sysc_power_up(struct r8a7779_pm_ch *r8a7779_ch);
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h b/arch/arm/mach-shmobile/include/mach/r8a7790.h
new file mode 100644
index 000000000000..2e919e61fa0d
--- /dev/null
+++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
@@ -0,0 +1,9 @@
+#ifndef __ASM_R8A7790_H__
+#define __ASM_R8A7790_H__
+
+void r8a7790_add_standard_devices(void);
+void r8a7790_clock_init(void);
+void r8a7790_pinmux_init(void);
+void r8a7790_timer_init(void);
+
+#endif /* __ASM_R8A7790_H__ */
diff --git a/arch/arm/mach-shmobile/include/mach/sh7372.h b/arch/arm/mach-shmobile/include/mach/sh7372.h
index 7ded4ebaf5cc..fd7cba024c39 100644
--- a/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ b/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -449,6 +449,18 @@ extern struct clk sh7372_dv_clki_clk;
extern struct clk sh7372_dv_clki_div2_clk;
extern struct clk sh7372_pllc2_clk;
+extern void sh7372_init_irq(void);
+extern void sh7372_map_io(void);
+extern void sh7372_earlytimer_init(void);
+extern void sh7372_add_early_devices(void);
+extern void sh7372_add_standard_devices(void);
+extern void sh7372_add_early_devices_dt(void);
+extern void sh7372_add_standard_devices_dt(void);
+extern void sh7372_clock_init(void);
+extern void sh7372_pinmux_init(void);
+extern void sh7372_pm_init(void);
+extern void sh7372_resume_core_standby_sysc(void);
+extern int sh7372_do_idle_sysc(unsigned long sleep_mode);
extern void sh7372_intcs_suspend(void);
extern void sh7372_intcs_resume(void);
extern void sh7372_intca_suspend(void);
diff --git a/arch/arm/mach-shmobile/include/mach/sh73a0.h b/arch/arm/mach-shmobile/include/mach/sh73a0.h
index fbc1584d6712..eb7a4320d487 100644
--- a/arch/arm/mach-shmobile/include/mach/sh73a0.h
+++ b/arch/arm/mach-shmobile/include/mach/sh73a0.h
@@ -444,6 +444,21 @@ enum {
#define SH73A0_PINT0_IRQ(irq) ((irq) + 700)
#define SH73A0_PINT1_IRQ(irq) ((irq) + 732)
+extern void sh73a0_init_delay(void);
+extern void sh73a0_init_irq(void);
+extern void sh73a0_init_irq_dt(void);
+extern void sh73a0_map_io(void);
+extern void sh73a0_earlytimer_init(void);
+extern void sh73a0_add_early_devices(void);
+extern void sh73a0_add_standard_devices(void);
+extern void sh73a0_add_standard_devices_dt(void);
+extern void sh73a0_clock_init(void);
+extern void sh73a0_pinmux_init(void);
+extern void sh73a0_pm_init(void);
+extern struct clk sh73a0_extal1_clk;
+extern struct clk sh73a0_extal2_clk;
+extern struct clk sh73a0_extcki_clk;
+extern struct clk sh73a0_extalr_clk;
extern struct smp_operations sh73a0_smp_ops;
#endif /* __ASM_SH73A0_H__ */
diff --git a/arch/arm/mach-shmobile/intc-r8a7740.c b/arch/arm/mach-shmobile/intc-r8a7740.c
index 9a69a31918ba..b741c8409a5a 100644
--- a/arch/arm/mach-shmobile/intc-r8a7740.c
+++ b/arch/arm/mach-shmobile/intc-r8a7740.c
@@ -18,620 +18,39 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
#include <linux/io.h>
-#include <linux/sh_intc.h>
-#include <mach/intc.h>
-#include <mach/irqs.h>
-#include <asm/mach-types.h>
-#include <asm/mach/arch.h>
-
-/*
- * INTCA
- */
-enum {
- UNUSED_INTCA = 0,
-
- /* interrupt sources INTCA */
- DIRC,
- ATAPI,
- IIC1_ALI, IIC1_TACKI, IIC1_WAITI, IIC1_DTEI,
- AP_ARM_COMMTX, AP_ARM_COMMRX,
- MFI, MFIS,
- BBIF1, BBIF2,
- USBHSDMAC,
- USBF_OUL_SOF, USBF_IXL_INT,
- SGX540,
- CMT1_0, CMT1_1, CMT1_2, CMT1_3,
- CMT2,
- CMT3,
- KEYSC,
- SCIFA0, SCIFA1, SCIFA2, SCIFA3,
- MSIOF2, MSIOF1,
- SCIFA4, SCIFA5, SCIFB,
- FLCTL_FLSTEI, FLCTL_FLTENDI, FLCTL_FLTREQ0I, FLCTL_FLTREQ1I,
- SDHI0_0, SDHI0_1, SDHI0_2, SDHI0_3,
- SDHI1_0, SDHI1_1, SDHI1_2, SDHI1_3,
- AP_ARM_L2CINT,
- IRDA,
- TPU0,
- SCIFA6, SCIFA7,
- GbEther,
- ICBS0,
- DDM,
- SDHI2_0, SDHI2_1, SDHI2_2, SDHI2_3,
- RWDT0,
- DMAC1_1_DEI0, DMAC1_1_DEI1, DMAC1_1_DEI2, DMAC1_1_DEI3,
- DMAC1_2_DEI4, DMAC1_2_DEI5, DMAC1_2_DADERR,
- DMAC2_1_DEI0, DMAC2_1_DEI1, DMAC2_1_DEI2, DMAC2_1_DEI3,
- DMAC2_2_DEI4, DMAC2_2_DEI5, DMAC2_2_DADERR,
- DMAC3_1_DEI0, DMAC3_1_DEI1, DMAC3_1_DEI2, DMAC3_1_DEI3,
- DMAC3_2_DEI4, DMAC3_2_DEI5, DMAC3_2_DADERR,
- SHWYSTAT_RT, SHWYSTAT_HS, SHWYSTAT_COM,
- HDMI,
- USBH_INT, USBH_OHCI, USBH_EHCI, USBH_PME, USBH_BIND,
- RSPI_OVRF, RSPI_SPTEF, RSPI_SPRF,
- SPU2_0, SPU2_1,
- FSI, FMSI,
- HDMI_SSS, HDMI_KEY,
- IPMMU,
- AP_ARM_CTIIRQ, AP_ARM_PMURQ,
- MFIS2,
- CPORTR2S,
- CMT14, CMT15,
- MMCIF_0, MMCIF_1, MMCIF_2,
- SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEI,
- STPRO_0, STPRO_1, STPRO_2, STPRO_3, STPRO_4,
-
- /* interrupt groups INTCA */
- DMAC1_1, DMAC1_2,
- DMAC2_1, DMAC2_2,
- DMAC3_1, DMAC3_2,
- AP_ARM1, AP_ARM2,
- SDHI0, SDHI1, SDHI2,
- SHWYSTAT,
- USBF, USBH1, USBH2,
- RSPI, SPU2, FLCTL, IIC1,
-};
-
-static struct intc_vect intca_vectors[] __initdata = {
- INTC_VECT(DIRC, 0x0560),
- INTC_VECT(ATAPI, 0x05E0),
- INTC_VECT(IIC1_ALI, 0x0780),
- INTC_VECT(IIC1_TACKI, 0x07A0),
- INTC_VECT(IIC1_WAITI, 0x07C0),
- INTC_VECT(IIC1_DTEI, 0x07E0),
- INTC_VECT(AP_ARM_COMMTX, 0x0840),
- INTC_VECT(AP_ARM_COMMRX, 0x0860),
- INTC_VECT(MFI, 0x0900),
- INTC_VECT(MFIS, 0x0920),
- INTC_VECT(BBIF1, 0x0940),
- INTC_VECT(BBIF2, 0x0960),
- INTC_VECT(USBHSDMAC, 0x0A00),
- INTC_VECT(USBF_OUL_SOF, 0x0A20),
- INTC_VECT(USBF_IXL_INT, 0x0A40),
- INTC_VECT(SGX540, 0x0A60),
- INTC_VECT(CMT1_0, 0x0B00),
- INTC_VECT(CMT1_1, 0x0B20),
- INTC_VECT(CMT1_2, 0x0B40),
- INTC_VECT(CMT1_3, 0x0B60),
- INTC_VECT(CMT2, 0x0B80),
- INTC_VECT(CMT3, 0x0BA0),
- INTC_VECT(KEYSC, 0x0BE0),
- INTC_VECT(SCIFA0, 0x0C00),
- INTC_VECT(SCIFA1, 0x0C20),
- INTC_VECT(SCIFA2, 0x0C40),
- INTC_VECT(SCIFA3, 0x0C60),
- INTC_VECT(MSIOF2, 0x0C80),
- INTC_VECT(MSIOF1, 0x0D00),
- INTC_VECT(SCIFA4, 0x0D20),
- INTC_VECT(SCIFA5, 0x0D40),
- INTC_VECT(SCIFB, 0x0D60),
- INTC_VECT(FLCTL_FLSTEI, 0x0D80),
- INTC_VECT(FLCTL_FLTENDI, 0x0DA0),
- INTC_VECT(FLCTL_FLTREQ0I, 0x0DC0),
- INTC_VECT(FLCTL_FLTREQ1I, 0x0DE0),
- INTC_VECT(SDHI0_0, 0x0E00),
- INTC_VECT(SDHI0_1, 0x0E20),
- INTC_VECT(SDHI0_2, 0x0E40),
- INTC_VECT(SDHI0_3, 0x0E60),
- INTC_VECT(SDHI1_0, 0x0E80),
- INTC_VECT(SDHI1_1, 0x0EA0),
- INTC_VECT(SDHI1_2, 0x0EC0),
- INTC_VECT(SDHI1_3, 0x0EE0),
- INTC_VECT(AP_ARM_L2CINT, 0x0FA0),
- INTC_VECT(IRDA, 0x0480),
- INTC_VECT(TPU0, 0x04A0),
- INTC_VECT(SCIFA6, 0x04C0),
- INTC_VECT(SCIFA7, 0x04E0),
- INTC_VECT(GbEther, 0x0500),
- INTC_VECT(ICBS0, 0x0540),
- INTC_VECT(DDM, 0x1140),
- INTC_VECT(SDHI2_0, 0x1200),
- INTC_VECT(SDHI2_1, 0x1220),
- INTC_VECT(SDHI2_2, 0x1240),
- INTC_VECT(SDHI2_3, 0x1260),
- INTC_VECT(RWDT0, 0x1280),
- INTC_VECT(DMAC1_1_DEI0, 0x2000),
- INTC_VECT(DMAC1_1_DEI1, 0x2020),
- INTC_VECT(DMAC1_1_DEI2, 0x2040),
- INTC_VECT(DMAC1_1_DEI3, 0x2060),
- INTC_VECT(DMAC1_2_DEI4, 0x2080),
- INTC_VECT(DMAC1_2_DEI5, 0x20A0),
- INTC_VECT(DMAC1_2_DADERR, 0x20C0),
- INTC_VECT(DMAC2_1_DEI0, 0x2100),
- INTC_VECT(DMAC2_1_DEI1, 0x2120),
- INTC_VECT(DMAC2_1_DEI2, 0x2140),
- INTC_VECT(DMAC2_1_DEI3, 0x2160),
- INTC_VECT(DMAC2_2_DEI4, 0x2180),
- INTC_VECT(DMAC2_2_DEI5, 0x21A0),
- INTC_VECT(DMAC2_2_DADERR, 0x21C0),
- INTC_VECT(DMAC3_1_DEI0, 0x2200),
- INTC_VECT(DMAC3_1_DEI1, 0x2220),
- INTC_VECT(DMAC3_1_DEI2, 0x2240),
- INTC_VECT(DMAC3_1_DEI3, 0x2260),
- INTC_VECT(DMAC3_2_DEI4, 0x2280),
- INTC_VECT(DMAC3_2_DEI5, 0x22A0),
- INTC_VECT(DMAC3_2_DADERR, 0x22C0),
- INTC_VECT(SHWYSTAT_RT, 0x1300),
- INTC_VECT(SHWYSTAT_HS, 0x1320),
- INTC_VECT(SHWYSTAT_COM, 0x1340),
- INTC_VECT(USBH_INT, 0x1540),
- INTC_VECT(USBH_OHCI, 0x1560),
- INTC_VECT(USBH_EHCI, 0x1580),
- INTC_VECT(USBH_PME, 0x15A0),
- INTC_VECT(USBH_BIND, 0x15C0),
- INTC_VECT(HDMI, 0x1700),
- INTC_VECT(RSPI_OVRF, 0x1780),
- INTC_VECT(RSPI_SPTEF, 0x17A0),
- INTC_VECT(RSPI_SPRF, 0x17C0),
- INTC_VECT(SPU2_0, 0x1800),
- INTC_VECT(SPU2_1, 0x1820),
- INTC_VECT(FSI, 0x1840),
- INTC_VECT(FMSI, 0x1860),
- INTC_VECT(HDMI_SSS, 0x18A0),
- INTC_VECT(HDMI_KEY, 0x18C0),
- INTC_VECT(IPMMU, 0x1920),
- INTC_VECT(AP_ARM_CTIIRQ, 0x1980),
- INTC_VECT(AP_ARM_PMURQ, 0x19A0),
- INTC_VECT(MFIS2, 0x1A00),
- INTC_VECT(CPORTR2S, 0x1A20),
- INTC_VECT(CMT14, 0x1A40),
- INTC_VECT(CMT15, 0x1A60),
- INTC_VECT(MMCIF_0, 0x1AA0),
- INTC_VECT(MMCIF_1, 0x1AC0),
- INTC_VECT(MMCIF_2, 0x1AE0),
- INTC_VECT(SIM_ERI, 0x1C00),
- INTC_VECT(SIM_RXI, 0x1C20),
- INTC_VECT(SIM_TXI, 0x1C40),
- INTC_VECT(SIM_TEI, 0x1C60),
- INTC_VECT(STPRO_0, 0x1C80),
- INTC_VECT(STPRO_1, 0x1CA0),
- INTC_VECT(STPRO_2, 0x1CC0),
- INTC_VECT(STPRO_3, 0x1CE0),
- INTC_VECT(STPRO_4, 0x1D00),
-};
-
-static struct intc_group intca_groups[] __initdata = {
- INTC_GROUP(DMAC1_1,
- DMAC1_1_DEI0, DMAC1_1_DEI1, DMAC1_1_DEI2, DMAC1_1_DEI3),
- INTC_GROUP(DMAC1_2,
- DMAC1_2_DEI4, DMAC1_2_DEI5, DMAC1_2_DADERR),
- INTC_GROUP(DMAC2_1,
- DMAC2_1_DEI0, DMAC2_1_DEI1, DMAC2_1_DEI2, DMAC2_1_DEI3),
- INTC_GROUP(DMAC2_2,
- DMAC2_2_DEI4, DMAC2_2_DEI5, DMAC2_2_DADERR),
- INTC_GROUP(DMAC3_1,
- DMAC3_1_DEI0, DMAC3_1_DEI1, DMAC3_1_DEI2, DMAC3_1_DEI3),
- INTC_GROUP(DMAC3_2,
- DMAC3_2_DEI4, DMAC3_2_DEI5, DMAC3_2_DADERR),
- INTC_GROUP(AP_ARM1,
- AP_ARM_COMMTX, AP_ARM_COMMRX),
- INTC_GROUP(AP_ARM2,
- AP_ARM_CTIIRQ, AP_ARM_PMURQ),
- INTC_GROUP(USBF,
- USBF_OUL_SOF, USBF_IXL_INT),
- INTC_GROUP(SDHI0,
- SDHI0_0, SDHI0_1, SDHI0_2, SDHI0_3),
- INTC_GROUP(SDHI1,
- SDHI1_0, SDHI1_1, SDHI1_2, SDHI1_3),
- INTC_GROUP(SDHI2,
- SDHI2_0, SDHI2_1, SDHI2_2, SDHI2_3),
- INTC_GROUP(SHWYSTAT,
- SHWYSTAT_RT, SHWYSTAT_HS, SHWYSTAT_COM),
- INTC_GROUP(USBH1, /* FIXME */
- USBH_INT, USBH_OHCI),
- INTC_GROUP(USBH2, /* FIXME */
- USBH_EHCI,
- USBH_PME, USBH_BIND),
- INTC_GROUP(RSPI,
- RSPI_OVRF, RSPI_SPTEF, RSPI_SPRF),
- INTC_GROUP(SPU2,
- SPU2_0, SPU2_1),
- INTC_GROUP(FLCTL,
- FLCTL_FLSTEI, FLCTL_FLTENDI, FLCTL_FLTREQ0I, FLCTL_FLTREQ1I),
- INTC_GROUP(IIC1,
- IIC1_ALI, IIC1_TACKI, IIC1_WAITI, IIC1_DTEI),
-};
-
-static struct intc_mask_reg intca_mask_registers[] __initdata = {
- { /* IMR0A / IMCR0A */ 0xe6940080, 0xe69400c0, 8,
- { DMAC2_1_DEI3, DMAC2_1_DEI2, DMAC2_1_DEI1, DMAC2_1_DEI0,
- 0, 0, AP_ARM_COMMTX, AP_ARM_COMMRX } },
- { /* IMR1A / IMCR1A */ 0xe6940084, 0xe69400c4, 8,
- { ATAPI, 0, DIRC, 0,
- DMAC1_1_DEI3, DMAC1_1_DEI2, DMAC1_1_DEI1, DMAC1_1_DEI0 } },
- { /* IMR2A / IMCR2A */ 0xe6940088, 0xe69400c8, 8,
- { 0, 0, 0, 0,
- BBIF1, BBIF2, MFIS, MFI } },
- { /* IMR3A / IMCR3A */ 0xe694008c, 0xe69400cc, 8,
- { DMAC3_1_DEI3, DMAC3_1_DEI2, DMAC3_1_DEI1, DMAC3_1_DEI0,
- DMAC3_2_DADERR, DMAC3_2_DEI5, DMAC3_2_DEI4, IRDA } },
- { /* IMR4A / IMCR4A */ 0xe6940090, 0xe69400d0, 8,
- { DDM, 0, 0, 0,
- 0, 0, 0, 0 } },
- { /* IMR5A / IMCR5A */ 0xe6940094, 0xe69400d4, 8,
- { KEYSC, DMAC1_2_DADERR, DMAC1_2_DEI5, DMAC1_2_DEI4,
- SCIFA3, SCIFA2, SCIFA1, SCIFA0 } },
- { /* IMR6A / IMCR6A */ 0xe6940098, 0xe69400d8, 8,
- { SCIFB, SCIFA5, SCIFA4, MSIOF1,
- 0, 0, MSIOF2, 0 } },
- { /* IMR7A / IMCR7A */ 0xe694009c, 0xe69400dc, 8,
- { SDHI0_3, SDHI0_2, SDHI0_1, SDHI0_0,
- FLCTL_FLTREQ1I, FLCTL_FLTREQ0I, FLCTL_FLTENDI, FLCTL_FLSTEI } },
- { /* IMR8A / IMCR8A */ 0xe69400a0, 0xe69400e0, 8,
- { SDHI1_3, SDHI1_2, SDHI1_1, SDHI1_0,
- 0, USBHSDMAC, 0, AP_ARM_L2CINT } },
- { /* IMR9A / IMCR9A */ 0xe69400a4, 0xe69400e4, 8,
- { CMT1_3, CMT1_2, CMT1_1, CMT1_0,
- CMT2, USBF_IXL_INT, USBF_OUL_SOF, SGX540 } },
- { /* IMR10A / IMCR10A */ 0xe69400a8, 0xe69400e8, 8,
- { 0, DMAC2_2_DADERR, DMAC2_2_DEI5, DMAC2_2_DEI4,
- 0, 0, 0, 0 } },
- { /* IMR11A / IMCR11A */ 0xe69400ac, 0xe69400ec, 8,
- { IIC1_DTEI, IIC1_WAITI, IIC1_TACKI, IIC1_ALI,
- ICBS0, 0, 0, 0 } },
- { /* IMR12A / IMCR12A */ 0xe69400b0, 0xe69400f0, 8,
- { 0, 0, TPU0, SCIFA6,
- SCIFA7, GbEther, 0, 0 } },
- { /* IMR13A / IMCR13A */ 0xe69400b4, 0xe69400f4, 8,
- { SDHI2_3, SDHI2_2, SDHI2_1, SDHI2_0,
- 0, CMT3, 0, RWDT0 } },
- { /* IMR0A3 / IMCR0A3 */ 0xe6950080, 0xe69500c0, 8,
- { SHWYSTAT_RT, SHWYSTAT_HS, SHWYSTAT_COM, 0,
- 0, 0, 0, 0 } },
- /* IMR1A3 / IMCR1A3 */
- { /* IMR2A3 / IMCR2A3 */ 0xe6950088, 0xe69500c8, 8,
- { 0, 0, USBH_INT, USBH_OHCI,
- USBH_EHCI, USBH_PME, USBH_BIND, 0 } },
- /* IMR3A3 / IMCR3A3 */
- { /* IMR4A3 / IMCR4A3 */ 0xe6950090, 0xe69500d0, 8,
- { HDMI, 0, 0, 0,
- RSPI_OVRF, RSPI_SPTEF, RSPI_SPRF, 0 } },
- { /* IMR5A3 / IMCR5A3 */ 0xe6950094, 0xe69500d4, 8,
- { SPU2_0, SPU2_1, FSI, FMSI,
- 0, HDMI_SSS, HDMI_KEY, 0 } },
- { /* IMR6A3 / IMCR6A3 */ 0xe6950098, 0xe69500d8, 8,
- { 0, IPMMU, 0, 0,
- AP_ARM_CTIIRQ, AP_ARM_PMURQ, 0, 0 } },
- { /* IMR7A3 / IMCR7A3 */ 0xe695009c, 0xe69500dc, 8,
- { MFIS2, CPORTR2S, CMT14, CMT15,
- 0, MMCIF_0, MMCIF_1, MMCIF_2 } },
- /* IMR8A3 / IMCR8A3 */
- { /* IMR9A3 / IMCR9A3 */ 0xe69500a4, 0xe69500e4, 8,
- { SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEI,
- STPRO_0, STPRO_1, STPRO_2, STPRO_3 } },
- { /* IMR10A3 / IMCR10A3 */ 0xe69500a8, 0xe69500e8, 8,
- { STPRO_4, 0, 0, 0,
- 0, 0, 0, 0 } },
-};
-
-static struct intc_prio_reg intca_prio_registers[] __initdata = {
- { 0xe6940000, 0, 16, 4, /* IPRAA */ { DMAC3_1, DMAC3_2, CMT2, ICBS0 } },
- { 0xe6940004, 0, 16, 4, /* IPRBA */ { IRDA, 0, BBIF1, BBIF2 } },
- { 0xe6940008, 0, 16, 4, /* IPRCA */ { ATAPI, 0, CMT1_1, AP_ARM1 } },
- { 0xe694000c, 0, 16, 4, /* IPRDA */ { 0, 0, CMT1_2, 0 } },
- { 0xe6940010, 0, 16, 4, /* IPREA */ { DMAC1_1, MFIS, MFI, USBF } },
- { 0xe6940014, 0, 16, 4, /* IPRFA */ { KEYSC, DMAC1_2,
- SGX540, CMT1_0 } },
- { 0xe6940018, 0, 16, 4, /* IPRGA */ { SCIFA0, SCIFA1,
- SCIFA2, SCIFA3 } },
- { 0xe694001c, 0, 16, 4, /* IPRGH */ { MSIOF2, USBHSDMAC,
- FLCTL, SDHI0 } },
- { 0xe6940020, 0, 16, 4, /* IPRIA */ { MSIOF1, SCIFA4, 0, IIC1 } },
- { 0xe6940024, 0, 16, 4, /* IPRJA */ { DMAC2_1, DMAC2_2,
- AP_ARM_L2CINT, 0 } },
- { 0xe6940028, 0, 16, 4, /* IPRKA */ { 0, CMT1_3, 0, SDHI1 } },
- { 0xe694002c, 0, 16, 4, /* IPRLA */ { TPU0, SCIFA6,
- SCIFA7, GbEther } },
- { 0xe6940030, 0, 16, 4, /* IPRMA */ { 0, CMT3, 0, RWDT0 } },
- { 0xe6940034, 0, 16, 4, /* IPRNA */ { SCIFB, SCIFA5, 0, DDM } },
- { 0xe6940038, 0, 16, 4, /* IPROA */ { 0, 0, DIRC, SDHI2 } },
- { 0xe6950000, 0, 16, 4, /* IPRAA3 */ { SHWYSTAT, 0, 0, 0 } },
- /* IPRBA3 */
- /* IPRCA3 */
- /* IPRDA3 */
- { 0xe6950010, 0, 16, 4, /* IPREA3 */ { USBH1, 0, 0, 0 } },
- { 0xe6950014, 0, 16, 4, /* IPRFA3 */ { USBH2, 0, 0, 0 } },
- /* IPRGA3 */
- /* IPRHA3 */
- { 0xe6950020, 0, 16, 4, /* IPRIA3 */ { HDMI, 0, 0, 0 } },
- { 0xe6950024, 0, 16, 4, /* IPRJA3 */ { RSPI, 0, 0, 0 } },
- { 0xe6950028, 0, 16, 4, /* IPRKA3 */ { SPU2, 0, FSI, FMSI } },
- { 0xe695002c, 0, 16, 4, /* IPRLA3 */ { 0, HDMI_SSS, HDMI_KEY, 0 } },
- { 0xe6950030, 0, 16, 4, /* IPRMA3 */ { IPMMU, 0, 0, 0 } },
- { 0xe6950034, 0, 16, 4, /* IPRNA3 */ { AP_ARM2, 0, 0, 0 } },
- { 0xe6950038, 0, 16, 4, /* IPROA3 */ { MFIS2, CPORTR2S,
- CMT14, CMT15 } },
- { 0xe695003c, 0, 16, 4, /* IPRPA3 */ { 0, MMCIF_0, MMCIF_1, MMCIF_2 } },
- /* IPRQA3 */
- /* IPRRA3 */
- { 0xe6950048, 0, 16, 4, /* IPRSA3 */ { SIM_ERI, SIM_RXI,
- SIM_TXI, SIM_TEI } },
- { 0xe695004c, 0, 16, 4, /* IPRTA3 */ { STPRO_0, STPRO_1,
- STPRO_2, STPRO_3 } },
- { 0xe6950050, 0, 16, 4, /* IPRUA3 */ { STPRO_4, 0, 0, 0 } },
-};
-
-static DECLARE_INTC_DESC(intca_desc, "r8a7740-intca",
- intca_vectors, intca_groups,
- intca_mask_registers, intca_prio_registers,
- NULL);
-
-INTC_IRQ_PINS_32(intca_irq_pins, 0xe6900000,
- INTC_VECT, "r8a7740-intca-irq-pins");
-
-
-/*
- * INTCS
- */
-enum {
- UNUSED_INTCS = 0,
-
- INTCS,
-
- /* interrupt sources INTCS */
-
- /* HUDI */
- /* STPRO */
- /* RTDMAC(1) */
- VPU5HA2,
- _2DG_TRAP, _2DG_GPM_INT, _2DG_CER_INT,
- /* MFI */
- /* BBIF2 */
- VPU5F,
- _2DG_BRK_INT,
- /* SGX540 */
- /* 2DDMAC */
- /* IPMMU */
- /* RTDMAC 2 */
- /* KEYSC */
- /* MSIOF */
- IIC0_ALI, IIC0_TACKI, IIC0_WAITI, IIC0_DTEI,
- TMU0_0, TMU0_1, TMU0_2,
- CMT0,
- /* CMT2 */
- LMB,
- CTI,
- VOU,
- /* RWDT0 */
- ICB,
- VIO6C,
- CEU20, CEU21,
- JPU,
- LCDC0,
- LCRC,
- /* RTDMAC2(1) */
- /* RTDMAC2(2) */
- LCDC1,
- /* SPU2 */
- /* FSI */
- /* FMSI */
- TMU1_0, TMU1_1, TMU1_2,
- CMT4,
- DISP,
- DSRV,
- /* MFIS2 */
- CPORTS2R,
-
- /* interrupt groups INTCS */
- _2DG1,
- IIC0, TMU1,
-};
-
-static struct intc_vect intcs_vectors[] = {
- /* HUDI */
- /* STPRO */
- /* RTDMAC(1) */
- INTCS_VECT(VPU5HA2, 0x0880),
- INTCS_VECT(_2DG_TRAP, 0x08A0),
- INTCS_VECT(_2DG_GPM_INT, 0x08C0),
- INTCS_VECT(_2DG_CER_INT, 0x08E0),
- /* MFI */
- /* BBIF2 */
- INTCS_VECT(VPU5F, 0x0980),
- INTCS_VECT(_2DG_BRK_INT, 0x09A0),
- /* SGX540 */
- /* 2DDMAC */
- /* IPMMU */
- /* RTDMAC(2) */
- /* KEYSC */
- /* MSIOF */
- INTCS_VECT(IIC0_ALI, 0x0E00),
- INTCS_VECT(IIC0_TACKI, 0x0E20),
- INTCS_VECT(IIC0_WAITI, 0x0E40),
- INTCS_VECT(IIC0_DTEI, 0x0E60),
- INTCS_VECT(TMU0_0, 0x0E80),
- INTCS_VECT(TMU0_1, 0x0EA0),
- INTCS_VECT(TMU0_2, 0x0EC0),
- INTCS_VECT(CMT0, 0x0F00),
- /* CMT2 */
- INTCS_VECT(LMB, 0x0F60),
- INTCS_VECT(CTI, 0x0400),
- INTCS_VECT(VOU, 0x0420),
- /* RWDT0 */
- INTCS_VECT(ICB, 0x0480),
- INTCS_VECT(VIO6C, 0x04E0),
- INTCS_VECT(CEU20, 0x0500),
- INTCS_VECT(CEU21, 0x0520),
- INTCS_VECT(JPU, 0x0560),
- INTCS_VECT(LCDC0, 0x0580),
- INTCS_VECT(LCRC, 0x05A0),
- /* RTDMAC2(1) */
- /* RTDMAC2(2) */
- INTCS_VECT(LCDC1, 0x1780),
- /* SPU2 */
- /* FSI */
- /* FMSI */
- INTCS_VECT(TMU1_0, 0x1900),
- INTCS_VECT(TMU1_1, 0x1920),
- INTCS_VECT(TMU1_2, 0x1940),
- INTCS_VECT(CMT4, 0x1980),
- INTCS_VECT(DISP, 0x19A0),
- INTCS_VECT(DSRV, 0x19C0),
- /* MFIS2 */
- INTCS_VECT(CPORTS2R, 0x1A20),
-
- INTC_VECT(INTCS, 0xf80),
-};
-
-static struct intc_group intcs_groups[] __initdata = {
- INTC_GROUP(_2DG1, /*FIXME*/
- _2DG_CER_INT, _2DG_GPM_INT, _2DG_TRAP),
- INTC_GROUP(IIC0,
- IIC0_DTEI, IIC0_WAITI, IIC0_TACKI, IIC0_ALI),
- INTC_GROUP(TMU1,
- TMU1_0, TMU1_1, TMU1_2),
-};
-
-static struct intc_mask_reg intcs_mask_registers[] = {
- /* IMR0SA / IMCR0SA */ /* all 0 */
- { /* IMR1SA / IMCR1SA */ 0xffd20184, 0xffd201c4, 8,
- { _2DG_CER_INT, _2DG_GPM_INT, _2DG_TRAP, VPU5HA2,
- 0, 0, 0, 0 /*STPRO*/ } },
- { /* IMR2SA / IMCR2SA */ 0xffd20188, 0xffd201c8, 8,
- { 0/*STPRO*/, 0, CEU21, VPU5F,
- 0/*BBIF2*/, 0, 0, 0/*MFI*/ } },
- { /* IMR3SA / IMCR3SA */ 0xffd2018c, 0xffd201cc, 8,
- { 0, 0, 0, 0, /*2DDMAC*/
- VIO6C, 0, 0, ICB } },
- { /* IMR4SA / IMCR4SA */ 0xffd20190, 0xffd201d0, 8,
- { 0, 0, VOU, CTI,
- JPU, 0, LCRC, LCDC0 } },
- /* IMR5SA / IMCR5SA */ /*KEYSC/RTDMAC2/RTDMAC1*/
- /* IMR6SA / IMCR6SA */ /*MSIOF/SGX540*/
- { /* IMR7SA / IMCR7SA */ 0xffd2019c, 0xffd201dc, 8,
- { 0, TMU0_2, TMU0_1, TMU0_0,
- 0, 0, 0, 0 } },
- { /* IMR8SA / IMCR8SA */ 0xffd201a0, 0xffd201e0, 8,
- { 0, 0, 0, 0,
- CEU20, 0, 0, 0 } },
- { /* IMR9SA / IMCR9SA */ 0xffd201a4, 0xffd201e4, 8,
- { 0, 0/*RWDT0*/, 0/*CMT2*/, CMT0,
- 0, 0, 0, 0 } },
- /* IMR10SA / IMCR10SA */ /*IPMMU*/
- { /* IMR11SA / IMCR11SA */ 0xffd201ac, 0xffd201ec, 8,
- { IIC0_DTEI, IIC0_WAITI, IIC0_TACKI, IIC0_ALI,
- 0, _2DG_BRK_INT, LMB, 0 } },
- /* IMR12SA / IMCR12SA */
- /* IMR13SA / IMCR13SA */
- /* IMR0SA3 / IMCR0SA3 */ /*RTDMAC2(1)/RTDMAC2(2)*/
- /* IMR1SA3 / IMCR1SA3 */
- /* IMR2SA3 / IMCR2SA3 */
- /* IMR3SA3 / IMCR3SA3 */
- { /* IMR4SA3 / IMCR4SA3 */ 0xffd50190, 0xffd501d0, 8,
- { 0, 0, 0, 0,
- LCDC1, 0, 0, 0 } },
- /* IMR5SA3 / IMCR5SA3 */ /* SPU2/FSI/FMSI */
- { /* IMR6SA3 / IMCR6SA3 */ 0xffd50198, 0xffd501d8, 8,
- { TMU1_0, TMU1_1, TMU1_2, 0,
- CMT4, DISP, DSRV, 0 } },
- { /* IMR7SA3 / IMCR7SA3 */ 0xffd5019c, 0xffd501dc, 8,
- { 0/*MFIS2*/, CPORTS2R, 0, 0,
- 0, 0, 0, 0 } },
- { /* INTAMASK */ 0xffd20104, 0, 16,
- { 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, INTCS } },
-};
-
-/* Priority is needed for INTCA to receive the INTCS interrupt */
-static struct intc_prio_reg intcs_prio_registers[] = {
- { 0xffd20000, 0, 16, 4, /* IPRAS */ { CTI, VOU, 0/*2DDMAC*/, ICB } },
- { 0xffd20004, 0, 16, 4, /* IPRBS */ { JPU, LCDC0, 0, LCRC } },
- /* IPRCS */ /*BBIF2*/
- /* IPRDS */
- { 0xffd20010, 0, 16, 4, /* IPRES */ { 0/*RTDMAC(1)*/, VPU5HA2,
- 0/*MFI*/, VPU5F } },
- { 0xffd20014, 0, 16, 4, /* IPRFS */ { 0/*KEYSC*/, 0/*RTDMAC(2)*/,
- 0/*CMT2*/, CMT0 } },
- { 0xffd20018, 0, 16, 4, /* IPRGS */ { TMU0_0, TMU0_1,
- TMU0_2, _2DG1 } },
- { 0xffd2001c, 0, 16, 4, /* IPRHS */ { 0, 0/*STPRO*/, 0/*STPRO*/,
- _2DG_BRK_INT/*FIXME*/ } },
- { 0xffd20020, 0, 16, 4, /* IPRIS */ { 0, 0/*MSIOF*/, 0, IIC0 } },
- { 0xffd20024, 0, 16, 4, /* IPRJS */ { CEU20, 0/*SGX540*/, 0, 0 } },
- { 0xffd20028, 0, 16, 4, /* IPRKS */ { VIO6C, 0, LMB, 0 } },
- { 0xffd2002c, 0, 16, 4, /* IPRLS */ { 0/*IPMMU*/, 0, CEU21, 0 } },
- /* IPRMS */ /*RWDT0*/
- /* IPRAS3 */ /*RTDMAC2(1)*/
- /* IPRBS3 */ /*RTDMAC2(2)*/
- /* IPRCS3 */
- /* IPRDS3 */
- /* IPRES3 */
- /* IPRFS3 */
- /* IPRGS3 */
- /* IPRHS3 */
- /* IPRIS3 */
- { 0xffd50024, 0, 16, 4, /* IPRJS3 */ { LCDC1, 0, 0, 0 } },
- /* IPRKS3 */ /*SPU2/FSI/FMSi*/
- /* IPRLS3 */
- { 0xffd50030, 0, 16, 4, /* IPRMS3 */ { TMU1, 0, 0, 0 } },
- { 0xffd50034, 0, 16, 4, /* IPRNS3 */ { CMT4, DISP, DSRV, 0 } },
- { 0xffd50038, 0, 16, 4, /* IPROS3 */ { 0/*MFIS2*/, CPORTS2R, 0, 0 } },
- /* IPRPS3 */
-};
-
-static struct resource intcs_resources[] __initdata = {
- [0] = {
- .start = 0xffd20000,
- .end = 0xffd201ff,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 0xffd50000,
- .end = 0xffd501ff,
- .flags = IORESOURCE_MEM,
- }
-};
-
-static struct intc_desc intcs_desc __initdata = {
- .name = "r8a7740-intcs",
- .resource = intcs_resources,
- .num_resources = ARRAY_SIZE(intcs_resources),
- .hw = INTC_HW_DESC(intcs_vectors, intcs_groups, intcs_mask_registers,
- intcs_prio_registers, NULL, NULL),
-};
-
-static void intcs_demux(unsigned int irq, struct irq_desc *desc)
-{
- void __iomem *reg = (void *)irq_get_handler_data(irq);
- unsigned int evtcodeas = ioread32(reg);
-
- generic_handle_irq(intcs_evt2irq(evtcodeas));
-}
+#include <linux/irqchip/arm-gic.h>
void __init r8a7740_init_irq(void)
{
- void __iomem *intevtsa = ioremap_nocache(0xffd20100, PAGE_SIZE);
-
- register_intc_controller(&intca_desc);
- register_intc_controller(&intca_irq_pins_desc);
- register_intc_controller(&intcs_desc);
-
- /* demux using INTEVTSA */
- irq_set_handler_data(evt2irq(0xf80), (void *)intevtsa);
- irq_set_chained_handler(evt2irq(0xf80), intcs_demux);
+ void __iomem *gic_dist_base = ioremap_nocache(0xc2800000, 0x1000);
+ void __iomem *gic_cpu_base = ioremap_nocache(0xc2000000, 0x1000);
+ void __iomem *intc_prio_base = ioremap_nocache(0xe6900010, 0x10);
+ void __iomem *intc_msk_base = ioremap_nocache(0xe6900040, 0x10);
+ void __iomem *pfc_inta_ctrl = ioremap_nocache(0xe605807c, 0x4);
+
+ /* initialize the Generic Interrupt Controller PL390 r0p0 */
+ gic_init(0, 29, gic_dist_base, gic_cpu_base);
+
+ /* route signals to GIC */
+ iowrite32(0x0, pfc_inta_ctrl);
+
+ /*
+ * To mask the shared interrupt to SPI 149 we must ensure to set
+ * PRIO *and* MASK. Else we run into IRQ floods when registering
+ * the intc_irqpin devices
+ */
+ iowrite32(0x0, intc_prio_base + 0x0);
+ iowrite32(0x0, intc_prio_base + 0x4);
+ iowrite32(0x0, intc_prio_base + 0x8);
+ iowrite32(0x0, intc_prio_base + 0xc);
+ iowrite8(0xff, intc_msk_base + 0x0);
+ iowrite8(0xff, intc_msk_base + 0x4);
+ iowrite8(0xff, intc_msk_base + 0x8);
+ iowrite8(0xff, intc_msk_base + 0xc);
+
+ iounmap(intc_prio_base);
+ iounmap(intc_msk_base);
+ iounmap(pfc_inta_ctrl);
}
diff --git a/arch/arm/mach-shmobile/setup-emev2.c b/arch/arm/mach-shmobile/setup-emev2.c
index e4545c152722..899a86c31ec9 100644
--- a/arch/arm/mach-shmobile/setup-emev2.c
+++ b/arch/arm/mach-shmobile/setup-emev2.c
@@ -456,7 +456,6 @@ DT_MACHINE_START(EMEV2_DT, "Generic Emma Mobile EV2 (Flattened Device Tree)")
.nr_irqs = NR_IRQS_LEGACY,
.init_irq = irqchip_init,
.init_machine = emev2_add_standard_devices_dt,
- .init_time = shmobile_timer_init,
.dt_compat = emev2_boards_compat_dt,
MACHINE_END
diff --git a/arch/arm/mach-shmobile/setup-r8a73a4.c b/arch/arm/mach-shmobile/setup-r8a73a4.c
new file mode 100644
index 000000000000..c5a75a7a508f
--- /dev/null
+++ b/arch/arm/mach-shmobile/setup-r8a73a4.c
@@ -0,0 +1,202 @@
+/*
+ * r8a73a4 processor support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/irq-renesas-irqc.h>
+#include <linux/serial_sci.h>
+#include <mach/common.h>
+#include <mach/irqs.h>
+#include <mach/r8a73a4.h>
+#include <asm/mach/arch.h>
+
+static const struct resource pfc_resources[] = {
+ DEFINE_RES_MEM(0xe6050000, 0x9000),
+};
+
+void __init r8a73a4_pinmux_init(void)
+{
+ platform_device_register_simple("pfc-r8a73a4", -1, pfc_resources,
+ ARRAY_SIZE(pfc_resources));
+}
+
+#define SCIF_COMMON(scif_type, baseaddr, irq) \
+ .type = scif_type, \
+ .mapbase = baseaddr, \
+ .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, \
+ .scbrr_algo_id = SCBRR_ALGO_4, \
+ .irqs = SCIx_IRQ_MUXED(irq)
+
+#define SCIFA_DATA(index, baseaddr, irq) \
+[index] = { \
+ SCIF_COMMON(PORT_SCIFA, baseaddr, irq), \
+ .scscr = SCSCR_RE | SCSCR_TE | SCSCR_CKE0, \
+}
+
+#define SCIFB_DATA(index, baseaddr, irq) \
+[index] = { \
+ SCIF_COMMON(PORT_SCIFB, baseaddr, irq), \
+ .scscr = SCSCR_RE | SCSCR_TE, \
+}
+
+enum { SCIFA0, SCIFA1, SCIFB0, SCIFB1, SCIFB2, SCIFB3 };
+
+static const struct plat_sci_port scif[] = {
+ SCIFA_DATA(SCIFA0, 0xe6c40000, gic_spi(144)), /* SCIFA0 */
+ SCIFA_DATA(SCIFA1, 0xe6c50000, gic_spi(145)), /* SCIFA1 */
+ SCIFB_DATA(SCIFB0, 0xe6c50000, gic_spi(145)), /* SCIFB0 */
+ SCIFB_DATA(SCIFB1, 0xe6c30000, gic_spi(149)), /* SCIFB1 */
+ SCIFB_DATA(SCIFB2, 0xe6ce0000, gic_spi(150)), /* SCIFB2 */
+ SCIFB_DATA(SCIFB3, 0xe6cf0000, gic_spi(151)), /* SCIFB3 */
+};
+
+static inline void r8a73a4_register_scif(int idx)
+{
+ platform_device_register_data(&platform_bus, "sh-sci", idx, &scif[idx],
+ sizeof(struct plat_sci_port));
+}
+
+static const struct renesas_irqc_config irqc0_data = {
+ .irq_base = irq_pin(0), /* IRQ0 -> IRQ31 */
+};
+
+static const struct resource irqc0_resources[] = {
+ DEFINE_RES_MEM(0xe61c0000, 0x200), /* IRQC Event Detector Block_0 */
+ DEFINE_RES_IRQ(gic_spi(0)), /* IRQ0 */
+ DEFINE_RES_IRQ(gic_spi(1)), /* IRQ1 */
+ DEFINE_RES_IRQ(gic_spi(2)), /* IRQ2 */
+ DEFINE_RES_IRQ(gic_spi(3)), /* IRQ3 */
+ DEFINE_RES_IRQ(gic_spi(4)), /* IRQ4 */
+ DEFINE_RES_IRQ(gic_spi(5)), /* IRQ5 */
+ DEFINE_RES_IRQ(gic_spi(6)), /* IRQ6 */
+ DEFINE_RES_IRQ(gic_spi(7)), /* IRQ7 */
+ DEFINE_RES_IRQ(gic_spi(8)), /* IRQ8 */
+ DEFINE_RES_IRQ(gic_spi(9)), /* IRQ9 */
+ DEFINE_RES_IRQ(gic_spi(10)), /* IRQ10 */
+ DEFINE_RES_IRQ(gic_spi(11)), /* IRQ11 */
+ DEFINE_RES_IRQ(gic_spi(12)), /* IRQ12 */
+ DEFINE_RES_IRQ(gic_spi(13)), /* IRQ13 */
+ DEFINE_RES_IRQ(gic_spi(14)), /* IRQ14 */
+ DEFINE_RES_IRQ(gic_spi(15)), /* IRQ15 */
+ DEFINE_RES_IRQ(gic_spi(16)), /* IRQ16 */
+ DEFINE_RES_IRQ(gic_spi(17)), /* IRQ17 */
+ DEFINE_RES_IRQ(gic_spi(18)), /* IRQ18 */
+ DEFINE_RES_IRQ(gic_spi(19)), /* IRQ19 */
+ DEFINE_RES_IRQ(gic_spi(20)), /* IRQ20 */
+ DEFINE_RES_IRQ(gic_spi(21)), /* IRQ21 */
+ DEFINE_RES_IRQ(gic_spi(22)), /* IRQ22 */
+ DEFINE_RES_IRQ(gic_spi(23)), /* IRQ23 */
+ DEFINE_RES_IRQ(gic_spi(24)), /* IRQ24 */
+ DEFINE_RES_IRQ(gic_spi(25)), /* IRQ25 */
+ DEFINE_RES_IRQ(gic_spi(26)), /* IRQ26 */
+ DEFINE_RES_IRQ(gic_spi(27)), /* IRQ27 */
+ DEFINE_RES_IRQ(gic_spi(28)), /* IRQ28 */
+ DEFINE_RES_IRQ(gic_spi(29)), /* IRQ29 */
+ DEFINE_RES_IRQ(gic_spi(30)), /* IRQ30 */
+ DEFINE_RES_IRQ(gic_spi(31)), /* IRQ31 */
+};
+
+static const struct renesas_irqc_config irqc1_data = {
+ .irq_base = irq_pin(32), /* IRQ32 -> IRQ57 */
+};
+
+static const struct resource irqc1_resources[] = {
+ DEFINE_RES_MEM(0xe61c0200, 0x200), /* IRQC Event Detector Block_1 */
+ DEFINE_RES_IRQ(gic_spi(32)), /* IRQ32 */
+ DEFINE_RES_IRQ(gic_spi(33)), /* IRQ33 */
+ DEFINE_RES_IRQ(gic_spi(34)), /* IRQ34 */
+ DEFINE_RES_IRQ(gic_spi(35)), /* IRQ35 */
+ DEFINE_RES_IRQ(gic_spi(36)), /* IRQ36 */
+ DEFINE_RES_IRQ(gic_spi(37)), /* IRQ37 */
+ DEFINE_RES_IRQ(gic_spi(38)), /* IRQ38 */
+ DEFINE_RES_IRQ(gic_spi(39)), /* IRQ39 */
+ DEFINE_RES_IRQ(gic_spi(40)), /* IRQ40 */
+ DEFINE_RES_IRQ(gic_spi(41)), /* IRQ41 */
+ DEFINE_RES_IRQ(gic_spi(42)), /* IRQ42 */
+ DEFINE_RES_IRQ(gic_spi(43)), /* IRQ43 */
+ DEFINE_RES_IRQ(gic_spi(44)), /* IRQ44 */
+ DEFINE_RES_IRQ(gic_spi(45)), /* IRQ45 */
+ DEFINE_RES_IRQ(gic_spi(46)), /* IRQ46 */
+ DEFINE_RES_IRQ(gic_spi(47)), /* IRQ47 */
+ DEFINE_RES_IRQ(gic_spi(48)), /* IRQ48 */
+ DEFINE_RES_IRQ(gic_spi(49)), /* IRQ49 */
+ DEFINE_RES_IRQ(gic_spi(50)), /* IRQ50 */
+ DEFINE_RES_IRQ(gic_spi(51)), /* IRQ51 */
+ DEFINE_RES_IRQ(gic_spi(52)), /* IRQ52 */
+ DEFINE_RES_IRQ(gic_spi(53)), /* IRQ53 */
+ DEFINE_RES_IRQ(gic_spi(54)), /* IRQ54 */
+ DEFINE_RES_IRQ(gic_spi(55)), /* IRQ55 */
+ DEFINE_RES_IRQ(gic_spi(56)), /* IRQ56 */
+ DEFINE_RES_IRQ(gic_spi(57)), /* IRQ57 */
+};
+
+#define r8a73a4_register_irqc(idx) \
+ platform_device_register_resndata(&platform_bus, "renesas_irqc", \
+ idx, irqc##idx##_resources, \
+ ARRAY_SIZE(irqc##idx##_resources), \
+ &irqc##idx##_data, \
+ sizeof(struct renesas_irqc_config))
+
+/* Thermal0 -> Thermal2 */
+static const struct resource thermal0_resources[] = {
+ DEFINE_RES_MEM(0xe61f0000, 0x14),
+ DEFINE_RES_MEM(0xe61f0100, 0x38),
+ DEFINE_RES_MEM(0xe61f0200, 0x38),
+ DEFINE_RES_MEM(0xe61f0300, 0x38),
+ DEFINE_RES_IRQ(gic_spi(69)),
+};
+
+#define r8a73a4_register_thermal() \
+ platform_device_register_simple("rcar_thermal", -1, \
+ thermal0_resources, \
+ ARRAY_SIZE(thermal0_resources))
+
+void __init r8a73a4_add_standard_devices(void)
+{
+ r8a73a4_register_scif(SCIFA0);
+ r8a73a4_register_scif(SCIFA1);
+ r8a73a4_register_scif(SCIFB0);
+ r8a73a4_register_scif(SCIFB1);
+ r8a73a4_register_scif(SCIFB2);
+ r8a73a4_register_scif(SCIFB3);
+ r8a73a4_register_irqc(0);
+ r8a73a4_register_irqc(1);
+ r8a73a4_register_thermal();
+}
+
+#ifdef CONFIG_USE_OF
+void __init r8a73a4_add_standard_devices_dt(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char *r8a73a4_boards_compat_dt[] __initdata = {
+ "renesas,r8a73a4",
+ NULL,
+};
+
+DT_MACHINE_START(R8A73A4_DT, "Generic R8A73A4 (Flattened Device Tree)")
+ .init_irq = irqchip_init,
+ .init_machine = r8a73a4_add_standard_devices_dt,
+ .init_time = shmobile_timer_init,
+ .dt_compat = r8a73a4_boards_compat_dt,
+MACHINE_END
+#endif /* CONFIG_USE_OF */
diff --git a/arch/arm/mach-shmobile/setup-r8a7740.c b/arch/arm/mach-shmobile/setup-r8a7740.c
index 8b85d4d8fab6..326a4ab0bd5f 100644
--- a/arch/arm/mach-shmobile/setup-r8a7740.c
+++ b/arch/arm/mach-shmobile/setup-r8a7740.c
@@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/platform_data/irq-renesas-intc-irqpin.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/serial_sci.h>
@@ -94,6 +95,126 @@ void __init r8a7740_pinmux_init(void)
platform_device_register(&r8a7740_pfc_device);
}
+static struct renesas_intc_irqpin_config irqpin0_platform_data = {
+ .irq_base = irq_pin(0), /* IRQ0 -> IRQ7 */
+};
+
+static struct resource irqpin0_resources[] = {
+ DEFINE_RES_MEM(0xe6900000, 4), /* ICR1A */
+ DEFINE_RES_MEM(0xe6900010, 4), /* INTPRI00A */
+ DEFINE_RES_MEM(0xe6900020, 1), /* INTREQ00A */
+ DEFINE_RES_MEM(0xe6900040, 1), /* INTMSK00A */
+ DEFINE_RES_MEM(0xe6900060, 1), /* INTMSKCLR00A */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ0 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ1 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ2 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ3 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ4 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ5 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ6 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ7 */
+};
+
+static struct platform_device irqpin0_device = {
+ .name = "renesas_intc_irqpin",
+ .id = 0,
+ .resource = irqpin0_resources,
+ .num_resources = ARRAY_SIZE(irqpin0_resources),
+ .dev = {
+ .platform_data = &irqpin0_platform_data,
+ },
+};
+
+static struct renesas_intc_irqpin_config irqpin1_platform_data = {
+ .irq_base = irq_pin(8), /* IRQ8 -> IRQ15 */
+};
+
+static struct resource irqpin1_resources[] = {
+ DEFINE_RES_MEM(0xe6900004, 4), /* ICR2A */
+ DEFINE_RES_MEM(0xe6900014, 4), /* INTPRI10A */
+ DEFINE_RES_MEM(0xe6900024, 1), /* INTREQ10A */
+ DEFINE_RES_MEM(0xe6900044, 1), /* INTMSK10A */
+ DEFINE_RES_MEM(0xe6900064, 1), /* INTMSKCLR10A */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ8 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ9 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ10 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ11 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ12 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ13 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ14 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ15 */
+};
+
+static struct platform_device irqpin1_device = {
+ .name = "renesas_intc_irqpin",
+ .id = 1,
+ .resource = irqpin1_resources,
+ .num_resources = ARRAY_SIZE(irqpin1_resources),
+ .dev = {
+ .platform_data = &irqpin1_platform_data,
+ },
+};
+
+static struct renesas_intc_irqpin_config irqpin2_platform_data = {
+ .irq_base = irq_pin(16), /* IRQ16 -> IRQ23 */
+};
+
+static struct resource irqpin2_resources[] = {
+ DEFINE_RES_MEM(0xe6900008, 4), /* ICR3A */
+ DEFINE_RES_MEM(0xe6900018, 4), /* INTPRI30A */
+ DEFINE_RES_MEM(0xe6900028, 1), /* INTREQ30A */
+ DEFINE_RES_MEM(0xe6900048, 1), /* INTMSK30A */
+ DEFINE_RES_MEM(0xe6900068, 1), /* INTMSKCLR30A */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ16 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ17 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ18 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ19 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ20 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ21 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ22 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ23 */
+};
+
+static struct platform_device irqpin2_device = {
+ .name = "renesas_intc_irqpin",
+ .id = 2,
+ .resource = irqpin2_resources,
+ .num_resources = ARRAY_SIZE(irqpin2_resources),
+ .dev = {
+ .platform_data = &irqpin2_platform_data,
+ },
+};
+
+static struct renesas_intc_irqpin_config irqpin3_platform_data = {
+ .irq_base = irq_pin(24), /* IRQ24 -> IRQ31 */
+};
+
+static struct resource irqpin3_resources[] = {
+ DEFINE_RES_MEM(0xe690000c, 4), /* ICR3A */
+ DEFINE_RES_MEM(0xe690001c, 4), /* INTPRI30A */
+ DEFINE_RES_MEM(0xe690002c, 1), /* INTREQ30A */
+ DEFINE_RES_MEM(0xe690004c, 1), /* INTMSK30A */
+ DEFINE_RES_MEM(0xe690006c, 1), /* INTMSKCLR30A */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ24 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ25 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ26 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ27 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ28 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ29 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ30 */
+ DEFINE_RES_IRQ(gic_spi(149)), /* IRQ31 */
+};
+
+static struct platform_device irqpin3_device = {
+ .name = "renesas_intc_irqpin",
+ .id = 3,
+ .resource = irqpin3_resources,
+ .num_resources = ARRAY_SIZE(irqpin3_resources),
+ .dev = {
+ .platform_data = &irqpin3_platform_data,
+ },
+};
+
/* SCIFA0 */
static struct plat_sci_port scif0_platform_data = {
.mapbase = 0xe6c40000,
@@ -101,7 +222,7 @@ static struct plat_sci_port scif0_platform_data = {
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIFA,
- .irqs = SCIx_IRQ_MUXED(evt2irq(0x0c00)),
+ .irqs = SCIx_IRQ_MUXED(gic_spi(100)),
};
static struct platform_device scif0_device = {
@@ -119,7 +240,7 @@ static struct plat_sci_port scif1_platform_data = {
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIFA,
- .irqs = SCIx_IRQ_MUXED(evt2irq(0x0c20)),
+ .irqs = SCIx_IRQ_MUXED(gic_spi(101)),
};
static struct platform_device scif1_device = {
@@ -137,7 +258,7 @@ static struct plat_sci_port scif2_platform_data = {
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIFA,
- .irqs = SCIx_IRQ_MUXED(evt2irq(0x0c40)),
+ .irqs = SCIx_IRQ_MUXED(gic_spi(102)),
};
static struct platform_device scif2_device = {
@@ -155,7 +276,7 @@ static struct plat_sci_port scif3_platform_data = {
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIFA,
- .irqs = SCIx_IRQ_MUXED(evt2irq(0x0c60)),
+ .irqs = SCIx_IRQ_MUXED(gic_spi(103)),
};
static struct platform_device scif3_device = {
@@ -173,7 +294,7 @@ static struct plat_sci_port scif4_platform_data = {
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIFA,
- .irqs = SCIx_IRQ_MUXED(evt2irq(0x0d20)),
+ .irqs = SCIx_IRQ_MUXED(gic_spi(104)),
};
static struct platform_device scif4_device = {
@@ -191,7 +312,7 @@ static struct plat_sci_port scif5_platform_data = {
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIFA,
- .irqs = SCIx_IRQ_MUXED(evt2irq(0x0d40)),
+ .irqs = SCIx_IRQ_MUXED(gic_spi(105)),
};
static struct platform_device scif5_device = {
@@ -209,7 +330,7 @@ static struct plat_sci_port scif6_platform_data = {
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIFA,
- .irqs = SCIx_IRQ_MUXED(evt2irq(0x04c0)),
+ .irqs = SCIx_IRQ_MUXED(gic_spi(106)),
};
static struct platform_device scif6_device = {
@@ -227,7 +348,7 @@ static struct plat_sci_port scif7_platform_data = {
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIFA,
- .irqs = SCIx_IRQ_MUXED(evt2irq(0x04e0)),
+ .irqs = SCIx_IRQ_MUXED(gic_spi(107)),
};
static struct platform_device scif7_device = {
@@ -245,7 +366,7 @@ static struct plat_sci_port scifb_platform_data = {
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIFB,
- .irqs = SCIx_IRQ_MUXED(evt2irq(0x0d60)),
+ .irqs = SCIx_IRQ_MUXED(gic_spi(108)),
};
static struct platform_device scifb_device = {
@@ -273,7 +394,7 @@ static struct resource cmt10_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = evt2irq(0x0b00),
+ .start = gic_spi(58),
.flags = IORESOURCE_IRQ,
},
};
@@ -304,7 +425,7 @@ static struct resource tmu00_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = intcs_evt2irq(0xe80),
+ .start = gic_spi(198),
.flags = IORESOURCE_IRQ,
},
};
@@ -334,7 +455,7 @@ static struct resource tmu01_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = intcs_evt2irq(0xea0),
+ .start = gic_spi(199),
.flags = IORESOURCE_IRQ,
},
};
@@ -364,7 +485,7 @@ static struct resource tmu02_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = intcs_evt2irq(0xec0),
+ .start = gic_spi(200),
.flags = IORESOURCE_IRQ,
},
};
@@ -411,6 +532,10 @@ static struct platform_device ipmmu_device = {
};
static struct platform_device *r8a7740_early_devices[] __initdata = {
+ &irqpin0_device,
+ &irqpin1_device,
+ &irqpin2_device,
+ &irqpin3_device,
&scif0_device,
&scif1_device,
&scif2_device,
@@ -525,14 +650,14 @@ static struct resource r8a7740_dmae0_resources[] = {
},
{
.name = "error_irq",
- .start = evt2irq(0x20c0),
- .end = evt2irq(0x20c0),
+ .start = gic_spi(34),
+ .end = gic_spi(34),
.flags = IORESOURCE_IRQ,
},
{
/* IRQ for channels 0-5 */
- .start = evt2irq(0x2000),
- .end = evt2irq(0x20a0),
+ .start = gic_spi(28),
+ .end = gic_spi(33),
.flags = IORESOURCE_IRQ,
},
};
@@ -553,14 +678,14 @@ static struct resource r8a7740_dmae1_resources[] = {
},
{
.name = "error_irq",
- .start = evt2irq(0x21c0),
- .end = evt2irq(0x21c0),
+ .start = gic_spi(41),
+ .end = gic_spi(41),
.flags = IORESOURCE_IRQ,
},
{
/* IRQ for channels 0-5 */
- .start = evt2irq(0x2100),
- .end = evt2irq(0x21a0),
+ .start = gic_spi(35),
+ .end = gic_spi(40),
.flags = IORESOURCE_IRQ,
},
};
@@ -581,14 +706,14 @@ static struct resource r8a7740_dmae2_resources[] = {
},
{
.name = "error_irq",
- .start = evt2irq(0x22c0),
- .end = evt2irq(0x22c0),
+ .start = gic_spi(48),
+ .end = gic_spi(48),
.flags = IORESOURCE_IRQ,
},
{
/* IRQ for channels 0-5 */
- .start = evt2irq(0x2200),
- .end = evt2irq(0x22a0),
+ .start = gic_spi(42),
+ .end = gic_spi(47),
.flags = IORESOURCE_IRQ,
},
};
@@ -677,8 +802,8 @@ static struct resource r8a7740_usb_dma_resources[] = {
},
{
/* IRQ for channels */
- .start = evt2irq(0x0a00),
- .end = evt2irq(0x0a00),
+ .start = gic_spi(49),
+ .end = gic_spi(49),
.flags = IORESOURCE_IRQ,
},
};
@@ -702,8 +827,8 @@ static struct resource i2c0_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = intcs_evt2irq(0xe00),
- .end = intcs_evt2irq(0xe60),
+ .start = gic_spi(201),
+ .end = gic_spi(204),
.flags = IORESOURCE_IRQ,
},
};
@@ -716,8 +841,8 @@ static struct resource i2c1_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = evt2irq(0x780), /* IIC1_ALI1 */
- .end = evt2irq(0x7e0), /* IIC1_DTEI1 */
+ .start = gic_spi(70), /* IIC1_ALI1 */
+ .end = gic_spi(73), /* IIC1_DTEI1 */
.flags = IORESOURCE_IRQ,
},
};
@@ -738,8 +863,8 @@ static struct platform_device i2c1_device = {
static struct resource pmu_resources[] = {
[0] = {
- .start = evt2irq(0x19a0),
- .end = evt2irq(0x19a0),
+ .start = gic_spi(83),
+ .end = gic_spi(83),
.flags = IORESOURCE_IRQ,
},
};
@@ -904,9 +1029,7 @@ DT_MACHINE_START(R8A7740_DT, "Generic R8A7740 (Flattened Device Tree)")
.map_io = r8a7740_map_io,
.init_early = r8a7740_add_early_devices_dt,
.init_irq = r8a7740_init_irq,
- .handle_irq = shmobile_handle_irq_intc,
.init_machine = r8a7740_add_standard_devices_dt,
- .init_time = shmobile_timer_init,
.dt_compat = r8a7740_boards_compat_dt,
MACHINE_END
diff --git a/arch/arm/mach-shmobile/setup-r8a7778.c b/arch/arm/mach-shmobile/setup-r8a7778.c
new file mode 100644
index 000000000000..30b4a336308f
--- /dev/null
+++ b/arch/arm/mach-shmobile/setup-r8a7778.c
@@ -0,0 +1,244 @@
+/*
+ * r8a7778 processor support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ * Copyright (C) 2013 Cogent Embedded, 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/irq-renesas-intc-irqpin.h>
+#include <linux/platform_device.h>
+#include <linux/irqchip.h>
+#include <linux/serial_sci.h>
+#include <linux/sh_timer.h>
+#include <mach/irqs.h>
+#include <mach/r8a7778.h>
+#include <mach/common.h>
+#include <asm/mach/arch.h>
+#include <asm/hardware/cache-l2x0.h>
+
+/* SCIF */
+#define SCIF_INFO(baseaddr, irq) \
+{ \
+ .mapbase = baseaddr, \
+ .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, \
+ .scscr = SCSCR_RE | SCSCR_TE | SCSCR_CKE1, \
+ .scbrr_algo_id = SCBRR_ALGO_2, \
+ .type = PORT_SCIF, \
+ .irqs = SCIx_IRQ_MUXED(irq), \
+}
+
+static struct plat_sci_port scif_platform_data[] = {
+ SCIF_INFO(0xffe40000, gic_iid(0x66)),
+ SCIF_INFO(0xffe41000, gic_iid(0x67)),
+ SCIF_INFO(0xffe42000, gic_iid(0x68)),
+ SCIF_INFO(0xffe43000, gic_iid(0x69)),
+ SCIF_INFO(0xffe44000, gic_iid(0x6a)),
+ SCIF_INFO(0xffe45000, gic_iid(0x6b)),
+};
+
+/* TMU */
+static struct resource sh_tmu0_resources[] = {
+ DEFINE_RES_MEM(0xffd80008, 12),
+ DEFINE_RES_IRQ(gic_iid(0x40)),
+};
+
+static struct sh_timer_config sh_tmu0_platform_data = {
+ .name = "TMU00",
+ .channel_offset = 0x4,
+ .timer_bit = 0,
+ .clockevent_rating = 200,
+};
+
+static struct resource sh_tmu1_resources[] = {
+ DEFINE_RES_MEM(0xffd80014, 12),
+ DEFINE_RES_IRQ(gic_iid(0x41)),
+};
+
+static struct sh_timer_config sh_tmu1_platform_data = {
+ .name = "TMU01",
+ .channel_offset = 0x10,
+ .timer_bit = 1,
+ .clocksource_rating = 200,
+};
+
+/* Ether */
+static struct resource ether_resources[] = {
+ DEFINE_RES_MEM(0xfde00000, 0x400),
+ DEFINE_RES_IRQ(gic_iid(0x89)),
+};
+
+#define r8a7778_register_tmu(idx) \
+ platform_device_register_resndata( \
+ &platform_bus, "sh_tmu", idx, \
+ sh_tmu##idx##_resources, \
+ ARRAY_SIZE(sh_tmu##idx##_resources), \
+ &sh_tmu##idx##_platform_data, \
+ sizeof(sh_tmu##idx##_platform_data))
+
+void __init r8a7778_add_standard_devices(void)
+{
+ int i;
+
+#ifdef CONFIG_CACHE_L2X0
+ void __iomem *base = ioremap_nocache(0xf0100000, 0x1000);
+ if (base) {
+ /*
+ * Early BRESP enable, Shared attribute override enable, 64K*16way
+ * don't call iounmap(base)
+ */
+ l2x0_init(base, 0x40470000, 0x82000fff);
+ }
+#endif
+
+ for (i = 0; i < ARRAY_SIZE(scif_platform_data); i++)
+ platform_device_register_data(&platform_bus, "sh-sci", i,
+ &scif_platform_data[i],
+ sizeof(struct plat_sci_port));
+
+ r8a7778_register_tmu(0);
+ r8a7778_register_tmu(1);
+}
+
+void __init r8a7778_add_ether_device(struct sh_eth_plat_data *pdata)
+{
+ platform_device_register_resndata(&platform_bus, "sh_eth", -1,
+ ether_resources,
+ ARRAY_SIZE(ether_resources),
+ pdata, sizeof(*pdata));
+}
+
+static struct renesas_intc_irqpin_config irqpin_platform_data = {
+ .irq_base = irq_pin(0), /* IRQ0 -> IRQ3 */
+ .sense_bitfield_width = 2,
+};
+
+static struct resource irqpin_resources[] = {
+ DEFINE_RES_MEM(0xfe78001c, 4), /* ICR1 */
+ DEFINE_RES_MEM(0xfe780010, 4), /* INTPRI */
+ DEFINE_RES_MEM(0xfe780024, 4), /* INTREQ */
+ DEFINE_RES_MEM(0xfe780044, 4), /* INTMSK0 */
+ DEFINE_RES_MEM(0xfe780064, 4), /* INTMSKCLR0 */
+ DEFINE_RES_IRQ(gic_iid(0x3b)), /* IRQ0 */
+ DEFINE_RES_IRQ(gic_iid(0x3c)), /* IRQ1 */
+ DEFINE_RES_IRQ(gic_iid(0x3d)), /* IRQ2 */
+ DEFINE_RES_IRQ(gic_iid(0x3e)), /* IRQ3 */
+};
+
+void __init r8a7778_init_irq_extpin(int irlm)
+{
+ void __iomem *icr0 = ioremap_nocache(0xfe780000, PAGE_SIZE);
+ unsigned long tmp;
+
+ if (!icr0) {
+ pr_warn("r8a7778: unable to setup external irq pin mode\n");
+ return;
+ }
+
+ tmp = ioread32(icr0);
+ if (irlm)
+ tmp |= 1 << 23; /* IRQ0 -> IRQ3 as individual pins */
+ else
+ tmp &= ~(1 << 23); /* IRL mode - not supported */
+ tmp |= (1 << 21); /* LVLMODE = 1 */
+ iowrite32(tmp, icr0);
+ iounmap(icr0);
+
+ if (irlm)
+ platform_device_register_resndata(
+ &platform_bus, "renesas_intc_irqpin", -1,
+ irqpin_resources, ARRAY_SIZE(irqpin_resources),
+ &irqpin_platform_data, sizeof(irqpin_platform_data));
+}
+
+#define INT2SMSKCR0 0x82288 /* 0xfe782288 */
+#define INT2SMSKCR1 0x8228c /* 0xfe78228c */
+
+#define INT2NTSR0 0x00018 /* 0xfe700018 */
+#define INT2NTSR1 0x0002c /* 0xfe70002c */
+static void __init r8a7778_init_irq_common(void)
+{
+ void __iomem *base = ioremap_nocache(0xfe700000, 0x00100000);
+
+ BUG_ON(!base);
+
+ /* route all interrupts to ARM */
+ __raw_writel(0x73ffffff, base + INT2NTSR0);
+ __raw_writel(0xffffffff, base + INT2NTSR1);
+
+ /* unmask all known interrupts in INTCS2 */
+ __raw_writel(0x08330773, base + INT2SMSKCR0);
+ __raw_writel(0x00311110, base + INT2SMSKCR1);
+
+ iounmap(base);
+}
+
+void __init r8a7778_init_irq(void)
+{
+ void __iomem *gic_dist_base;
+ void __iomem *gic_cpu_base;
+
+ gic_dist_base = ioremap_nocache(0xfe438000, PAGE_SIZE);
+ gic_cpu_base = ioremap_nocache(0xfe430000, PAGE_SIZE);
+ BUG_ON(!gic_dist_base || !gic_cpu_base);
+
+ /* use GIC to handle interrupts */
+ gic_init(0, 29, gic_dist_base, gic_cpu_base);
+
+ r8a7778_init_irq_common();
+}
+
+void __init r8a7778_init_delay(void)
+{
+ shmobile_setup_delay(800, 1, 3); /* Cortex-A9 @ 800MHz */
+}
+
+#ifdef CONFIG_USE_OF
+void __init r8a7778_init_irq_dt(void)
+{
+ irqchip_init();
+ r8a7778_init_irq_common();
+}
+
+static const struct of_dev_auxdata r8a7778_auxdata_lookup[] __initconst = {
+ {},
+};
+
+void __init r8a7778_add_standard_devices_dt(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table,
+ r8a7778_auxdata_lookup, NULL);
+}
+
+static const char *r8a7778_compat_dt[] __initdata = {
+ "renesas,r8a7778",
+ NULL,
+};
+
+DT_MACHINE_START(R8A7778_DT, "Generic R8A7778 (Flattened Device Tree)")
+ .init_early = r8a7778_init_delay,
+ .init_irq = r8a7778_init_irq_dt,
+ .init_machine = r8a7778_add_standard_devices_dt,
+ .init_time = shmobile_timer_init,
+ .dt_compat = r8a7778_compat_dt,
+MACHINE_END
+
+#endif /* CONFIG_USE_OF */
diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c
index 042df35e71a0..b0b394842ea5 100644
--- a/arch/arm/mach-shmobile/setup-r8a7779.c
+++ b/arch/arm/mach-shmobile/setup-r8a7779.c
@@ -1,8 +1,9 @@
/*
* r8a7779 processor support
*
- * Copyright (C) 2011 Renesas Solutions Corp.
+ * Copyright (C) 2011, 2013 Renesas Solutions Corp.
* Copyright (C) 2011 Magnus Damm
+ * Copyright (C) 2013 Cogent Embedded, 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
@@ -22,6 +23,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of_platform.h>
+#include <linux/platform_data/gpio-rcar.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/input.h>
@@ -68,11 +70,6 @@ static struct resource r8a7779_pfc_resources[] = {
.end = 0xfffc023b,
.flags = IORESOURCE_MEM,
},
- [1] = {
- .start = 0xffc40000,
- .end = 0xffc46fff,
- .flags = IORESOURCE_MEM,
- }
};
static struct platform_device r8a7779_pfc_device = {
@@ -82,9 +79,59 @@ static struct platform_device r8a7779_pfc_device = {
.num_resources = ARRAY_SIZE(r8a7779_pfc_resources),
};
+#define R8A7779_GPIO(idx, npins) \
+static struct resource r8a7779_gpio##idx##_resources[] = { \
+ [0] = { \
+ .start = 0xffc40000 + 0x1000 * (idx), \
+ .end = 0xffc4002b + 0x1000 * (idx), \
+ .flags = IORESOURCE_MEM, \
+ }, \
+ [1] = { \
+ .start = gic_iid(0xad + (idx)), \
+ .flags = IORESOURCE_IRQ, \
+ } \
+}; \
+ \
+static struct gpio_rcar_config r8a7779_gpio##idx##_platform_data = { \
+ .gpio_base = 32 * (idx), \
+ .irq_base = 0, \
+ .number_of_pins = npins, \
+ .pctl_name = "pfc-r8a7779", \
+}; \
+ \
+static struct platform_device r8a7779_gpio##idx##_device = { \
+ .name = "gpio_rcar", \
+ .id = idx, \
+ .resource = r8a7779_gpio##idx##_resources, \
+ .num_resources = ARRAY_SIZE(r8a7779_gpio##idx##_resources), \
+ .dev = { \
+ .platform_data = &r8a7779_gpio##idx##_platform_data, \
+ }, \
+}
+
+R8A7779_GPIO(0, 32);
+R8A7779_GPIO(1, 32);
+R8A7779_GPIO(2, 32);
+R8A7779_GPIO(3, 32);
+R8A7779_GPIO(4, 32);
+R8A7779_GPIO(5, 32);
+R8A7779_GPIO(6, 9);
+
+static struct platform_device *r8a7779_pinctrl_devices[] __initdata = {
+ &r8a7779_pfc_device,
+ &r8a7779_gpio0_device,
+ &r8a7779_gpio1_device,
+ &r8a7779_gpio2_device,
+ &r8a7779_gpio3_device,
+ &r8a7779_gpio4_device,
+ &r8a7779_gpio5_device,
+ &r8a7779_gpio6_device,
+};
+
void __init r8a7779_pinmux_init(void)
{
- platform_device_register(&r8a7779_pfc_device);
+ platform_add_devices(r8a7779_pinctrl_devices,
+ ARRAY_SIZE(r8a7779_pinctrl_devices));
}
static struct plat_sci_port scif0_platform_data = {
@@ -347,6 +394,18 @@ static struct platform_device sata_device = {
},
};
+/* Ether */
+static struct resource ether_resources[] = {
+ {
+ .start = 0xfde00000,
+ .end = 0xfde003ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = gic_iid(0xb4),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static struct platform_device *r8a7779_devices_dt[] __initdata = {
&scif0_device,
&scif1_device,
@@ -382,6 +441,14 @@ void __init r8a7779_add_standard_devices(void)
ARRAY_SIZE(r8a7779_late_devices));
}
+void __init r8a7779_add_ether_device(struct sh_eth_plat_data *pdata)
+{
+ platform_device_register_resndata(&platform_bus, "sh_eth", -1,
+ ether_resources,
+ ARRAY_SIZE(ether_resources),
+ pdata, sizeof(*pdata));
+}
+
/* do nothing for !CONFIG_SMP or !CONFIG_HAVE_TWD */
void __init __weak r8a7779_register_twd(void) { }
diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c b/arch/arm/mach-shmobile/setup-r8a7790.c
new file mode 100644
index 000000000000..49de2d56f86d
--- /dev/null
+++ b/arch/arm/mach-shmobile/setup-r8a7790.c
@@ -0,0 +1,150 @@
+/*
+ * r8a7790 processor support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/serial_sci.h>
+#include <linux/platform_data/irq-renesas-irqc.h>
+#include <mach/common.h>
+#include <mach/irqs.h>
+#include <mach/r8a7790.h>
+#include <asm/mach/arch.h>
+
+static const struct resource pfc_resources[] = {
+ DEFINE_RES_MEM(0xe6060000, 0x250),
+ DEFINE_RES_MEM(0xe6050000, 0x5050),
+};
+
+void __init r8a7790_pinmux_init(void)
+{
+ platform_device_register_simple("pfc-r8a7790", -1, pfc_resources,
+ ARRAY_SIZE(pfc_resources));
+}
+
+#define SCIF_COMMON(scif_type, baseaddr, irq) \
+ .type = scif_type, \
+ .mapbase = baseaddr, \
+ .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, \
+ .irqs = SCIx_IRQ_MUXED(irq)
+
+#define SCIFA_DATA(index, baseaddr, irq) \
+[index] = { \
+ SCIF_COMMON(PORT_SCIFA, baseaddr, irq), \
+ .scbrr_algo_id = SCBRR_ALGO_4, \
+ .scscr = SCSCR_RE | SCSCR_TE | SCSCR_CKE0, \
+}
+
+#define SCIFB_DATA(index, baseaddr, irq) \
+[index] = { \
+ SCIF_COMMON(PORT_SCIFB, baseaddr, irq), \
+ .scbrr_algo_id = SCBRR_ALGO_4, \
+ .scscr = SCSCR_RE | SCSCR_TE, \
+}
+
+#define SCIF_DATA(index, baseaddr, irq) \
+[index] = { \
+ SCIF_COMMON(PORT_SCIF, baseaddr, irq), \
+ .scbrr_algo_id = SCBRR_ALGO_2, \
+ .scscr = SCSCR_RE | SCSCR_TE | SCSCR_CKE1, \
+}
+
+enum { SCIFA0, SCIFA1, SCIFB0, SCIFB1, SCIFB2, SCIFA2, SCIF0, SCIF1 };
+
+static const struct plat_sci_port scif[] = {
+ SCIFA_DATA(SCIFA0, 0xe6c40000, gic_spi(144)), /* SCIFA0 */
+ SCIFA_DATA(SCIFA1, 0xe6c50000, gic_spi(145)), /* SCIFA1 */
+ SCIFB_DATA(SCIFB0, 0xe6c20000, gic_spi(148)), /* SCIFB0 */
+ SCIFB_DATA(SCIFB1, 0xe6c30000, gic_spi(149)), /* SCIFB1 */
+ SCIFB_DATA(SCIFB2, 0xe6ce0000, gic_spi(150)), /* SCIFB2 */
+ SCIFA_DATA(SCIFA2, 0xe6c60000, gic_spi(151)), /* SCIFA2 */
+ SCIF_DATA(SCIF0, 0xe6e60000, gic_spi(152)), /* SCIF0 */
+ SCIF_DATA(SCIF1, 0xe6e68000, gic_spi(153)), /* SCIF1 */
+};
+
+static inline void r8a7790_register_scif(int idx)
+{
+ platform_device_register_data(&platform_bus, "sh-sci", idx, &scif[idx],
+ sizeof(struct plat_sci_port));
+}
+
+static struct renesas_irqc_config irqc0_data = {
+ .irq_base = irq_pin(0), /* IRQ0 -> IRQ3 */
+};
+
+static struct resource irqc0_resources[] = {
+ DEFINE_RES_MEM(0xe61c0000, 0x200), /* IRQC Event Detector Block_0 */
+ DEFINE_RES_IRQ(gic_spi(0)), /* IRQ0 */
+ DEFINE_RES_IRQ(gic_spi(1)), /* IRQ1 */
+ DEFINE_RES_IRQ(gic_spi(2)), /* IRQ2 */
+ DEFINE_RES_IRQ(gic_spi(3)), /* IRQ3 */
+};
+
+#define r8a7790_register_irqc(idx) \
+ platform_device_register_resndata(&platform_bus, "renesas_irqc", \
+ idx, irqc##idx##_resources, \
+ ARRAY_SIZE(irqc##idx##_resources), \
+ &irqc##idx##_data, \
+ sizeof(struct renesas_irqc_config))
+
+void __init r8a7790_add_standard_devices(void)
+{
+ r8a7790_register_scif(SCIFA0);
+ r8a7790_register_scif(SCIFA1);
+ r8a7790_register_scif(SCIFB0);
+ r8a7790_register_scif(SCIFB1);
+ r8a7790_register_scif(SCIFB2);
+ r8a7790_register_scif(SCIFA2);
+ r8a7790_register_scif(SCIF0);
+ r8a7790_register_scif(SCIF1);
+ r8a7790_register_irqc(0);
+}
+
+void __init r8a7790_timer_init(void)
+{
+ void __iomem *cntcr;
+
+ /* make sure arch timer is started by setting bit 0 of CNTCT */
+ cntcr = ioremap(0xe6080000, PAGE_SIZE);
+ iowrite32(1, cntcr);
+ iounmap(cntcr);
+
+ shmobile_timer_init();
+}
+
+#ifdef CONFIG_USE_OF
+void __init r8a7790_add_standard_devices_dt(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char *r8a7790_boards_compat_dt[] __initdata = {
+ "renesas,r8a7790",
+ NULL,
+};
+
+DT_MACHINE_START(R8A7790_DT, "Generic R8A7790 (Flattened Device Tree)")
+ .init_irq = irqchip_init,
+ .init_machine = r8a7790_add_standard_devices_dt,
+ .init_time = r8a7790_timer_init,
+ .dt_compat = r8a7790_boards_compat_dt,
+MACHINE_END
+#endif /* CONFIG_USE_OF */
diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c
index 59c7146bf66f..5502d624aca6 100644
--- a/arch/arm/mach-shmobile/setup-sh7372.c
+++ b/arch/arm/mach-shmobile/setup-sh7372.c
@@ -1175,7 +1175,6 @@ DT_MACHINE_START(SH7372_DT, "Generic SH7372 (Flattened Device Tree)")
.init_irq = sh7372_init_irq,
.handle_irq = shmobile_handle_irq_intc,
.init_machine = sh7372_add_standard_devices_dt,
- .init_time = shmobile_timer_init,
.dt_compat = sh7372_boards_compat_dt,
MACHINE_END
diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c
index e8cd93a5c550..fdf3894b1cc3 100644
--- a/arch/arm/mach-shmobile/setup-sh73a0.c
+++ b/arch/arm/mach-shmobile/setup-sh73a0.c
@@ -1037,7 +1037,6 @@ DT_MACHINE_START(SH73A0_DT, "Generic SH73A0 (Flattened Device Tree)")
.nr_irqs = NR_IRQS_LEGACY,
.init_irq = irqchip_init,
.init_machine = sh73a0_add_standard_devices_dt,
- .init_time = shmobile_timer_init,
.dt_compat = sh73a0_boards_compat_dt,
MACHINE_END
#endif /* CONFIG_USE_OF */
diff --git a/arch/arm/mach-shmobile/timer.c b/arch/arm/mach-shmobile/timer.c
index 3d16d4dff01b..f321dbeb2379 100644
--- a/arch/arm/mach-shmobile/timer.c
+++ b/arch/arm/mach-shmobile/timer.c
@@ -19,10 +19,8 @@
*
*/
#include <linux/platform_device.h>
+#include <linux/clocksource.h>
#include <linux/delay.h>
-#include <asm/arch_timer.h>
-#include <asm/mach/time.h>
-#include <asm/smp_twd.h>
void __init shmobile_setup_delay(unsigned int max_cpu_core_mhz,
unsigned int mult, unsigned int div)
@@ -63,6 +61,5 @@ void __init shmobile_earlytimer_init(void)
void __init shmobile_timer_init(void)
{
- arch_timer_of_register();
- arch_timer_sched_clock_init();
+ clocksource_of_init();
}
diff --git a/arch/arm/mach-spear/Makefile b/arch/arm/mach-spear/Makefile
index af9bffb94f1c..a946c19ab31a 100644
--- a/arch/arm/mach-spear/Makefile
+++ b/arch/arm/mach-spear/Makefile
@@ -7,10 +7,10 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include
# Common support
obj-y := restart.o time.o
-obj-$(CONFIG_SMP) += headsmp.o platsmp.o
-obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+smp-$(CONFIG_SMP) += headsmp.o platsmp.o
+smp-$(CONFIG_HOTPLUG_CPU) += hotplug.o
-obj-$(CONFIG_ARCH_SPEAR13XX) += spear13xx.o
+obj-$(CONFIG_ARCH_SPEAR13XX) += spear13xx.o $(smp-y)
obj-$(CONFIG_MACH_SPEAR1310) += spear1310.o
obj-$(CONFIG_MACH_SPEAR1340) += spear1340.o
diff --git a/arch/arm/mach-spear/generic.h b/arch/arm/mach-spear/generic.h
index 8ba7e75b648d..a9fd45362fee 100644
--- a/arch/arm/mach-spear/generic.h
+++ b/arch/arm/mach-spear/generic.h
@@ -22,11 +22,6 @@ extern void spear13xx_timer_init(void);
extern void spear3xx_timer_init(void);
extern struct pl022_ssp_controller pl022_plat_data;
extern struct pl08x_platform_data pl080_plat_data;
-extern struct dw_dma_platform_data dmac_plat_data;
-extern struct dw_dma_slave cf_dma_priv;
-extern struct dw_dma_slave nand_read_dma_priv;
-extern struct dw_dma_slave nand_write_dma_priv;
-bool dw_dma_filter(struct dma_chan *chan, void *slave);
void __init spear_setup_of_timer(void);
void __init spear3xx_clk_init(void __iomem *misc_base,
diff --git a/arch/arm/mach-spear/include/mach/spear.h b/arch/arm/mach-spear/include/mach/spear.h
index 374ddc393df1..cf3a5369eeca 100644
--- a/arch/arm/mach-spear/include/mach/spear.h
+++ b/arch/arm/mach-spear/include/mach/spear.h
@@ -82,8 +82,6 @@
#define VA_L2CC_BASE IOMEM(UL(0xFB000000))
/* others */
-#define DMAC0_BASE UL(0xEA800000)
-#define DMAC1_BASE UL(0xEB000000)
#define MCIF_CF_BASE UL(0xB2800000)
/* Debug uart for linux, will be used for debug and uncompress messages */
diff --git a/arch/arm/mach-spear/spear1310.c b/arch/arm/mach-spear/spear1310.c
index ed3b5c287a7b..9eaac2c881ea 100644
--- a/arch/arm/mach-spear/spear1310.c
+++ b/arch/arm/mach-spear/spear1310.c
@@ -23,40 +23,12 @@
#include <mach/spear.h>
/* Base addresses */
-#define SPEAR1310_SSP1_BASE UL(0x5D400000)
-#define SPEAR1310_SATA0_BASE UL(0xB1000000)
-#define SPEAR1310_SATA1_BASE UL(0xB1800000)
-#define SPEAR1310_SATA2_BASE UL(0xB4000000)
-
#define SPEAR1310_RAS_GRP1_BASE UL(0xD8000000)
#define VA_SPEAR1310_RAS_GRP1_BASE UL(0xFA000000)
-static struct arasan_cf_pdata cf_pdata = {
- .cf_if_clk = CF_IF_CLK_166M,
- .quirk = CF_BROKEN_UDMA,
- .dma_priv = &cf_dma_priv,
-};
-
-/* ssp device registration */
-static struct pl022_ssp_controller ssp1_plat_data = {
- .enable_dma = 0,
-};
-
-/* Add SPEAr1310 auxdata to pass platform data */
-static struct of_dev_auxdata spear1310_auxdata_lookup[] __initdata = {
- OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_pdata),
- OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data),
- OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data),
- OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data),
-
- OF_DEV_AUXDATA("arm,pl022", SPEAR1310_SSP1_BASE, NULL, &ssp1_plat_data),
- {}
-};
-
static void __init spear1310_dt_init(void)
{
- of_platform_populate(NULL, of_default_bus_match_table,
- spear1310_auxdata_lookup, NULL);
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
static const char * const spear1310_dt_board_compat[] = {
diff --git a/arch/arm/mach-spear/spear1340.c b/arch/arm/mach-spear/spear1340.c
index 75e38644bbfb..a04a7fe76f71 100644
--- a/arch/arm/mach-spear/spear1340.c
+++ b/arch/arm/mach-spear/spear1340.c
@@ -16,18 +16,16 @@
#include <linux/ahci_platform.h>
#include <linux/amba/serial.h>
#include <linux/delay.h>
-#include <linux/dw_dmac.h>
#include <linux/of_platform.h>
#include <linux/irqchip.h>
#include <asm/mach/arch.h>
#include "generic.h"
#include <mach/spear.h>
-#include "spear13xx-dma.h"
+/* FIXME: Move SATA PHY code into a standalone driver */
/* Base addresses */
#define SPEAR1340_SATA_BASE UL(0xB1000000)
-#define SPEAR1340_UART1_BASE UL(0xB4100000)
/* Power Management Registers */
#define SPEAR1340_PCM_CFG (VA_MISC_BASE + 0x100)
@@ -79,28 +77,6 @@
(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
-static struct dw_dma_slave uart1_dma_param[] = {
- {
- /* Tx */
- .cfg_hi = DWC_CFGH_DST_PER(SPEAR1340_DMA_REQ_UART1_TX),
- .cfg_lo = 0,
- .src_master = DMA_MASTER_MEMORY,
- .dst_master = SPEAR1340_DMA_MASTER_UART1,
- }, {
- /* Rx */
- .cfg_hi = DWC_CFGH_SRC_PER(SPEAR1340_DMA_REQ_UART1_RX),
- .cfg_lo = 0,
- .src_master = SPEAR1340_DMA_MASTER_UART1,
- .dst_master = DMA_MASTER_MEMORY,
- }
-};
-
-static struct amba_pl011_data uart1_data = {
- .dma_filter = dw_dma_filter,
- .dma_tx_param = &uart1_dma_param[0],
- .dma_rx_param = &uart1_dma_param[1],
-};
-
/* SATA device registration */
static int sata_miphy_init(struct device *dev, void __iomem *addr)
{
@@ -159,14 +135,8 @@ static struct ahci_platform_data sata_pdata = {
/* Add SPEAr1340 auxdata to pass platform data */
static struct of_dev_auxdata spear1340_auxdata_lookup[] __initdata = {
- OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_dma_priv),
- OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data),
- OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data),
- OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data),
-
OF_DEV_AUXDATA("snps,spear-ahci", SPEAR1340_SATA_BASE, NULL,
&sata_pdata),
- OF_DEV_AUXDATA("arm,pl011", SPEAR1340_UART1_BASE, NULL, &uart1_data),
{}
};
diff --git a/arch/arm/mach-spear/spear13xx-dma.h b/arch/arm/mach-spear/spear13xx-dma.h
deleted file mode 100644
index d50bdb605925..000000000000
--- a/arch/arm/mach-spear/spear13xx-dma.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * arch/arm/mach-spear13xx/include/mach/dma.h
- *
- * DMA information for SPEAr13xx machine family
- *
- * Copyright (C) 2012 ST Microelectronics
- * Viresh Kumar <viresh.linux@gmail.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#ifndef __MACH_DMA_H
-#define __MACH_DMA_H
-
-/* request id of all the peripherals */
-enum dma_master_info {
- /* Accessible from only one master */
- DMA_MASTER_MCIF = 0,
- DMA_MASTER_FSMC = 1,
- /* Accessible from both 0 & 1 */
- DMA_MASTER_MEMORY = 0,
- DMA_MASTER_ADC = 0,
- DMA_MASTER_UART0 = 0,
- DMA_MASTER_SSP0 = 0,
- DMA_MASTER_I2C0 = 0,
-
-#ifdef CONFIG_MACH_SPEAR1310
- /* Accessible from only one master */
- SPEAR1310_DMA_MASTER_JPEG = 1,
-
- /* Accessible from both 0 & 1 */
- SPEAR1310_DMA_MASTER_I2S = 0,
- SPEAR1310_DMA_MASTER_UART1 = 0,
- SPEAR1310_DMA_MASTER_UART2 = 0,
- SPEAR1310_DMA_MASTER_UART3 = 0,
- SPEAR1310_DMA_MASTER_UART4 = 0,
- SPEAR1310_DMA_MASTER_UART5 = 0,
- SPEAR1310_DMA_MASTER_I2C1 = 0,
- SPEAR1310_DMA_MASTER_I2C2 = 0,
- SPEAR1310_DMA_MASTER_I2C3 = 0,
- SPEAR1310_DMA_MASTER_I2C4 = 0,
- SPEAR1310_DMA_MASTER_I2C5 = 0,
- SPEAR1310_DMA_MASTER_I2C6 = 0,
- SPEAR1310_DMA_MASTER_I2C7 = 0,
- SPEAR1310_DMA_MASTER_SSP1 = 0,
-#endif
-
-#ifdef CONFIG_MACH_SPEAR1340
- /* Accessible from only one master */
- SPEAR1340_DMA_MASTER_I2S_PLAY = 1,
- SPEAR1340_DMA_MASTER_I2S_REC = 1,
- SPEAR1340_DMA_MASTER_I2C1 = 1,
- SPEAR1340_DMA_MASTER_UART1 = 1,
-
- /* following are accessible from both master 0 & 1 */
- SPEAR1340_DMA_MASTER_SPDIF = 0,
- SPEAR1340_DMA_MASTER_CAM = 1,
- SPEAR1340_DMA_MASTER_VIDEO_IN = 0,
- SPEAR1340_DMA_MASTER_MALI = 0,
-#endif
-};
-
-enum request_id {
- DMA_REQ_ADC = 0,
- DMA_REQ_SSP0_TX = 4,
- DMA_REQ_SSP0_RX = 5,
- DMA_REQ_UART0_TX = 6,
- DMA_REQ_UART0_RX = 7,
- DMA_REQ_I2C0_TX = 8,
- DMA_REQ_I2C0_RX = 9,
-
-#ifdef CONFIG_MACH_SPEAR1310
- SPEAR1310_DMA_REQ_FROM_JPEG = 2,
- SPEAR1310_DMA_REQ_TO_JPEG = 3,
- SPEAR1310_DMA_REQ_I2S_TX = 10,
- SPEAR1310_DMA_REQ_I2S_RX = 11,
-
- SPEAR1310_DMA_REQ_I2C1_RX = 0,
- SPEAR1310_DMA_REQ_I2C1_TX = 1,
- SPEAR1310_DMA_REQ_I2C2_RX = 2,
- SPEAR1310_DMA_REQ_I2C2_TX = 3,
- SPEAR1310_DMA_REQ_I2C3_RX = 4,
- SPEAR1310_DMA_REQ_I2C3_TX = 5,
- SPEAR1310_DMA_REQ_I2C4_RX = 6,
- SPEAR1310_DMA_REQ_I2C4_TX = 7,
- SPEAR1310_DMA_REQ_I2C5_RX = 8,
- SPEAR1310_DMA_REQ_I2C5_TX = 9,
- SPEAR1310_DMA_REQ_I2C6_RX = 10,
- SPEAR1310_DMA_REQ_I2C6_TX = 11,
- SPEAR1310_DMA_REQ_UART1_RX = 12,
- SPEAR1310_DMA_REQ_UART1_TX = 13,
- SPEAR1310_DMA_REQ_UART2_RX = 14,
- SPEAR1310_DMA_REQ_UART2_TX = 15,
- SPEAR1310_DMA_REQ_UART5_RX = 16,
- SPEAR1310_DMA_REQ_UART5_TX = 17,
- SPEAR1310_DMA_REQ_SSP1_RX = 18,
- SPEAR1310_DMA_REQ_SSP1_TX = 19,
- SPEAR1310_DMA_REQ_I2C7_RX = 20,
- SPEAR1310_DMA_REQ_I2C7_TX = 21,
- SPEAR1310_DMA_REQ_UART3_RX = 28,
- SPEAR1310_DMA_REQ_UART3_TX = 29,
- SPEAR1310_DMA_REQ_UART4_RX = 30,
- SPEAR1310_DMA_REQ_UART4_TX = 31,
-#endif
-
-#ifdef CONFIG_MACH_SPEAR1340
- SPEAR1340_DMA_REQ_SPDIF_TX = 2,
- SPEAR1340_DMA_REQ_SPDIF_RX = 3,
- SPEAR1340_DMA_REQ_I2S_TX = 10,
- SPEAR1340_DMA_REQ_I2S_RX = 11,
- SPEAR1340_DMA_REQ_UART1_TX = 12,
- SPEAR1340_DMA_REQ_UART1_RX = 13,
- SPEAR1340_DMA_REQ_I2C1_TX = 14,
- SPEAR1340_DMA_REQ_I2C1_RX = 15,
- SPEAR1340_DMA_REQ_CAM0_EVEN = 0,
- SPEAR1340_DMA_REQ_CAM0_ODD = 1,
- SPEAR1340_DMA_REQ_CAM1_EVEN = 2,
- SPEAR1340_DMA_REQ_CAM1_ODD = 3,
- SPEAR1340_DMA_REQ_CAM2_EVEN = 4,
- SPEAR1340_DMA_REQ_CAM2_ODD = 5,
- SPEAR1340_DMA_REQ_CAM3_EVEN = 6,
- SPEAR1340_DMA_REQ_CAM3_ODD = 7,
-#endif
-};
-
-#endif /* __MACH_DMA_H */
diff --git a/arch/arm/mach-spear/spear13xx.c b/arch/arm/mach-spear/spear13xx.c
index 6dd208997176..3621599c38ad 100644
--- a/arch/arm/mach-spear/spear13xx.c
+++ b/arch/arm/mach-spear/spear13xx.c
@@ -16,70 +16,12 @@
#include <linux/amba/pl022.h>
#include <linux/clk.h>
#include <linux/clocksource.h>
-#include <linux/dw_dmac.h>
#include <linux/err.h>
#include <linux/of.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/map.h>
-#include "generic.h"
#include <mach/spear.h>
-
-#include "spear13xx-dma.h"
-
-/* common dw_dma filter routine to be used by peripherals */
-bool dw_dma_filter(struct dma_chan *chan, void *slave)
-{
- struct dw_dma_slave *dws = (struct dw_dma_slave *)slave;
-
- if (chan->device->dev == dws->dma_dev) {
- chan->private = slave;
- return true;
- } else {
- return false;
- }
-}
-
-/* ssp device registration */
-static struct dw_dma_slave ssp_dma_param[] = {
- {
- /* Tx */
- .cfg_hi = DWC_CFGH_DST_PER(DMA_REQ_SSP0_TX),
- .cfg_lo = 0,
- .src_master = DMA_MASTER_MEMORY,
- .dst_master = DMA_MASTER_SSP0,
- }, {
- /* Rx */
- .cfg_hi = DWC_CFGH_SRC_PER(DMA_REQ_SSP0_RX),
- .cfg_lo = 0,
- .src_master = DMA_MASTER_SSP0,
- .dst_master = DMA_MASTER_MEMORY,
- }
-};
-
-struct pl022_ssp_controller pl022_plat_data = {
- .enable_dma = 1,
- .dma_filter = dw_dma_filter,
- .dma_rx_param = &ssp_dma_param[1],
- .dma_tx_param = &ssp_dma_param[0],
-};
-
-/* CF device registration */
-struct dw_dma_slave cf_dma_priv = {
- .cfg_hi = 0,
- .cfg_lo = 0,
- .src_master = 0,
- .dst_master = 0,
-};
-
-/* dmac device registeration */
-struct dw_dma_platform_data dmac_plat_data = {
- .nr_channels = 8,
- .chan_allocation_order = CHAN_ALLOCATION_DESCENDING,
- .chan_priority = CHAN_PRIORITY_DESCENDING,
- .block_size = 4095U,
- .nr_masters = 2,
- .data_width = { 3, 3, 0, 0 },
-};
+#include "generic.h"
void __init spear13xx_l2x0_init(void)
{
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
index 25160aeaa3b7..54bb80b012ac 100644
--- a/arch/arm/mach-versatile/core.c
+++ b/arch/arm/mach-versatile/core.c
@@ -749,12 +749,25 @@ void versatile_restart(char mode, const char *cmd)
/* Early initializations */
void __init versatile_init_early(void)
{
+ u32 val;
void __iomem *sys = __io_address(VERSATILE_SYS_BASE);
osc4_clk.vcoreg = sys + VERSATILE_SYS_OSCCLCD_OFFSET;
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
versatile_sched_clock_init(sys + VERSATILE_SYS_24MHz_OFFSET, 24000000);
+
+ /*
+ * set clock frequency:
+ * VERSATILE_REFCLK is 32KHz
+ * VERSATILE_TIMCLK is 1MHz
+ */
+ val = readl(__io_address(VERSATILE_SCTL_BASE));
+ writel((VERSATILE_TIMCLK << VERSATILE_TIMER1_EnSel) |
+ (VERSATILE_TIMCLK << VERSATILE_TIMER2_EnSel) |
+ (VERSATILE_TIMCLK << VERSATILE_TIMER3_EnSel) |
+ (VERSATILE_TIMCLK << VERSATILE_TIMER4_EnSel) | val,
+ __io_address(VERSATILE_SCTL_BASE));
}
void __init versatile_init(void)
@@ -785,19 +798,6 @@ void __init versatile_init(void)
*/
void __init versatile_timer_init(void)
{
- u32 val;
-
- /*
- * set clock frequency:
- * VERSATILE_REFCLK is 32KHz
- * VERSATILE_TIMCLK is 1MHz
- */
- val = readl(__io_address(VERSATILE_SCTL_BASE));
- writel((VERSATILE_TIMCLK << VERSATILE_TIMER1_EnSel) |
- (VERSATILE_TIMCLK << VERSATILE_TIMER2_EnSel) |
- (VERSATILE_TIMCLK << VERSATILE_TIMER3_EnSel) |
- (VERSATILE_TIMCLK << VERSATILE_TIMER4_EnSel) | val,
- __io_address(VERSATILE_SCTL_BASE));
/*
* Initialise to a known state (all timers off)
diff --git a/arch/arm/mach-versatile/versatile_dt.c b/arch/arm/mach-versatile/versatile_dt.c
index 2558f2e957c3..3621b000a0f6 100644
--- a/arch/arm/mach-versatile/versatile_dt.c
+++ b/arch/arm/mach-versatile/versatile_dt.c
@@ -45,7 +45,6 @@ DT_MACHINE_START(VERSATILE_PB, "ARM-Versatile (Device Tree Support)")
.map_io = versatile_map_io,
.init_early = versatile_init_early,
.init_irq = versatile_init_irq,
- .init_time = versatile_timer_init,
.init_machine = versatile_dt_init,
.dt_compat = versatile_dt_match,
.restart = versatile_restart,
diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c
index 9366f37902d9..8802030df98d 100644
--- a/arch/arm/mach-vexpress/v2m.c
+++ b/arch/arm/mach-vexpress/v2m.c
@@ -1,6 +1,7 @@
/*
* Versatile Express V2M Motherboard Support
*/
+#include <linux/clocksource.h>
#include <linux/device.h>
#include <linux/amba/bus.h>
#include <linux/amba/mmci.h>
@@ -25,7 +26,6 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
-#include <asm/arch_timer.h>
#include <asm/mach-types.h>
#include <asm/sizes.h>
#include <asm/mach/arch.h>
@@ -63,9 +63,6 @@ static void __init v2m_sp804_init(void __iomem *base, unsigned int irq)
if (WARN_ON(!base || irq == NO_IRQ))
return;
- writel(0, base + TIMER_1_BASE + TIMER_CTRL);
- writel(0, base + TIMER_2_BASE + TIMER_CTRL);
-
sp804_clocksource_init(base + TIMER_2_BASE, "v2m-timer1");
sp804_clockevents_init(base + TIMER_1_BASE, irq, "v2m-timer0");
}
@@ -430,29 +427,11 @@ void __init v2m_dt_init_early(void)
static void __init v2m_dt_timer_init(void)
{
- struct device_node *node = NULL;
-
of_clk_init(NULL);
clocksource_of_init();
- do {
- node = of_find_compatible_node(node, NULL, "arm,sp804");
- } while (node && vexpress_get_site_by_node(node) != VEXPRESS_SITE_MB);
- if (node) {
- pr_info("Using SP804 '%s' as a clock & events source\n",
- node->full_name);
- WARN_ON(clk_register_clkdev(of_clk_get_by_name(node,
- "timclken1"), "v2m-timer0", "sp804"));
- WARN_ON(clk_register_clkdev(of_clk_get_by_name(node,
- "timclken2"), "v2m-timer1", "sp804"));
- v2m_sp804_init(of_iomap(node, 0),
- irq_of_parse_and_map(node, 0));
- }
-
- arch_timer_of_register();
- if (arch_timer_sched_clock_init() != 0)
- versatile_sched_clock_init(vexpress_get_24mhz_clock_base(),
+ versatile_sched_clock_init(vexpress_get_24mhz_clock_base(),
24000000);
}
@@ -471,7 +450,6 @@ static void __init v2m_dt_init(void)
static const char * const v2m_dt_match[] __initconst = {
"arm,vexpress",
- "xen,xenvm",
NULL,
};
diff --git a/arch/arm/mach-virt/virt.c b/arch/arm/mach-virt/virt.c
index 31666f6b4373..061f283f579e 100644
--- a/arch/arm/mach-virt/virt.c
+++ b/arch/arm/mach-virt/virt.c
@@ -23,23 +23,16 @@
#include <linux/of_platform.h>
#include <linux/smp.h>
-#include <asm/arch_timer.h>
#include <asm/mach/arch.h>
-#include <asm/mach/time.h>
static void __init virt_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
-static void __init virt_timer_init(void)
-{
- WARN_ON(arch_timer_of_register() != 0);
- WARN_ON(arch_timer_sched_clock_init() != 0);
-}
-
static const char *virt_dt_match[] = {
"linux,dummy-virt",
+ "xen,xenvm",
NULL
};
@@ -47,7 +40,6 @@ extern struct smp_operations virt_smp_ops;
DT_MACHINE_START(VIRT, "Dummy Virtual Machine")
.init_irq = irqchip_init,
- .init_time = virt_timer_init,
.init_machine = virt_init,
.smp = smp_ops(virt_smp_ops),
.dt_compat = virt_dt_match,
diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig
index cf3226b041f5..c1d61f281e68 100644
--- a/arch/arm/mach-zynq/Kconfig
+++ b/arch/arm/mach-zynq/Kconfig
@@ -10,6 +10,7 @@ config ARCH_ZYNQ
select ICST
select MIGHT_HAVE_CACHE_L2X0
select USE_OF
+ select HAVE_SMP
select SPARSE_IRQ
select CADENCE_TTC_TIMER
help
diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile
index 320faedeb484..1b25d92ebf22 100644
--- a/arch/arm/mach-zynq/Makefile
+++ b/arch/arm/mach-zynq/Makefile
@@ -3,4 +3,8 @@
#
# Common support
-obj-y := common.o
+obj-y := common.o slcr.o
+CFLAGS_REMOVE_hotplug.o =-march=armv6k
+CFLAGS_hotplug.o =-Wa,-march=armv7-a -mcpu=cortex-a9
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+obj-$(CONFIG_SMP) += headsmp.o platsmp.o
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 68e0907de5d0..5bfe7035b73d 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -33,20 +33,23 @@
#include <asm/mach-types.h>
#include <asm/page.h>
#include <asm/pgtable.h>
+#include <asm/smp_scu.h>
#include <asm/hardware/cache-l2x0.h>
#include "common.h"
+void __iomem *zynq_scu_base;
+
static struct of_device_id zynq_of_bus_ids[] __initdata = {
{ .compatible = "simple-bus", },
{}
};
/**
- * xilinx_init_machine() - System specific initialization, intended to be
- * called from board specific initialization.
+ * zynq_init_machine - System specific initialization, intended to be
+ * called from board specific initialization.
*/
-static void __init xilinx_init_machine(void)
+static void __init zynq_init_machine(void)
{
/*
* 64KB way size, 8-way associativity, parity disabled
@@ -56,50 +59,56 @@ static void __init xilinx_init_machine(void)
of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL);
}
-#define SCU_PERIPH_PHYS 0xF8F00000
-#define SCU_PERIPH_SIZE SZ_8K
-#define SCU_PERIPH_VIRT (VMALLOC_END - SCU_PERIPH_SIZE)
+static void __init zynq_timer_init(void)
+{
+ zynq_slcr_init();
+ clocksource_of_init();
+}
-static struct map_desc scu_desc __initdata = {
- .virtual = SCU_PERIPH_VIRT,
- .pfn = __phys_to_pfn(SCU_PERIPH_PHYS),
- .length = SCU_PERIPH_SIZE,
- .type = MT_DEVICE,
+static struct map_desc zynq_cortex_a9_scu_map __initdata = {
+ .length = SZ_256,
+ .type = MT_DEVICE,
};
-static void __init xilinx_zynq_timer_init(void)
+static void __init zynq_scu_map_io(void)
{
- struct device_node *np;
- void __iomem *slcr;
-
- np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
- slcr = of_iomap(np, 0);
- WARN_ON(!slcr);
+ unsigned long base;
- xilinx_zynq_clocks_init(slcr);
-
- clocksource_of_init();
+ base = scu_a9_get_base();
+ zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
+ /* Expected address is in vmalloc area that's why simple assign here */
+ zynq_cortex_a9_scu_map.virtual = base;
+ iotable_init(&zynq_cortex_a9_scu_map, 1);
+ zynq_scu_base = (void __iomem *)base;
+ BUG_ON(!zynq_scu_base);
}
/**
- * xilinx_map_io() - Create memory mappings needed for early I/O.
+ * zynq_map_io - Create memory mappings needed for early I/O.
*/
-static void __init xilinx_map_io(void)
+static void __init zynq_map_io(void)
{
debug_ll_io_init();
- iotable_init(&scu_desc, 1);
+ zynq_scu_map_io();
+}
+
+static void zynq_system_reset(char mode, const char *cmd)
+{
+ zynq_slcr_system_reset();
}
-static const char *xilinx_dt_match[] = {
+static const char * const zynq_dt_match[] = {
"xlnx,zynq-zc702",
"xlnx,zynq-7000",
NULL
};
MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
- .map_io = xilinx_map_io,
+ .smp = smp_ops(zynq_smp_ops),
+ .map_io = zynq_map_io,
.init_irq = irqchip_init,
- .init_machine = xilinx_init_machine,
- .init_time = xilinx_zynq_timer_init,
- .dt_compat = xilinx_dt_match,
+ .init_machine = zynq_init_machine,
+ .init_time = zynq_timer_init,
+ .dt_compat = zynq_dt_match,
+ .restart = zynq_system_reset,
MACHINE_END
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index 5050bb10bb12..fbbd0e21c404 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -17,4 +17,24 @@
#ifndef __MACH_ZYNQ_COMMON_H__
#define __MACH_ZYNQ_COMMON_H__
+extern int zynq_slcr_init(void);
+extern void zynq_slcr_system_reset(void);
+extern void zynq_slcr_cpu_stop(int cpu);
+extern void zynq_slcr_cpu_start(int cpu);
+
+#ifdef CONFIG_SMP
+extern void secondary_startup(void);
+extern char zynq_secondary_trampoline;
+extern char zynq_secondary_trampoline_jump;
+extern char zynq_secondary_trampoline_end;
+extern int __cpuinit zynq_cpun_start(u32 address, int cpu);
+extern struct smp_operations zynq_smp_ops __initdata;
+#endif
+
+extern void __iomem *zynq_slcr_base;
+extern void __iomem *zynq_scu_base;
+
+/* Hotplug */
+extern void zynq_platform_cpu_die(unsigned int cpu);
+
#endif
diff --git a/arch/arm/mach-zynq/headsmp.S b/arch/arm/mach-zynq/headsmp.S
new file mode 100644
index 000000000000..d183cd234a9b
--- /dev/null
+++ b/arch/arm/mach-zynq/headsmp.S
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ * Copyright (c) 2012-2013 Xilinx
+ *
+ * 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/linkage.h>
+#include <linux/init.h>
+
+ __CPUINIT
+
+ENTRY(zynq_secondary_trampoline)
+ ldr r0, [pc]
+ bx r0
+.globl zynq_secondary_trampoline_jump
+zynq_secondary_trampoline_jump:
+ /* Space for jumping address */
+ .word /* cpu 1 */
+.globl zynq_secondary_trampoline_end
+zynq_secondary_trampoline_end:
+
+ENDPROC(zynq_secondary_trampoline)
diff --git a/arch/arm/mach-zynq/hotplug.c b/arch/arm/mach-zynq/hotplug.c
new file mode 100644
index 000000000000..c89672bd1de2
--- /dev/null
+++ b/arch/arm/mach-zynq/hotplug.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012-2013 Xilinx
+ *
+ * based on linux/arch/arm/mach-realview/hotplug.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cp15.h>
+#include "common.h"
+
+static inline void zynq_cpu_enter_lowpower(void)
+{
+ unsigned int v;
+
+ flush_cache_all();
+ asm volatile(
+ " mcr p15, 0, %1, c7, c5, 0\n"
+ " dsb\n"
+ /*
+ * Turn off coherency
+ */
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " bic %0, %0, #0x40\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ " mrc p15, 0, %0, c1, c0, 0\n"
+ " bic %0, %0, %2\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ : "=&r" (v)
+ : "r" (0), "Ir" (CR_C)
+ : "cc");
+}
+
+static inline void zynq_cpu_leave_lowpower(void)
+{
+ unsigned int v;
+
+ asm volatile(
+ " mrc p15, 0, %0, c1, c0, 0\n"
+ " orr %0, %0, %1\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " orr %0, %0, #0x40\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ : "=&r" (v)
+ : "Ir" (CR_C)
+ : "cc");
+}
+
+static inline void zynq_platform_do_lowpower(unsigned int cpu, int *spurious)
+{
+ /*
+ * there is no power-control hardware on this platform, so all
+ * we can do is put the core into WFI; this is safe as the calling
+ * code will have already disabled interrupts
+ */
+ for (;;) {
+ dsb();
+ wfi();
+
+ /*
+ * Getting here, means that we have come out of WFI without
+ * having been woken up - this shouldn't happen
+ *
+ * Just note it happening - when we're woken, we can report
+ * its occurrence.
+ */
+ (*spurious)++;
+ }
+}
+
+/*
+ * platform-specific code to shutdown a CPU
+ *
+ * Called with IRQs disabled
+ */
+void zynq_platform_cpu_die(unsigned int cpu)
+{
+ int spurious = 0;
+
+ /*
+ * we're ready for shutdown now, so do it
+ */
+ zynq_cpu_enter_lowpower();
+ zynq_platform_do_lowpower(cpu, &spurious);
+
+ /*
+ * bring this CPU back into the world of cache
+ * coherency, and then restore interrupts
+ */
+ zynq_cpu_leave_lowpower();
+
+ if (spurious)
+ pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
+}
diff --git a/arch/arm/mach-zynq/platsmp.c b/arch/arm/mach-zynq/platsmp.c
new file mode 100644
index 000000000000..5fc167e07619
--- /dev/null
+++ b/arch/arm/mach-zynq/platsmp.c
@@ -0,0 +1,136 @@
+/*
+ * This file contains Xilinx specific SMP code, used to start up
+ * the second processor.
+ *
+ * Copyright (C) 2011-2013 Xilinx
+ *
+ * based on linux/arch/arm/mach-realview/platsmp.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ *
+ * 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/jiffies.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/smp_scu.h>
+#include <linux/irqchip/arm-gic.h>
+#include "common.h"
+
+/*
+ * Store number of cores in the system
+ * Because of scu_get_core_count() must be in __init section and can't
+ * be called from zynq_cpun_start() because it is in __cpuinit section.
+ */
+static int ncores;
+
+int __cpuinit zynq_cpun_start(u32 address, int cpu)
+{
+ u32 trampoline_code_size = &zynq_secondary_trampoline_end -
+ &zynq_secondary_trampoline;
+
+ if (cpu > ncores) {
+ pr_warn("CPU No. is not available in the system\n");
+ return -1;
+ }
+
+ /* MS: Expectation that SLCR are directly map and accessible */
+ /* Not possible to jump to non aligned address */
+ if (!(address & 3) && (!address || (address >= trampoline_code_size))) {
+ /* Store pointer to ioremap area which points to address 0x0 */
+ static u8 __iomem *zero;
+ u32 trampoline_size = &zynq_secondary_trampoline_jump -
+ &zynq_secondary_trampoline;
+
+ zynq_slcr_cpu_stop(cpu);
+
+ if (__pa(PAGE_OFFSET)) {
+ zero = ioremap(0, trampoline_code_size);
+ if (!zero) {
+ pr_warn("BOOTUP jump vectors not accessible\n");
+ return -1;
+ }
+ } else {
+ zero = (__force u8 __iomem *)PAGE_OFFSET;
+ }
+
+ /*
+ * This is elegant way how to jump to any address
+ * 0x0: Load address at 0x8 to r0
+ * 0x4: Jump by mov instruction
+ * 0x8: Jumping address
+ */
+ memcpy((__force void *)zero, &zynq_secondary_trampoline,
+ trampoline_size);
+ writel(address, zero + trampoline_size);
+
+ flush_cache_all();
+ outer_flush_range(0, trampoline_code_size);
+ smp_wmb();
+
+ if (__pa(PAGE_OFFSET))
+ iounmap(zero);
+
+ zynq_slcr_cpu_start(cpu);
+
+ return 0;
+ }
+
+ pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address);
+
+ return -1;
+}
+EXPORT_SYMBOL(zynq_cpun_start);
+
+static int __cpuinit zynq_boot_secondary(unsigned int cpu,
+ struct task_struct *idle)
+{
+ return zynq_cpun_start(virt_to_phys(secondary_startup), cpu);
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+static void __init zynq_smp_init_cpus(void)
+{
+ int i;
+
+ ncores = scu_get_core_count(zynq_scu_base);
+
+ for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++)
+ set_cpu_possible(i, true);
+}
+
+static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
+{
+ int i;
+
+ /*
+ * Initialise the present map, which describes the set of CPUs
+ * actually populated at the present time.
+ */
+ for (i = 0; i < max_cpus; i++)
+ set_cpu_present(i, true);
+
+ scu_enable(zynq_scu_base);
+}
+
+struct smp_operations zynq_smp_ops __initdata = {
+ .smp_init_cpus = zynq_smp_init_cpus,
+ .smp_prepare_cpus = zynq_smp_prepare_cpus,
+ .smp_boot_secondary = zynq_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = zynq_platform_cpu_die,
+#endif
+};
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
new file mode 100644
index 000000000000..c70969b9c258
--- /dev/null
+++ b/arch/arm/mach-zynq/slcr.c
@@ -0,0 +1,125 @@
+/*
+ * Xilinx SLCR driver
+ *
+ * Copyright (c) 2011-2013 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * 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/export.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/clk/zynq.h>
+#include "common.h"
+
+#define SLCR_UNLOCK_MAGIC 0xDF0D
+#define SLCR_UNLOCK 0x8 /* SCLR unlock register */
+
+#define SLCR_PS_RST_CTRL_OFFSET 0x200 /* PS Software Reset Control */
+
+#define SLCR_A9_CPU_CLKSTOP 0x10
+#define SLCR_A9_CPU_RST 0x1
+
+#define SLCR_A9_CPU_RST_CTRL 0x244 /* CPU Software Reset Control */
+#define SLCR_REBOOT_STATUS 0x258 /* PS Reboot Status */
+
+void __iomem *zynq_slcr_base;
+
+/**
+ * zynq_slcr_system_reset - Reset the entire system.
+ */
+void zynq_slcr_system_reset(void)
+{
+ u32 reboot;
+
+ /*
+ * Unlock the SLCR then reset the system.
+ * Note that this seems to require raw i/o
+ * functions or there's a lockup?
+ */
+ writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK);
+
+ /*
+ * Clear 0x0F000000 bits of reboot status register to workaround
+ * the FSBL not loading the bitstream after soft-reboot
+ * This is a temporary solution until we know more.
+ */
+ reboot = readl(zynq_slcr_base + SLCR_REBOOT_STATUS);
+ writel(reboot & 0xF0FFFFFF, zynq_slcr_base + SLCR_REBOOT_STATUS);
+ writel(1, zynq_slcr_base + SLCR_PS_RST_CTRL_OFFSET);
+}
+
+/**
+ * zynq_slcr_cpu_start - Start cpu
+ * @cpu: cpu number
+ */
+void zynq_slcr_cpu_start(int cpu)
+{
+ /* enable CPUn */
+ writel(SLCR_A9_CPU_CLKSTOP << cpu,
+ zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
+ /* enable CLK for CPUn */
+ writel(0x0 << cpu, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
+}
+
+/**
+ * zynq_slcr_cpu_stop - Stop cpu
+ * @cpu: cpu number
+ */
+void zynq_slcr_cpu_stop(int cpu)
+{
+ /* stop CLK and reset CPUn */
+ writel((SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu,
+ zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
+}
+
+/**
+ * zynq_slcr_init
+ * Returns 0 on success, negative errno otherwise.
+ *
+ * Called early during boot from platform code to remap SLCR area.
+ */
+int __init zynq_slcr_init(void)
+{
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
+ if (!np) {
+ pr_err("%s: no slcr node found\n", __func__);
+ BUG();
+ }
+
+ zynq_slcr_base = of_iomap(np, 0);
+ if (!zynq_slcr_base) {
+ pr_err("%s: Unable to map I/O memory\n", __func__);
+ BUG();
+ }
+
+ /* unlock the SLCR so that registers can be changed */
+ writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK);
+
+ pr_info("%s mapped to %p\n", np->name, zynq_slcr_base);
+
+ xilinx_zynq_clocks_init(zynq_slcr_base);
+
+ of_node_put(np);
+
+ return 0;
+}
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index e6dbc8dbe6a6..869254cebf84 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -52,6 +52,13 @@ static u32 omap_reserved_systimers;
static LIST_HEAD(omap_timer_list);
static DEFINE_SPINLOCK(dm_timer_lock);
+enum {
+ REQUEST_ANY = 0,
+ REQUEST_BY_ID,
+ REQUEST_BY_CAP,
+ REQUEST_BY_NODE,
+};
+
/**
* omap_dm_timer_read_reg - read timer registers in posted and non-posted mode
* @timer: timer pointer over which read operation to perform
@@ -177,29 +184,82 @@ int omap_dm_timer_reserve_systimer(int id)
return 0;
}
-struct omap_dm_timer *omap_dm_timer_request(void)
+static struct omap_dm_timer *_omap_dm_timer_request(int req_type, void *data)
{
struct omap_dm_timer *timer = NULL, *t;
+ struct device_node *np = NULL;
unsigned long flags;
- int ret = 0;
+ u32 cap = 0;
+ int id = 0;
+
+ switch (req_type) {
+ case REQUEST_BY_ID:
+ id = *(int *)data;
+ break;
+ case REQUEST_BY_CAP:
+ cap = *(u32 *)data;
+ break;
+ case REQUEST_BY_NODE:
+ np = (struct device_node *)data;
+ break;
+ default:
+ /* REQUEST_ANY */
+ break;
+ }
spin_lock_irqsave(&dm_timer_lock, flags);
list_for_each_entry(t, &omap_timer_list, node) {
if (t->reserved)
continue;
- timer = t;
- timer->reserved = 1;
- break;
+ switch (req_type) {
+ case REQUEST_BY_ID:
+ if (id == t->pdev->id) {
+ timer = t;
+ timer->reserved = 1;
+ goto found;
+ }
+ break;
+ case REQUEST_BY_CAP:
+ if (cap == (t->capability & cap)) {
+ /*
+ * If timer is not NULL, we have already found
+ * one timer but it was not an exact match
+ * because it had more capabilites that what
+ * was required. Therefore, unreserve the last
+ * timer found and see if this one is a better
+ * match.
+ */
+ if (timer)
+ timer->reserved = 0;
+ timer = t;
+ timer->reserved = 1;
+
+ /* Exit loop early if we find an exact match */
+ if (t->capability == cap)
+ goto found;
+ }
+ break;
+ case REQUEST_BY_NODE:
+ if (np == t->pdev->dev.of_node) {
+ timer = t;
+ timer->reserved = 1;
+ goto found;
+ }
+ break;
+ default:
+ /* REQUEST_ANY */
+ timer = t;
+ timer->reserved = 1;
+ goto found;
+ }
}
+found:
spin_unlock_irqrestore(&dm_timer_lock, flags);
- if (timer) {
- ret = omap_dm_timer_prepare(timer);
- if (ret) {
- timer->reserved = 0;
- timer = NULL;
- }
+ if (timer && omap_dm_timer_prepare(timer)) {
+ timer->reserved = 0;
+ timer = NULL;
}
if (!timer)
@@ -207,43 +267,23 @@ struct omap_dm_timer *omap_dm_timer_request(void)
return timer;
}
+
+struct omap_dm_timer *omap_dm_timer_request(void)
+{
+ return _omap_dm_timer_request(REQUEST_ANY, NULL);
+}
EXPORT_SYMBOL_GPL(omap_dm_timer_request);
struct omap_dm_timer *omap_dm_timer_request_specific(int id)
{
- struct omap_dm_timer *timer = NULL, *t;
- unsigned long flags;
- int ret = 0;
-
/* Requesting timer by ID is not supported when device tree is used */
if (of_have_populated_dt()) {
- pr_warn("%s: Please use omap_dm_timer_request_by_cap()\n",
+ pr_warn("%s: Please use omap_dm_timer_request_by_cap/node()\n",
__func__);
return NULL;
}
- spin_lock_irqsave(&dm_timer_lock, flags);
- list_for_each_entry(t, &omap_timer_list, node) {
- if (t->pdev->id == id && !t->reserved) {
- timer = t;
- timer->reserved = 1;
- break;
- }
- }
- spin_unlock_irqrestore(&dm_timer_lock, flags);
-
- if (timer) {
- ret = omap_dm_timer_prepare(timer);
- if (ret) {
- timer->reserved = 0;
- timer = NULL;
- }
- }
-
- if (!timer)
- pr_debug("%s: timer%d request failed!\n", __func__, id);
-
- return timer;
+ return _omap_dm_timer_request(REQUEST_BY_ID, &id);
}
EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
@@ -258,46 +298,25 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
*/
struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap)
{
- struct omap_dm_timer *timer = NULL, *t;
- unsigned long flags;
+ return _omap_dm_timer_request(REQUEST_BY_CAP, &cap);
+}
+EXPORT_SYMBOL_GPL(omap_dm_timer_request_by_cap);
- if (!cap)
+/**
+ * omap_dm_timer_request_by_node - Request a timer by device-tree node
+ * @np: Pointer to device-tree timer node
+ *
+ * Request a timer based upon a device node pointer. Returns pointer to
+ * timer handle on success and a NULL pointer on failure.
+ */
+struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np)
+{
+ if (!np)
return NULL;
- spin_lock_irqsave(&dm_timer_lock, flags);
- list_for_each_entry(t, &omap_timer_list, node) {
- if ((!t->reserved) && ((t->capability & cap) == cap)) {
- /*
- * If timer is not NULL, we have already found one timer
- * but it was not an exact match because it had more
- * capabilites that what was required. Therefore,
- * unreserve the last timer found and see if this one
- * is a better match.
- */
- if (timer)
- timer->reserved = 0;
-
- timer = t;
- timer->reserved = 1;
-
- /* Exit loop early if we find an exact match */
- if (t->capability == cap)
- break;
- }
- }
- spin_unlock_irqrestore(&dm_timer_lock, flags);
-
- if (timer && omap_dm_timer_prepare(timer)) {
- timer->reserved = 0;
- timer = NULL;
- }
-
- if (!timer)
- pr_debug("%s: timer request failed!\n", __func__);
-
- return timer;
+ return _omap_dm_timer_request(REQUEST_BY_NODE, np);
}
-EXPORT_SYMBOL_GPL(omap_dm_timer_request_by_cap);
+EXPORT_SYMBOL_GPL(omap_dm_timer_request_by_node);
int omap_dm_timer_free(struct omap_dm_timer *timer)
{
@@ -314,7 +333,21 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_free);
void omap_dm_timer_enable(struct omap_dm_timer *timer)
{
+ int c;
+
pm_runtime_get_sync(&timer->pdev->dev);
+
+ if (!(timer->capability & OMAP_TIMER_ALWON)) {
+ if (timer->get_context_loss_count) {
+ c = timer->get_context_loss_count(&timer->pdev->dev);
+ if (c != timer->ctx_loss_count) {
+ omap_timer_restore_context(timer);
+ timer->ctx_loss_count = c;
+ }
+ } else {
+ omap_timer_restore_context(timer);
+ }
+ }
}
EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
@@ -409,13 +442,6 @@ int omap_dm_timer_start(struct omap_dm_timer *timer)
omap_dm_timer_enable(timer);
- if (!(timer->capability & OMAP_TIMER_ALWON)) {
- if (timer->get_context_loss_count &&
- timer->get_context_loss_count(&timer->pdev->dev) !=
- timer->ctx_loss_count)
- omap_timer_restore_context(timer);
- }
-
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
if (!(l & OMAP_TIMER_CTRL_ST)) {
l |= OMAP_TIMER_CTRL_ST;
@@ -440,12 +466,6 @@ int omap_dm_timer_stop(struct omap_dm_timer *timer)
__omap_dm_timer_stop(timer, timer->posted, rate);
- if (!(timer->capability & OMAP_TIMER_ALWON)) {
- if (timer->get_context_loss_count)
- timer->ctx_loss_count =
- timer->get_context_loss_count(&timer->pdev->dev);
- }
-
/*
* Since the register values are computed and written within
* __omap_dm_timer_stop, we need to use read to retrieve the
@@ -552,13 +572,6 @@ int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
omap_dm_timer_enable(timer);
- if (!(timer->capability & OMAP_TIMER_ALWON)) {
- if (timer->get_context_loss_count &&
- timer->get_context_loss_count(&timer->pdev->dev) !=
- timer->ctx_loss_count)
- omap_timer_restore_context(timer);
- }
-
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
if (autoreload) {
l |= OMAP_TIMER_CTRL_AR;
@@ -769,6 +782,8 @@ int omap_dm_timers_active(void)
}
EXPORT_SYMBOL_GPL(omap_dm_timers_active);
+static const struct of_device_id omap_timer_match[];
+
/**
* omap_dm_timer_probe - probe function called for every registered device
* @pdev: pointer to current timer platform device
@@ -782,7 +797,11 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
struct omap_dm_timer *timer;
struct resource *mem, *irq;
struct device *dev = &pdev->dev;
- struct dmtimer_platform_data *pdata = pdev->dev.platform_data;
+ const struct of_device_id *match;
+ const struct dmtimer_platform_data *pdata;
+
+ match = of_match_device(of_match_ptr(omap_timer_match), dev);
+ pdata = match ? match->data : dev->platform_data;
if (!pdata && !dev->of_node) {
dev_err(dev, "%s: no platform data.\n", __func__);
@@ -823,12 +842,14 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
timer->capability |= OMAP_TIMER_SECURE;
} else {
timer->id = pdev->id;
- timer->errata = pdata->timer_errata;
timer->capability = pdata->timer_capability;
timer->reserved = omap_dm_timer_reserved_systimer(timer->id);
timer->get_context_loss_count = pdata->get_context_loss_count;
}
+ if (pdata)
+ timer->errata = pdata->timer_errata;
+
timer->irq = irq->start;
timer->pdev = pdev;
@@ -881,8 +902,34 @@ static int omap_dm_timer_remove(struct platform_device *pdev)
return ret;
}
+static const struct dmtimer_platform_data omap3plus_pdata = {
+ .timer_errata = OMAP_TIMER_ERRATA_I103_I767,
+};
+
static const struct of_device_id omap_timer_match[] = {
- { .compatible = "ti,omap2-timer", },
+ {
+ .compatible = "ti,omap2420-timer",
+ },
+ {
+ .compatible = "ti,omap3430-timer",
+ .data = &omap3plus_pdata,
+ },
+ {
+ .compatible = "ti,omap4430-timer",
+ .data = &omap3plus_pdata,
+ },
+ {
+ .compatible = "ti,omap5430-timer",
+ .data = &omap3plus_pdata,
+ },
+ {
+ .compatible = "ti,am335x-timer",
+ .data = &omap3plus_pdata,
+ },
+ {
+ .compatible = "ti,am335x-timer-1ms",
+ .data = &omap3plus_pdata,
+ },
{},
};
MODULE_DEVICE_TABLE(of, omap_timer_match);
diff --git a/arch/arm/plat-omap/include/plat/dmtimer.h b/arch/arm/plat-omap/include/plat/dmtimer.h
index a3fbc48c332e..fb92abb91628 100644
--- a/arch/arm/plat-omap/include/plat/dmtimer.h
+++ b/arch/arm/plat-omap/include/plat/dmtimer.h
@@ -128,6 +128,7 @@ int omap_dm_timer_reserve_systimer(int id);
struct omap_dm_timer *omap_dm_timer_request(void);
struct omap_dm_timer *omap_dm_timer_request_specific(int timer_id);
struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap);
+struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np);
int omap_dm_timer_free(struct omap_dm_timer *timer);
void omap_dm_timer_enable(struct omap_dm_timer *timer);
void omap_dm_timer_disable(struct omap_dm_timer *timer);
diff --git a/arch/arm/plat-orion/Makefile b/arch/arm/plat-orion/Makefile
index ad97400ba3ad..2eca54b65906 100644
--- a/arch/arm/plat-orion/Makefile
+++ b/arch/arm/plat-orion/Makefile
@@ -3,12 +3,6 @@
#
ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include
-obj-$(CONFIG_ARCH_MVEBU) += addr-map.o
-obj-$(CONFIG_ARCH_KIRKWOOD) += addr-map.o
-obj-$(CONFIG_ARCH_DOVE) += addr-map.o
-obj-$(CONFIG_ARCH_ORION5X) += addr-map.o
-obj-$(CONFIG_ARCH_MV78XX0) += addr-map.o
-
orion-gpio-$(CONFIG_GENERIC_GPIO) += gpio.o
obj-$(CONFIG_PLAT_ORION_LEGACY) += irq.o pcie.o time.o common.o mpp.o
obj-$(CONFIG_PLAT_ORION_LEGACY) += $(orion-gpio-y)
diff --git a/arch/arm/plat-orion/addr-map.c b/arch/arm/plat-orion/addr-map.c
deleted file mode 100644
index 807ac8e5cbc0..000000000000
--- a/arch/arm/plat-orion/addr-map.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * arch/arm/plat-orion/addr-map.c
- *
- * Address map functions for Marvell Orion based SoCs
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/mbus.h>
-#include <linux/io.h>
-#include <plat/addr-map.h>
-
-struct mbus_dram_target_info orion_mbus_dram_info;
-
-const struct mbus_dram_target_info *mv_mbus_dram_info(void)
-{
- return &orion_mbus_dram_info;
-}
-EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
-
-/*
- * DDR target is the same on all Orion platforms.
- */
-#define TARGET_DDR 0
-
-/*
- * Helpers to get DDR bank info
- */
-#define DDR_BASE_CS_OFF(n) (0x0000 + ((n) << 3))
-#define DDR_SIZE_CS_OFF(n) (0x0004 + ((n) << 3))
-
-/*
- * CPU Address Decode Windows registers
- */
-#define WIN_CTRL_OFF 0x0000
-#define WIN_BASE_OFF 0x0004
-#define WIN_REMAP_LO_OFF 0x0008
-#define WIN_REMAP_HI_OFF 0x000c
-
-#define ATTR_HW_COHERENCY (0x1 << 4)
-
-/*
- * Default implementation
- */
-static void __init __iomem *
-orion_win_cfg_base(const struct orion_addr_map_cfg *cfg, int win)
-{
- return cfg->bridge_virt_base + (win << 4);
-}
-
-/*
- * Default implementation
- */
-static int __init orion_cpu_win_can_remap(const struct orion_addr_map_cfg *cfg,
- const int win)
-{
- if (win < cfg->remappable_wins)
- return 1;
-
- return 0;
-}
-
-void __init orion_setup_cpu_win(const struct orion_addr_map_cfg *cfg,
- const int win, const u32 base,
- const u32 size, const u8 target,
- const u8 attr, const int remap)
-{
- void __iomem *addr = cfg->win_cfg_base(cfg, win);
- u32 ctrl, base_high, remap_addr;
-
- if (win >= cfg->num_wins) {
- printk(KERN_ERR "setup_cpu_win: trying to allocate window "
- "%d when only %d allowed\n", win, cfg->num_wins);
- }
-
- base_high = base & 0xffff0000;
- ctrl = ((size - 1) & 0xffff0000) | (attr << 8) | (target << 4) | 1;
-
- writel(base_high, addr + WIN_BASE_OFF);
- writel(ctrl, addr + WIN_CTRL_OFF);
- if (cfg->cpu_win_can_remap(cfg, win)) {
- if (remap < 0)
- remap_addr = base;
- else
- remap_addr = remap;
- writel(remap_addr & 0xffff0000, addr + WIN_REMAP_LO_OFF);
- writel(0, addr + WIN_REMAP_HI_OFF);
- }
-}
-
-/*
- * Configure a number of windows.
- */
-static void __init orion_setup_cpu_wins(const struct orion_addr_map_cfg * cfg,
- const struct orion_addr_map_info *info)
-{
- while (info->win != -1) {
- orion_setup_cpu_win(cfg, info->win, info->base, info->size,
- info->target, info->attr, info->remap);
- info++;
- }
-}
-
-static void __init orion_disable_wins(const struct orion_addr_map_cfg * cfg)
-{
- void __iomem *addr;
- int i;
-
- for (i = 0; i < cfg->num_wins; i++) {
- addr = cfg->win_cfg_base(cfg, i);
-
- writel(0, addr + WIN_BASE_OFF);
- writel(0, addr + WIN_CTRL_OFF);
- if (cfg->cpu_win_can_remap(cfg, i)) {
- writel(0, addr + WIN_REMAP_LO_OFF);
- writel(0, addr + WIN_REMAP_HI_OFF);
- }
- }
-}
-
-/*
- * Disable, clear and configure windows.
- */
-void __init orion_config_wins(struct orion_addr_map_cfg * cfg,
- const struct orion_addr_map_info *info)
-{
- if (!cfg->cpu_win_can_remap)
- cfg->cpu_win_can_remap = orion_cpu_win_can_remap;
-
- if (!cfg->win_cfg_base)
- cfg->win_cfg_base = orion_win_cfg_base;
-
- orion_disable_wins(cfg);
-
- if (info)
- orion_setup_cpu_wins(cfg, info);
-}
-
-/*
- * Setup MBUS dram target info.
- */
-void __init orion_setup_cpu_mbus_target(const struct orion_addr_map_cfg *cfg,
- const void __iomem *ddr_window_cpu_base)
-{
- int i;
- int cs;
-
- orion_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
-
- for (i = 0, cs = 0; i < 4; i++) {
- u32 base = readl(ddr_window_cpu_base + DDR_BASE_CS_OFF(i));
- u32 size = readl(ddr_window_cpu_base + DDR_SIZE_CS_OFF(i));
-
- /*
- * We only take care of entries for which the chip
- * select is enabled, and that don't have high base
- * address bits set (devices can only access the first
- * 32 bits of the memory).
- */
- if ((size & 1) && !(base & 0xF)) {
- struct mbus_dram_window *w;
-
- w = &orion_mbus_dram_info.cs[cs++];
- w->cs_index = i;
- w->mbus_attr = 0xf & ~(1 << i);
- if (cfg->hw_io_coherency)
- w->mbus_attr |= ATTR_HW_COHERENCY;
- w->base = base & 0xffff0000;
- w->size = (size | 0x0000ffff) + 1;
- }
- }
- orion_mbus_dram_info.num_cs = cs;
-}
diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c
index c29ee7ea200b..e39c2ba6e2fb 100644
--- a/arch/arm/plat-orion/gpio.c
+++ b/arch/arm/plat-orion/gpio.c
@@ -439,6 +439,64 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc)
}
}
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct orion_gpio_chip *ochip =
+ container_of(chip, struct orion_gpio_chip, chip);
+ u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk;
+ int i;
+
+ out = readl_relaxed(GPIO_OUT(ochip));
+ io_conf = readl_relaxed(GPIO_IO_CONF(ochip));
+ blink = readl_relaxed(GPIO_BLINK_EN(ochip));
+ in_pol = readl_relaxed(GPIO_IN_POL(ochip));
+ data_in = readl_relaxed(GPIO_DATA_IN(ochip));
+ cause = readl_relaxed(GPIO_EDGE_CAUSE(ochip));
+ edg_msk = readl_relaxed(GPIO_EDGE_MASK(ochip));
+ lvl_msk = readl_relaxed(GPIO_LEVEL_MASK(ochip));
+
+ for (i = 0; i < chip->ngpio; i++) {
+ const char *label;
+ u32 msk;
+ bool is_out;
+
+ label = gpiochip_is_requested(chip, i);
+ if (!label)
+ continue;
+
+ msk = 1 << i;
+ is_out = !(io_conf & msk);
+
+ seq_printf(s, " gpio-%-3d (%-20.20s)", chip->base + i, label);
+
+ if (is_out) {
+ seq_printf(s, " out %s %s\n",
+ out & msk ? "hi" : "lo",
+ blink & msk ? "(blink )" : "");
+ continue;
+ }
+
+ seq_printf(s, " in %s (act %s) - IRQ",
+ (data_in ^ in_pol) & msk ? "hi" : "lo",
+ in_pol & msk ? "lo" : "hi");
+ if (!((edg_msk | lvl_msk) & msk)) {
+ seq_printf(s, " disabled\n");
+ continue;
+ }
+ if (edg_msk & msk)
+ seq_printf(s, " edge ");
+ if (lvl_msk & msk)
+ seq_printf(s, " level");
+ seq_printf(s, " (%s)\n", cause & msk ? "pending" : "clear ");
+ }
+}
+#else
+#define orion_gpio_dbg_show NULL
+#endif
+
void __init orion_gpio_init(struct device_node *np,
int gpio_base, int ngpio,
void __iomem *base, int mask_offset,
@@ -471,6 +529,7 @@ void __init orion_gpio_init(struct device_node *np,
#ifdef CONFIG_OF
ochip->chip.of_node = np;
#endif
+ ochip->chip.dbg_show = orion_gpio_dbg_show;
spin_lock_init(&ochip->lock);
ochip->base = (void __iomem *)base;
diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig
index 54d186106f9f..f8ed2de0a678 100644
--- a/arch/arm/plat-samsung/Kconfig
+++ b/arch/arm/plat-samsung/Kconfig
@@ -93,9 +93,9 @@ config SAMSUNG_IRQ_VIC_TIMER
Internal configuration to build the VIC timer interrupt code.
config S5P_IRQ
- def_bool (ARCH_S5P64X0 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS)
+ def_bool (ARCH_S5P64X0 || ARCH_S5PC100 || ARCH_S5PV210)
help
- Support common interrup part for ARCH_S5P and ARCH_EXYNOS SoCs
+ Support common interrupt part for ARCH_S5P SoCs
config S5P_EXT_INT
bool
diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index 8dc0605a9ce9..d30042e39974 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -2,6 +2,7 @@
#include <xen/events.h>
#include <xen/grant_table.h>
#include <xen/hvm.h>
+#include <xen/interface/vcpu.h>
#include <xen/interface/xen.h>
#include <xen/interface/memory.h>
#include <xen/interface/hvm/params.h>
@@ -9,9 +10,11 @@
#include <xen/platform_pci.h>
#include <xen/xenbus.h>
#include <xen/page.h>
+#include <xen/interface/sched.h>
#include <xen/xen-ops.h>
#include <asm/xen/hypervisor.h>
#include <asm/xen/hypercall.h>
+#include <asm/system_misc.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/module.h>
@@ -32,6 +35,7 @@ struct shared_info xen_dummy_shared_info;
struct shared_info *HYPERVISOR_shared_info = (void *)&xen_dummy_shared_info;
DEFINE_PER_CPU(struct vcpu_info *, xen_vcpu);
+static struct vcpu_info __percpu *xen_vcpu_info;
/* These are unused until we support booting "pre-ballooned" */
unsigned long xen_released_pages;
@@ -148,6 +152,47 @@ int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
}
EXPORT_SYMBOL_GPL(xen_unmap_domain_mfn_range);
+static int __init xen_secondary_init(unsigned int cpu)
+{
+ struct vcpu_register_vcpu_info info;
+ struct vcpu_info *vcpup;
+ int err;
+
+ pr_info("Xen: initializing cpu%d\n", cpu);
+ vcpup = per_cpu_ptr(xen_vcpu_info, cpu);
+
+ info.mfn = __pa(vcpup) >> PAGE_SHIFT;
+ info.offset = offset_in_page(vcpup);
+
+ err = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info);
+ if (err) {
+ pr_debug("register_vcpu_info failed: err=%d\n", err);
+ } else {
+ /* This cpu is using the registered vcpu info, even if
+ later ones fail to. */
+ per_cpu(xen_vcpu, cpu) = vcpup;
+ }
+ return 0;
+}
+
+static void xen_restart(char str, const char *cmd)
+{
+ struct sched_shutdown r = { .reason = SHUTDOWN_reboot };
+ int rc;
+ rc = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r);
+ if (rc)
+ BUG();
+}
+
+static void xen_power_off(void)
+{
+ struct sched_shutdown r = { .reason = SHUTDOWN_poweroff };
+ int rc;
+ rc = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r);
+ if (rc)
+ BUG();
+}
+
/*
* see Documentation/devicetree/bindings/arm/xen.txt for the
* documentation of the Xen Device Tree format.
@@ -163,6 +208,7 @@ static int __init xen_guest_init(void)
const char *version = NULL;
const char *xen_prefix = "xen,xen-";
struct resource res;
+ int i;
node = of_find_compatible_node(NULL, NULL, "xen,xen");
if (!node) {
@@ -209,18 +255,26 @@ static int __init xen_guest_init(void)
/* xen_vcpu is a pointer to the vcpu_info struct in the shared_info
* page, we use it in the event channel upcall and in some pvclock
- * related functions. We don't need the vcpu_info placement
- * optimizations because we don't use any pv_mmu or pv_irq op on
- * HVM.
+ * related functions.
* The shared info contains exactly 1 CPU (the boot CPU). The guest
* is required to use VCPUOP_register_vcpu_info to place vcpu info
- * for secondary CPUs as they are brought up. */
- per_cpu(xen_vcpu, 0) = &HYPERVISOR_shared_info->vcpu_info[0];
+ * for secondary CPUs as they are brought up.
+ * For uniformity we use VCPUOP_register_vcpu_info even on cpu0.
+ */
+ xen_vcpu_info = __alloc_percpu(sizeof(struct vcpu_info),
+ sizeof(struct vcpu_info));
+ if (xen_vcpu_info == NULL)
+ return -ENOMEM;
+ for_each_online_cpu(i)
+ xen_secondary_init(i);
gnttab_init();
if (!xen_initial_domain())
xenbus_probe(NULL);
+ pm_power_off = xen_power_off;
+ arm_pm_restart = xen_restart;
+
return 0;
}
core_initcall(xen_guest_init);
@@ -231,6 +285,11 @@ static irqreturn_t xen_arm_callback(int irq, void *arg)
return IRQ_HANDLED;
}
+static __init void xen_percpu_enable_events(void *unused)
+{
+ enable_percpu_irq(xen_events_irq, 0);
+}
+
static int __init xen_init_events(void)
{
if (!xen_domain() || xen_events_irq < 0)
@@ -239,12 +298,12 @@ static int __init xen_init_events(void)
xen_init_IRQ();
if (request_percpu_irq(xen_events_irq, xen_arm_callback,
- "events", xen_vcpu)) {
+ "events", &xen_vcpu)) {
pr_err("Error requesting IRQ %d\n", xen_events_irq);
return -EINVAL;
}
- enable_percpu_irq(xen_events_irq, 0);
+ on_each_cpu(xen_percpu_enable_events, NULL, 0);
return 0;
}
@@ -259,4 +318,5 @@ EXPORT_SYMBOL_GPL(HYPERVISOR_sched_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_hvm_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_memory_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_physdev_op);
+EXPORT_SYMBOL_GPL(HYPERVISOR_vcpu_op);
EXPORT_SYMBOL_GPL(privcmd_call);
diff --git a/arch/arm/xen/hypercall.S b/arch/arm/xen/hypercall.S
index 71f723984cbd..199cb2da7663 100644
--- a/arch/arm/xen/hypercall.S
+++ b/arch/arm/xen/hypercall.S
@@ -87,6 +87,7 @@ HYPERCALL2(event_channel_op);
HYPERCALL2(hvm_op);
HYPERCALL2(memory_op);
HYPERCALL2(physdev_op);
+HYPERCALL3(vcpu_op);
ENTRY(privcmd_call)
stmdb sp!, {r4}
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 73b6e764034c..43b0e9f10a39 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -6,6 +6,7 @@ config ARM64
select ARCH_WANT_FRAME_POINTERS
select ARM_AMBA
select ARM_ARCH_TIMER
+ select ARM_GIC
select CLONE_BACKWARDS
select COMMON_CLK
select GENERIC_CLOCKEVENTS
@@ -31,6 +32,8 @@ config ARM64
select OF
select OF_EARLY_FLATTREE
select PERF_USE_VMALLOC
+ select POWER_RESET
+ select POWER_SUPPLY
select RTC_LIB
select SPARSE_IRQ
select SYSCTL_EXCEPTION_TRACE
@@ -105,6 +108,7 @@ config ARCH_VEXPRESS
bool "ARMv8 software model (Versatile Express)"
select ARCH_REQUIRE_GPIOLIB
select COMMON_CLK_VERSATILE
+ select POWER_RESET_VEXPRESS
select VEXPRESS_CONFIG
help
This enables support for the ARMv8 software model (Versatile
diff --git a/arch/arm64/boot/dts/foundation-v8.dts b/arch/arm64/boot/dts/foundation-v8.dts
index 198682b6de31..84fcc5018284 100644
--- a/arch/arm64/boot/dts/foundation-v8.dts
+++ b/arch/arm64/boot/dts/foundation-v8.dts
@@ -23,7 +23,7 @@
};
cpus {
- #address-cells = <1>;
+ #address-cells = <2>;
#size-cells = <0>;
cpu@0 {
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index 91e2a6a6fcd4..bf6ab242f047 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -130,4 +130,9 @@ static inline u64 arch_counter_get_cntvct(void)
return cval;
}
+static inline int arch_timer_arch_init(void)
+{
+ return 0;
+}
+
#endif
diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
index 95e407255347..a6e1750369ef 100644
--- a/arch/arm64/include/asm/system_misc.h
+++ b/arch/arm64/include/asm/system_misc.h
@@ -41,7 +41,7 @@ extern void show_pte(struct mm_struct *mm, unsigned long addr);
extern void __show_regs(struct pt_regs *);
void soft_restart(unsigned long);
-extern void (*pm_restart)(const char *cmd);
+extern void (*arm_pm_restart)(char str, const char *cmd);
#define UDBG_UNDEFINED (1 << 0)
#define UDBG_SYSCALL (1 << 1)
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index f4919721f7dd..46f02c3b5015 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -81,8 +81,8 @@ void soft_restart(unsigned long addr)
void (*pm_power_off)(void);
EXPORT_SYMBOL_GPL(pm_power_off);
-void (*pm_restart)(const char *cmd);
-EXPORT_SYMBOL_GPL(pm_restart);
+void (*arm_pm_restart)(char str, const char *cmd);
+EXPORT_SYMBOL_GPL(arm_pm_restart);
void arch_cpu_idle_prepare(void)
{
@@ -131,8 +131,8 @@ void machine_restart(char *cmd)
local_fiq_disable();
/* Now call the architecture specific reboot code. */
- if (pm_restart)
- pm_restart(cmd);
+ if (arm_pm_restart)
+ arm_pm_restart('h', cmd);
/*
* Whoops - the architecture was unable to reboot.
diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
index b0ef18d14c3b..a551f88ae2c1 100644
--- a/arch/arm64/kernel/time.c
+++ b/arch/arm64/kernel/time.c
@@ -32,6 +32,7 @@
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/delay.h>
+#include <linux/clocksource.h>
#include <clocksource/arm_arch_timer.h>
@@ -77,10 +78,11 @@ void __init time_init(void)
{
u32 arch_timer_rate;
- if (arch_timer_init())
- panic("Unable to initialise architected timer.\n");
+ clocksource_of_init();
arch_timer_rate = arch_timer_get_rate();
+ if (!arch_timer_rate)
+ panic("Unable to initialise architected timer.\n");
/* Cache the sched_clock multiplier to save a divide in the hot path. */
sched_clock_mult = NSEC_PER_SEC / arch_timer_rate;
diff --git a/arch/arm64/lib/bitops.S b/arch/arm64/lib/bitops.S
index 36216d30cb9a..e5db797790d3 100644
--- a/arch/arm64/lib/bitops.S
+++ b/arch/arm64/lib/bitops.S
@@ -21,13 +21,13 @@
/*
* x0: bits 5:0 bit offset
- * bits 63:6 word offset
+ * bits 31:6 word offset
* x1: address
*/
.macro bitop, name, instr
ENTRY( \name )
- and x3, x0, #63 // Get bit offset
- eor x0, x0, x3 // Clear low bits
+ and w3, w0, #63 // Get bit offset
+ eor w0, w0, w3 // Clear low bits
mov x2, #1
add x1, x1, x0, lsr #3 // Get word offset
lsl x3, x2, x3 // Create mask
@@ -41,8 +41,8 @@ ENDPROC(\name )
.macro testop, name, instr
ENTRY( \name )
- and x3, x0, #63 // Get bit offset
- eor x0, x0, x3 // Clear low bits
+ and w3, w0, #63 // Get bit offset
+ eor w0, w0, w3 // Clear low bits
mov x2, #1
add x1, x1, x0, lsr #3 // Get word offset
lsl x4, x2, x3 // Create mask
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 52638171d6fd..98af6e760cce 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -148,6 +148,7 @@ void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
#define VM_FAULT_BADACCESS 0x020000
#define ESR_WRITE (1 << 6)
+#define ESR_CM (1 << 8)
#define ESR_LNX_EXEC (1 << 24)
/*
@@ -206,7 +207,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
struct task_struct *tsk;
struct mm_struct *mm;
int fault, sig, code;
- int write = esr & ESR_WRITE;
+ bool write = (esr & ESR_WRITE) && !(esr & ESR_CM);
unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
(write ? FAULT_FLAG_WRITE : 0);
diff --git a/arch/cris/kernel/profile.c b/arch/cris/kernel/profile.c
index b82e08615d1b..cd9f15b92f8f 100644
--- a/arch/cris/kernel/profile.c
+++ b/arch/cris/kernel/profile.c
@@ -76,7 +76,7 @@ static int __init init_cris_profile(void)
entry = proc_create("system_profile", S_IWUSR | S_IRUGO, NULL,
&cris_proc_profile_operations);
if (entry) {
- entry->size = SAMPLE_BUFFER_SIZE;
+ proc_set_size(entry, SAMPLE_BUFFER_SIZE);
}
prof_running = 1;
diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig
index 04dff5bdcbf7..841325f60df8 100644
--- a/arch/hexagon/Kconfig
+++ b/arch/hexagon/Kconfig
@@ -30,8 +30,6 @@ config HEXAGON
select GENERIC_CLOCKEVENTS_BROADCAST
select MODULES_USE_ELF_RELA
select GENERIC_CPU_DEVICES
- select GENERIC_KERNEL_THREAD
- select GENERIC_KERNEL_EXECVE
---help---
Qualcomm Hexagon is a processor architecture designed for high
performance and low power across a wide variety of applications.
diff --git a/arch/hexagon/kernel/vm_entry.S b/arch/hexagon/kernel/vm_entry.S
index e3086185fc9f..67c6ccc14770 100644
--- a/arch/hexagon/kernel/vm_entry.S
+++ b/arch/hexagon/kernel/vm_entry.S
@@ -291,12 +291,12 @@ event_dispatch:
/* "Nested control path" -- if the previous mode was kernel */
{
R0 = memw(R29 + #_PT_ER_VMEST);
- R16.L = #LO(do_work_pending);
+ R26.L = #LO(do_work_pending);
}
{
P0 = tstbit(R0, #HVM_VMEST_UM_SFT);
if (!P0.new) jump:nt restore_all;
- R16.H = #HI(do_work_pending);
+ R26.H = #HI(do_work_pending);
R0 = #VM_INT_DISABLE;
}
@@ -304,7 +304,7 @@ event_dispatch:
* Check also the return from fork/system call, normally coming back from
* user mode
*
- * R16 needs to have do_work_pending, and R0 should have VM_INT_DISABLE
+ * R26 needs to have do_work_pending, and R0 should have VM_INT_DISABLE
*/
check_work_pending:
@@ -313,7 +313,7 @@ check_work_pending:
{
R0 = R29; /* regs should still be at top of stack */
R1 = memw(THREADINFO_REG + #_THREAD_INFO_FLAGS);
- callr R16;
+ callr R26;
}
{
@@ -375,11 +375,11 @@ _K_enter_debug:
ret_from_fork:
{
call schedule_tail
- R16.H = #HI(do_work_pending);
+ R26.H = #HI(do_work_pending);
}
{
P0 = cmp.eq(R24, #0);
- R16.L = #LO(do_work_pending);
+ R26.L = #LO(do_work_pending);
R0 = #VM_INT_DISABLE;
}
if P0 jump check_work_pending
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig
index 433e75a2ee9a..cad060f288cf 100644
--- a/arch/parisc/Kconfig
+++ b/arch/parisc/Kconfig
@@ -13,6 +13,7 @@ config PARISC
select BUG
select HAVE_PERF_EVENTS
select GENERIC_ATOMIC64 if !64BIT
+ select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select HAVE_GENERIC_HARDIRQS
select BROKEN_RODATA
select GENERIC_IRQ_PROBE
@@ -242,6 +243,14 @@ config SMP
If you don't know what to do here, say N.
+config IRQSTACKS
+ bool "Use separate kernel stacks when processing interrupts"
+ default n
+ help
+ If you say Y here the kernel will use separate kernel stacks
+ for handling hard and soft interrupts. This can help avoid
+ overflowing the process kernel stacks.
+
config HOTPLUG_CPU
bool
default y if SMP
diff --git a/arch/parisc/Kconfig.debug b/arch/parisc/Kconfig.debug
index bc989e522a04..08a332f6ee87 100644
--- a/arch/parisc/Kconfig.debug
+++ b/arch/parisc/Kconfig.debug
@@ -13,3 +13,14 @@ config DEBUG_RODATA
If in doubt, say "N".
endmenu
+
+config DEBUG_STACKOVERFLOW
+ bool "Check for stack overflows"
+ default y
+ depends on DEBUG_KERNEL
+ ---help---
+ Say Y here if you want to check the overflows of kernel, IRQ
+ and exception stacks. This option will cause messages of the
+ stacks in detail when free stack space drops below a certain
+ limit.
+ If in doubt, say "N".
diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile
index 113e28206503..2f967cc6649e 100644
--- a/arch/parisc/Makefile
+++ b/arch/parisc/Makefile
@@ -24,9 +24,7 @@ CHECKFLAGS += -D__hppa__=1
LIBGCC = $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name)
MACHINE := $(shell uname -m)
-ifeq ($(MACHINE),parisc*)
-NATIVE := 1
-endif
+NATIVE := $(if $(filter parisc%,$(MACHINE)),1,0)
ifdef CONFIG_64BIT
UTS_MACHINE := parisc64
diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h
index f38e1984b242..472886ceab1d 100644
--- a/arch/parisc/include/asm/atomic.h
+++ b/arch/parisc/include/asm/atomic.h
@@ -229,6 +229,29 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0)
+/*
+ * atomic64_dec_if_positive - decrement by 1 if old value positive
+ * @v: pointer of type atomic_t
+ *
+ * The function returns the old value of *v minus 1, even if
+ * the atomic variable, v, was not decremented.
+ */
+static inline long atomic64_dec_if_positive(atomic64_t *v)
+{
+ long c, old, dec;
+ c = atomic64_read(v);
+ for (;;) {
+ dec = c - 1;
+ if (unlikely(dec < 0))
+ break;
+ old = atomic64_cmpxchg((v), c, dec);
+ if (likely(old == c))
+ break;
+ c = old;
+ }
+ return dec;
+}
+
#endif /* !CONFIG_64BIT */
diff --git a/arch/parisc/include/asm/dma-mapping.h b/arch/parisc/include/asm/dma-mapping.h
index 106b395688e1..d0eae5f2bd87 100644
--- a/arch/parisc/include/asm/dma-mapping.h
+++ b/arch/parisc/include/asm/dma-mapping.h
@@ -46,6 +46,9 @@ extern struct hppa_dma_ops pcx_dma_ops;
extern struct hppa_dma_ops *hppa_dma_ops;
+#define dma_alloc_attrs(d, s, h, f, a) dma_alloc_coherent(d, s, h, f)
+#define dma_free_attrs(d, s, h, f, a) dma_free_coherent(d, s, h, f)
+
static inline void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t flag)
diff --git a/arch/parisc/include/asm/hardirq.h b/arch/parisc/include/asm/hardirq.h
index 0d68184a76cb..12373c4dabab 100644
--- a/arch/parisc/include/asm/hardirq.h
+++ b/arch/parisc/include/asm/hardirq.h
@@ -1,11 +1,41 @@
/* hardirq.h: PA-RISC hard IRQ support.
*
* Copyright (C) 2001 Matthew Wilcox <matthew@wil.cx>
+ * Copyright (C) 2013 Helge Deller <deller@gmx.de>
*/
#ifndef _PARISC_HARDIRQ_H
#define _PARISC_HARDIRQ_H
-#include <asm-generic/hardirq.h>
+#include <linux/cache.h>
+#include <linux/threads.h>
+#include <linux/irq.h>
+
+typedef struct {
+ unsigned int __softirq_pending;
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+ unsigned int kernel_stack_usage;
+#endif
+#ifdef CONFIG_SMP
+ unsigned int irq_resched_count;
+ unsigned int irq_call_count;
+#endif
+ unsigned int irq_tlb_count;
+} ____cacheline_aligned irq_cpustat_t;
+
+DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
+
+#define __ARCH_IRQ_STAT
+#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
+#define inc_irq_stat(member) this_cpu_inc(irq_stat.member)
+#define local_softirq_pending() this_cpu_read(irq_stat.__softirq_pending)
+
+#define __ARCH_SET_SOFTIRQ_PENDING
+
+#define set_softirq_pending(x) \
+ this_cpu_write(irq_stat.__softirq_pending, (x))
+#define or_softirq_pending(x) this_cpu_or(irq_stat.__softirq_pending, (x))
+
+#define ack_bad_irq(irq) WARN(1, "unexpected IRQ trap at vector %02x\n", irq)
#endif /* _PARISC_HARDIRQ_H */
diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h
index 09b54a57a48d..064015547d1e 100644
--- a/arch/parisc/include/asm/processor.h
+++ b/arch/parisc/include/asm/processor.h
@@ -20,8 +20,6 @@
#endif /* __ASSEMBLY__ */
-#define KERNEL_STACK_SIZE (4*PAGE_SIZE)
-
/*
* Default implementation of macro that returns current
* instruction pointer ("program counter").
@@ -61,6 +59,23 @@
#ifndef __ASSEMBLY__
/*
+ * IRQ STACK - used for irq handler
+ */
+#ifdef __KERNEL__
+
+#define IRQ_STACK_SIZE (4096 << 2) /* 16k irq stack size */
+
+union irq_stack_union {
+ unsigned long stack[IRQ_STACK_SIZE/sizeof(unsigned long)];
+};
+
+DECLARE_PER_CPU(union irq_stack_union, irq_stack_union);
+
+void call_on_stack(unsigned long p1, void *func, unsigned long new_stack);
+
+#endif /* __KERNEL__ */
+
+/*
* Data detected about CPUs at boot time which is the same for all CPU's.
* HP boxes are SMP - ie identical processors.
*
@@ -97,7 +112,6 @@ struct cpuinfo_parisc {
unsigned long txn_addr; /* MMIO addr of EIR or id_eid */
#ifdef CONFIG_SMP
unsigned long pending_ipi; /* bitmap of type ipi_message_type */
- unsigned long ipi_count; /* number ipi Interrupts */
#endif
unsigned long bh_count; /* number of times bh was invoked */
unsigned long prof_counter; /* per CPU profiling support */
diff --git a/arch/parisc/include/asm/thread_info.h b/arch/parisc/include/asm/thread_info.h
index 6182832e5b6c..540c88fa8f86 100644
--- a/arch/parisc/include/asm/thread_info.h
+++ b/arch/parisc/include/asm/thread_info.h
@@ -40,7 +40,7 @@ struct thread_info {
/* thread information allocation */
-#define THREAD_SIZE_ORDER 2
+#define THREAD_SIZE_ORDER 2 /* PA-RISC requires at least 16k stack */
/* Be sure to hunt all references to this down when you change the size of
* the kernel stack */
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
diff --git a/arch/parisc/include/asm/tlbflush.h b/arch/parisc/include/asm/tlbflush.h
index 8f1a8100bf2d..5273da991e06 100644
--- a/arch/parisc/include/asm/tlbflush.h
+++ b/arch/parisc/include/asm/tlbflush.h
@@ -22,6 +22,8 @@ extern spinlock_t pa_tlb_lock;
extern void flush_tlb_all(void);
extern void flush_tlb_all_local(void *);
+#define smp_flush_tlb_all() flush_tlb_all()
+
/*
* flush_tlb_mm()
*
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index 83ded26cad06..65fb4cbc3a0f 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -606,7 +606,7 @@ void clear_user_highpage(struct page *page, unsigned long vaddr)
/* Clear using TMPALIAS region. The page doesn't need to
be flushed but the kernel mapping needs to be purged. */
- vto = kmap_atomic(page, KM_USER0);
+ vto = kmap_atomic(page);
/* The PA-RISC 2.0 Architecture book states on page F-6:
"Before a write-capable translation is enabled, *all*
@@ -641,8 +641,8 @@ void copy_user_highpage(struct page *to, struct page *from,
the `to' page must be flushed in copy_user_page_asm since
it can be used to bring in executable code. */
- vfrom = kmap_atomic(from, KM_USER0);
- vto = kmap_atomic(to, KM_USER1);
+ vfrom = kmap_atomic(from);
+ vto = kmap_atomic(to);
purge_kernel_dcache_page_asm((unsigned long)vto);
purge_tlb_start(flags);
diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S
index f33201bf8977..4bb96ad9b0b1 100644
--- a/arch/parisc/kernel/entry.S
+++ b/arch/parisc/kernel/entry.S
@@ -400,7 +400,15 @@
#if PT_NLEVELS == 3
extru \va,31-ASM_PMD_SHIFT,ASM_BITS_PER_PMD,\index
#else
+# if defined(CONFIG_64BIT)
+ extrd,u \va,63-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
+ #else
+ # if PAGE_SIZE > 4096
+ extru \va,31-ASM_PGDIR_SHIFT,32-ASM_PGDIR_SHIFT,\index
+ # else
extru \va,31-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
+ # endif
+# endif
#endif
dep %r0,31,PAGE_SHIFT,\pmd /* clear offset */
copy %r0,\pte
@@ -615,7 +623,7 @@
.text
- .align PAGE_SIZE
+ .align 4096
ENTRY(fault_vector_20)
/* First vector is invalid (0) */
@@ -825,11 +833,6 @@ ENTRY(syscall_exit_rfi)
STREG %r19,PT_SR7(%r16)
intr_return:
- /* NOTE: Need to enable interrupts incase we schedule. */
- ssm PSW_SM_I, %r0
-
-intr_check_resched:
-
/* check for reschedule */
mfctl %cr30,%r1
LDREG TI_FLAGS(%r1),%r19 /* sched.h: TIF_NEED_RESCHED */
@@ -856,6 +859,11 @@ intr_check_sig:
LDREG PT_IASQ1(%r16), %r20
cmpib,COND(=),n 0,%r20,intr_restore /* backward */
+ /* NOTE: We need to enable interrupts if we have to deliver
+ * signals. We used to do this earlier but it caused kernel
+ * stack overflows. */
+ ssm PSW_SM_I, %r0
+
copy %r0, %r25 /* long in_syscall = 0 */
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
@@ -907,6 +915,10 @@ intr_do_resched:
cmpib,COND(=) 0, %r20, intr_do_preempt
nop
+ /* NOTE: We need to enable interrupts if we schedule. We used
+ * to do this earlier but it caused kernel stack overflows. */
+ ssm PSW_SM_I, %r0
+
#ifdef CONFIG_64BIT
ldo -16(%r30),%r29 /* Reference param save area */
#endif
@@ -1694,7 +1706,8 @@ ENTRY(sys_\name\()_wrapper)
ldo TASK_REGS(%r1),%r1
reg_save %r1
mfctl %cr27, %r28
- b sys_\name
+ ldil L%sys_\name, %r31
+ be R%sys_\name(%sr4,%r31)
STREG %r28, PT_CR27(%r1)
ENDPROC(sys_\name\()_wrapper)
.endm
@@ -1997,6 +2010,47 @@ ftrace_stub:
ENDPROC(return_to_handler)
#endif /* CONFIG_FUNCTION_TRACER */
+#ifdef CONFIG_IRQSTACKS
+/* void call_on_stack(unsigned long param1, void *func,
+ unsigned long new_stack) */
+ENTRY(call_on_stack)
+ copy %sp, %r1
+
+ /* Regarding the HPPA calling conventions for function pointers,
+ we assume the PIC register is not changed across call. For
+ CONFIG_64BIT, the argument pointer is left to point at the
+ argument region allocated for the call to call_on_stack. */
+# ifdef CONFIG_64BIT
+ /* Switch to new stack. We allocate two 128 byte frames. */
+ ldo 256(%arg2), %sp
+ /* Save previous stack pointer and return pointer in frame marker */
+ STREG %rp, -144(%sp)
+ /* Calls always use function descriptor */
+ LDREG 16(%arg1), %arg1
+ bve,l (%arg1), %rp
+ STREG %r1, -136(%sp)
+ LDREG -144(%sp), %rp
+ bve (%rp)
+ LDREG -136(%sp), %sp
+# else
+ /* Switch to new stack. We allocate two 64 byte frames. */
+ ldo 128(%arg2), %sp
+ /* Save previous stack pointer and return pointer in frame marker */
+ STREG %r1, -68(%sp)
+ STREG %rp, -84(%sp)
+ /* Calls use function descriptor if PLABEL bit is set */
+ bb,>=,n %arg1, 30, 1f
+ depwi 0,31,2, %arg1
+ LDREG 0(%arg1), %arg1
+1:
+ be,l 0(%sr4,%arg1), %sr0, %r31
+ copy %r31, %rp
+ LDREG -84(%sp), %rp
+ bv (%rp)
+ LDREG -68(%sp), %sp
+# endif /* CONFIG_64BIT */
+ENDPROC(call_on_stack)
+#endif /* CONFIG_IRQSTACKS */
get_register:
/*
diff --git a/arch/parisc/kernel/hpmc.S b/arch/parisc/kernel/hpmc.S
index 5595a2f31181..e158b6fbf1b4 100644
--- a/arch/parisc/kernel/hpmc.S
+++ b/arch/parisc/kernel/hpmc.S
@@ -55,13 +55,13 @@
* IODC requires 7K byte stack. That leaves 1K byte for os_hpmc.
*/
- .align PAGE_SIZE
+ .align 4096
hpmc_stack:
.block 16384
#define HPMC_IODC_BUF_SIZE 0x8000
- .align PAGE_SIZE
+ .align 4096
hpmc_iodc_buf:
.block HPMC_IODC_BUF_SIZE
diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c
index 8094d3ed3b64..e255db0bb761 100644
--- a/arch/parisc/kernel/irq.c
+++ b/arch/parisc/kernel/irq.c
@@ -152,6 +152,39 @@ static struct irq_chip cpu_interrupt_type = {
.irq_retrigger = NULL,
};
+DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
+#define irq_stats(x) (&per_cpu(irq_stat, x))
+
+/*
+ * /proc/interrupts printing for arch specific interrupts
+ */
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+ int j;
+
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+ seq_printf(p, "%*s: ", prec, "STK");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->kernel_stack_usage);
+ seq_printf(p, " Kernel stack usage\n");
+#endif
+#ifdef CONFIG_SMP
+ seq_printf(p, "%*s: ", prec, "RES");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->irq_resched_count);
+ seq_printf(p, " Rescheduling interrupts\n");
+ seq_printf(p, "%*s: ", prec, "CAL");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->irq_call_count);
+ seq_printf(p, " Function call interrupts\n");
+#endif
+ seq_printf(p, "%*s: ", prec, "TLB");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", irq_stats(j)->irq_tlb_count);
+ seq_printf(p, " TLB shootdowns\n");
+ return 0;
+}
+
int show_interrupts(struct seq_file *p, void *v)
{
int i = *(loff_t *) v, j;
@@ -219,6 +252,9 @@ int show_interrupts(struct seq_file *p, void *v)
raw_spin_unlock_irqrestore(&desc->lock, flags);
}
+ if (i == NR_IRQS)
+ arch_show_interrupts(p, 3);
+
return 0;
}
@@ -330,6 +366,66 @@ static inline int eirr_to_irq(unsigned long eirr)
return (BITS_PER_LONG - bit) + TIMER_IRQ;
}
+int sysctl_panic_on_stackoverflow = 1;
+
+static inline void stack_overflow_check(struct pt_regs *regs)
+{
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+ #define STACK_MARGIN (256*6)
+
+ /* Our stack starts directly behind the thread_info struct. */
+ unsigned long stack_start = (unsigned long) current_thread_info();
+ unsigned long sp = regs->gr[30];
+ unsigned long stack_usage;
+ unsigned int *last_usage;
+
+ /* if sr7 != 0, we interrupted a userspace process which we do not want
+ * to check for stack overflow. We will only check the kernel stack. */
+ if (regs->sr[7])
+ return;
+
+ /* calculate kernel stack usage */
+ stack_usage = sp - stack_start;
+ last_usage = &per_cpu(irq_stat.kernel_stack_usage, smp_processor_id());
+
+ if (unlikely(stack_usage > *last_usage))
+ *last_usage = stack_usage;
+
+ if (likely(stack_usage < (THREAD_SIZE - STACK_MARGIN)))
+ return;
+
+ pr_emerg("stackcheck: %s will most likely overflow kernel stack "
+ "(sp:%lx, stk bottom-top:%lx-%lx)\n",
+ current->comm, sp, stack_start, stack_start + THREAD_SIZE);
+
+ if (sysctl_panic_on_stackoverflow)
+ panic("low stack detected by irq handler - check messages\n");
+#endif
+}
+
+#ifdef CONFIG_IRQSTACKS
+DEFINE_PER_CPU(union irq_stack_union, irq_stack_union);
+
+static void execute_on_irq_stack(void *func, unsigned long param1)
+{
+ unsigned long *irq_stack_start;
+ unsigned long irq_stack;
+ int cpu = smp_processor_id();
+
+ irq_stack_start = &per_cpu(irq_stack_union, cpu).stack[0];
+ irq_stack = (unsigned long) irq_stack_start;
+ irq_stack = ALIGN(irq_stack, 16); /* align for stack frame usage */
+
+ BUG_ON(*irq_stack_start); /* report bug if we were called recursive. */
+ *irq_stack_start = 1;
+
+ /* This is where we switch to the IRQ stack. */
+ call_on_stack(param1, func, irq_stack);
+
+ *irq_stack_start = 0;
+}
+#endif /* CONFIG_IRQSTACKS */
+
/* ONLY called from entry.S:intr_extint() */
void do_cpu_irq_mask(struct pt_regs *regs)
{
@@ -364,7 +460,13 @@ void do_cpu_irq_mask(struct pt_regs *regs)
goto set_out;
}
#endif
+ stack_overflow_check(regs);
+
+#ifdef CONFIG_IRQSTACKS
+ execute_on_irq_stack(&generic_handle_irq, irq);
+#else
generic_handle_irq(irq);
+#endif /* CONFIG_IRQSTACKS */
out:
irq_exit();
@@ -420,6 +522,4 @@ void __init init_IRQ(void)
cpu_eiem = EIEM_MASK(TIMER_IRQ);
#endif
set_eiem(cpu_eiem); /* EIEM : enable all external intr */
-
}
-
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index 312b48422a56..5e1de6072be5 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -563,6 +563,15 @@ ENDPROC(copy_page_asm)
* %r23 physical page (shifted for tlb insert) of "from" translation
*/
+ /* Drop prot bits and convert to page addr for iitlbt and idtlbt */
+ #define PAGE_ADD_SHIFT (PAGE_SHIFT-12)
+ .macro convert_phys_for_tlb_insert20 phys
+ extrd,u \phys, 56-PAGE_ADD_SHIFT, 32-PAGE_ADD_SHIFT, \phys
+#if _PAGE_SIZE_ENCODING_DEFAULT
+ depdi _PAGE_SIZE_ENCODING_DEFAULT, 63, (63-58), \phys
+#endif
+ .endm
+
/*
* We can't do this since copy_user_page is used to bring in
* file data that might have instructions. Since the data would
@@ -589,15 +598,14 @@ ENTRY(copy_user_page_asm)
sub %r25, %r1, %r23
ldil L%(TMPALIAS_MAP_START), %r28
- /* FIXME for different page sizes != 4k */
#ifdef CONFIG_64BIT
#if (TMPALIAS_MAP_START >= 0x80000000)
depdi 0, 31,32, %r28 /* clear any sign extension */
#endif
- extrd,u %r26,56,32, %r26 /* convert phys addr to tlb insert format */
- extrd,u %r23,56,32, %r23 /* convert phys addr to tlb insert format */
+ convert_phys_for_tlb_insert20 %r26 /* convert phys addr to tlb insert format */
+ convert_phys_for_tlb_insert20 %r23 /* convert phys addr to tlb insert format */
depd %r24,63,22, %r28 /* Form aliased virtual address 'to' */
- depdi 0, 63,12, %r28 /* Clear any offset bits */
+ depdi 0, 63,PAGE_SHIFT, %r28 /* Clear any offset bits */
copy %r28, %r29
depdi 1, 41,1, %r29 /* Form aliased virtual address 'from' */
#else
@@ -747,11 +755,10 @@ ENTRY(clear_user_page_asm)
#ifdef CONFIG_64BIT
#if (TMPALIAS_MAP_START >= 0x80000000)
depdi 0, 31,32, %r28 /* clear any sign extension */
- /* FIXME: page size dependend */
#endif
- extrd,u %r26, 56,32, %r26 /* convert phys addr to tlb insert format */
+ convert_phys_for_tlb_insert20 %r26 /* convert phys addr to tlb insert format */
depd %r25, 63,22, %r28 /* Form aliased virtual address 'to' */
- depdi 0, 63,12, %r28 /* Clear any offset bits */
+ depdi 0, 63,PAGE_SHIFT, %r28 /* Clear any offset bits */
#else
extrw,u %r26, 24,25, %r26 /* convert phys addr to tlb insert format */
depw %r25, 31,22, %r28 /* Form aliased virtual address 'to' */
@@ -832,11 +839,10 @@ ENTRY(flush_dcache_page_asm)
#ifdef CONFIG_64BIT
#if (TMPALIAS_MAP_START >= 0x80000000)
depdi 0, 31,32, %r28 /* clear any sign extension */
- /* FIXME: page size dependend */
#endif
- extrd,u %r26, 56,32, %r26 /* convert phys addr to tlb insert format */
+ convert_phys_for_tlb_insert20 %r26 /* convert phys addr to tlb insert format */
depd %r25, 63,22, %r28 /* Form aliased virtual address 'to' */
- depdi 0, 63,12, %r28 /* Clear any offset bits */
+ depdi 0, 63,PAGE_SHIFT, %r28 /* Clear any offset bits */
#else
extrw,u %r26, 24,25, %r26 /* convert phys addr to tlb insert format */
depw %r25, 31,22, %r28 /* Form aliased virtual address 'to' */
@@ -909,11 +915,10 @@ ENTRY(flush_icache_page_asm)
#ifdef CONFIG_64BIT
#if (TMPALIAS_MAP_START >= 0x80000000)
depdi 0, 31,32, %r28 /* clear any sign extension */
- /* FIXME: page size dependend */
#endif
- extrd,u %r26, 56,32, %r26 /* convert phys addr to tlb insert format */
+ convert_phys_for_tlb_insert20 %r26 /* convert phys addr to tlb insert format */
depd %r25, 63,22, %r28 /* Form aliased virtual address 'to' */
- depdi 0, 63,12, %r28 /* Clear any offset bits */
+ depdi 0, 63,PAGE_SHIFT, %r28 /* Clear any offset bits */
#else
extrw,u %r26, 24,25, %r26 /* convert phys addr to tlb insert format */
depw %r25, 31,22, %r28 /* Form aliased virtual address 'to' */
@@ -959,7 +964,7 @@ ENTRY(flush_icache_page_asm)
fic,m %r1(%sr4,%r28)
fic,m %r1(%sr4,%r28)
fic,m %r1(%sr4,%r28)
- cmpb,COND(<<) %r28, %r25,1b
+ cmpb,COND(<<) %r28, %r25,1b
fic,m %r1(%sr4,%r28)
sync
diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c
index a3328c2616b0..76b63e726a53 100644
--- a/arch/parisc/kernel/setup.c
+++ b/arch/parisc/kernel/setup.c
@@ -129,6 +129,8 @@ void __init setup_arch(char **cmdline_p)
printk(KERN_INFO "The 32-bit Kernel has started...\n");
#endif
+ printk(KERN_INFO "Default page size is %dKB.\n", (int)(PAGE_SIZE / 1024));
+
pdc_console_init();
#ifdef CONFIG_64BIT
diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c
index fd1bb1519c2b..e3614fb343e5 100644
--- a/arch/parisc/kernel/smp.c
+++ b/arch/parisc/kernel/smp.c
@@ -127,7 +127,7 @@ ipi_interrupt(int irq, void *dev_id)
unsigned long flags;
/* Count this now; we may make a call that never returns. */
- p->ipi_count++;
+ inc_irq_stat(irq_call_count);
mb(); /* Order interrupt and bit testing. */
@@ -155,6 +155,7 @@ ipi_interrupt(int irq, void *dev_id)
case IPI_RESCHEDULE:
smp_debug(100, KERN_DEBUG "CPU%d IPI_RESCHEDULE\n", this_cpu);
+ inc_irq_stat(irq_resched_count);
scheduler_ipi();
break;
@@ -263,17 +264,6 @@ void arch_send_call_function_single_ipi(int cpu)
}
/*
- * Flush all other CPU's tlb and then mine. Do this with on_each_cpu()
- * as we want to ensure all TLB's flushed before proceeding.
- */
-
-void
-smp_flush_tlb_all(void)
-{
- on_each_cpu(flush_tlb_all_local, NULL, 1);
-}
-
-/*
* Called by secondaries to update state and initialize CPU registers.
*/
static void __init
diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S
index 5e055240f00b..e767ab733e32 100644
--- a/arch/parisc/kernel/syscall.S
+++ b/arch/parisc/kernel/syscall.S
@@ -1,12 +1,35 @@
/*
* Linux/PA-RISC Project (http://www.parisc-linux.org/)
*
- * System call entry code Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai>
+ * System call entry code / Linux gateway page
+ * Copyright (c) Matthew Wilcox 1999 <willy@bofh.ai>
* Licensed under the GNU GPL.
* thanks to Philipp Rumpf, Mike Shaver and various others
* sorry about the wall, puffin..
*/
+/*
+How does the Linux gateway page on PA-RISC work?
+------------------------------------------------
+The Linux gateway page on PA-RISC is "special".
+It actually has PAGE_GATEWAY bits set (this is linux terminology; in parisc
+terminology it's Execute, promote to PL0) in the page map. So anything
+executing on this page executes with kernel level privilege (there's more to it
+than that: to have this happen, you also have to use a branch with a ,gate
+completer to activate the privilege promotion). The upshot is that everything
+that runs on the gateway page runs at kernel privilege but with the current
+user process address space (although you have access to kernel space via %sr2).
+For the 0x100 syscall entry, we redo the space registers to point to the kernel
+address space (preserving the user address space in %sr3), move to wide mode if
+required, save the user registers and branch into the kernel syscall entry
+point. For all the other functions, we execute at kernel privilege but don't
+flip address spaces. The basic upshot of this is that these code snippets are
+executed atomically (because the kernel can't be pre-empted) and they may
+perform architecturally forbidden (to PL3) operations (like setting control
+registers).
+*/
+
+
#include <asm/asm-offsets.h>
#include <asm/unistd.h>
#include <asm/errno.h>
@@ -15,6 +38,7 @@
#include <asm/thread_info.h>
#include <asm/assembly.h>
#include <asm/processor.h>
+#include <asm/cache.h>
#include <linux/linkage.h>
@@ -643,7 +667,7 @@ ENTRY(end_linux_gateway_page)
.section .rodata,"a"
- .align PAGE_SIZE
+ .align 8
/* Light-weight-syscall table */
/* Start of lws table. */
ENTRY(lws_table)
@@ -652,13 +676,13 @@ ENTRY(lws_table)
END(lws_table)
/* End of lws table */
- .align PAGE_SIZE
+ .align 8
ENTRY(sys_call_table)
#include "syscall_table.S"
END(sys_call_table)
#ifdef CONFIG_64BIT
- .align PAGE_SIZE
+ .align 8
ENTRY(sys_call_table64)
#define SYSCALL_TABLE_64BIT
#include "syscall_table.S"
@@ -674,7 +698,7 @@ END(sys_call_table64)
with ldcw.
*/
.section .data
- .align PAGE_SIZE
+ .align L1_CACHE_BYTES
ENTRY(lws_lock_start)
/* lws locks */
.rept 16
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c
index f702bff0bed9..fe41a98043bb 100644
--- a/arch/parisc/kernel/traps.c
+++ b/arch/parisc/kernel/traps.c
@@ -522,10 +522,10 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
*/
if (((unsigned long)regs->iaoq[0] & 3) &&
((unsigned long)regs->iasq[0] != (unsigned long)regs->sr[7])) {
- /* Kill the user process later */
- regs->iaoq[0] = 0 | 3;
+ /* Kill the user process later */
+ regs->iaoq[0] = 0 | 3;
regs->iaoq[1] = regs->iaoq[0] + 4;
- regs->iasq[0] = regs->iasq[1] = regs->sr[7];
+ regs->iasq[0] = regs->iasq[1] = regs->sr[7];
regs->gr[0] &= ~PSW_B;
return;
}
@@ -541,8 +541,8 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
/* set up a new led state on systems shipped with a LED State panel */
pdc_chassis_send_status(PDC_CHASSIS_DIRECT_HPMC);
-
- parisc_terminate("High Priority Machine Check (HPMC)",
+
+ parisc_terminate("High Priority Machine Check (HPMC)",
regs, code, 0);
/* NOT REACHED */
@@ -584,13 +584,13 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
/* Break instruction trap */
handle_break(regs);
return;
-
+
case 10:
/* Privileged operation trap */
die_if_kernel("Privileged operation", regs, code);
si.si_code = ILL_PRVOPC;
goto give_sigill;
-
+
case 11:
/* Privileged register trap */
if ((regs->iir & 0xffdfffe0) == 0x034008a0) {
@@ -634,7 +634,7 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
if(user_mode(regs)){
si.si_signo = SIGFPE;
/* Set to zero, and let the userspace app figure it out from
- the insn pointed to by si_addr */
+ the insn pointed to by si_addr */
si.si_code = 0;
si.si_addr = (void __user *) regs->iaoq[0];
force_sig_info(SIGFPE, &si, current);
@@ -648,7 +648,7 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
die_if_kernel("Floating point exception", regs, 0); /* quiet */
handle_fpe(regs);
return;
-
+
case 15:
/* Data TLB miss fault/Data page fault */
/* Fall through */
@@ -660,15 +660,15 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
case 17:
/* Non-access data TLB miss fault/Non-access data page fault */
/* FIXME:
- Still need to add slow path emulation code here!
- If the insn used a non-shadow register, then the tlb
+ Still need to add slow path emulation code here!
+ If the insn used a non-shadow register, then the tlb
handlers could not have their side-effect (e.g. probe
writing to a target register) emulated since rfir would
erase the changes to said register. Instead we have to
setup everything, call this function we are in, and emulate
by hand. Technically we need to emulate:
fdc,fdce,pdc,"fic,4f",prober,probeir,probew, probeiw
- */
+ */
fault_address = regs->ior;
fault_space = regs->isr;
break;
diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S
index 64a999882e4f..4bb095a2f6fc 100644
--- a/arch/parisc/kernel/vmlinux.lds.S
+++ b/arch/parisc/kernel/vmlinux.lds.S
@@ -95,7 +95,7 @@ SECTIONS
NOTES
/* Data */
- RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE)
+ RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, PAGE_SIZE)
/* PA-RISC locks requires 16-byte alignment */
. = ALIGN(16);
diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c
index 157b931e7b09..ce939ac8622b 100644
--- a/arch/parisc/mm/init.c
+++ b/arch/parisc/mm/init.c
@@ -1069,6 +1069,7 @@ void flush_tlb_all(void)
{
int do_recycle;
+ inc_irq_stat(irq_tlb_count);
do_recycle = 0;
spin_lock(&sid_lock);
if (dirty_space_ids > RECYCLE_THRESHOLD) {
@@ -1089,6 +1090,7 @@ void flush_tlb_all(void)
#else
void flush_tlb_all(void)
{
+ inc_irq_stat(irq_tlb_count);
spin_lock(&sid_lock);
flush_tlb_all_local(NULL);
recycle_sids();
diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h
index fcc54ad159ba..26807e5aff51 100644
--- a/arch/powerpc/include/asm/cputable.h
+++ b/arch/powerpc/include/asm/cputable.h
@@ -224,8 +224,10 @@ extern const char *powerpc_base_platform;
/* We only set the TM feature if the kernel was compiled with TM supprt */
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
#define CPU_FTR_TM_COMP CPU_FTR_TM
+#define PPC_FEATURE2_HTM_COMP PPC_FEATURE2_HTM
#else
#define CPU_FTR_TM_COMP 0
+#define PPC_FEATURE2_HTM_COMP 0
#endif
/* We need to mark all pages as being coherent if we're SMP or we have a
diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h
index e45c4947a772..d615b28dda82 100644
--- a/arch/powerpc/include/asm/hw_irq.h
+++ b/arch/powerpc/include/asm/hw_irq.h
@@ -95,15 +95,13 @@ static inline bool arch_irqs_disabled(void)
#define __hard_irq_disable() __mtmsrd(local_paca->kernel_msr, 1)
#endif
-static inline void hard_irq_disable(void)
-{
- __hard_irq_disable();
- get_paca()->soft_enabled = 0;
- get_paca()->irq_happened |= PACA_IRQ_HARD_DIS;
-}
-
-/* include/linux/interrupt.h needs hard_irq_disable to be a macro */
-#define hard_irq_disable hard_irq_disable
+#define hard_irq_disable() do { \
+ __hard_irq_disable(); \
+ if (local_paca->soft_enabled) \
+ trace_hardirqs_off(); \
+ get_paca()->soft_enabled = 0; \
+ get_paca()->irq_happened |= PACA_IRQ_HARD_DIS; \
+} while(0)
static inline bool lazy_irq_pending(void)
{
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index 3f3f691be2e7..92386fc4e82a 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -29,6 +29,7 @@ struct rtc_time;
struct file;
struct pci_controller;
struct kimage;
+struct pci_host_bridge;
struct machdep_calls {
char *name;
@@ -108,6 +109,8 @@ struct machdep_calls {
void (*pcibios_fixup)(void);
int (*pci_probe_mode)(struct pci_bus *);
void (*pci_irq_fixup)(struct pci_dev *dev);
+ int (*pcibios_root_bridge_prepare)(struct pci_host_bridge
+ *bridge);
/* To setup PHBs when using automatic OF platform driver for PCI */
int (*pci_setup_phb)(struct pci_controller *host);
diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h
index ffbc5fd549ac..8b11b5bd9938 100644
--- a/arch/powerpc/include/asm/pci-bridge.h
+++ b/arch/powerpc/include/asm/pci-bridge.h
@@ -39,11 +39,6 @@ struct pci_controller {
resource_size_t io_base_phys;
resource_size_t pci_io_size;
- /* Some machines (PReP) have a non 1:1 mapping of
- * the PCI memory space in the CPU bus space
- */
- resource_size_t pci_mem_offset;
-
/* Some machines have a special region to forward the ISA
* "memory" cycles such as VGA memory regions. Left to 0
* if unsupported
@@ -86,6 +81,7 @@ struct pci_controller {
*/
struct resource io_resource;
struct resource mem_resources[3];
+ resource_size_t mem_offset[3];
int global_number; /* PCI domain number */
resource_size_t dma_window_base_cur;
@@ -163,6 +159,8 @@ struct pci_dn {
int pci_ext_config_space; /* for pci devices */
+ int force_32bit_msi:1;
+
struct pci_dev *pcidev; /* back-pointer to the pci device */
#ifdef CONFIG_EEH
struct eeh_dev *edev; /* eeh device */
diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h
index 0c34e4803499..eccfc161e58e 100644
--- a/arch/powerpc/include/asm/ppc-opcode.h
+++ b/arch/powerpc/include/asm/ppc-opcode.h
@@ -115,6 +115,10 @@
#define PPC_INST_MFSPR_DSCR_MASK 0xfc1fffff
#define PPC_INST_MTSPR_DSCR 0x7c1103a6
#define PPC_INST_MTSPR_DSCR_MASK 0xfc1fffff
+#define PPC_INST_MFSPR_DSCR_USER 0x7c0302a6
+#define PPC_INST_MFSPR_DSCR_USER_MASK 0xfc1fffff
+#define PPC_INST_MTSPR_DSCR_USER 0x7c0303a6
+#define PPC_INST_MTSPR_DSCR_USER_MASK 0xfc1fffff
#define PPC_INST_SLBFEE 0x7c0007a7
#define PPC_INST_STRING 0x7c00042a
diff --git a/arch/powerpc/include/uapi/asm/cputable.h b/arch/powerpc/include/uapi/asm/cputable.h
index ed9dd8156962..5b7657959faa 100644
--- a/arch/powerpc/include/uapi/asm/cputable.h
+++ b/arch/powerpc/include/uapi/asm/cputable.h
@@ -1,6 +1,7 @@
#ifndef _UAPI__ASM_POWERPC_CPUTABLE_H
#define _UAPI__ASM_POWERPC_CPUTABLE_H
+/* in AT_HWCAP */
#define PPC_FEATURE_32 0x80000000
#define PPC_FEATURE_64 0x40000000
#define PPC_FEATURE_601_INSTR 0x20000000
@@ -33,4 +34,12 @@
#define PPC_FEATURE_TRUE_LE 0x00000002
#define PPC_FEATURE_PPC_LE 0x00000001
+/* in AT_HWCAP2 */
+#define PPC_FEATURE2_ARCH_2_07 0x80000000
+#define PPC_FEATURE2_HTM 0x40000000
+#define PPC_FEATURE2_DSCR 0x20000000
+#define PPC_FEATURE2_EBB 0x10000000
+#define PPC_FEATURE2_ISEL 0x08000000
+#define PPC_FEATURE2_TAR 0x04000000
+
#endif /* _UAPI__ASM_POWERPC_CPUTABLE_H */
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index ae9f433daabf..c60bbec25c1f 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -98,10 +98,14 @@ extern void __restore_cpu_e6500(void);
PPC_FEATURE_SMT | PPC_FEATURE_ICACHE_SNOOP | \
PPC_FEATURE_TRUE_LE | \
PPC_FEATURE_PSERIES_PERFMON_COMPAT)
+#define COMMON_USER2_POWER7 (PPC_FEATURE2_DSCR)
#define COMMON_USER_POWER8 (COMMON_USER_PPC64 | PPC_FEATURE_ARCH_2_06 |\
PPC_FEATURE_SMT | PPC_FEATURE_ICACHE_SNOOP | \
PPC_FEATURE_TRUE_LE | \
PPC_FEATURE_PSERIES_PERFMON_COMPAT)
+#define COMMON_USER2_POWER8 (PPC_FEATURE2_ARCH_2_07 | \
+ PPC_FEATURE2_HTM_COMP | PPC_FEATURE2_DSCR | \
+ PPC_FEATURE2_ISEL | PPC_FEATURE2_TAR)
#define COMMON_USER_PA6T (COMMON_USER_PPC64 | PPC_FEATURE_PA6T |\
PPC_FEATURE_TRUE_LE | \
PPC_FEATURE_HAS_ALTIVEC_COMP)
@@ -428,6 +432,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER7 (architected)",
.cpu_features = CPU_FTRS_POWER7,
.cpu_user_features = COMMON_USER_POWER7,
+ .cpu_user_features2 = COMMON_USER2_POWER7,
.mmu_features = MMU_FTRS_POWER7,
.icache_bsize = 128,
.dcache_bsize = 128,
@@ -443,6 +448,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER8 (architected)",
.cpu_features = CPU_FTRS_POWER8,
.cpu_user_features = COMMON_USER_POWER8,
+ .cpu_user_features2 = COMMON_USER2_POWER8,
.mmu_features = MMU_FTRS_POWER8,
.icache_bsize = 128,
.dcache_bsize = 128,
@@ -458,6 +464,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER7 (raw)",
.cpu_features = CPU_FTRS_POWER7,
.cpu_user_features = COMMON_USER_POWER7,
+ .cpu_user_features2 = COMMON_USER2_POWER7,
.mmu_features = MMU_FTRS_POWER7,
.icache_bsize = 128,
.dcache_bsize = 128,
@@ -475,6 +482,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER7+ (raw)",
.cpu_features = CPU_FTRS_POWER7,
.cpu_user_features = COMMON_USER_POWER7,
+ .cpu_user_features = COMMON_USER2_POWER7,
.mmu_features = MMU_FTRS_POWER7,
.icache_bsize = 128,
.dcache_bsize = 128,
@@ -492,6 +500,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER8 (raw)",
.cpu_features = CPU_FTRS_POWER8,
.cpu_user_features = COMMON_USER_POWER8,
+ .cpu_user_features2 = COMMON_USER2_POWER8,
.mmu_features = MMU_FTRS_POWER8,
.icache_bsize = 128,
.dcache_bsize = 128,
@@ -1995,6 +2004,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_user_features = COMMON_USER_BOOKE |
PPC_FEATURE_HAS_SPE_COMP |
PPC_FEATURE_HAS_EFP_SINGLE_COMP,
+ .cpu_user_features2 = PPC_FEATURE2_ISEL,
.mmu_features = MMU_FTR_TYPE_FSL_E,
.icache_bsize = 32,
.dcache_bsize = 32,
@@ -2014,6 +2024,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
PPC_FEATURE_HAS_SPE_COMP |
PPC_FEATURE_HAS_EFP_SINGLE_COMP |
PPC_FEATURE_HAS_EFP_DOUBLE_COMP,
+ .cpu_user_features2 = PPC_FEATURE2_ISEL,
.mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS,
.icache_bsize = 32,
.dcache_bsize = 32,
@@ -2030,6 +2041,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "e500mc",
.cpu_features = CPU_FTRS_E500MC,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .cpu_user_features2 = PPC_FEATURE2_ISEL,
.mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS |
MMU_FTR_USE_TLBILX,
.icache_bsize = 64,
@@ -2048,6 +2060,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "e5500",
.cpu_features = CPU_FTRS_E5500,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .cpu_user_features2 = PPC_FEATURE2_ISEL,
.mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS |
MMU_FTR_USE_TLBILX,
.icache_bsize = 64,
@@ -2069,6 +2082,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_E6500,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU |
PPC_FEATURE_HAS_ALTIVEC_COMP,
+ .cpu_user_features2 = PPC_FEATURE2_ISEL,
.mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS |
MMU_FTR_USE_TLBILX,
.icache_bsize = 64,
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index f325dc923409..f5c5c90799a7 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -786,22 +786,8 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose,
hose->isa_mem_size = size;
}
- /* We get the PCI/Mem offset from the first range or
- * the, current one if the offset came from an ISA
- * hole. If they don't match, bugger.
- */
- if (memno == 0 ||
- (isa_hole >= 0 && pci_addr != 0 &&
- hose->pci_mem_offset == isa_mb))
- hose->pci_mem_offset = cpu_addr - pci_addr;
- else if (pci_addr != 0 &&
- hose->pci_mem_offset != cpu_addr - pci_addr) {
- printk(KERN_INFO
- " \\--> Skipped (offset mismatch) !\n");
- continue;
- }
-
/* Build resource */
+ hose->mem_offset[memno] = cpu_addr - pci_addr;
res = &hose->mem_resources[memno++];
res->flags = IORESOURCE_MEM;
if (pci_space & 0x40000000)
@@ -817,20 +803,6 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose,
res->child = NULL;
}
}
-
- /* If there's an ISA hole and the pci_mem_offset is -not- matching
- * the ISA hole offset, then we need to remove the ISA hole from
- * the resource list for that brige
- */
- if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) {
- unsigned int next = isa_hole + 1;
- printk(KERN_INFO " Removing ISA hole at 0x%016llx\n", isa_mb);
- if (next < memno)
- memmove(&hose->mem_resources[isa_hole],
- &hose->mem_resources[next],
- sizeof(struct resource) * (memno - next));
- hose->mem_resources[--memno].flags = 0;
- }
}
/* Decide whether to display the domain number in /proc */
@@ -845,6 +817,14 @@ int pci_proc_domain(struct pci_bus *bus)
return 1;
}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ if (ppc_md.pcibios_root_bridge_prepare)
+ return ppc_md.pcibios_root_bridge_prepare(bridge);
+
+ return 0;
+}
+
/* This header fixup will do the resource fixup for all devices as they are
* probed, but not for bridge ranges
*/
@@ -908,6 +888,7 @@ static int pcibios_uninitialized_bridge_resource(struct pci_bus *bus,
struct pci_controller *hose = pci_bus_to_host(bus);
struct pci_dev *dev = bus->self;
resource_size_t offset;
+ struct pci_bus_region region;
u16 command;
int i;
@@ -917,10 +898,10 @@ static int pcibios_uninitialized_bridge_resource(struct pci_bus *bus,
/* Job is a bit different between memory and IO */
if (res->flags & IORESOURCE_MEM) {
- /* If the BAR is non-0 (res != pci_mem_offset) then it's probably been
- * initialized by somebody
- */
- if (res->start != hose->pci_mem_offset)
+ pcibios_resource_to_bus(dev, &region, res);
+
+ /* If the BAR is non-0 then it's probably been initialized */
+ if (region.start != 0)
return 0;
/* The BAR is 0, let's check if memory decoding is enabled on
@@ -932,11 +913,11 @@ static int pcibios_uninitialized_bridge_resource(struct pci_bus *bus,
/* Memory decoding is enabled and the BAR is 0. If any of the bridge
* resources covers that starting address (0 then it's good enough for
- * us for memory
+ * us for memory space)
*/
for (i = 0; i < 3; i++) {
if ((hose->mem_resources[i].flags & IORESOURCE_MEM) &&
- hose->mem_resources[i].start == hose->pci_mem_offset)
+ hose->mem_resources[i].start == hose->mem_offset[i])
return 0;
}
@@ -1373,10 +1354,9 @@ static void __init pcibios_reserve_legacy_regions(struct pci_bus *bus)
no_io:
/* Check for memory */
- offset = hose->pci_mem_offset;
- pr_debug("hose mem offset: %016llx\n", (unsigned long long)offset);
for (i = 0; i < 3; i++) {
pres = &hose->mem_resources[i];
+ offset = hose->mem_offset[i];
if (!(pres->flags & IORESOURCE_MEM))
continue;
pr_debug("hose mem res: %pR\n", pres);
@@ -1516,6 +1496,7 @@ static void pcibios_setup_phb_resources(struct pci_controller *hose,
struct list_head *resources)
{
struct resource *res;
+ resource_size_t offset;
int i;
/* Hookup PHB IO resource */
@@ -1525,49 +1506,37 @@ static void pcibios_setup_phb_resources(struct pci_controller *hose,
printk(KERN_WARNING "PCI: I/O resource not set for host"
" bridge %s (domain %d)\n",
hose->dn->full_name, hose->global_number);
-#ifdef CONFIG_PPC32
- /* Workaround for lack of IO resource only on 32-bit */
- res->start = (unsigned long)hose->io_base_virt - isa_io_base;
- res->end = res->start + IO_SPACE_LIMIT;
- res->flags = IORESOURCE_IO;
-#endif /* CONFIG_PPC32 */
- }
+ } else {
+ offset = pcibios_io_space_offset(hose);
- pr_debug("PCI: PHB IO resource = %016llx-%016llx [%lx]\n",
- (unsigned long long)res->start,
- (unsigned long long)res->end,
- (unsigned long)res->flags);
- pci_add_resource_offset(resources, res, pcibios_io_space_offset(hose));
+ pr_debug("PCI: PHB IO resource = %08llx-%08llx [%lx] off 0x%08llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end,
+ (unsigned long)res->flags,
+ (unsigned long long)offset);
+ pci_add_resource_offset(resources, res, offset);
+ }
/* Hookup PHB Memory resources */
for (i = 0; i < 3; ++i) {
res = &hose->mem_resources[i];
if (!res->flags) {
- if (i > 0)
- continue;
printk(KERN_ERR "PCI: Memory resource 0 not set for "
"host bridge %s (domain %d)\n",
hose->dn->full_name, hose->global_number);
-#ifdef CONFIG_PPC32
- /* Workaround for lack of MEM resource only on 32-bit */
- res->start = hose->pci_mem_offset;
- res->end = (resource_size_t)-1LL;
- res->flags = IORESOURCE_MEM;
-#endif /* CONFIG_PPC32 */
+ continue;
}
+ offset = hose->mem_offset[i];
- pr_debug("PCI: PHB MEM resource %d = %016llx-%016llx [%lx]\n", i,
+
+ pr_debug("PCI: PHB MEM resource %d = %08llx-%08llx [%lx] off 0x%08llx\n", i,
(unsigned long long)res->start,
(unsigned long long)res->end,
- (unsigned long)res->flags);
- pci_add_resource_offset(resources, res, hose->pci_mem_offset);
- }
-
- pr_debug("PCI: PHB MEM offset = %016llx\n",
- (unsigned long long)hose->pci_mem_offset);
- pr_debug("PCI: PHB IO offset = %08lx\n",
- (unsigned long)hose->io_base_virt - _IO_BASE);
+ (unsigned long)res->flags,
+ (unsigned long long)offset);
+ pci_add_resource_offset(resources, res, offset);
+ }
}
/*
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c
index e37c2152acf4..432459c817fa 100644
--- a/arch/powerpc/kernel/pci_32.c
+++ b/arch/powerpc/kernel/pci_32.c
@@ -295,7 +295,7 @@ long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn)
case IOBASE_BRIDGE_NUMBER:
return (long)hose->first_busno;
case IOBASE_MEMORY:
- return (long)hose->pci_mem_offset;
+ return (long)hose->mem_offset[0];
case IOBASE_IO:
return (long)hose->io_base_phys;
case IOBASE_ISA_IO:
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index 51a133a78a09..873050d26840 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -246,7 +246,7 @@ long sys_pciconfig_iobase(long which, unsigned long in_bus,
case IOBASE_BRIDGE_NUMBER:
return (long)hose->first_busno;
case IOBASE_MEMORY:
- return (long)hose->pci_mem_offset;
+ return (long)hose->mem_offset[0];
case IOBASE_IO:
return (long)hose->io_base_phys;
case IOBASE_ISA_IO:
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index 3ce1f864c2d3..e68a84568b8b 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -180,7 +180,7 @@ SYSFS_PMCSETUP(dscr, SPRN_DSCR);
SYSFS_PMCSETUP(pir, SPRN_PIR);
static DEVICE_ATTR(mmcra, 0600, show_mmcra, store_mmcra);
-static DEVICE_ATTR(spurr, 0600, show_spurr, NULL);
+static DEVICE_ATTR(spurr, 0400, show_spurr, NULL);
static DEVICE_ATTR(dscr, 0600, show_dscr, store_dscr);
static DEVICE_ATTR(purr, 0600, show_purr, store_purr);
static DEVICE_ATTR(pir, 0400, show_pir, NULL);
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 37cc40ef5043..83efa2f7d926 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -970,7 +970,10 @@ static int emulate_instruction(struct pt_regs *regs)
#ifdef CONFIG_PPC64
/* Emulate the mfspr rD, DSCR. */
- if (((instword & PPC_INST_MFSPR_DSCR_MASK) == PPC_INST_MFSPR_DSCR) &&
+ if ((((instword & PPC_INST_MFSPR_DSCR_USER_MASK) ==
+ PPC_INST_MFSPR_DSCR_USER) ||
+ ((instword & PPC_INST_MFSPR_DSCR_MASK) ==
+ PPC_INST_MFSPR_DSCR)) &&
cpu_has_feature(CPU_FTR_DSCR)) {
PPC_WARN_EMULATED(mfdscr, regs);
rd = (instword >> 21) & 0x1f;
@@ -978,7 +981,10 @@ static int emulate_instruction(struct pt_regs *regs)
return 0;
}
/* Emulate the mtspr DSCR, rD. */
- if (((instword & PPC_INST_MTSPR_DSCR_MASK) == PPC_INST_MTSPR_DSCR) &&
+ if ((((instword & PPC_INST_MTSPR_DSCR_USER_MASK) ==
+ PPC_INST_MTSPR_DSCR_USER) ||
+ ((instword & PPC_INST_MTSPR_DSCR_MASK) ==
+ PPC_INST_MTSPR_DSCR)) &&
cpu_has_feature(CPU_FTR_DSCR)) {
PPC_WARN_EMULATED(mtdscr, regs);
rd = (instword >> 21) & 0x1f;
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index 3e4c4ed19335..88ac0eeaadde 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -1230,6 +1230,7 @@ void flush_hash_page(unsigned long vpn, real_pte_t pte, int psize, int ssize,
* unmapping it first, it may see the speculated version.
*/
if (local && cpu_has_feature(CPU_FTR_TM) &&
+ current->thread.regs &&
MSR_TM_ACTIVE(current->thread.regs->msr)) {
tm_enable();
tm_abort(TM_CAUSE_TLBI);
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index e56bb651da1a..946306b1bb4e 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -550,7 +550,7 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev)
*/
iommu = cell_iommu_for_node(dev_to_node(dev));
if (iommu == NULL || list_empty(&iommu->windows)) {
- printk(KERN_ERR "iommu: missing iommu for %s (node %d)\n",
+ dev_err(dev, "iommu: missing iommu for %s (node %d)\n",
of_node_full_name(dev->of_node), dev_to_node(dev));
return NULL;
}
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c
index 8b1213993b10..f85db3a69b4a 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -715,7 +715,7 @@ static ssize_t spu_stat_show(struct device *dev,
spu->stats.libassist);
}
-static DEVICE_ATTR(stat, 0644, spu_stat_show, NULL);
+static DEVICE_ATTR(stat, 0444, spu_stat_show, NULL);
#ifdef CONFIG_KEXEC
diff --git a/arch/powerpc/platforms/embedded6xx/mpc10x.h b/arch/powerpc/platforms/embedded6xx/mpc10x.h
index b30a6a3b5bd2..b290b63661f1 100644
--- a/arch/powerpc/platforms/embedded6xx/mpc10x.h
+++ b/arch/powerpc/platforms/embedded6xx/mpc10x.h
@@ -81,17 +81,6 @@
#define MPC10X_MAPB_PCI_MEM_OFFSET (MPC10X_MAPB_ISA_MEM_BASE - \
MPC10X_MAPB_PCI_MEM_START)
-/* Set hose members to values appropriate for the mem map used */
-#define MPC10X_SETUP_HOSE(hose, map) { \
- (hose)->pci_mem_offset = MPC10X_MAP##map##_PCI_MEM_OFFSET; \
- (hose)->io_space.start = MPC10X_MAP##map##_PCI_IO_START; \
- (hose)->io_space.end = MPC10X_MAP##map##_PCI_IO_END; \
- (hose)->mem_space.start = MPC10X_MAP##map##_PCI_MEM_START; \
- (hose)->mem_space.end = MPC10X_MAP##map##_PCI_MEM_END; \
- (hose)->io_base_virt = (void *)MPC10X_MAP##map##_ISA_IO_BASE; \
-}
-
-
/* Miscellaneous Configuration register offsets */
#define MPC10X_CFG_PIR_REG 0x09
#define MPC10X_CFG_PIR_HOST_BRIDGE 0x00
diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c
index 2b8af75abc23..cf7009b8c7b6 100644
--- a/arch/powerpc/platforms/powermac/pci.c
+++ b/arch/powerpc/platforms/powermac/pci.c
@@ -824,6 +824,7 @@ static void __init parse_region_decode(struct pci_controller *hose,
hose->mem_resources[cur].name = hose->dn->full_name;
hose->mem_resources[cur].start = base;
hose->mem_resources[cur].end = end;
+ hose->mem_offset[cur] = 0;
DBG(" %d: 0x%08lx-0x%08lx\n", cur, base, end);
} else {
DBG(" : -0x%08lx\n", end);
@@ -866,7 +867,6 @@ static void __init setup_u3_ht(struct pci_controller* hose)
hose->io_resource.start = 0;
hose->io_resource.end = 0x003fffff;
hose->io_resource.flags = IORESOURCE_IO;
- hose->pci_mem_offset = 0;
hose->first_busno = 0;
hose->last_busno = 0xef;
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index aaa0dba49471..ade4463226c6 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -34,7 +34,6 @@ int __init early_init_dt_scan_opal(unsigned long node,
{
const void *basep, *entryp;
unsigned long basesz, entrysz;
- u64 glue;
if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
return 0;
@@ -61,6 +60,16 @@ int __init early_init_dt_scan_opal(unsigned long node,
printk("OPAL V1 detected !\n");
}
+ return 1;
+}
+
+static int __init opal_register_exception_handlers(void)
+{
+ u64 glue;
+
+ if (!(powerpc_firmware_features & FW_FEATURE_OPAL))
+ return -ENODEV;
+
/* Hookup some exception handlers. We use the fwnmi area at 0x7000
* to provide the glue space to OPAL
*/
@@ -74,9 +83,11 @@ int __init early_init_dt_scan_opal(unsigned long node,
glue += 128;
opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
- return 1;
+ return 0;
}
+early_initcall(opal_register_exception_handlers);
+
int opal_get_chars(uint32_t vtermno, char *buf, int count)
{
s64 len, rc;
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 8c6c9cf91c13..1da578b7c1bf 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -915,11 +915,14 @@ static void pnv_ioda_setup_pe_seg(struct pci_controller *hose,
index++;
}
} else if (res->flags & IORESOURCE_MEM) {
+ /* WARNING: Assumes M32 is mem region 0 in PHB. We need to
+ * harden that algorithm when we start supporting M64
+ */
region.start = res->start -
- hose->pci_mem_offset -
+ hose->mem_offset[0] -
phb->ioda.m32_pci_base;
region.end = res->end -
- hose->pci_mem_offset -
+ hose->mem_offset[0] -
phb->ioda.m32_pci_base;
index = region.start / phb->ioda.m32_segsize;
@@ -1089,7 +1092,7 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, int ioda_type)
/* Detect specific models for error handling */
if (of_device_is_compatible(np, "ibm,p7ioc-pciex"))
phb->model = PNV_PHB_MODEL_P7IOC;
- else if (of_device_is_compatible(np, "ibm,p8-pciex"))
+ else if (of_device_is_compatible(np, "ibm,power8-pciex"))
phb->model = PNV_PHB_MODEL_PHB3;
else
phb->model = PNV_PHB_MODEL_UNKNOWN;
@@ -1115,8 +1118,7 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, int ioda_type)
phb->ioda.m32_size += 0x10000;
phb->ioda.m32_segsize = phb->ioda.m32_size / phb->ioda.total_pe;
- phb->ioda.m32_pci_base = hose->mem_resources[0].start -
- hose->pci_mem_offset;
+ phb->ioda.m32_pci_base = hose->mem_resources[0].start - hose->mem_offset[0];
phb->ioda.io_size = hose->pci_io_size;
phb->ioda.io_segsize = phb->ioda.io_size / phb->ioda.total_pe;
phb->ioda.io_pci_base = 0; /* XXX calculate this ? */
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index 0bdc735db16f..6a3ecca5b725 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -77,9 +77,11 @@ int pnv_smp_kick_cpu(int nr)
if (!paca[nr].cpu_start && firmware_has_feature(FW_FEATURE_OPALv2)) {
pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu);
rc = opal_start_cpu(pcpu, start_here);
- if (rc != OPAL_SUCCESS)
+ if (rc != OPAL_SUCCESS) {
pr_warn("OPAL Error %ld starting CPU %d\n",
rc, nr);
+ return -ENODEV;
+ }
}
return smp_generic_kick_cpu(nr);
}
diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c
index e5b084723131..420524e6f8c9 100644
--- a/arch/powerpc/platforms/pseries/msi.c
+++ b/arch/powerpc/platforms/pseries/msi.c
@@ -24,6 +24,7 @@ static int query_token, change_token;
#define RTAS_RESET_FN 2
#define RTAS_CHANGE_MSI_FN 3
#define RTAS_CHANGE_MSIX_FN 4
+#define RTAS_CHANGE_32MSI_FN 5
static struct pci_dn *get_pdn(struct pci_dev *pdev)
{
@@ -58,7 +59,8 @@ static int rtas_change_msi(struct pci_dn *pdn, u32 func, u32 num_irqs)
seq_num = 1;
do {
- if (func == RTAS_CHANGE_MSI_FN || func == RTAS_CHANGE_MSIX_FN)
+ if (func == RTAS_CHANGE_MSI_FN || func == RTAS_CHANGE_MSIX_FN ||
+ func == RTAS_CHANGE_32MSI_FN)
rc = rtas_call(change_token, 6, 4, rtas_ret, addr,
BUID_HI(buid), BUID_LO(buid),
func, num_irqs, seq_num);
@@ -426,9 +428,12 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
*/
again:
if (type == PCI_CAP_ID_MSI) {
- rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec);
+ if (pdn->force_32bit_msi)
+ rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec);
+ else
+ rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec);
- if (rc < 0) {
+ if (rc < 0 && !pdn->force_32bit_msi) {
pr_debug("rtas_msi: trying the old firmware call.\n");
rc = rtas_change_msi(pdn, RTAS_CHANGE_FN, nvec);
}
@@ -512,3 +517,13 @@ static int rtas_msi_init(void)
return 0;
}
arch_initcall(rtas_msi_init);
+
+static void quirk_radeon(struct pci_dev *dev)
+{
+ struct pci_dn *pdn = get_pdn(dev);
+
+ if (pdn)
+ pdn->force_32bit_msi = 1;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x68f2, quirk_radeon);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0xaa68, quirk_radeon);
diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c
index 0b580f413a9a..5f93856cdf47 100644
--- a/arch/powerpc/platforms/pseries/pci.c
+++ b/arch/powerpc/platforms/pseries/pci.c
@@ -108,3 +108,56 @@ static void fixup_winbond_82c105(struct pci_dev* dev)
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105,
fixup_winbond_82c105);
+
+int pseries_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ struct device_node *dn, *pdn;
+ struct pci_bus *bus;
+ const uint32_t *pcie_link_speed_stats;
+
+ bus = bridge->bus;
+
+ dn = pcibios_get_phb_of_node(bus);
+ if (!dn)
+ return 0;
+
+ for (pdn = dn; pdn != NULL; pdn = of_get_next_parent(pdn)) {
+ pcie_link_speed_stats = (const uint32_t *) of_get_property(pdn,
+ "ibm,pcie-link-speed-stats", NULL);
+ if (pcie_link_speed_stats)
+ break;
+ }
+
+ of_node_put(pdn);
+
+ if (!pcie_link_speed_stats) {
+ pr_err("no ibm,pcie-link-speed-stats property\n");
+ return 0;
+ }
+
+ switch (pcie_link_speed_stats[0]) {
+ case 0x01:
+ bus->max_bus_speed = PCIE_SPEED_2_5GT;
+ break;
+ case 0x02:
+ bus->max_bus_speed = PCIE_SPEED_5_0GT;
+ break;
+ default:
+ bus->max_bus_speed = PCI_SPEED_UNKNOWN;
+ break;
+ }
+
+ switch (pcie_link_speed_stats[1]) {
+ case 0x01:
+ bus->cur_bus_speed = PCIE_SPEED_2_5GT;
+ break;
+ case 0x02:
+ bus->cur_bus_speed = PCIE_SPEED_5_0GT;
+ break;
+ default:
+ bus->cur_bus_speed = PCI_SPEED_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h
index 8af71e4cc17f..c2a3a258001c 100644
--- a/arch/powerpc/platforms/pseries/pseries.h
+++ b/arch/powerpc/platforms/pseries/pseries.h
@@ -63,4 +63,8 @@ extern int dlpar_detach_node(struct device_node *);
/* Snooze Delay, pseries_idle */
DECLARE_PER_CPU(long, smt_snooze_delay);
+/* PCI root bridge prepare function override for pseries */
+struct pci_host_bridge;
+int pseries_root_bridge_prepare(struct pci_host_bridge *bridge);
+
#endif /* _PSERIES_PSERIES_H */
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index ac932a9eb440..c11c8238797c 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -466,6 +466,8 @@ static void __init pSeries_setup_arch(void)
else
ppc_md.enable_pmcs = power4_enable_pmcs;
+ ppc_md.pcibios_root_bridge_prepare = pseries_root_bridge_prepare;
+
if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
long rc;
if ((rc = pSeries_enable_reloc_on_exc()) != H_SUCCESS) {
diff --git a/arch/powerpc/platforms/wsp/wsp_pci.c b/arch/powerpc/platforms/wsp/wsp_pci.c
index 8e22f561d171..62cb527493e7 100644
--- a/arch/powerpc/platforms/wsp/wsp_pci.c
+++ b/arch/powerpc/platforms/wsp/wsp_pci.c
@@ -502,7 +502,7 @@ static void __init wsp_pcie_configure_hw(struct pci_controller *hose)
(~(hose->mem_resources[0].end -
hose->mem_resources[0].start)) & 0x3ffffff0000ul);
out_be64(hose->cfg_data + PCIE_REG_M32A_START_ADDR,
- (hose->mem_resources[0].start - hose->pci_mem_offset) | 1);
+ (hose->mem_resources[0].start - hose->mem_offset[0]) | 1);
/* Clear all TVT entries
*
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index cffe7edac858..028ac1f71b51 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -178,7 +178,7 @@ static void setup_pci_atmu(struct pci_controller *hose)
struct ccsr_pci __iomem *pci = hose->private_data;
int i, j, n, mem_log, win_idx = 3, start_idx = 1, end_idx = 4;
u64 mem, sz, paddr_hi = 0;
- u64 paddr_lo = ULLONG_MAX;
+ u64 offset = 0, paddr_lo = ULLONG_MAX;
u32 pcicsrbar = 0, pcicsrbar_sz;
u32 piwar = PIWAR_EN | PIWAR_PF | PIWAR_TGI_LOCAL |
PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP;
@@ -208,8 +208,9 @@ static void setup_pci_atmu(struct pci_controller *hose)
paddr_lo = min(paddr_lo, (u64)hose->mem_resources[i].start);
paddr_hi = max(paddr_hi, (u64)hose->mem_resources[i].end);
- n = setup_one_atmu(pci, j, &hose->mem_resources[i],
- hose->pci_mem_offset);
+ /* We assume all memory resources have the same offset */
+ offset = hose->mem_offset[i];
+ n = setup_one_atmu(pci, j, &hose->mem_resources[i], offset);
if (n < 0 || j >= 5) {
pr_err("Ran out of outbound PCI ATMUs for resource %d!\n", i);
@@ -239,8 +240,8 @@ static void setup_pci_atmu(struct pci_controller *hose)
}
/* convert to pci address space */
- paddr_hi -= hose->pci_mem_offset;
- paddr_lo -= hose->pci_mem_offset;
+ paddr_hi -= offset;
+ paddr_lo -= offset;
if (paddr_hi == paddr_lo) {
pr_err("%s: No outbound window space\n", name);
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index d30e6a676c89..ee21b5e71aec 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -1001,8 +1001,12 @@ static int mpic_host_map(struct irq_domain *h, unsigned int virq,
if (hw == mpic->spurious_vec)
return -EINVAL;
- if (mpic->protected && test_bit(hw, mpic->protected))
- return -EINVAL;
+ if (mpic->protected && test_bit(hw, mpic->protected)) {
+ pr_warning("mpic: Mapping of source 0x%x failed, "
+ "source protected by firmware !\n",\
+ (unsigned int)hw);
+ return -EPERM;
+ }
#ifdef CONFIG_SMP
else if (hw >= mpic->ipi_vecs[0]) {
@@ -1029,8 +1033,12 @@ static int mpic_host_map(struct irq_domain *h, unsigned int virq,
if (mpic_map_error_int(mpic, virq, hw))
return 0;
- if (hw >= mpic->num_sources)
+ if (hw >= mpic->num_sources) {
+ pr_warning("mpic: Mapping of source 0x%x failed, "
+ "source out of range !\n",\
+ (unsigned int)hw);
return -EINVAL;
+ }
mpic_msi_reserve_hwirq(mpic, hw);
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index 56e8b3c3c890..64603a10b863 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -257,6 +257,7 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
+ resource_size_t offset = hose->mem_offset[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
@@ -270,7 +271,7 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
/* Configure the resource */
if (ppc4xx_setup_one_pci_PMM(hose, reg,
res->start,
- res->start - hose->pci_mem_offset,
+ res->start - offset,
resource_size(res),
res->flags,
j) == 0) {
@@ -279,7 +280,7 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
/* If the resource PCI address is 0 then we have our
* ISA memory hole
*/
- if (res->start == hose->pci_mem_offset)
+ if (res->start == offset)
found_isa_hole = 1;
}
}
@@ -457,6 +458,7 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
+ resource_size_t offset = hose->mem_offset[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
@@ -470,7 +472,7 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
/* Configure the resource */
if (ppc4xx_setup_one_pcix_POM(hose, reg,
res->start,
- res->start - hose->pci_mem_offset,
+ res->start - offset,
resource_size(res),
res->flags,
j) == 0) {
@@ -479,7 +481,7 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
/* If the resource PCI address is 0 then we have our
* ISA memory hole
*/
- if (res->start == hose->pci_mem_offset)
+ if (res->start == offset)
found_isa_hole = 1;
}
}
@@ -1792,6 +1794,7 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
+ resource_size_t offset = hose->mem_offset[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
@@ -1805,7 +1808,7 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
/* Configure the resource */
if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
res->start,
- res->start - hose->pci_mem_offset,
+ res->start - offset,
resource_size(res),
res->flags,
j) == 0) {
@@ -1814,7 +1817,7 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
/* If the resource PCI address is 0 then we have our
* ISA memory hole
*/
- if (res->start == hose->pci_mem_offset)
+ if (res->start == offset)
found_isa_hole = 1;
}
}
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index 5f7d7ba2874c..7a539f4f5e30 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/mount.h>
+#include <linux/aio.h>
#include <asm/ebcdic.h>
#include "hypfs.h"
diff --git a/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
index 1825b0bd523d..4c17fb6970b1 100644
--- a/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
+++ b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
@@ -9,7 +9,9 @@
* for more details.
*/
+#include <linux/bug.h>
#include <linux/init.h>
+#include <linux/ioport.h>
#include <linux/kernel.h>
#include <cpu/pfc.h>
diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c
index 9b40c9c12a0c..6cfc1b09ec25 100644
--- a/arch/sparc/kernel/leon_smp.c
+++ b/arch/sparc/kernel/leon_smp.c
@@ -253,24 +253,15 @@ void __init leon_smp_done(void)
/* Free unneeded trap tables */
if (!cpu_present(1)) {
- ClearPageReserved(virt_to_page(&trapbase_cpu1));
- init_page_count(virt_to_page(&trapbase_cpu1));
- free_page((unsigned long)&trapbase_cpu1);
- totalram_pages++;
+ free_reserved_page(virt_to_page(&trapbase_cpu1));
num_physpages++;
}
if (!cpu_present(2)) {
- ClearPageReserved(virt_to_page(&trapbase_cpu2));
- init_page_count(virt_to_page(&trapbase_cpu2));
- free_page((unsigned long)&trapbase_cpu2);
- totalram_pages++;
+ free_reserved_page(virt_to_page(&trapbase_cpu2));
num_physpages++;
}
if (!cpu_present(3)) {
- ClearPageReserved(virt_to_page(&trapbase_cpu3));
- init_page_count(virt_to_page(&trapbase_cpu3));
- free_page((unsigned long)&trapbase_cpu3);
- totalram_pages++;
+ free_reserved_page(virt_to_page(&trapbase_cpu3));
num_physpages++;
}
/* Ok, they are spinning and ready to go. */
diff --git a/arch/sparc/mm/init_32.c b/arch/sparc/mm/init_32.c
index 4490c397bb5b..af472cf7c69a 100644
--- a/arch/sparc/mm/init_32.c
+++ b/arch/sparc/mm/init_32.c
@@ -366,45 +366,14 @@ void __init mem_init(void)
void free_initmem (void)
{
- unsigned long addr;
- unsigned long freed;
-
- addr = (unsigned long)(&__init_begin);
- freed = (unsigned long)(&__init_end) - addr;
- for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
- struct page *p;
-
- memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
- p = virt_to_page(addr);
-
- ClearPageReserved(p);
- init_page_count(p);
- __free_page(p);
- totalram_pages++;
- num_physpages++;
- }
- printk(KERN_INFO "Freeing unused kernel memory: %ldk freed\n",
- freed >> 10);
+ num_physpages += free_initmem_default(POISON_FREE_INITMEM);
}
#ifdef CONFIG_BLK_DEV_INITRD
void free_initrd_mem(unsigned long start, unsigned long end)
{
- if (start < end)
- printk(KERN_INFO "Freeing initrd memory: %ldk freed\n",
- (end - start) >> 10);
- for (; start < end; start += PAGE_SIZE) {
- struct page *p;
-
- memset((void *)start, POISON_FREE_INITMEM, PAGE_SIZE);
- p = virt_to_page(start);
-
- ClearPageReserved(p);
- init_page_count(p);
- __free_page(p);
- totalram_pages++;
- num_physpages++;
- }
+ num_physpages += free_reserved_area(start, end, POISON_FREE_INITMEM,
+ "initrd");
}
#endif
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index cf72a8a5b3aa..a7171997adfd 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -2059,8 +2059,7 @@ void __init mem_init(void)
/* We subtract one to account for the mem_map_zero page
* allocated below.
*/
- totalram_pages -= 1;
- num_physpages = totalram_pages;
+ num_physpages = totalram_pages - 1;
/*
* Set up the zero page, mark it reserved, so that page count
@@ -2071,7 +2070,7 @@ void __init mem_init(void)
prom_printf("paging_init: Cannot alloc zero page.\n");
prom_halt();
}
- SetPageReserved(mem_map_zero);
+ mark_page_reserved(mem_map_zero);
codepages = (((unsigned long) _etext) - ((unsigned long) _start));
codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT;
@@ -2111,37 +2110,22 @@ void free_initmem(void)
initend = (unsigned long)(__init_end) & PAGE_MASK;
for (; addr < initend; addr += PAGE_SIZE) {
unsigned long page;
- struct page *p;
page = (addr +
((unsigned long) __va(kern_base)) -
((unsigned long) KERNBASE));
memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
- if (do_free) {
- p = virt_to_page(page);
-
- ClearPageReserved(p);
- init_page_count(p);
- __free_page(p);
- totalram_pages++;
- }
+ if (do_free)
+ free_reserved_page(virt_to_page(page));
}
}
#ifdef CONFIG_BLK_DEV_INITRD
void free_initrd_mem(unsigned long start, unsigned long end)
{
- if (start < end)
- printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
- for (; start < end; start += PAGE_SIZE) {
- struct page *p = virt_to_page(start);
-
- ClearPageReserved(p);
- init_page_count(p);
- __free_page(p);
- totalram_pages++;
- }
+ num_physpages += free_reserved_area(start, end, POISON_FREE_INITMEM,
+ "initrd");
}
#endif
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c
index 41bf72073ccc..879990cb66c6 100644
--- a/arch/um/drivers/ubd_kern.c
+++ b/arch/um/drivers/ubd_kern.c
@@ -87,7 +87,7 @@ static DEFINE_MUTEX(ubd_lock);
static DEFINE_MUTEX(ubd_mutex); /* replaces BKL, might not be needed */
static int ubd_open(struct block_device *bdev, fmode_t mode);
-static int ubd_release(struct gendisk *disk, fmode_t mode);
+static void ubd_release(struct gendisk *disk, fmode_t mode);
static int ubd_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg);
static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
@@ -1138,7 +1138,7 @@ out:
return err;
}
-static int ubd_release(struct gendisk *disk, fmode_t mode)
+static void ubd_release(struct gendisk *disk, fmode_t mode)
{
struct ubd *ubd_dev = disk->private_data;
@@ -1146,7 +1146,6 @@ static int ubd_release(struct gendisk *disk, fmode_t mode)
if(--ubd_dev->count == 0)
ubd_close_dev(ubd_dev);
mutex_unlock(&ubd_mutex);
- return 0;
}
static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
diff --git a/arch/x86/include/asm/irq_remapping.h b/arch/x86/include/asm/irq_remapping.h
index 95fd3527f632..d806b228d2c0 100644
--- a/arch/x86/include/asm/irq_remapping.h
+++ b/arch/x86/include/asm/irq_remapping.h
@@ -24,10 +24,18 @@
#include <asm/io_apic.h>
+struct IO_APIC_route_entry;
+struct io_apic_irq_attr;
+struct irq_chip;
+struct msi_msg;
+struct pci_dev;
+struct irq_cfg;
+
#ifdef CONFIG_IRQ_REMAP
extern void setup_irq_remapping_ops(void);
extern int irq_remapping_supported(void);
+extern void set_irq_remapping_broken(void);
extern int irq_remapping_prepare(void);
extern int irq_remapping_enable(void);
extern void irq_remapping_disable(void);
@@ -54,6 +62,7 @@ void irq_remap_modify_chip_defaults(struct irq_chip *chip);
static inline void setup_irq_remapping_ops(void) { }
static inline int irq_remapping_supported(void) { return 0; }
+static inline void set_irq_remapping_broken(void) { }
static inline int irq_remapping_prepare(void) { return -ENODEV; }
static inline int irq_remapping_enable(void) { return -ENODEV; }
static inline void irq_remapping_disable(void) { }
diff --git a/arch/x86/include/asm/rwsem.h b/arch/x86/include/asm/rwsem.h
index 2dbe4a721ce5..cad82c9c2fde 100644
--- a/arch/x86/include/asm/rwsem.h
+++ b/arch/x86/include/asm/rwsem.h
@@ -105,8 +105,8 @@ static inline void __down_write_nested(struct rw_semaphore *sem, int subclass)
asm volatile("# beginning down_write\n\t"
LOCK_PREFIX " xadd %1,(%2)\n\t"
/* adds 0xffff0001, returns the old value */
- " test %1,%1\n\t"
- /* was the count 0 before? */
+ " test " __ASM_SEL(%w1,%k1) "," __ASM_SEL(%w1,%k1) "\n\t"
+ /* was the active mask 0 before? */
" jz 1f\n"
" call call_rwsem_down_write_failed\n"
"1:\n"
@@ -126,11 +126,25 @@ static inline void __down_write(struct rw_semaphore *sem)
*/
static inline int __down_write_trylock(struct rw_semaphore *sem)
{
- long ret = cmpxchg(&sem->count, RWSEM_UNLOCKED_VALUE,
- RWSEM_ACTIVE_WRITE_BIAS);
- if (ret == RWSEM_UNLOCKED_VALUE)
- return 1;
- return 0;
+ long result, tmp;
+ asm volatile("# beginning __down_write_trylock\n\t"
+ " mov %0,%1\n\t"
+ "1:\n\t"
+ " test " __ASM_SEL(%w1,%k1) "," __ASM_SEL(%w1,%k1) "\n\t"
+ /* was the active mask 0 before? */
+ " jnz 2f\n\t"
+ " mov %1,%2\n\t"
+ " add %3,%2\n\t"
+ LOCK_PREFIX " cmpxchg %2,%0\n\t"
+ " jnz 1b\n\t"
+ "2:\n\t"
+ " sete %b1\n\t"
+ " movzbl %b1, %k1\n\t"
+ "# ending __down_write_trylock\n\t"
+ : "+m" (sem->count), "=&a" (result), "=&r" (tmp)
+ : "er" (RWSEM_ACTIVE_WRITE_BIAS)
+ : "memory", "cc");
+ return result;
}
/*
diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c
index 3755ef494390..94ab6b90dd3f 100644
--- a/arch/x86/kernel/early-quirks.c
+++ b/arch/x86/kernel/early-quirks.c
@@ -18,6 +18,7 @@
#include <asm/apic.h>
#include <asm/iommu.h>
#include <asm/gart.h>
+#include <asm/irq_remapping.h>
static void __init fix_hypertransport_config(int num, int slot, int func)
{
@@ -192,6 +193,21 @@ static void __init ati_bugs_contd(int num, int slot, int func)
}
#endif
+static void __init intel_remapping_check(int num, int slot, int func)
+{
+ u8 revision;
+
+ revision = read_pci_config_byte(num, slot, func, PCI_REVISION_ID);
+
+ /*
+ * Revision 0x13 of this chipset supports irq remapping
+ * but has an erratum that breaks its behavior, flag it as such
+ */
+ if (revision == 0x13)
+ set_irq_remapping_broken();
+
+}
+
#define QFLAG_APPLY_ONCE 0x1
#define QFLAG_APPLIED 0x2
#define QFLAG_DONE (QFLAG_APPLY_ONCE|QFLAG_APPLIED)
@@ -221,6 +237,10 @@ static struct chipset early_qrk[] __initdata = {
PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs },
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS,
PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs_contd },
+ { PCI_VENDOR_ID_INTEL, 0x3403, PCI_CLASS_BRIDGE_HOST,
+ PCI_BASE_CLASS_BRIDGE, 0, intel_remapping_check },
+ { PCI_VENDOR_ID_INTEL, 0x3406, PCI_CLASS_BRIDGE_HOST,
+ PCI_BASE_CLASS_BRIDGE, 0, intel_remapping_check },
{}
};
diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
index 88608cc11b8c..0345f43d34f3 100644
--- a/arch/xtensa/platforms/iss/simdisk.c
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -139,13 +139,12 @@ static int simdisk_open(struct block_device *bdev, fmode_t mode)
return 0;
}
-static int simdisk_release(struct gendisk *disk, fmode_t mode)
+static void simdisk_release(struct gendisk *disk, fmode_t mode)
{
struct simdisk *dev = disk->private_data;
spin_lock(&dev->lock);
--dev->users;
spin_unlock(&dev->lock);
- return 0;
}
static const struct block_device_operations simdisk_ops = {
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index b2b9837f9dd3..e8918ffaf96d 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -972,10 +972,10 @@ int blkcg_activate_policy(struct request_queue *q,
if (!new_blkg)
return -ENOMEM;
- preloaded = !radix_tree_preload(GFP_KERNEL);
-
blk_queue_bypass_start(q);
+ preloaded = !radix_tree_preload(GFP_KERNEL);
+
/*
* Make sure the root blkg exists and count the existing blkgs. As
* @q is bypassing at this point, blkg_lookup_create() can't be
diff --git a/block/blk-core.c b/block/blk-core.c
index 7c288358a745..33c33bc99ddd 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -30,6 +30,7 @@
#include <linux/list_sort.h>
#include <linux/delay.h>
#include <linux/ratelimit.h>
+#include <linux/pm_runtime.h>
#define CREATE_TRACE_POINTS
#include <trace/events/block.h>
@@ -159,20 +160,10 @@ static void req_bio_endio(struct request *rq, struct bio *bio,
else if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
error = -EIO;
- if (unlikely(nbytes > bio->bi_size)) {
- printk(KERN_ERR "%s: want %u bytes done, %u left\n",
- __func__, nbytes, bio->bi_size);
- nbytes = bio->bi_size;
- }
-
if (unlikely(rq->cmd_flags & REQ_QUIET))
set_bit(BIO_QUIET, &bio->bi_flags);
- bio->bi_size -= nbytes;
- bio->bi_sector += (nbytes >> 9);
-
- if (bio_integrity(bio))
- bio_integrity_advance(bio, nbytes);
+ bio_advance(bio, nbytes);
/* don't actually finish bio if it's part of flush sequence */
if (bio->bi_size == 0 && !(rq->cmd_flags & REQ_FLUSH_SEQ))
@@ -1264,6 +1255,16 @@ void part_round_stats(int cpu, struct hd_struct *part)
}
EXPORT_SYMBOL_GPL(part_round_stats);
+#ifdef CONFIG_PM_RUNTIME
+static void blk_pm_put_request(struct request *rq)
+{
+ if (rq->q->dev && !(rq->cmd_flags & REQ_PM) && !--rq->q->nr_pending)
+ pm_runtime_mark_last_busy(rq->q->dev);
+}
+#else
+static inline void blk_pm_put_request(struct request *rq) {}
+#endif
+
/*
* queue lock must be held
*/
@@ -1274,6 +1275,8 @@ void __blk_put_request(struct request_queue *q, struct request *req)
if (unlikely(--req->ref_count))
return;
+ blk_pm_put_request(req);
+
elv_completed_request(q, req);
/* this is a bio leak */
@@ -1597,7 +1600,7 @@ static void handle_bad_sector(struct bio *bio)
printk(KERN_INFO "%s: rw=%ld, want=%Lu, limit=%Lu\n",
bdevname(bio->bi_bdev, b),
bio->bi_rw,
- (unsigned long long)bio->bi_sector + bio_sectors(bio),
+ (unsigned long long)bio_end_sector(bio),
(long long)(i_size_read(bio->bi_bdev->bd_inode) >> 9));
set_bit(BIO_EOF, &bio->bi_flags);
@@ -2053,6 +2056,28 @@ static void blk_account_io_done(struct request *req)
}
}
+#ifdef CONFIG_PM_RUNTIME
+/*
+ * Don't process normal requests when queue is suspended
+ * or in the process of suspending/resuming
+ */
+static struct request *blk_pm_peek_request(struct request_queue *q,
+ struct request *rq)
+{
+ if (q->dev && (q->rpm_status == RPM_SUSPENDED ||
+ (q->rpm_status != RPM_ACTIVE && !(rq->cmd_flags & REQ_PM))))
+ return NULL;
+ else
+ return rq;
+}
+#else
+static inline struct request *blk_pm_peek_request(struct request_queue *q,
+ struct request *rq)
+{
+ return rq;
+}
+#endif
+
/**
* blk_peek_request - peek at the top of a request queue
* @q: request queue to peek at
@@ -2075,6 +2100,11 @@ struct request *blk_peek_request(struct request_queue *q)
int ret;
while ((rq = __elv_next_request(q)) != NULL) {
+
+ rq = blk_pm_peek_request(q, rq);
+ if (!rq)
+ break;
+
if (!(rq->cmd_flags & REQ_STARTED)) {
/*
* This is the first time the device driver
@@ -2253,8 +2283,7 @@ EXPORT_SYMBOL(blk_fetch_request);
**/
bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
{
- int total_bytes, bio_nbytes, next_idx = 0;
- struct bio *bio;
+ int total_bytes;
if (!req->bio)
return false;
@@ -2300,56 +2329,21 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
blk_account_io_completion(req, nr_bytes);
- total_bytes = bio_nbytes = 0;
- while ((bio = req->bio) != NULL) {
- int nbytes;
+ total_bytes = 0;
+ while (req->bio) {
+ struct bio *bio = req->bio;
+ unsigned bio_bytes = min(bio->bi_size, nr_bytes);
- if (nr_bytes >= bio->bi_size) {
+ if (bio_bytes == bio->bi_size)
req->bio = bio->bi_next;
- nbytes = bio->bi_size;
- req_bio_endio(req, bio, nbytes, error);
- next_idx = 0;
- bio_nbytes = 0;
- } else {
- int idx = bio->bi_idx + next_idx;
- if (unlikely(idx >= bio->bi_vcnt)) {
- blk_dump_rq_flags(req, "__end_that");
- printk(KERN_ERR "%s: bio idx %d >= vcnt %d\n",
- __func__, idx, bio->bi_vcnt);
- break;
- }
+ req_bio_endio(req, bio, bio_bytes, error);
- nbytes = bio_iovec_idx(bio, idx)->bv_len;
- BIO_BUG_ON(nbytes > bio->bi_size);
+ total_bytes += bio_bytes;
+ nr_bytes -= bio_bytes;
- /*
- * not a complete bvec done
- */
- if (unlikely(nbytes > nr_bytes)) {
- bio_nbytes += nr_bytes;
- total_bytes += nr_bytes;
- break;
- }
-
- /*
- * advance to the next vector
- */
- next_idx++;
- bio_nbytes += nbytes;
- }
-
- total_bytes += nbytes;
- nr_bytes -= nbytes;
-
- bio = req->bio;
- if (bio) {
- /*
- * end more in this run, or just return 'not-done'
- */
- if (unlikely(nr_bytes <= 0))
- break;
- }
+ if (!nr_bytes)
+ break;
}
/*
@@ -2365,16 +2359,6 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
return false;
}
- /*
- * if the request wasn't completed, update state
- */
- if (bio_nbytes) {
- req_bio_endio(req, bio, bio_nbytes, error);
- bio->bi_idx += next_idx;
- bio_iovec(bio)->bv_offset += nr_bytes;
- bio_iovec(bio)->bv_len -= nr_bytes;
- }
-
req->__data_len -= total_bytes;
req->buffer = bio_data(req->bio);
@@ -3046,6 +3030,149 @@ void blk_finish_plug(struct blk_plug *plug)
}
EXPORT_SYMBOL(blk_finish_plug);
+#ifdef CONFIG_PM_RUNTIME
+/**
+ * blk_pm_runtime_init - Block layer runtime PM initialization routine
+ * @q: the queue of the device
+ * @dev: the device the queue belongs to
+ *
+ * Description:
+ * Initialize runtime-PM-related fields for @q and start auto suspend for
+ * @dev. Drivers that want to take advantage of request-based runtime PM
+ * should call this function after @dev has been initialized, and its
+ * request queue @q has been allocated, and runtime PM for it can not happen
+ * yet(either due to disabled/forbidden or its usage_count > 0). In most
+ * cases, driver should call this function before any I/O has taken place.
+ *
+ * This function takes care of setting up using auto suspend for the device,
+ * the autosuspend delay is set to -1 to make runtime suspend impossible
+ * until an updated value is either set by user or by driver. Drivers do
+ * not need to touch other autosuspend settings.
+ *
+ * The block layer runtime PM is request based, so only works for drivers
+ * that use request as their IO unit instead of those directly use bio's.
+ */
+void blk_pm_runtime_init(struct request_queue *q, struct device *dev)
+{
+ q->dev = dev;
+ q->rpm_status = RPM_ACTIVE;
+ pm_runtime_set_autosuspend_delay(q->dev, -1);
+ pm_runtime_use_autosuspend(q->dev);
+}
+EXPORT_SYMBOL(blk_pm_runtime_init);
+
+/**
+ * blk_pre_runtime_suspend - Pre runtime suspend check
+ * @q: the queue of the device
+ *
+ * Description:
+ * This function will check if runtime suspend is allowed for the device
+ * by examining if there are any requests pending in the queue. If there
+ * are requests pending, the device can not be runtime suspended; otherwise,
+ * the queue's status will be updated to SUSPENDING and the driver can
+ * proceed to suspend the device.
+ *
+ * For the not allowed case, we mark last busy for the device so that
+ * runtime PM core will try to autosuspend it some time later.
+ *
+ * This function should be called near the start of the device's
+ * runtime_suspend callback.
+ *
+ * Return:
+ * 0 - OK to runtime suspend the device
+ * -EBUSY - Device should not be runtime suspended
+ */
+int blk_pre_runtime_suspend(struct request_queue *q)
+{
+ int ret = 0;
+
+ spin_lock_irq(q->queue_lock);
+ if (q->nr_pending) {
+ ret = -EBUSY;
+ pm_runtime_mark_last_busy(q->dev);
+ } else {
+ q->rpm_status = RPM_SUSPENDING;
+ }
+ spin_unlock_irq(q->queue_lock);
+ return ret;
+}
+EXPORT_SYMBOL(blk_pre_runtime_suspend);
+
+/**
+ * blk_post_runtime_suspend - Post runtime suspend processing
+ * @q: the queue of the device
+ * @err: return value of the device's runtime_suspend function
+ *
+ * Description:
+ * Update the queue's runtime status according to the return value of the
+ * device's runtime suspend function and mark last busy for the device so
+ * that PM core will try to auto suspend the device at a later time.
+ *
+ * This function should be called near the end of the device's
+ * runtime_suspend callback.
+ */
+void blk_post_runtime_suspend(struct request_queue *q, int err)
+{
+ spin_lock_irq(q->queue_lock);
+ if (!err) {
+ q->rpm_status = RPM_SUSPENDED;
+ } else {
+ q->rpm_status = RPM_ACTIVE;
+ pm_runtime_mark_last_busy(q->dev);
+ }
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_post_runtime_suspend);
+
+/**
+ * blk_pre_runtime_resume - Pre runtime resume processing
+ * @q: the queue of the device
+ *
+ * Description:
+ * Update the queue's runtime status to RESUMING in preparation for the
+ * runtime resume of the device.
+ *
+ * This function should be called near the start of the device's
+ * runtime_resume callback.
+ */
+void blk_pre_runtime_resume(struct request_queue *q)
+{
+ spin_lock_irq(q->queue_lock);
+ q->rpm_status = RPM_RESUMING;
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_pre_runtime_resume);
+
+/**
+ * blk_post_runtime_resume - Post runtime resume processing
+ * @q: the queue of the device
+ * @err: return value of the device's runtime_resume function
+ *
+ * Description:
+ * Update the queue's runtime status according to the return value of the
+ * device's runtime_resume function. If it is successfully resumed, process
+ * the requests that are queued into the device's queue when it is resuming
+ * and then mark last busy and initiate autosuspend for it.
+ *
+ * This function should be called near the end of the device's
+ * runtime_resume callback.
+ */
+void blk_post_runtime_resume(struct request_queue *q, int err)
+{
+ spin_lock_irq(q->queue_lock);
+ if (!err) {
+ q->rpm_status = RPM_ACTIVE;
+ __blk_run_queue(q);
+ pm_runtime_mark_last_busy(q->dev);
+ pm_runtime_autosuspend(q->dev);
+ } else {
+ q->rpm_status = RPM_SUSPENDED;
+ }
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_post_runtime_resume);
+#endif
+
int __init blk_dev_init(void)
{
BUILD_BUG_ON(__REQ_NR_BITS > 8 *
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 4f0ade74cfd0..d5cd3131c57a 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -2270,11 +2270,8 @@ cfq_find_rq_fmerge(struct cfq_data *cfqd, struct bio *bio)
return NULL;
cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio));
- if (cfqq) {
- sector_t sector = bio->bi_sector + bio_sectors(bio);
-
- return elv_rb_find(&cfqq->sort_list, sector);
- }
+ if (cfqq)
+ return elv_rb_find(&cfqq->sort_list, bio_end_sector(bio));
return NULL;
}
diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c
index 90037b5eb17f..ba19a3afab79 100644
--- a/block/deadline-iosched.c
+++ b/block/deadline-iosched.c
@@ -132,7 +132,7 @@ deadline_merge(struct request_queue *q, struct request **req, struct bio *bio)
* check for front merge
*/
if (dd->front_merges) {
- sector_t sector = bio->bi_sector + bio_sectors(bio);
+ sector_t sector = bio_end_sector(bio);
__rq = elv_rb_find(&dd->sort_list[bio_data_dir(bio)], sector);
if (__rq) {
diff --git a/block/elevator.c b/block/elevator.c
index a0ffdd943c98..eba5b04c29b1 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -34,6 +34,7 @@
#include <linux/blktrace_api.h>
#include <linux/hash.h>
#include <linux/uaccess.h>
+#include <linux/pm_runtime.h>
#include <trace/events/block.h>
@@ -536,6 +537,27 @@ void elv_bio_merged(struct request_queue *q, struct request *rq,
e->type->ops.elevator_bio_merged_fn(q, rq, bio);
}
+#ifdef CONFIG_PM_RUNTIME
+static void blk_pm_requeue_request(struct request *rq)
+{
+ if (rq->q->dev && !(rq->cmd_flags & REQ_PM))
+ rq->q->nr_pending--;
+}
+
+static void blk_pm_add_request(struct request_queue *q, struct request *rq)
+{
+ if (q->dev && !(rq->cmd_flags & REQ_PM) && q->nr_pending++ == 0 &&
+ (q->rpm_status == RPM_SUSPENDED || q->rpm_status == RPM_SUSPENDING))
+ pm_request_resume(q->dev);
+}
+#else
+static inline void blk_pm_requeue_request(struct request *rq) {}
+static inline void blk_pm_add_request(struct request_queue *q,
+ struct request *rq)
+{
+}
+#endif
+
void elv_requeue_request(struct request_queue *q, struct request *rq)
{
/*
@@ -550,6 +572,8 @@ void elv_requeue_request(struct request_queue *q, struct request *rq)
rq->cmd_flags &= ~REQ_STARTED;
+ blk_pm_requeue_request(rq);
+
__elv_add_request(q, rq, ELEVATOR_INSERT_REQUEUE);
}
@@ -572,6 +596,8 @@ void __elv_add_request(struct request_queue *q, struct request *rq, int where)
{
trace_block_rq_insert(q, rq);
+ blk_pm_add_request(q, rq);
+
rq->q = q;
if (rq->cmd_flags & REQ_SOFTBARRIER) {
diff --git a/block/partitions/efi.c b/block/partitions/efi.c
index ff5804e2f1d2..c85fc895ecdb 100644
--- a/block/partitions/efi.c
+++ b/block/partitions/efi.c
@@ -238,7 +238,7 @@ static gpt_entry *alloc_read_gpt_entries(struct parsed_partitions *state,
le32_to_cpu(gpt->sizeof_partition_entry);
if (!count)
return NULL;
- pte = kzalloc(count, GFP_KERNEL);
+ pte = kmalloc(count, GFP_KERNEL);
if (!pte)
return NULL;
@@ -267,7 +267,7 @@ static gpt_header *alloc_read_gpt_header(struct parsed_partitions *state,
gpt_header *gpt;
unsigned ssz = bdev_logical_block_size(state->bdev);
- gpt = kzalloc(ssz, GFP_KERNEL);
+ gpt = kmalloc(ssz, GFP_KERNEL);
if (!gpt)
return NULL;
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index 9a87daa6f4fb..a5ffcc988f0b 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -27,6 +27,7 @@
#include <linux/ratelimit.h>
#include <linux/slab.h>
#include <linux/times.h>
+#include <linux/uio.h>
#include <asm/uaccess.h>
#include <scsi/scsi.h>
diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c
index 405022d302c3..7638121cb5d1 100644
--- a/drivers/ata/pata_arasan_cf.c
+++ b/drivers/ata/pata_arasan_cf.c
@@ -209,8 +209,6 @@ struct arasan_cf_dev {
struct dma_chan *dma_chan;
/* Mask for DMA transfers */
dma_cap_mask_t mask;
- /* dma channel private data */
- void *dma_priv;
/* DMA transfer work */
struct work_struct work;
/* DMA delayed finish work */
@@ -308,6 +306,7 @@ static void cf_card_detect(struct arasan_cf_dev *acdev, bool hotplugged)
static int cf_init(struct arasan_cf_dev *acdev)
{
struct arasan_cf_pdata *pdata = dev_get_platdata(acdev->host->dev);
+ unsigned int if_clk;
unsigned long flags;
int ret = 0;
@@ -325,8 +324,12 @@ static int cf_init(struct arasan_cf_dev *acdev)
spin_lock_irqsave(&acdev->host->lock, flags);
/* configure CF interface clock */
- writel((pdata->cf_if_clk <= CF_IF_CLK_200M) ? pdata->cf_if_clk :
- CF_IF_CLK_166M, acdev->vbase + CLK_CFG);
+ /* TODO: read from device tree */
+ if_clk = CF_IF_CLK_166M;
+ if (pdata && pdata->cf_if_clk <= CF_IF_CLK_200M)
+ if_clk = pdata->cf_if_clk;
+
+ writel(if_clk, acdev->vbase + CLK_CFG);
writel(TRUE_IDE_MODE | CFHOST_ENB, acdev->vbase + OP_MODE);
cf_interrupt_enable(acdev, CARD_DETECT_IRQ, 1);
@@ -357,12 +360,6 @@ static void dma_callback(void *dev)
complete(&acdev->dma_completion);
}
-static bool filter(struct dma_chan *chan, void *slave)
-{
- chan->private = slave;
- return true;
-}
-
static inline void dma_complete(struct arasan_cf_dev *acdev)
{
struct ata_queued_cmd *qc = acdev->qc;
@@ -530,8 +527,7 @@ static void data_xfer(struct work_struct *work)
/* request dma channels */
/* dma_request_channel may sleep, so calling from process context */
- acdev->dma_chan = dma_request_channel(acdev->mask, filter,
- acdev->dma_priv);
+ acdev->dma_chan = dma_request_slave_channel(acdev->host->dev, "data");
if (!acdev->dma_chan) {
dev_err(acdev->host->dev, "Unable to get dma_chan\n");
goto chan_request_fail;
@@ -798,6 +794,7 @@ static int arasan_cf_probe(struct platform_device *pdev)
struct ata_host *host;
struct ata_port *ap;
struct resource *res;
+ u32 quirk;
irq_handler_t irq_handler = NULL;
int ret = 0;
@@ -817,12 +814,17 @@ static int arasan_cf_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ if (pdata)
+ quirk = pdata->quirk;
+ else
+ quirk = CF_BROKEN_UDMA; /* as it is on spear1340 */
+
/* if irq is 0, support only PIO */
acdev->irq = platform_get_irq(pdev, 0);
if (acdev->irq)
irq_handler = arasan_cf_interrupt;
else
- pdata->quirk |= CF_BROKEN_MWDMA | CF_BROKEN_UDMA;
+ quirk |= CF_BROKEN_MWDMA | CF_BROKEN_UDMA;
acdev->pbase = res->start;
acdev->vbase = devm_ioremap_nocache(&pdev->dev, res->start,
@@ -859,17 +861,16 @@ static int arasan_cf_probe(struct platform_device *pdev)
INIT_WORK(&acdev->work, data_xfer);
INIT_DELAYED_WORK(&acdev->dwork, delayed_finish);
dma_cap_set(DMA_MEMCPY, acdev->mask);
- acdev->dma_priv = pdata->dma_priv;
/* Handle platform specific quirks */
- if (pdata->quirk) {
- if (pdata->quirk & CF_BROKEN_PIO) {
+ if (quirk) {
+ if (quirk & CF_BROKEN_PIO) {
ap->ops->set_piomode = NULL;
ap->pio_mask = 0;
}
- if (pdata->quirk & CF_BROKEN_MWDMA)
+ if (quirk & CF_BROKEN_MWDMA)
ap->mwdma_mask = 0;
- if (pdata->quirk & CF_BROKEN_UDMA)
+ if (quirk & CF_BROKEN_UDMA)
ap->udma_mask = 0;
}
ap->flags |= ATA_FLAG_PIO_POLLING | ATA_FLAG_NO_ATAPI;
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
index 386146d792d1..4ff85b8785ee 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -1634,7 +1634,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
return 0;
}
-static int floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk, fmode_t mode)
{
struct amiga_floppy_struct *p = disk->private_data;
int drive = p - unit;
@@ -1654,7 +1654,6 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
floppy_off (drive | 0x40000000);
#endif
mutex_unlock(&amiflop_mutex);
- return 0;
}
/*
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index a129f8c8073d..916d9ed5c8aa 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -169,7 +169,7 @@ aoeblk_open(struct block_device *bdev, fmode_t mode)
return -ENODEV;
}
-static int
+static void
aoeblk_release(struct gendisk *disk, fmode_t mode)
{
struct aoedev *d = disk->private_data;
@@ -180,11 +180,9 @@ aoeblk_release(struct gendisk *disk, fmode_t mode)
if (--d->nopen == 0) {
spin_unlock_irqrestore(&d->lock, flags);
aoecmd_cfg(d->aoemajor, d->aoeminor);
- return 0;
+ return;
}
spin_unlock_irqrestore(&d->lock, flags);
-
- return 0;
}
static void
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index 92b6d7c51e39..fc803ecbbce4 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -920,16 +920,14 @@ bio_pagedec(struct bio *bio)
static void
bufinit(struct buf *buf, struct request *rq, struct bio *bio)
{
- struct bio_vec *bv;
-
memset(buf, 0, sizeof(*buf));
buf->rq = rq;
buf->bio = bio;
buf->resid = bio->bi_size;
buf->sector = bio->bi_sector;
bio_pageinc(bio);
- buf->bv = bv = &bio->bi_io_vec[bio->bi_idx];
- buf->bv_resid = bv->bv_len;
+ buf->bv = bio_iovec(bio);
+ buf->bv_resid = buf->bv->bv_len;
WARN_ON(buf->bv_resid == 0);
}
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index ede16c64ff07..0e30c6e5492a 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -367,7 +367,7 @@ static void fd_probe( int drive );
static int fd_test_drive_present( int drive );
static void config_types( void );
static int floppy_open(struct block_device *bdev, fmode_t mode);
-static int floppy_release(struct gendisk *disk, fmode_t mode);
+static void floppy_release(struct gendisk *disk, fmode_t mode);
/************************* End of Prototypes **************************/
@@ -1886,7 +1886,7 @@ static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
return ret;
}
-static int floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk, fmode_t mode)
{
struct atari_floppy_struct *p = disk->private_data;
mutex_lock(&ataflop_mutex);
@@ -1897,7 +1897,6 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
p->ref = 0;
}
mutex_unlock(&ataflop_mutex);
- return 0;
}
static const struct block_device_operations floppy_fops = {
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index 531ceb31d0ff..f1a29f8e9d33 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -334,8 +334,7 @@ static void brd_make_request(struct request_queue *q, struct bio *bio)
int err = -EIO;
sector = bio->bi_sector;
- if (sector + (bio->bi_size >> SECTOR_SHIFT) >
- get_capacity(bdev->bd_disk))
+ if (bio_end_sector(bio) > get_capacity(bdev->bd_disk))
goto out;
if (unlikely(bio->bi_rw & REQ_DISCARD)) {
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index e18c99140c0a..6374dc103521 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -75,6 +75,12 @@ module_param(cciss_simple_mode, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(cciss_simple_mode,
"Use 'simple mode' rather than 'performant mode'");
+static int cciss_allow_hpsa;
+module_param(cciss_allow_hpsa, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(cciss_allow_hpsa,
+ "Prevent cciss driver from accessing hardware known to be "
+ " supported by the hpsa driver");
+
static DEFINE_MUTEX(cciss_mutex);
static struct proc_dir_entry *proc_cciss;
@@ -161,7 +167,7 @@ static irqreturn_t do_cciss_intx(int irq, void *dev_id);
static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id);
static int cciss_open(struct block_device *bdev, fmode_t mode);
static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode);
-static int cciss_release(struct gendisk *disk, fmode_t mode);
+static void cciss_release(struct gendisk *disk, fmode_t mode);
static int do_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg);
static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
@@ -1123,7 +1129,7 @@ static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode)
/*
* Close. Sync first.
*/
-static int cciss_release(struct gendisk *disk, fmode_t mode)
+static void cciss_release(struct gendisk *disk, fmode_t mode)
{
ctlr_info_t *h;
drive_info_struct *drv;
@@ -1135,7 +1141,6 @@ static int cciss_release(struct gendisk *disk, fmode_t mode)
drv->usage_count--;
h->usage_count--;
mutex_unlock(&cciss_mutex);
- return 0;
}
static int do_ioctl(struct block_device *bdev, fmode_t mode,
@@ -4116,9 +4121,13 @@ static int cciss_lookup_board_id(struct pci_dev *pdev, u32 *board_id)
*board_id = ((subsystem_device_id << 16) & 0xffff0000) |
subsystem_vendor_id;
- for (i = 0; i < ARRAY_SIZE(products); i++)
+ for (i = 0; i < ARRAY_SIZE(products); i++) {
+ /* Stand aside for hpsa driver on request */
+ if (cciss_allow_hpsa)
+ return -ENODEV;
if (*board_id == products[i].board_id)
return i;
+ }
dev_warn(&pdev->dev, "unrecognized board ID: 0x%08x, ignoring.\n",
*board_id);
return -ENODEV;
@@ -4960,6 +4969,16 @@ static int cciss_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
ctlr_info_t *h;
unsigned long flags;
+ /*
+ * By default the cciss driver is used for all older HP Smart Array
+ * controllers. There are module paramaters that allow a user to
+ * override this behavior and instead use the hpsa SCSI driver. If
+ * this is the case cciss may be loaded first from the kdump initrd
+ * image and cause a kernel panic. So if reset_devices is true and
+ * cciss_allow_hpsa is set just bail.
+ */
+ if ((reset_devices) && (cciss_allow_hpsa == 1))
+ return -ENODEV;
rc = cciss_init_reset_devices(pdev);
if (rc) {
if (rc != -ENOTSUPP)
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index 3b9e8ebcb96b..639d26b90b91 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -160,7 +160,7 @@ static int sendcmd(
unsigned int log_unit );
static int ida_unlocked_open(struct block_device *bdev, fmode_t mode);
-static int ida_release(struct gendisk *disk, fmode_t mode);
+static void ida_release(struct gendisk *disk, fmode_t mode);
static int ida_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg);
static int ida_getgeo(struct block_device *bdev, struct hd_geometry *geo);
static int ida_ctlr_ioctl(ctlr_info_t *h, int dsk, ida_ioctl_t *io);
@@ -856,7 +856,7 @@ static int ida_unlocked_open(struct block_device *bdev, fmode_t mode)
/*
* Close. Sync first.
*/
-static int ida_release(struct gendisk *disk, fmode_t mode)
+static void ida_release(struct gendisk *disk, fmode_t mode)
{
ctlr_info_t *host;
@@ -864,8 +864,6 @@ static int ida_release(struct gendisk *disk, fmode_t mode)
host = get_host(disk);
host->usage_count--;
mutex_unlock(&cpqarray_mutex);
-
- return 0;
}
/*
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
index 92510f8ad013..6608076dc39e 100644
--- a/drivers/block/drbd/drbd_actlog.c
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -104,7 +104,6 @@ struct update_al_work {
int err;
};
-static int al_write_transaction(struct drbd_conf *mdev);
void *drbd_md_get_buffer(struct drbd_conf *mdev)
{
@@ -168,7 +167,11 @@ static int _drbd_md_sync_page_io(struct drbd_conf *mdev,
bio->bi_end_io = drbd_md_io_complete;
bio->bi_rw = rw;
- if (!get_ldev_if_state(mdev, D_ATTACHING)) { /* Corresponding put_ldev in drbd_md_io_complete() */
+ if (!(rw & WRITE) && mdev->state.disk == D_DISKLESS && mdev->ldev == NULL)
+ /* special case, drbd_md_read() during drbd_adm_attach(): no get_ldev */
+ ;
+ else if (!get_ldev_if_state(mdev, D_ATTACHING)) {
+ /* Corresponding put_ldev in drbd_md_io_complete() */
dev_err(DEV, "ASSERT FAILED: get_ldev_if_state() == 1 in _drbd_md_sync_page_io()\n");
err = -ENODEV;
goto out;
@@ -199,9 +202,10 @@ int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
BUG_ON(!bdev->md_bdev);
- dev_dbg(DEV, "meta_data io: %s [%d]:%s(,%llus,%s)\n",
+ dev_dbg(DEV, "meta_data io: %s [%d]:%s(,%llus,%s) %pS\n",
current->comm, current->pid, __func__,
- (unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ");
+ (unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ",
+ (void*)_RET_IP_ );
if (sector < drbd_md_first_sector(bdev) ||
sector + 7 > drbd_md_last_sector(bdev))
@@ -209,7 +213,8 @@ int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
current->comm, current->pid, __func__,
(unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ");
- err = _drbd_md_sync_page_io(mdev, bdev, iop, sector, rw, MD_BLOCK_SIZE);
+ /* we do all our meta data IO in aligned 4k blocks. */
+ err = _drbd_md_sync_page_io(mdev, bdev, iop, sector, rw, 4096);
if (err) {
dev_err(DEV, "drbd_md_sync_page_io(,%llus,%s) failed with error %d\n",
(unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ", err);
@@ -217,44 +222,99 @@ int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
return err;
}
-static struct lc_element *_al_get(struct drbd_conf *mdev, unsigned int enr)
+static struct bm_extent *find_active_resync_extent(struct drbd_conf *mdev, unsigned int enr)
{
- struct lc_element *al_ext;
struct lc_element *tmp;
- int wake;
-
- spin_lock_irq(&mdev->al_lock);
tmp = lc_find(mdev->resync, enr/AL_EXT_PER_BM_SECT);
if (unlikely(tmp != NULL)) {
struct bm_extent *bm_ext = lc_entry(tmp, struct bm_extent, lce);
- if (test_bit(BME_NO_WRITES, &bm_ext->flags)) {
- wake = !test_and_set_bit(BME_PRIORITY, &bm_ext->flags);
- spin_unlock_irq(&mdev->al_lock);
- if (wake)
- wake_up(&mdev->al_wait);
- return NULL;
- }
+ if (test_bit(BME_NO_WRITES, &bm_ext->flags))
+ return bm_ext;
+ }
+ return NULL;
+}
+
+static struct lc_element *_al_get(struct drbd_conf *mdev, unsigned int enr, bool nonblock)
+{
+ struct lc_element *al_ext;
+ struct bm_extent *bm_ext;
+ int wake;
+
+ spin_lock_irq(&mdev->al_lock);
+ bm_ext = find_active_resync_extent(mdev, enr);
+ if (bm_ext) {
+ wake = !test_and_set_bit(BME_PRIORITY, &bm_ext->flags);
+ spin_unlock_irq(&mdev->al_lock);
+ if (wake)
+ wake_up(&mdev->al_wait);
+ return NULL;
}
- al_ext = lc_get(mdev->act_log, enr);
+ if (nonblock)
+ al_ext = lc_try_get(mdev->act_log, enr);
+ else
+ al_ext = lc_get(mdev->act_log, enr);
spin_unlock_irq(&mdev->al_lock);
return al_ext;
}
-void drbd_al_begin_io(struct drbd_conf *mdev, struct drbd_interval *i)
+bool drbd_al_begin_io_fastpath(struct drbd_conf *mdev, struct drbd_interval *i)
{
/* for bios crossing activity log extent boundaries,
* we may need to activate two extents in one go */
unsigned first = i->sector >> (AL_EXTENT_SHIFT-9);
unsigned last = i->size == 0 ? first : (i->sector + (i->size >> 9) - 1) >> (AL_EXTENT_SHIFT-9);
- unsigned enr;
- bool locked = false;
+ D_ASSERT((unsigned)(last - first) <= 1);
+ D_ASSERT(atomic_read(&mdev->local_cnt) > 0);
+
+ /* FIXME figure out a fast path for bios crossing AL extent boundaries */
+ if (first != last)
+ return false;
+
+ return _al_get(mdev, first, true);
+}
+
+bool drbd_al_begin_io_prepare(struct drbd_conf *mdev, struct drbd_interval *i)
+{
+ /* for bios crossing activity log extent boundaries,
+ * we may need to activate two extents in one go */
+ unsigned first = i->sector >> (AL_EXTENT_SHIFT-9);
+ unsigned last = i->size == 0 ? first : (i->sector + (i->size >> 9) - 1) >> (AL_EXTENT_SHIFT-9);
+ unsigned enr;
+ bool need_transaction = false;
D_ASSERT(first <= last);
D_ASSERT(atomic_read(&mdev->local_cnt) > 0);
- for (enr = first; enr <= last; enr++)
- wait_event(mdev->al_wait, _al_get(mdev, enr) != NULL);
+ for (enr = first; enr <= last; enr++) {
+ struct lc_element *al_ext;
+ wait_event(mdev->al_wait,
+ (al_ext = _al_get(mdev, enr, false)) != NULL);
+ if (al_ext->lc_number != enr)
+ need_transaction = true;
+ }
+ return need_transaction;
+}
+
+static int al_write_transaction(struct drbd_conf *mdev, bool delegate);
+
+/* When called through generic_make_request(), we must delegate
+ * activity log I/O to the worker thread: a further request
+ * submitted via generic_make_request() within the same task
+ * would be queued on current->bio_list, and would only start
+ * after this function returns (see generic_make_request()).
+ *
+ * However, if we *are* the worker, we must not delegate to ourselves.
+ */
+
+/*
+ * @delegate: delegate activity log I/O to the worker thread
+ */
+void drbd_al_begin_io_commit(struct drbd_conf *mdev, bool delegate)
+{
+ bool locked = false;
+
+ BUG_ON(delegate && current == mdev->tconn->worker.task);
/* Serialize multiple transactions.
* This uses test_and_set_bit, memory barrier is implicit.
@@ -264,13 +324,6 @@ void drbd_al_begin_io(struct drbd_conf *mdev, struct drbd_interval *i)
(locked = lc_try_lock_for_transaction(mdev->act_log)));
if (locked) {
- /* drbd_al_write_transaction(mdev,al_ext,enr);
- * recurses into generic_make_request(), which
- * disallows recursion, bios being serialized on the
- * current->bio_tail list now.
- * we have to delegate updates to the activity log
- * to the worker thread. */
-
/* Double check: it may have been committed by someone else,
* while we have been waiting for the lock. */
if (mdev->act_log->pending_changes) {
@@ -280,11 +333,8 @@ void drbd_al_begin_io(struct drbd_conf *mdev, struct drbd_interval *i)
write_al_updates = rcu_dereference(mdev->ldev->disk_conf)->al_updates;
rcu_read_unlock();
- if (write_al_updates) {
- al_write_transaction(mdev);
- mdev->al_writ_cnt++;
- }
-
+ if (write_al_updates)
+ al_write_transaction(mdev, delegate);
spin_lock_irq(&mdev->al_lock);
/* FIXME
if (err)
@@ -298,6 +348,66 @@ void drbd_al_begin_io(struct drbd_conf *mdev, struct drbd_interval *i)
}
}
+/*
+ * @delegate: delegate activity log I/O to the worker thread
+ */
+void drbd_al_begin_io(struct drbd_conf *mdev, struct drbd_interval *i, bool delegate)
+{
+ BUG_ON(delegate && current == mdev->tconn->worker.task);
+
+ if (drbd_al_begin_io_prepare(mdev, i))
+ drbd_al_begin_io_commit(mdev, delegate);
+}
+
+int drbd_al_begin_io_nonblock(struct drbd_conf *mdev, struct drbd_interval *i)
+{
+ struct lru_cache *al = mdev->act_log;
+ /* for bios crossing activity log extent boundaries,
+ * we may need to activate two extents in one go */
+ unsigned first = i->sector >> (AL_EXTENT_SHIFT-9);
+ unsigned last = i->size == 0 ? first : (i->sector + (i->size >> 9) - 1) >> (AL_EXTENT_SHIFT-9);
+ unsigned nr_al_extents;
+ unsigned available_update_slots;
+ unsigned enr;
+
+ D_ASSERT(first <= last);
+
+ nr_al_extents = 1 + last - first; /* worst case: all touched extends are cold. */
+ available_update_slots = min(al->nr_elements - al->used,
+ al->max_pending_changes - al->pending_changes);
+
+ /* We want all necessary updates for a given request within the same transaction
+ * We could first check how many updates are *actually* needed,
+ * and use that instead of the worst-case nr_al_extents */
+ if (available_update_slots < nr_al_extents)
+ return -EWOULDBLOCK;
+
+ /* Is resync active in this area? */
+ for (enr = first; enr <= last; enr++) {
+ struct lc_element *tmp;
+ tmp = lc_find(mdev->resync, enr/AL_EXT_PER_BM_SECT);
+ if (unlikely(tmp != NULL)) {
+ struct bm_extent *bm_ext = lc_entry(tmp, struct bm_extent, lce);
+ if (test_bit(BME_NO_WRITES, &bm_ext->flags)) {
+ if (!test_and_set_bit(BME_PRIORITY, &bm_ext->flags))
+ return -EBUSY;
+ return -EWOULDBLOCK;
+ }
+ }
+ }
+
+ /* Checkout the refcounts.
+ * Given that we checked for available elements and update slots above,
+ * this has to be successful. */
+ for (enr = first; enr <= last; enr++) {
+ struct lc_element *al_ext;
+ al_ext = lc_get_cumulative(mdev->act_log, enr);
+ if (!al_ext)
+ dev_info(DEV, "LOGIC BUG for enr=%u\n", enr);
+ }
+ return 0;
+}
+
void drbd_al_complete_io(struct drbd_conf *mdev, struct drbd_interval *i)
{
/* for bios crossing activity log extent boundaries,
@@ -350,6 +460,24 @@ static unsigned int rs_extent_to_bm_page(unsigned int rs_enr)
(BM_EXT_SHIFT - BM_BLOCK_SHIFT));
}
+static sector_t al_tr_number_to_on_disk_sector(struct drbd_conf *mdev)
+{
+ const unsigned int stripes = mdev->ldev->md.al_stripes;
+ const unsigned int stripe_size_4kB = mdev->ldev->md.al_stripe_size_4k;
+
+ /* transaction number, modulo on-disk ring buffer wrap around */
+ unsigned int t = mdev->al_tr_number % (mdev->ldev->md.al_size_4k);
+
+ /* ... to aligned 4k on disk block */
+ t = ((t % stripes) * stripe_size_4kB) + t/stripes;
+
+ /* ... to 512 byte sector in activity log */
+ t *= 8;
+
+ /* ... plus offset to the on disk position */
+ return mdev->ldev->md.md_offset + mdev->ldev->md.al_offset + t;
+}
+
static int
_al_write_transaction(struct drbd_conf *mdev)
{
@@ -432,23 +560,27 @@ _al_write_transaction(struct drbd_conf *mdev)
if (mdev->al_tr_cycle >= mdev->act_log->nr_elements)
mdev->al_tr_cycle = 0;
- sector = mdev->ldev->md.md_offset
- + mdev->ldev->md.al_offset
- + mdev->al_tr_pos * (MD_BLOCK_SIZE>>9);
+ sector = al_tr_number_to_on_disk_sector(mdev);
crc = crc32c(0, buffer, 4096);
buffer->crc32c = cpu_to_be32(crc);
if (drbd_bm_write_hinted(mdev))
err = -EIO;
- /* drbd_chk_io_error done already */
- else if (drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
- err = -EIO;
- drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
- } else {
- /* advance ringbuffer position and transaction counter */
- mdev->al_tr_pos = (mdev->al_tr_pos + 1) % (MD_AL_SECTORS*512/MD_BLOCK_SIZE);
- mdev->al_tr_number++;
+ else {
+ bool write_al_updates;
+ rcu_read_lock();
+ write_al_updates = rcu_dereference(mdev->ldev->disk_conf)->al_updates;
+ rcu_read_unlock();
+ if (write_al_updates) {
+ if (drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
+ err = -EIO;
+ drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
+ } else {
+ mdev->al_tr_number++;
+ mdev->al_writ_cnt++;
+ }
+ }
}
drbd_md_put_buffer(mdev);
@@ -474,20 +606,18 @@ static int w_al_write_transaction(struct drbd_work *w, int unused)
/* Calls from worker context (see w_restart_disk_io()) need to write the
transaction directly. Others came through generic_make_request(),
those need to delegate it to the worker. */
-static int al_write_transaction(struct drbd_conf *mdev)
+static int al_write_transaction(struct drbd_conf *mdev, bool delegate)
{
- struct update_al_work al_work;
-
- if (current == mdev->tconn->worker.task)
+ if (delegate) {
+ struct update_al_work al_work;
+ init_completion(&al_work.event);
+ al_work.w.cb = w_al_write_transaction;
+ al_work.w.mdev = mdev;
+ drbd_queue_work_front(&mdev->tconn->sender_work, &al_work.w);
+ wait_for_completion(&al_work.event);
+ return al_work.err;
+ } else
return _al_write_transaction(mdev);
-
- init_completion(&al_work.event);
- al_work.w.cb = w_al_write_transaction;
- al_work.w.mdev = mdev;
- drbd_queue_work_front(&mdev->tconn->sender_work, &al_work.w);
- wait_for_completion(&al_work.event);
-
- return al_work.err;
}
static int _try_lc_del(struct drbd_conf *mdev, struct lc_element *al_ext)
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
index 8dc29502dc08..64fbb8385cdc 100644
--- a/drivers/block/drbd/drbd_bitmap.c
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -612,6 +612,17 @@ static void bm_memset(struct drbd_bitmap *b, size_t offset, int c, size_t len)
}
}
+/* For the layout, see comment above drbd_md_set_sector_offsets(). */
+static u64 drbd_md_on_disk_bits(struct drbd_backing_dev *ldev)
+{
+ u64 bitmap_sectors;
+ if (ldev->md.al_offset == 8)
+ bitmap_sectors = ldev->md.md_size_sect - ldev->md.bm_offset;
+ else
+ bitmap_sectors = ldev->md.al_offset - ldev->md.bm_offset;
+ return bitmap_sectors << (9 + 3);
+}
+
/*
* make sure the bitmap has enough room for the attached storage,
* if necessary, resize.
@@ -668,7 +679,7 @@ int drbd_bm_resize(struct drbd_conf *mdev, sector_t capacity, int set_new_bits)
words = ALIGN(bits, 64) >> LN2_BPL;
if (get_ldev(mdev)) {
- u64 bits_on_disk = ((u64)mdev->ldev->md.md_size_sect-MD_BM_OFFSET) << 12;
+ u64 bits_on_disk = drbd_md_on_disk_bits(mdev->ldev);
put_ldev(mdev);
if (bits > bits_on_disk) {
dev_info(DEV, "bits = %lu\n", bits);
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index 6b51afa1aae1..f943aacfdad8 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -753,13 +753,16 @@ struct drbd_md {
u32 flags;
u32 md_size_sect;
- s32 al_offset; /* signed relative sector offset to al area */
+ s32 al_offset; /* signed relative sector offset to activity log */
s32 bm_offset; /* signed relative sector offset to bitmap */
- /* u32 al_nr_extents; important for restoring the AL
- * is stored into ldev->dc.al_extents, which in turn
- * gets applied to act_log->nr_elements
- */
+ /* cached value of bdev->disk_conf->meta_dev_idx (see below) */
+ s32 meta_dev_idx;
+
+ /* see al_tr_number_to_on_disk_sector() */
+ u32 al_stripes;
+ u32 al_stripe_size_4k;
+ u32 al_size_4k; /* cached product of the above */
};
struct drbd_backing_dev {
@@ -891,6 +894,14 @@ struct drbd_tconn { /* is a resource from the config file */
} send;
};
+struct submit_worker {
+ struct workqueue_struct *wq;
+ struct work_struct worker;
+
+ spinlock_t lock;
+ struct list_head writes;
+};
+
struct drbd_conf {
struct drbd_tconn *tconn;
int vnr; /* volume number within the connection */
@@ -1009,7 +1020,6 @@ struct drbd_conf {
struct lru_cache *act_log; /* activity log */
unsigned int al_tr_number;
int al_tr_cycle;
- int al_tr_pos; /* position of the next transaction in the journal */
wait_queue_head_t seq_wait;
atomic_t packet_seq;
unsigned int peer_seq;
@@ -1032,6 +1042,10 @@ struct drbd_conf {
atomic_t ap_in_flight; /* App sectors in flight (waiting for ack) */
unsigned int peer_max_bio_size;
unsigned int local_max_bio_size;
+
+ /* any requests that would block in drbd_make_request()
+ * are deferred to this single-threaded work queue */
+ struct submit_worker submit;
};
static inline struct drbd_conf *minor_to_mdev(unsigned int minor)
@@ -1148,25 +1162,44 @@ extern int drbd_bitmap_io_from_worker(struct drbd_conf *mdev,
char *why, enum bm_flag flags);
extern int drbd_bmio_set_n_write(struct drbd_conf *mdev);
extern int drbd_bmio_clear_n_write(struct drbd_conf *mdev);
-extern void drbd_go_diskless(struct drbd_conf *mdev);
extern void drbd_ldev_destroy(struct drbd_conf *mdev);
/* Meta data layout
- We reserve a 128MB Block (4k aligned)
- * either at the end of the backing device
- * or on a separate meta data device. */
+ *
+ * We currently have two possible layouts.
+ * Offsets in (512 byte) sectors.
+ * external:
+ * |----------- md_size_sect ------------------|
+ * [ 4k superblock ][ activity log ][ Bitmap ]
+ * | al_offset == 8 |
+ * | bm_offset = al_offset + X |
+ * ==> bitmap sectors = md_size_sect - bm_offset
+ *
+ * Variants:
+ * old, indexed fixed size meta data:
+ *
+ * internal:
+ * |----------- md_size_sect ------------------|
+ * [data.....][ Bitmap ][ activity log ][ 4k superblock ][padding*]
+ * | al_offset < 0 |
+ * | bm_offset = al_offset - Y |
+ * ==> bitmap sectors = Y = al_offset - bm_offset
+ *
+ * [padding*] are zero or up to 7 unused 512 Byte sectors to the
+ * end of the device, so that the [4k superblock] will be 4k aligned.
+ *
+ * The activity log consists of 4k transaction blocks,
+ * which are written in a ring-buffer, or striped ring-buffer like fashion,
+ * which are writtensize used to be fixed 32kB,
+ * but is about to become configurable.
+ */
-/* The following numbers are sectors */
-/* Allows up to about 3.8TB, so if you want more,
+/* Our old fixed size meta data layout
+ * allows up to about 3.8TB, so if you want more,
* you need to use the "flexible" meta data format. */
-#define MD_RESERVED_SECT (128LU << 11) /* 128 MB, unit sectors */
-#define MD_AL_OFFSET 8 /* 8 Sectors after start of meta area */
-#define MD_AL_SECTORS 64 /* = 32 kB on disk activity log ring buffer */
-#define MD_BM_OFFSET (MD_AL_OFFSET + MD_AL_SECTORS)
-
-/* we do all meta data IO in 4k blocks */
-#define MD_BLOCK_SHIFT 12
-#define MD_BLOCK_SIZE (1<<MD_BLOCK_SHIFT)
+#define MD_128MB_SECT (128LLU << 11) /* 128 MB, unit sectors */
+#define MD_4kB_SECT 8
+#define MD_32kB_SECT 64
/* One activity log extent represents 4M of storage */
#define AL_EXTENT_SHIFT 22
@@ -1256,7 +1289,6 @@ struct bm_extent {
/* in one sector of the bitmap, we have this many activity_log extents. */
#define AL_EXT_PER_BM_SECT (1 << (BM_EXT_SHIFT - AL_EXTENT_SHIFT))
-#define BM_WORDS_PER_AL_EXT (1 << (AL_EXTENT_SHIFT-BM_BLOCK_SHIFT-LN2_BPL))
#define BM_BLOCKS_PER_BM_EXT_B (BM_EXT_SHIFT - BM_BLOCK_SHIFT)
#define BM_BLOCKS_PER_BM_EXT_MASK ((1<<BM_BLOCKS_PER_BM_EXT_B) - 1)
@@ -1276,16 +1308,18 @@ struct bm_extent {
*/
#define DRBD_MAX_SECTORS_32 (0xffffffffLU)
-#define DRBD_MAX_SECTORS_BM \
- ((MD_RESERVED_SECT - MD_BM_OFFSET) * (1LL<<(BM_EXT_SHIFT-9)))
-#if DRBD_MAX_SECTORS_BM < DRBD_MAX_SECTORS_32
-#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_BM
-#define DRBD_MAX_SECTORS_FLEX DRBD_MAX_SECTORS_BM
-#elif !defined(CONFIG_LBDAF) && BITS_PER_LONG == 32
+/* we have a certain meta data variant that has a fixed on-disk size of 128
+ * MiB, of which 4k are our "superblock", and 32k are the fixed size activity
+ * log, leaving this many sectors for the bitmap.
+ */
+
+#define DRBD_MAX_SECTORS_FIXED_BM \
+ ((MD_128MB_SECT - MD_32kB_SECT - MD_4kB_SECT) * (1LL<<(BM_EXT_SHIFT-9)))
+#if !defined(CONFIG_LBDAF) && BITS_PER_LONG == 32
#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_32
#define DRBD_MAX_SECTORS_FLEX DRBD_MAX_SECTORS_32
#else
-#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_BM
+#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_FIXED_BM
/* 16 TB in units of sectors */
#if BITS_PER_LONG == 32
/* adjust by one page worth of bitmap,
@@ -1418,6 +1452,7 @@ extern void conn_free_crypto(struct drbd_tconn *tconn);
extern int proc_details;
/* drbd_req */
+extern void do_submit(struct work_struct *ws);
extern void __drbd_make_request(struct drbd_conf *, struct bio *, unsigned long);
extern void drbd_make_request(struct request_queue *q, struct bio *bio);
extern int drbd_read_remote(struct drbd_conf *mdev, struct drbd_request *req);
@@ -1576,7 +1611,10 @@ extern const char *drbd_conn_str(enum drbd_conns s);
extern const char *drbd_role_str(enum drbd_role s);
/* drbd_actlog.c */
-extern void drbd_al_begin_io(struct drbd_conf *mdev, struct drbd_interval *i);
+extern int drbd_al_begin_io_nonblock(struct drbd_conf *mdev, struct drbd_interval *i);
+extern void drbd_al_begin_io_commit(struct drbd_conf *mdev, bool delegate);
+extern bool drbd_al_begin_io_fastpath(struct drbd_conf *mdev, struct drbd_interval *i);
+extern void drbd_al_begin_io(struct drbd_conf *mdev, struct drbd_interval *i, bool delegate);
extern void drbd_al_complete_io(struct drbd_conf *mdev, struct drbd_interval *i);
extern void drbd_rs_complete_io(struct drbd_conf *mdev, sector_t sector);
extern int drbd_rs_begin_io(struct drbd_conf *mdev, sector_t sector);
@@ -1755,9 +1793,9 @@ static inline void drbd_chk_io_error_(struct drbd_conf *mdev,
* BTW, for internal meta data, this happens to be the maximum capacity
* we could agree upon with our peer node.
*/
-static inline sector_t _drbd_md_first_sector(int meta_dev_idx, struct drbd_backing_dev *bdev)
+static inline sector_t drbd_md_first_sector(struct drbd_backing_dev *bdev)
{
- switch (meta_dev_idx) {
+ switch (bdev->md.meta_dev_idx) {
case DRBD_MD_INDEX_INTERNAL:
case DRBD_MD_INDEX_FLEX_INT:
return bdev->md.md_offset + bdev->md.bm_offset;
@@ -1767,36 +1805,19 @@ static inline sector_t _drbd_md_first_sector(int meta_dev_idx, struct drbd_backi
}
}
-static inline sector_t drbd_md_first_sector(struct drbd_backing_dev *bdev)
-{
- int meta_dev_idx;
-
- rcu_read_lock();
- meta_dev_idx = rcu_dereference(bdev->disk_conf)->meta_dev_idx;
- rcu_read_unlock();
-
- return _drbd_md_first_sector(meta_dev_idx, bdev);
-}
-
/**
* drbd_md_last_sector() - Return the last sector number of the meta data area
* @bdev: Meta data block device.
*/
static inline sector_t drbd_md_last_sector(struct drbd_backing_dev *bdev)
{
- int meta_dev_idx;
-
- rcu_read_lock();
- meta_dev_idx = rcu_dereference(bdev->disk_conf)->meta_dev_idx;
- rcu_read_unlock();
-
- switch (meta_dev_idx) {
+ switch (bdev->md.meta_dev_idx) {
case DRBD_MD_INDEX_INTERNAL:
case DRBD_MD_INDEX_FLEX_INT:
- return bdev->md.md_offset + MD_AL_OFFSET - 1;
+ return bdev->md.md_offset + MD_4kB_SECT -1;
case DRBD_MD_INDEX_FLEX_EXT:
default:
- return bdev->md.md_offset + bdev->md.md_size_sect;
+ return bdev->md.md_offset + bdev->md.md_size_sect -1;
}
}
@@ -1818,18 +1839,13 @@ static inline sector_t drbd_get_capacity(struct block_device *bdev)
static inline sector_t drbd_get_max_capacity(struct drbd_backing_dev *bdev)
{
sector_t s;
- int meta_dev_idx;
- rcu_read_lock();
- meta_dev_idx = rcu_dereference(bdev->disk_conf)->meta_dev_idx;
- rcu_read_unlock();
-
- switch (meta_dev_idx) {
+ switch (bdev->md.meta_dev_idx) {
case DRBD_MD_INDEX_INTERNAL:
case DRBD_MD_INDEX_FLEX_INT:
s = drbd_get_capacity(bdev->backing_bdev)
? min_t(sector_t, DRBD_MAX_SECTORS_FLEX,
- _drbd_md_first_sector(meta_dev_idx, bdev))
+ drbd_md_first_sector(bdev))
: 0;
break;
case DRBD_MD_INDEX_FLEX_EXT:
@@ -1848,39 +1864,24 @@ static inline sector_t drbd_get_max_capacity(struct drbd_backing_dev *bdev)
}
/**
- * drbd_md_ss__() - Return the sector number of our meta data super block
- * @mdev: DRBD device.
+ * drbd_md_ss() - Return the sector number of our meta data super block
* @bdev: Meta data block device.
*/
-static inline sector_t drbd_md_ss__(struct drbd_conf *mdev,
- struct drbd_backing_dev *bdev)
+static inline sector_t drbd_md_ss(struct drbd_backing_dev *bdev)
{
- int meta_dev_idx;
+ const int meta_dev_idx = bdev->md.meta_dev_idx;
- rcu_read_lock();
- meta_dev_idx = rcu_dereference(bdev->disk_conf)->meta_dev_idx;
- rcu_read_unlock();
-
- switch (meta_dev_idx) {
- default: /* external, some index */
- return MD_RESERVED_SECT * meta_dev_idx;
- case DRBD_MD_INDEX_INTERNAL:
- /* with drbd08, internal meta data is always "flexible" */
- case DRBD_MD_INDEX_FLEX_INT:
- /* sizeof(struct md_on_disk_07) == 4k
- * position: last 4k aligned block of 4k size */
- if (!bdev->backing_bdev) {
- if (__ratelimit(&drbd_ratelimit_state)) {
- dev_err(DEV, "bdev->backing_bdev==NULL\n");
- dump_stack();
- }
- return 0;
- }
- return (drbd_get_capacity(bdev->backing_bdev) & ~7ULL)
- - MD_AL_OFFSET;
- case DRBD_MD_INDEX_FLEX_EXT:
+ if (meta_dev_idx == DRBD_MD_INDEX_FLEX_EXT)
return 0;
- }
+
+ /* Since drbd08, internal meta data is always "flexible".
+ * position: last 4k aligned block of 4k size */
+ if (meta_dev_idx == DRBD_MD_INDEX_INTERNAL ||
+ meta_dev_idx == DRBD_MD_INDEX_FLEX_INT)
+ return (drbd_get_capacity(bdev->backing_bdev) & ~7ULL) - 8;
+
+ /* external, some index; this is the old fixed size layout */
+ return MD_128MB_SECT * bdev->md.meta_dev_idx;
}
static inline void
@@ -2053,9 +2054,11 @@ static inline void put_ldev(struct drbd_conf *mdev)
if (mdev->state.disk == D_DISKLESS)
/* even internal references gone, safe to destroy */
drbd_ldev_destroy(mdev);
- if (mdev->state.disk == D_FAILED)
+ if (mdev->state.disk == D_FAILED) {
/* all application IO references gone. */
- drbd_go_diskless(mdev);
+ if (!test_and_set_bit(GO_DISKLESS, &mdev->flags))
+ drbd_queue_work(&mdev->tconn->sender_work, &mdev->go_diskless);
+ }
wake_up(&mdev->misc_wait);
}
}
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index e98da675f0c1..a5dca6affcbb 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -45,7 +45,7 @@
#include <linux/reboot.h>
#include <linux/notifier.h>
#include <linux/kthread.h>
-
+#include <linux/workqueue.h>
#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
#include <linux/vmalloc.h>
@@ -63,7 +63,7 @@ int drbd_asender(struct drbd_thread *);
int drbd_init(void);
static int drbd_open(struct block_device *bdev, fmode_t mode);
-static int drbd_release(struct gendisk *gd, fmode_t mode);
+static void drbd_release(struct gendisk *gd, fmode_t mode);
static int w_md_sync(struct drbd_work *w, int unused);
static void md_sync_timer_fn(unsigned long data);
static int w_bitmap_io(struct drbd_work *w, int unused);
@@ -1849,13 +1849,12 @@ static int drbd_open(struct block_device *bdev, fmode_t mode)
return rv;
}
-static int drbd_release(struct gendisk *gd, fmode_t mode)
+static void drbd_release(struct gendisk *gd, fmode_t mode)
{
struct drbd_conf *mdev = gd->private_data;
mutex_lock(&drbd_main_mutex);
mdev->open_cnt--;
mutex_unlock(&drbd_main_mutex);
- return 0;
}
static void drbd_set_defaults(struct drbd_conf *mdev)
@@ -2300,6 +2299,7 @@ static void drbd_cleanup(void)
idr_for_each_entry(&minors, mdev, i) {
idr_remove(&minors, mdev_to_minor(mdev));
idr_remove(&mdev->tconn->volumes, mdev->vnr);
+ destroy_workqueue(mdev->submit.wq);
del_gendisk(mdev->vdisk);
/* synchronize_rcu(); No other threads running at this point */
kref_put(&mdev->kref, &drbd_minor_destroy);
@@ -2589,6 +2589,21 @@ void conn_destroy(struct kref *kref)
kfree(tconn);
}
+int init_submitter(struct drbd_conf *mdev)
+{
+ /* opencoded create_singlethread_workqueue(),
+ * to be able to say "drbd%d", ..., minor */
+ mdev->submit.wq = alloc_workqueue("drbd%u_submit",
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 1, mdev->minor);
+ if (!mdev->submit.wq)
+ return -ENOMEM;
+
+ INIT_WORK(&mdev->submit.worker, do_submit);
+ spin_lock_init(&mdev->submit.lock);
+ INIT_LIST_HEAD(&mdev->submit.writes);
+ return 0;
+}
+
enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, int vnr)
{
struct drbd_conf *mdev;
@@ -2678,6 +2693,12 @@ enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor,
goto out_idr_remove_minor;
}
+ if (init_submitter(mdev)) {
+ err = ERR_NOMEM;
+ drbd_msg_put_info("unable to create submit workqueue");
+ goto out_idr_remove_vol;
+ }
+
add_disk(disk);
kref_init(&mdev->kref); /* one ref for both idrs and the the add_disk */
@@ -2688,6 +2709,8 @@ enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor,
return NO_ERROR;
+out_idr_remove_vol:
+ idr_remove(&tconn->volumes, vnr_got);
out_idr_remove_minor:
idr_remove(&minors, minor_got);
synchronize_rcu();
@@ -2795,6 +2818,7 @@ void drbd_free_bc(struct drbd_backing_dev *ldev)
blkdev_put(ldev->backing_bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
blkdev_put(ldev->md_bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
+ kfree(ldev->disk_conf);
kfree(ldev);
}
@@ -2834,8 +2858,9 @@ void conn_md_sync(struct drbd_tconn *tconn)
rcu_read_unlock();
}
+/* aligned 4kByte */
struct meta_data_on_disk {
- u64 la_size; /* last agreed size. */
+ u64 la_size_sect; /* last agreed size. */
u64 uuid[UI_SIZE]; /* UUIDs. */
u64 device_uuid;
u64 reserved_u64_1;
@@ -2843,13 +2868,17 @@ struct meta_data_on_disk {
u32 magic;
u32 md_size_sect;
u32 al_offset; /* offset to this block */
- u32 al_nr_extents; /* important for restoring the AL */
+ u32 al_nr_extents; /* important for restoring the AL (userspace) */
/* `-- act_log->nr_elements <-- ldev->dc.al_extents */
u32 bm_offset; /* offset to the bitmap, from here */
u32 bm_bytes_per_bit; /* BM_BLOCK_SIZE */
u32 la_peer_max_bio_size; /* last peer max_bio_size */
- u32 reserved_u32[3];
+ /* see al_tr_number_to_on_disk_sector() */
+ u32 al_stripes;
+ u32 al_stripe_size_4k;
+
+ u8 reserved_u8[4096 - (7*8 + 10*4)];
} __packed;
/**
@@ -2862,6 +2891,10 @@ void drbd_md_sync(struct drbd_conf *mdev)
sector_t sector;
int i;
+ /* Don't accidentally change the DRBD meta data layout. */
+ BUILD_BUG_ON(UI_SIZE != 4);
+ BUILD_BUG_ON(sizeof(struct meta_data_on_disk) != 4096);
+
del_timer(&mdev->md_sync_timer);
/* timer may be rearmed by drbd_md_mark_dirty() now. */
if (!test_and_clear_bit(MD_DIRTY, &mdev->flags))
@@ -2876,9 +2909,9 @@ void drbd_md_sync(struct drbd_conf *mdev)
if (!buffer)
goto out;
- memset(buffer, 0, 512);
+ memset(buffer, 0, sizeof(*buffer));
- buffer->la_size = cpu_to_be64(drbd_get_capacity(mdev->this_bdev));
+ buffer->la_size_sect = cpu_to_be64(drbd_get_capacity(mdev->this_bdev));
for (i = UI_CURRENT; i < UI_SIZE; i++)
buffer->uuid[i] = cpu_to_be64(mdev->ldev->md.uuid[i]);
buffer->flags = cpu_to_be32(mdev->ldev->md.flags);
@@ -2893,7 +2926,10 @@ void drbd_md_sync(struct drbd_conf *mdev)
buffer->bm_offset = cpu_to_be32(mdev->ldev->md.bm_offset);
buffer->la_peer_max_bio_size = cpu_to_be32(mdev->peer_max_bio_size);
- D_ASSERT(drbd_md_ss__(mdev, mdev->ldev) == mdev->ldev->md.md_offset);
+ buffer->al_stripes = cpu_to_be32(mdev->ldev->md.al_stripes);
+ buffer->al_stripe_size_4k = cpu_to_be32(mdev->ldev->md.al_stripe_size_4k);
+
+ D_ASSERT(drbd_md_ss(mdev->ldev) == mdev->ldev->md.md_offset);
sector = mdev->ldev->md.md_offset;
if (drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
@@ -2911,13 +2947,141 @@ out:
put_ldev(mdev);
}
+static int check_activity_log_stripe_size(struct drbd_conf *mdev,
+ struct meta_data_on_disk *on_disk,
+ struct drbd_md *in_core)
+{
+ u32 al_stripes = be32_to_cpu(on_disk->al_stripes);
+ u32 al_stripe_size_4k = be32_to_cpu(on_disk->al_stripe_size_4k);
+ u64 al_size_4k;
+
+ /* both not set: default to old fixed size activity log */
+ if (al_stripes == 0 && al_stripe_size_4k == 0) {
+ al_stripes = 1;
+ al_stripe_size_4k = MD_32kB_SECT/8;
+ }
+
+ /* some paranoia plausibility checks */
+
+ /* we need both values to be set */
+ if (al_stripes == 0 || al_stripe_size_4k == 0)
+ goto err;
+
+ al_size_4k = (u64)al_stripes * al_stripe_size_4k;
+
+ /* Upper limit of activity log area, to avoid potential overflow
+ * problems in al_tr_number_to_on_disk_sector(). As right now, more
+ * than 72 * 4k blocks total only increases the amount of history,
+ * limiting this arbitrarily to 16 GB is not a real limitation ;-) */
+ if (al_size_4k > (16 * 1024 * 1024/4))
+ goto err;
+
+ /* Lower limit: we need at least 8 transaction slots (32kB)
+ * to not break existing setups */
+ if (al_size_4k < MD_32kB_SECT/8)
+ goto err;
+
+ in_core->al_stripe_size_4k = al_stripe_size_4k;
+ in_core->al_stripes = al_stripes;
+ in_core->al_size_4k = al_size_4k;
+
+ return 0;
+err:
+ dev_err(DEV, "invalid activity log striping: al_stripes=%u, al_stripe_size_4k=%u\n",
+ al_stripes, al_stripe_size_4k);
+ return -EINVAL;
+}
+
+static int check_offsets_and_sizes(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
+{
+ sector_t capacity = drbd_get_capacity(bdev->md_bdev);
+ struct drbd_md *in_core = &bdev->md;
+ s32 on_disk_al_sect;
+ s32 on_disk_bm_sect;
+
+ /* The on-disk size of the activity log, calculated from offsets, and
+ * the size of the activity log calculated from the stripe settings,
+ * should match.
+ * Though we could relax this a bit: it is ok, if the striped activity log
+ * fits in the available on-disk activity log size.
+ * Right now, that would break how resize is implemented.
+ * TODO: make drbd_determine_dev_size() (and the drbdmeta tool) aware
+ * of possible unused padding space in the on disk layout. */
+ if (in_core->al_offset < 0) {
+ if (in_core->bm_offset > in_core->al_offset)
+ goto err;
+ on_disk_al_sect = -in_core->al_offset;
+ on_disk_bm_sect = in_core->al_offset - in_core->bm_offset;
+ } else {
+ if (in_core->al_offset != MD_4kB_SECT)
+ goto err;
+ if (in_core->bm_offset < in_core->al_offset + in_core->al_size_4k * MD_4kB_SECT)
+ goto err;
+
+ on_disk_al_sect = in_core->bm_offset - MD_4kB_SECT;
+ on_disk_bm_sect = in_core->md_size_sect - in_core->bm_offset;
+ }
+
+ /* old fixed size meta data is exactly that: fixed. */
+ if (in_core->meta_dev_idx >= 0) {
+ if (in_core->md_size_sect != MD_128MB_SECT
+ || in_core->al_offset != MD_4kB_SECT
+ || in_core->bm_offset != MD_4kB_SECT + MD_32kB_SECT
+ || in_core->al_stripes != 1
+ || in_core->al_stripe_size_4k != MD_32kB_SECT/8)
+ goto err;
+ }
+
+ if (capacity < in_core->md_size_sect)
+ goto err;
+ if (capacity - in_core->md_size_sect < drbd_md_first_sector(bdev))
+ goto err;
+
+ /* should be aligned, and at least 32k */
+ if ((on_disk_al_sect & 7) || (on_disk_al_sect < MD_32kB_SECT))
+ goto err;
+
+ /* should fit (for now: exactly) into the available on-disk space;
+ * overflow prevention is in check_activity_log_stripe_size() above. */
+ if (on_disk_al_sect != in_core->al_size_4k * MD_4kB_SECT)
+ goto err;
+
+ /* again, should be aligned */
+ if (in_core->bm_offset & 7)
+ goto err;
+
+ /* FIXME check for device grow with flex external meta data? */
+
+ /* can the available bitmap space cover the last agreed device size? */
+ if (on_disk_bm_sect < (in_core->la_size_sect+7)/MD_4kB_SECT/8/512)
+ goto err;
+
+ return 0;
+
+err:
+ dev_err(DEV, "meta data offsets don't make sense: idx=%d "
+ "al_s=%u, al_sz4k=%u, al_offset=%d, bm_offset=%d, "
+ "md_size_sect=%u, la_size=%llu, md_capacity=%llu\n",
+ in_core->meta_dev_idx,
+ in_core->al_stripes, in_core->al_stripe_size_4k,
+ in_core->al_offset, in_core->bm_offset, in_core->md_size_sect,
+ (unsigned long long)in_core->la_size_sect,
+ (unsigned long long)capacity);
+
+ return -EINVAL;
+}
+
+
/**
* drbd_md_read() - Reads in the meta data super block
* @mdev: DRBD device.
* @bdev: Device from which the meta data should be read in.
*
- * Return 0 (NO_ERROR) on success, and an enum drbd_ret_code in case
+ * Return NO_ERROR on success, and an enum drbd_ret_code in case
* something goes wrong.
+ *
+ * Called exactly once during drbd_adm_attach(), while still being D_DISKLESS,
+ * even before @bdev is assigned to @mdev->ldev.
*/
int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
{
@@ -2925,12 +3089,17 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
u32 magic, flags;
int i, rv = NO_ERROR;
- if (!get_ldev_if_state(mdev, D_ATTACHING))
- return ERR_IO_MD_DISK;
+ if (mdev->state.disk != D_DISKLESS)
+ return ERR_DISK_CONFIGURED;
buffer = drbd_md_get_buffer(mdev);
if (!buffer)
- goto out;
+ return ERR_NOMEM;
+
+ /* First, figure out where our meta data superblock is located,
+ * and read it. */
+ bdev->md.meta_dev_idx = bdev->disk_conf->meta_dev_idx;
+ bdev->md.md_offset = drbd_md_ss(bdev);
if (drbd_md_sync_page_io(mdev, bdev, bdev->md.md_offset, READ)) {
/* NOTE: can't do normal error processing here as this is
@@ -2949,45 +3118,51 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
rv = ERR_MD_UNCLEAN;
goto err;
}
+
+ rv = ERR_MD_INVALID;
if (magic != DRBD_MD_MAGIC_08) {
if (magic == DRBD_MD_MAGIC_07)
dev_err(DEV, "Found old (0.7) meta data magic. Did you \"drbdadm create-md\"?\n");
else
dev_err(DEV, "Meta data magic not found. Did you \"drbdadm create-md\"?\n");
- rv = ERR_MD_INVALID;
goto err;
}
- if (be32_to_cpu(buffer->al_offset) != bdev->md.al_offset) {
- dev_err(DEV, "unexpected al_offset: %d (expected %d)\n",
- be32_to_cpu(buffer->al_offset), bdev->md.al_offset);
- rv = ERR_MD_INVALID;
+
+ if (be32_to_cpu(buffer->bm_bytes_per_bit) != BM_BLOCK_SIZE) {
+ dev_err(DEV, "unexpected bm_bytes_per_bit: %u (expected %u)\n",
+ be32_to_cpu(buffer->bm_bytes_per_bit), BM_BLOCK_SIZE);
goto err;
}
+
+
+ /* convert to in_core endian */
+ bdev->md.la_size_sect = be64_to_cpu(buffer->la_size_sect);
+ for (i = UI_CURRENT; i < UI_SIZE; i++)
+ bdev->md.uuid[i] = be64_to_cpu(buffer->uuid[i]);
+ bdev->md.flags = be32_to_cpu(buffer->flags);
+ bdev->md.device_uuid = be64_to_cpu(buffer->device_uuid);
+
+ bdev->md.md_size_sect = be32_to_cpu(buffer->md_size_sect);
+ bdev->md.al_offset = be32_to_cpu(buffer->al_offset);
+ bdev->md.bm_offset = be32_to_cpu(buffer->bm_offset);
+
+ if (check_activity_log_stripe_size(mdev, buffer, &bdev->md))
+ goto err;
+ if (check_offsets_and_sizes(mdev, bdev))
+ goto err;
+
if (be32_to_cpu(buffer->bm_offset) != bdev->md.bm_offset) {
dev_err(DEV, "unexpected bm_offset: %d (expected %d)\n",
be32_to_cpu(buffer->bm_offset), bdev->md.bm_offset);
- rv = ERR_MD_INVALID;
goto err;
}
if (be32_to_cpu(buffer->md_size_sect) != bdev->md.md_size_sect) {
dev_err(DEV, "unexpected md_size: %u (expected %u)\n",
be32_to_cpu(buffer->md_size_sect), bdev->md.md_size_sect);
- rv = ERR_MD_INVALID;
goto err;
}
- if (be32_to_cpu(buffer->bm_bytes_per_bit) != BM_BLOCK_SIZE) {
- dev_err(DEV, "unexpected bm_bytes_per_bit: %u (expected %u)\n",
- be32_to_cpu(buffer->bm_bytes_per_bit), BM_BLOCK_SIZE);
- rv = ERR_MD_INVALID;
- goto err;
- }
-
- bdev->md.la_size_sect = be64_to_cpu(buffer->la_size);
- for (i = UI_CURRENT; i < UI_SIZE; i++)
- bdev->md.uuid[i] = be64_to_cpu(buffer->uuid[i]);
- bdev->md.flags = be32_to_cpu(buffer->flags);
- bdev->md.device_uuid = be64_to_cpu(buffer->device_uuid);
+ rv = NO_ERROR;
spin_lock_irq(&mdev->tconn->req_lock);
if (mdev->state.conn < C_CONNECTED) {
@@ -3000,8 +3175,6 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
err:
drbd_md_put_buffer(mdev);
- out:
- put_ldev(mdev);
return rv;
}
@@ -3239,8 +3412,12 @@ static int w_go_diskless(struct drbd_work *w, int unused)
* end up here after a failed attach, before ldev was even assigned.
*/
if (mdev->bitmap && mdev->ldev) {
+ /* An interrupted resync or similar is allowed to recounts bits
+ * while we detach.
+ * Any modifications would not be expected anymore, though.
+ */
if (drbd_bitmap_io_from_worker(mdev, drbd_bm_write,
- "detach", BM_LOCKED_MASK)) {
+ "detach", BM_LOCKED_TEST_ALLOWED)) {
if (test_bit(WAS_READ_ERROR, &mdev->flags)) {
drbd_md_set_flag(mdev, MDF_FULL_SYNC);
drbd_md_sync(mdev);
@@ -3252,13 +3429,6 @@ static int w_go_diskless(struct drbd_work *w, int unused)
return 0;
}
-void drbd_go_diskless(struct drbd_conf *mdev)
-{
- D_ASSERT(mdev->state.disk == D_FAILED);
- if (!test_and_set_bit(GO_DISKLESS, &mdev->flags))
- drbd_queue_work(&mdev->tconn->sender_work, &mdev->go_diskless);
-}
-
/**
* drbd_queue_bitmap_io() - Queues an IO operation on the whole bitmap
* @mdev: DRBD device.
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 2af26fc95280..9e3f441e7e84 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -696,37 +696,52 @@ out:
return 0;
}
-/* initializes the md.*_offset members, so we are able to find
- * the on disk meta data */
+/* Initializes the md.*_offset members, so we are able to find
+ * the on disk meta data.
+ *
+ * We currently have two possible layouts:
+ * external:
+ * |----------- md_size_sect ------------------|
+ * [ 4k superblock ][ activity log ][ Bitmap ]
+ * | al_offset == 8 |
+ * | bm_offset = al_offset + X |
+ * ==> bitmap sectors = md_size_sect - bm_offset
+ *
+ * internal:
+ * |----------- md_size_sect ------------------|
+ * [data.....][ Bitmap ][ activity log ][ 4k superblock ]
+ * | al_offset < 0 |
+ * | bm_offset = al_offset - Y |
+ * ==> bitmap sectors = Y = al_offset - bm_offset
+ *
+ * Activity log size used to be fixed 32kB,
+ * but is about to become configurable.
+ */
static void drbd_md_set_sector_offsets(struct drbd_conf *mdev,
struct drbd_backing_dev *bdev)
{
sector_t md_size_sect = 0;
- int meta_dev_idx;
+ unsigned int al_size_sect = bdev->md.al_size_4k * 8;
- rcu_read_lock();
- meta_dev_idx = rcu_dereference(bdev->disk_conf)->meta_dev_idx;
+ bdev->md.md_offset = drbd_md_ss(bdev);
- switch (meta_dev_idx) {
+ switch (bdev->md.meta_dev_idx) {
default:
/* v07 style fixed size indexed meta data */
- bdev->md.md_size_sect = MD_RESERVED_SECT;
- bdev->md.md_offset = drbd_md_ss__(mdev, bdev);
- bdev->md.al_offset = MD_AL_OFFSET;
- bdev->md.bm_offset = MD_BM_OFFSET;
+ bdev->md.md_size_sect = MD_128MB_SECT;
+ bdev->md.al_offset = MD_4kB_SECT;
+ bdev->md.bm_offset = MD_4kB_SECT + al_size_sect;
break;
case DRBD_MD_INDEX_FLEX_EXT:
/* just occupy the full device; unit: sectors */
bdev->md.md_size_sect = drbd_get_capacity(bdev->md_bdev);
- bdev->md.md_offset = 0;
- bdev->md.al_offset = MD_AL_OFFSET;
- bdev->md.bm_offset = MD_BM_OFFSET;
+ bdev->md.al_offset = MD_4kB_SECT;
+ bdev->md.bm_offset = MD_4kB_SECT + al_size_sect;
break;
case DRBD_MD_INDEX_INTERNAL:
case DRBD_MD_INDEX_FLEX_INT:
- bdev->md.md_offset = drbd_md_ss__(mdev, bdev);
/* al size is still fixed */
- bdev->md.al_offset = -MD_AL_SECTORS;
+ bdev->md.al_offset = -al_size_sect;
/* we need (slightly less than) ~ this much bitmap sectors: */
md_size_sect = drbd_get_capacity(bdev->backing_bdev);
md_size_sect = ALIGN(md_size_sect, BM_SECT_PER_EXT);
@@ -735,14 +750,13 @@ static void drbd_md_set_sector_offsets(struct drbd_conf *mdev,
/* plus the "drbd meta data super block",
* and the activity log; */
- md_size_sect += MD_BM_OFFSET;
+ md_size_sect += MD_4kB_SECT + al_size_sect;
bdev->md.md_size_sect = md_size_sect;
/* bitmap offset is adjusted by 'super' block size */
- bdev->md.bm_offset = -md_size_sect + MD_AL_OFFSET;
+ bdev->md.bm_offset = -md_size_sect + MD_4kB_SECT;
break;
}
- rcu_read_unlock();
}
/* input size is expected to be in KB */
@@ -805,7 +819,7 @@ void drbd_resume_io(struct drbd_conf *mdev)
enum determine_dev_size drbd_determine_dev_size(struct drbd_conf *mdev, enum dds_flags flags) __must_hold(local)
{
sector_t prev_first_sect, prev_size; /* previous meta location */
- sector_t la_size, u_size;
+ sector_t la_size_sect, u_size;
sector_t size;
char ppb[10];
@@ -828,7 +842,7 @@ enum determine_dev_size drbd_determine_dev_size(struct drbd_conf *mdev, enum dds
prev_first_sect = drbd_md_first_sector(mdev->ldev);
prev_size = mdev->ldev->md.md_size_sect;
- la_size = mdev->ldev->md.la_size_sect;
+ la_size_sect = mdev->ldev->md.la_size_sect;
/* TODO: should only be some assert here, not (re)init... */
drbd_md_set_sector_offsets(mdev, mdev->ldev);
@@ -864,7 +878,7 @@ enum determine_dev_size drbd_determine_dev_size(struct drbd_conf *mdev, enum dds
if (rv == dev_size_error)
goto out;
- la_size_changed = (la_size != mdev->ldev->md.la_size_sect);
+ la_size_changed = (la_size_sect != mdev->ldev->md.la_size_sect);
md_moved = prev_first_sect != drbd_md_first_sector(mdev->ldev)
|| prev_size != mdev->ldev->md.md_size_sect;
@@ -886,9 +900,9 @@ enum determine_dev_size drbd_determine_dev_size(struct drbd_conf *mdev, enum dds
drbd_md_mark_dirty(mdev);
}
- if (size > la_size)
+ if (size > la_size_sect)
rv = grew;
- if (size < la_size)
+ if (size < la_size_sect)
rv = shrunk;
out:
lc_unlock(mdev->act_log);
@@ -903,7 +917,7 @@ drbd_new_dev_size(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
sector_t u_size, int assume_peer_has_space)
{
sector_t p_size = mdev->p_size; /* partner's disk size. */
- sector_t la_size = bdev->md.la_size_sect; /* last agreed size. */
+ sector_t la_size_sect = bdev->md.la_size_sect; /* last agreed size. */
sector_t m_size; /* my size */
sector_t size = 0;
@@ -917,8 +931,8 @@ drbd_new_dev_size(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
if (p_size && m_size) {
size = min_t(sector_t, p_size, m_size);
} else {
- if (la_size) {
- size = la_size;
+ if (la_size_sect) {
+ size = la_size_sect;
if (m_size && m_size < size)
size = m_size;
if (p_size && p_size < size)
@@ -1127,15 +1141,32 @@ static bool should_set_defaults(struct genl_info *info)
return 0 != (flags & DRBD_GENL_F_SET_DEFAULTS);
}
-static void enforce_disk_conf_limits(struct disk_conf *dc)
+static unsigned int drbd_al_extents_max(struct drbd_backing_dev *bdev)
{
- if (dc->al_extents < DRBD_AL_EXTENTS_MIN)
- dc->al_extents = DRBD_AL_EXTENTS_MIN;
- if (dc->al_extents > DRBD_AL_EXTENTS_MAX)
- dc->al_extents = DRBD_AL_EXTENTS_MAX;
+ /* This is limited by 16 bit "slot" numbers,
+ * and by available on-disk context storage.
+ *
+ * Also (u16)~0 is special (denotes a "free" extent).
+ *
+ * One transaction occupies one 4kB on-disk block,
+ * we have n such blocks in the on disk ring buffer,
+ * the "current" transaction may fail (n-1),
+ * and there is 919 slot numbers context information per transaction.
+ *
+ * 72 transaction blocks amounts to more than 2**16 context slots,
+ * so cap there first.
+ */
+ const unsigned int max_al_nr = DRBD_AL_EXTENTS_MAX;
+ const unsigned int sufficient_on_disk =
+ (max_al_nr + AL_CONTEXT_PER_TRANSACTION -1)
+ /AL_CONTEXT_PER_TRANSACTION;
+
+ unsigned int al_size_4k = bdev->md.al_size_4k;
+
+ if (al_size_4k > sufficient_on_disk)
+ return max_al_nr;
- if (dc->c_plan_ahead > DRBD_C_PLAN_AHEAD_MAX)
- dc->c_plan_ahead = DRBD_C_PLAN_AHEAD_MAX;
+ return (al_size_4k - 1) * AL_CONTEXT_PER_TRANSACTION;
}
int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
@@ -1182,7 +1213,13 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
if (!expect(new_disk_conf->resync_rate >= 1))
new_disk_conf->resync_rate = 1;
- enforce_disk_conf_limits(new_disk_conf);
+ if (new_disk_conf->al_extents < DRBD_AL_EXTENTS_MIN)
+ new_disk_conf->al_extents = DRBD_AL_EXTENTS_MIN;
+ if (new_disk_conf->al_extents > drbd_al_extents_max(mdev->ldev))
+ new_disk_conf->al_extents = drbd_al_extents_max(mdev->ldev);
+
+ if (new_disk_conf->c_plan_ahead > DRBD_C_PLAN_AHEAD_MAX)
+ new_disk_conf->c_plan_ahead = DRBD_C_PLAN_AHEAD_MAX;
fifo_size = (new_disk_conf->c_plan_ahead * 10 * SLEEP_TIME) / HZ;
if (fifo_size != mdev->rs_plan_s->size) {
@@ -1330,7 +1367,8 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
goto fail;
}
- enforce_disk_conf_limits(new_disk_conf);
+ if (new_disk_conf->c_plan_ahead > DRBD_C_PLAN_AHEAD_MAX)
+ new_disk_conf->c_plan_ahead = DRBD_C_PLAN_AHEAD_MAX;
new_plan = fifo_alloc((new_disk_conf->c_plan_ahead * 10 * SLEEP_TIME) / HZ);
if (!new_plan) {
@@ -1343,6 +1381,12 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
goto fail;
}
+ write_lock_irq(&global_state_lock);
+ retcode = drbd_resync_after_valid(mdev, new_disk_conf->resync_after);
+ write_unlock_irq(&global_state_lock);
+ if (retcode != NO_ERROR)
+ goto fail;
+
rcu_read_lock();
nc = rcu_dereference(mdev->tconn->net_conf);
if (nc) {
@@ -1399,8 +1443,16 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
goto fail;
}
- /* RT - for drbd_get_max_capacity() DRBD_MD_INDEX_FLEX_INT */
- drbd_md_set_sector_offsets(mdev, nbc);
+ /* Read our meta data super block early.
+ * This also sets other on-disk offsets. */
+ retcode = drbd_md_read(mdev, nbc);
+ if (retcode != NO_ERROR)
+ goto fail;
+
+ if (new_disk_conf->al_extents < DRBD_AL_EXTENTS_MIN)
+ new_disk_conf->al_extents = DRBD_AL_EXTENTS_MIN;
+ if (new_disk_conf->al_extents > drbd_al_extents_max(nbc))
+ new_disk_conf->al_extents = drbd_al_extents_max(nbc);
if (drbd_get_max_capacity(nbc) < new_disk_conf->disk_size) {
dev_err(DEV, "max capacity %llu smaller than disk size %llu\n",
@@ -1416,7 +1468,7 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
min_md_device_sectors = (2<<10);
} else {
max_possible_sectors = DRBD_MAX_SECTORS;
- min_md_device_sectors = MD_RESERVED_SECT * (new_disk_conf->meta_dev_idx + 1);
+ min_md_device_sectors = MD_128MB_SECT * (new_disk_conf->meta_dev_idx + 1);
}
if (drbd_get_capacity(nbc->md_bdev) < min_md_device_sectors) {
@@ -1467,8 +1519,6 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
if (!get_ldev_if_state(mdev, D_ATTACHING))
goto force_diskless;
- drbd_md_set_sector_offsets(mdev, nbc);
-
if (!mdev->bitmap) {
if (drbd_bm_init(mdev)) {
retcode = ERR_NOMEM;
@@ -1476,10 +1526,6 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
}
}
- retcode = drbd_md_read(mdev, nbc);
- if (retcode != NO_ERROR)
- goto force_diskless_dec;
-
if (mdev->state.conn < C_CONNECTED &&
mdev->state.role == R_PRIMARY &&
(mdev->ed_uuid & ~((u64)1)) != (nbc->md.uuid[UI_CURRENT] & ~((u64)1))) {
@@ -2158,8 +2204,11 @@ static enum drbd_state_rv conn_try_disconnect(struct drbd_tconn *tconn, bool for
return SS_SUCCESS;
case SS_PRIMARY_NOP:
/* Our state checking code wants to see the peer outdated. */
- rv = conn_request_state(tconn, NS2(conn, C_DISCONNECTING,
- pdsk, D_OUTDATED), CS_VERBOSE);
+ rv = conn_request_state(tconn, NS2(conn, C_DISCONNECTING, pdsk, D_OUTDATED), 0);
+
+ if (rv == SS_OUTDATE_WO_CONN) /* lost connection before graceful disconnect succeeded */
+ rv = conn_request_state(tconn, NS(conn, C_DISCONNECTING), CS_VERBOSE);
+
break;
case SS_CW_FAILED_BY_PEER:
/* The peer probably wants to see us outdated. */
@@ -2406,22 +2455,19 @@ int drbd_adm_invalidate(struct sk_buff *skb, struct genl_info *info)
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
drbd_flush_workqueue(mdev);
- retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED);
-
- if (retcode < SS_SUCCESS && retcode != SS_NEED_CONNECTION)
- retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T));
-
- while (retcode == SS_NEED_CONNECTION) {
- spin_lock_irq(&mdev->tconn->req_lock);
- if (mdev->state.conn < C_CONNECTED)
- retcode = _drbd_set_state(_NS(mdev, disk, D_INCONSISTENT), CS_VERBOSE, NULL);
- spin_unlock_irq(&mdev->tconn->req_lock);
-
- if (retcode != SS_NEED_CONNECTION)
- break;
-
+ /* If we happen to be C_STANDALONE R_SECONDARY, just change to
+ * D_INCONSISTENT, and set all bits in the bitmap. Otherwise,
+ * try to start a resync handshake as sync target for full sync.
+ */
+ if (mdev->state.conn == C_STANDALONE && mdev->state.role == R_SECONDARY) {
+ retcode = drbd_request_state(mdev, NS(disk, D_INCONSISTENT));
+ if (retcode >= SS_SUCCESS) {
+ if (drbd_bitmap_io(mdev, &drbd_bmio_set_n_write,
+ "set_n_write from invalidate", BM_LOCKED_MASK))
+ retcode = ERR_IO_MD_DISK;
+ }
+ } else
retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T));
- }
drbd_resume_io(mdev);
out:
@@ -2475,21 +2521,22 @@ int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info)
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
drbd_flush_workqueue(mdev);
- retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S), CS_ORDERED);
- if (retcode < SS_SUCCESS) {
- if (retcode == SS_NEED_CONNECTION && mdev->state.role == R_PRIMARY) {
- /* The peer will get a resync upon connect anyways.
- * Just make that into a full resync. */
- retcode = drbd_request_state(mdev, NS(pdsk, D_INCONSISTENT));
- if (retcode >= SS_SUCCESS) {
- if (drbd_bitmap_io(mdev, &drbd_bmio_set_susp_al,
- "set_n_write from invalidate_peer",
- BM_LOCKED_SET_ALLOWED))
- retcode = ERR_IO_MD_DISK;
- }
- } else
- retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S));
- }
+ /* If we happen to be C_STANDALONE R_PRIMARY, just set all bits
+ * in the bitmap. Otherwise, try to start a resync handshake
+ * as sync source for full sync.
+ */
+ if (mdev->state.conn == C_STANDALONE && mdev->state.role == R_PRIMARY) {
+ /* The peer will get a resync upon connect anyways. Just make that
+ into a full resync. */
+ retcode = drbd_request_state(mdev, NS(pdsk, D_INCONSISTENT));
+ if (retcode >= SS_SUCCESS) {
+ if (drbd_bitmap_io(mdev, &drbd_bmio_set_susp_al,
+ "set_n_write from invalidate_peer",
+ BM_LOCKED_SET_ALLOWED))
+ retcode = ERR_IO_MD_DISK;
+ }
+ } else
+ retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S));
drbd_resume_io(mdev);
out:
@@ -3162,6 +3209,7 @@ static enum drbd_ret_code adm_delete_minor(struct drbd_conf *mdev)
CS_VERBOSE + CS_WAIT_COMPLETE);
idr_remove(&mdev->tconn->volumes, mdev->vnr);
idr_remove(&minors, mdev_to_minor(mdev));
+ destroy_workqueue(mdev->submit.wq);
del_gendisk(mdev->vdisk);
synchronize_rcu();
kref_put(&mdev->kref, &drbd_minor_destroy);
diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c
index 928adb815b09..bf31d41dbaad 100644
--- a/drivers/block/drbd/drbd_proc.c
+++ b/drivers/block/drbd/drbd_proc.c
@@ -313,8 +313,14 @@ static int drbd_seq_show(struct seq_file *seq, void *v)
static int drbd_proc_open(struct inode *inode, struct file *file)
{
- if (try_module_get(THIS_MODULE))
- return single_open(file, drbd_seq_show, PDE_DATA(inode));
+ int err;
+
+ if (try_module_get(THIS_MODULE)) {
+ err = single_open(file, drbd_seq_show, PDE_DATA(inode));
+ if (err)
+ module_put(THIS_MODULE);
+ return err;
+ }
return -ENODEV;
}
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index 83c5ae0ed56b..4222affff488 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -850,6 +850,7 @@ int drbd_connected(struct drbd_conf *mdev)
err = drbd_send_current_state(mdev);
clear_bit(USE_DEGR_WFC_T, &mdev->flags);
clear_bit(RESIZE_PENDING, &mdev->flags);
+ atomic_set(&mdev->ap_in_flight, 0);
mod_timer(&mdev->request_timer, jiffies + HZ); /* just start it here. */
return err;
}
@@ -2266,7 +2267,7 @@ static int receive_Data(struct drbd_tconn *tconn, struct packet_info *pi)
drbd_set_out_of_sync(mdev, peer_req->i.sector, peer_req->i.size);
peer_req->flags |= EE_CALL_AL_COMPLETE_IO;
peer_req->flags &= ~EE_MAY_SET_IN_SYNC;
- drbd_al_begin_io(mdev, &peer_req->i);
+ drbd_al_begin_io(mdev, &peer_req->i, true);
}
err = drbd_submit_peer_request(mdev, peer_req, rw, DRBD_FAULT_DT_WR);
@@ -2662,7 +2663,6 @@ static int drbd_asb_recover_1p(struct drbd_conf *mdev) __must_hold(local)
if (hg == -1 && mdev->state.role == R_PRIMARY) {
enum drbd_state_rv rv2;
- drbd_set_role(mdev, R_SECONDARY, 0);
/* drbd_change_state() does not sleep while in SS_IN_TRANSIENT_STATE,
* we might be here in C_WF_REPORT_PARAMS which is transient.
* we do not need to wait for the after state change work either. */
@@ -3993,7 +3993,7 @@ static int receive_state(struct drbd_tconn *tconn, struct packet_info *pi)
clear_bit(DISCARD_MY_DATA, &mdev->flags);
- drbd_md_sync(mdev); /* update connected indicator, la_size, ... */
+ drbd_md_sync(mdev); /* update connected indicator, la_size_sect, ... */
return 0;
}
@@ -4660,8 +4660,8 @@ static int drbd_do_features(struct drbd_tconn *tconn)
#if !defined(CONFIG_CRYPTO_HMAC) && !defined(CONFIG_CRYPTO_HMAC_MODULE)
static int drbd_do_auth(struct drbd_tconn *tconn)
{
- dev_err(DEV, "This kernel was build without CONFIG_CRYPTO_HMAC.\n");
- dev_err(DEV, "You need to disable 'cram-hmac-alg' in drbd.conf.\n");
+ conn_err(tconn, "This kernel was build without CONFIG_CRYPTO_HMAC.\n");
+ conn_err(tconn, "You need to disable 'cram-hmac-alg' in drbd.conf.\n");
return -1;
}
#else
@@ -5258,9 +5258,11 @@ int drbd_asender(struct drbd_thread *thi)
bool ping_timeout_active = false;
struct net_conf *nc;
int ping_timeo, tcp_cork, ping_int;
+ struct sched_param param = { .sched_priority = 2 };
- current->policy = SCHED_RR; /* Make this a realtime task! */
- current->rt_priority = 2; /* more important than all other tasks */
+ rv = sched_setscheduler(current, SCHED_RR, &param);
+ if (rv < 0)
+ conn_err(tconn, "drbd_asender: ERROR set priority, ret=%d\n", rv);
while (get_t_state(thi) == RUNNING) {
drbd_thread_current_set_cpu(thi);
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 2b8303ad63c9..c24379ffd4e3 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -34,14 +34,14 @@
static bool drbd_may_do_local_read(struct drbd_conf *mdev, sector_t sector, int size);
/* Update disk stats at start of I/O request */
-static void _drbd_start_io_acct(struct drbd_conf *mdev, struct drbd_request *req, struct bio *bio)
+static void _drbd_start_io_acct(struct drbd_conf *mdev, struct drbd_request *req)
{
- const int rw = bio_data_dir(bio);
+ const int rw = bio_data_dir(req->master_bio);
int cpu;
cpu = part_stat_lock();
part_round_stats(cpu, &mdev->vdisk->part0);
part_stat_inc(cpu, &mdev->vdisk->part0, ios[rw]);
- part_stat_add(cpu, &mdev->vdisk->part0, sectors[rw], bio_sectors(bio));
+ part_stat_add(cpu, &mdev->vdisk->part0, sectors[rw], req->i.size >> 9);
(void) cpu; /* The macro invocations above want the cpu argument, I do not like
the compiler warning about cpu only assigned but never used... */
part_inc_in_flight(&mdev->vdisk->part0, rw);
@@ -263,8 +263,7 @@ void drbd_req_complete(struct drbd_request *req, struct bio_and_error *m)
else
root = &mdev->read_requests;
drbd_remove_request_interval(root, req);
- } else if (!(s & RQ_POSTPONED))
- D_ASSERT((s & (RQ_NET_MASK & ~RQ_NET_DONE)) == 0);
+ }
/* Before we can signal completion to the upper layers,
* we may need to close the current transfer log epoch.
@@ -755,6 +754,11 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
D_ASSERT(req->rq_state & RQ_NET_PENDING);
mod_rq_state(req, m, RQ_NET_PENDING, RQ_NET_OK|RQ_NET_DONE);
break;
+
+ case QUEUE_AS_DRBD_BARRIER:
+ start_new_tl_epoch(mdev->tconn);
+ mod_rq_state(req, m, 0, RQ_NET_OK|RQ_NET_DONE);
+ break;
};
return rv;
@@ -861,8 +865,10 @@ static void maybe_pull_ahead(struct drbd_conf *mdev)
bool congested = false;
enum drbd_on_congestion on_congestion;
+ rcu_read_lock();
nc = rcu_dereference(tconn->net_conf);
on_congestion = nc ? nc->on_congestion : OC_BLOCK;
+ rcu_read_unlock();
if (on_congestion == OC_BLOCK ||
tconn->agreed_pro_version < 96)
return;
@@ -956,14 +962,8 @@ static int drbd_process_write_request(struct drbd_request *req)
struct drbd_conf *mdev = req->w.mdev;
int remote, send_oos;
- rcu_read_lock();
remote = drbd_should_do_remote(mdev->state);
- if (remote) {
- maybe_pull_ahead(mdev);
- remote = drbd_should_do_remote(mdev->state);
- }
send_oos = drbd_should_send_out_of_sync(mdev->state);
- rcu_read_unlock();
/* Need to replicate writes. Unless it is an empty flush,
* which is better mapped to a DRBD P_BARRIER packet,
@@ -975,8 +975,8 @@ static int drbd_process_write_request(struct drbd_request *req)
/* The only size==0 bios we expect are empty flushes. */
D_ASSERT(req->master_bio->bi_rw & REQ_FLUSH);
if (remote)
- start_new_tl_epoch(mdev->tconn);
- return 0;
+ _req_mod(req, QUEUE_AS_DRBD_BARRIER);
+ return remote;
}
if (!remote && !send_oos)
@@ -1020,12 +1020,24 @@ drbd_submit_req_private_bio(struct drbd_request *req)
bio_endio(bio, -EIO);
}
-void __drbd_make_request(struct drbd_conf *mdev, struct bio *bio, unsigned long start_time)
+static void drbd_queue_write(struct drbd_conf *mdev, struct drbd_request *req)
{
- const int rw = bio_rw(bio);
- struct bio_and_error m = { NULL, };
+ spin_lock(&mdev->submit.lock);
+ list_add_tail(&req->tl_requests, &mdev->submit.writes);
+ spin_unlock(&mdev->submit.lock);
+ queue_work(mdev->submit.wq, &mdev->submit.worker);
+}
+
+/* returns the new drbd_request pointer, if the caller is expected to
+ * drbd_send_and_submit() it (to save latency), or NULL if we queued the
+ * request on the submitter thread.
+ * Returns ERR_PTR(-ENOMEM) if we cannot allocate a drbd_request.
+ */
+struct drbd_request *
+drbd_request_prepare(struct drbd_conf *mdev, struct bio *bio, unsigned long start_time)
+{
+ const int rw = bio_data_dir(bio);
struct drbd_request *req;
- bool no_remote = false;
/* allocate outside of all locks; */
req = drbd_req_new(mdev, bio);
@@ -1035,7 +1047,7 @@ void __drbd_make_request(struct drbd_conf *mdev, struct bio *bio, unsigned long
* if user cannot handle io errors, that's not our business. */
dev_err(DEV, "could not kmalloc() req\n");
bio_endio(bio, -ENOMEM);
- return;
+ return ERR_PTR(-ENOMEM);
}
req->start_time = start_time;
@@ -1044,28 +1056,40 @@ void __drbd_make_request(struct drbd_conf *mdev, struct bio *bio, unsigned long
req->private_bio = NULL;
}
- /* For WRITES going to the local disk, grab a reference on the target
- * extent. This waits for any resync activity in the corresponding
- * resync extent to finish, and, if necessary, pulls in the target
- * extent into the activity log, which involves further disk io because
- * of transactional on-disk meta data updates.
- * Empty flushes don't need to go into the activity log, they can only
- * flush data for pending writes which are already in there. */
+ /* Update disk stats */
+ _drbd_start_io_acct(mdev, req);
+
if (rw == WRITE && req->private_bio && req->i.size
&& !test_bit(AL_SUSPENDED, &mdev->flags)) {
+ if (!drbd_al_begin_io_fastpath(mdev, &req->i)) {
+ drbd_queue_write(mdev, req);
+ return NULL;
+ }
req->rq_state |= RQ_IN_ACT_LOG;
- drbd_al_begin_io(mdev, &req->i);
}
+ return req;
+}
+
+static void drbd_send_and_submit(struct drbd_conf *mdev, struct drbd_request *req)
+{
+ const int rw = bio_rw(req->master_bio);
+ struct bio_and_error m = { NULL, };
+ bool no_remote = false;
+
spin_lock_irq(&mdev->tconn->req_lock);
if (rw == WRITE) {
/* This may temporarily give up the req_lock,
* but will re-aquire it before it returns here.
* Needs to be before the check on drbd_suspended() */
complete_conflicting_writes(req);
+ /* no more giving up req_lock from now on! */
+
+ /* check for congestion, and potentially stop sending
+ * full data updates, but start sending "dirty bits" only. */
+ maybe_pull_ahead(mdev);
}
- /* no more giving up req_lock from now on! */
if (drbd_suspended(mdev)) {
/* push back and retry: */
@@ -1078,9 +1102,6 @@ void __drbd_make_request(struct drbd_conf *mdev, struct bio *bio, unsigned long
goto out;
}
- /* Update disk stats */
- _drbd_start_io_acct(mdev, req, bio);
-
/* We fail READ/READA early, if we can not serve it.
* We must do this before req is registered on any lists.
* Otherwise, drbd_req_complete() will queue failed READ for retry. */
@@ -1137,7 +1158,116 @@ out:
if (m.bio)
complete_master_bio(mdev, &m);
- return;
+}
+
+void __drbd_make_request(struct drbd_conf *mdev, struct bio *bio, unsigned long start_time)
+{
+ struct drbd_request *req = drbd_request_prepare(mdev, bio, start_time);
+ if (IS_ERR_OR_NULL(req))
+ return;
+ drbd_send_and_submit(mdev, req);
+}
+
+static void submit_fast_path(struct drbd_conf *mdev, struct list_head *incoming)
+{
+ struct drbd_request *req, *tmp;
+ list_for_each_entry_safe(req, tmp, incoming, tl_requests) {
+ const int rw = bio_data_dir(req->master_bio);
+
+ if (rw == WRITE /* rw != WRITE should not even end up here! */
+ && req->private_bio && req->i.size
+ && !test_bit(AL_SUSPENDED, &mdev->flags)) {
+ if (!drbd_al_begin_io_fastpath(mdev, &req->i))
+ continue;
+
+ req->rq_state |= RQ_IN_ACT_LOG;
+ }
+
+ list_del_init(&req->tl_requests);
+ drbd_send_and_submit(mdev, req);
+ }
+}
+
+static bool prepare_al_transaction_nonblock(struct drbd_conf *mdev,
+ struct list_head *incoming,
+ struct list_head *pending)
+{
+ struct drbd_request *req, *tmp;
+ int wake = 0;
+ int err;
+
+ spin_lock_irq(&mdev->al_lock);
+ list_for_each_entry_safe(req, tmp, incoming, tl_requests) {
+ err = drbd_al_begin_io_nonblock(mdev, &req->i);
+ if (err == -EBUSY)
+ wake = 1;
+ if (err)
+ continue;
+ req->rq_state |= RQ_IN_ACT_LOG;
+ list_move_tail(&req->tl_requests, pending);
+ }
+ spin_unlock_irq(&mdev->al_lock);
+ if (wake)
+ wake_up(&mdev->al_wait);
+
+ return !list_empty(pending);
+}
+
+void do_submit(struct work_struct *ws)
+{
+ struct drbd_conf *mdev = container_of(ws, struct drbd_conf, submit.worker);
+ LIST_HEAD(incoming);
+ LIST_HEAD(pending);
+ struct drbd_request *req, *tmp;
+
+ for (;;) {
+ spin_lock(&mdev->submit.lock);
+ list_splice_tail_init(&mdev->submit.writes, &incoming);
+ spin_unlock(&mdev->submit.lock);
+
+ submit_fast_path(mdev, &incoming);
+ if (list_empty(&incoming))
+ break;
+
+ wait_event(mdev->al_wait, prepare_al_transaction_nonblock(mdev, &incoming, &pending));
+ /* Maybe more was queued, while we prepared the transaction?
+ * Try to stuff them into this transaction as well.
+ * Be strictly non-blocking here, no wait_event, we already
+ * have something to commit.
+ * Stop if we don't make any more progres.
+ */
+ for (;;) {
+ LIST_HEAD(more_pending);
+ LIST_HEAD(more_incoming);
+ bool made_progress;
+
+ /* It is ok to look outside the lock,
+ * it's only an optimization anyways */
+ if (list_empty(&mdev->submit.writes))
+ break;
+
+ spin_lock(&mdev->submit.lock);
+ list_splice_tail_init(&mdev->submit.writes, &more_incoming);
+ spin_unlock(&mdev->submit.lock);
+
+ if (list_empty(&more_incoming))
+ break;
+
+ made_progress = prepare_al_transaction_nonblock(mdev, &more_incoming, &more_pending);
+
+ list_splice_tail_init(&more_pending, &pending);
+ list_splice_tail_init(&more_incoming, &incoming);
+
+ if (!made_progress)
+ break;
+ }
+ drbd_al_begin_io_commit(mdev, false);
+
+ list_for_each_entry_safe(req, tmp, &pending, tl_requests) {
+ list_del_init(&req->tl_requests);
+ drbd_send_and_submit(mdev, req);
+ }
+ }
}
void drbd_make_request(struct request_queue *q, struct bio *bio)
diff --git a/drivers/block/drbd/drbd_req.h b/drivers/block/drbd/drbd_req.h
index c08d22964d06..978cb1addc98 100644
--- a/drivers/block/drbd/drbd_req.h
+++ b/drivers/block/drbd/drbd_req.h
@@ -88,6 +88,14 @@ enum drbd_req_event {
QUEUE_FOR_NET_READ,
QUEUE_FOR_SEND_OOS,
+ /* An empty flush is queued as P_BARRIER,
+ * which will cause it to complete "successfully",
+ * even if the local disk flush failed.
+ *
+ * Just like "real" requests, empty flushes (blkdev_issue_flush()) will
+ * only see an error if neither local nor remote data is reachable. */
+ QUEUE_AS_DRBD_BARRIER,
+
SEND_CANCELED,
SEND_FAILED,
HANDED_OVER_TO_NETWORK,
diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c
index 0fe220cfb9e9..90c5be2b1d30 100644
--- a/drivers/block/drbd/drbd_state.c
+++ b/drivers/block/drbd/drbd_state.c
@@ -570,6 +570,13 @@ is_valid_state(struct drbd_conf *mdev, union drbd_state ns)
mdev->tconn->agreed_pro_version < 88)
rv = SS_NOT_SUPPORTED;
+ else if (ns.role == R_PRIMARY && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE)
+ rv = SS_NO_UP_TO_DATE_DISK;
+
+ else if ((ns.conn == C_STARTING_SYNC_S || ns.conn == C_STARTING_SYNC_T) &&
+ ns.pdsk == D_UNKNOWN)
+ rv = SS_NEED_CONNECTION;
+
else if (ns.conn >= C_CONNECTED && ns.pdsk == D_UNKNOWN)
rv = SS_CONNECTED_OUTDATES;
@@ -635,6 +642,10 @@ is_valid_soft_transition(union drbd_state os, union drbd_state ns, struct drbd_t
&& os.conn < C_WF_REPORT_PARAMS)
rv = SS_NEED_CONNECTION; /* No NetworkFailure -> SyncTarget etc... */
+ if (ns.conn == C_DISCONNECTING && ns.pdsk == D_OUTDATED &&
+ os.conn < C_CONNECTED && os.pdsk > D_OUTDATED)
+ rv = SS_OUTDATE_WO_CONN;
+
return rv;
}
@@ -1377,13 +1388,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
&drbd_bmio_set_n_write, &abw_start_sync,
"set_n_write from StartingSync", BM_LOCKED_TEST_ALLOWED);
- /* We are invalidating our self... */
- if (os.conn < C_CONNECTED && ns.conn < C_CONNECTED &&
- os.disk > D_INCONSISTENT && ns.disk == D_INCONSISTENT)
- /* other bitmap operation expected during this phase */
- drbd_queue_bitmap_io(mdev, &drbd_bmio_set_n_write, NULL,
- "set_n_write from invalidate", BM_LOCKED_MASK);
-
/* first half of local IO error, failure to attach,
* or administrative detach */
if (os.disk != D_FAILED && ns.disk == D_FAILED) {
@@ -1748,13 +1752,9 @@ _conn_rq_cond(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state
if (test_and_clear_bit(CONN_WD_ST_CHG_FAIL, &tconn->flags))
return SS_CW_FAILED_BY_PEER;
- rv = tconn->cstate != C_WF_REPORT_PARAMS ? SS_CW_NO_NEED : SS_UNKNOWN_ERROR;
-
- if (rv == SS_UNKNOWN_ERROR)
- rv = conn_is_valid_transition(tconn, mask, val, 0);
-
- if (rv == SS_SUCCESS)
- rv = SS_UNKNOWN_ERROR; /* cont waiting, otherwise fail. */
+ rv = conn_is_valid_transition(tconn, mask, val, 0);
+ if (rv == SS_SUCCESS && tconn->cstate == C_WF_REPORT_PARAMS)
+ rv = SS_UNKNOWN_ERROR; /* continue waiting */
return rv;
}
diff --git a/drivers/block/drbd/drbd_strings.c b/drivers/block/drbd/drbd_strings.c
index 9a664bd27404..58e08ff2b2ce 100644
--- a/drivers/block/drbd/drbd_strings.c
+++ b/drivers/block/drbd/drbd_strings.c
@@ -89,6 +89,7 @@ static const char *drbd_state_sw_errors[] = {
[-SS_LOWER_THAN_OUTDATED] = "Disk state is lower than outdated",
[-SS_IN_TRANSIENT_STATE] = "In transient state, retry after next state change",
[-SS_CONCURRENT_ST_CHG] = "Concurrent state changes detected and aborted",
+ [-SS_OUTDATE_WO_CONN] = "Need a connection for a graceful disconnect/outdate peer",
[-SS_O_VOL_PEER_PRI] = "Other vol primary on peer not allowed by config",
};
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c
index 424dc7bdf9b7..891c0ecaa292 100644
--- a/drivers/block/drbd/drbd_worker.c
+++ b/drivers/block/drbd/drbd_worker.c
@@ -89,7 +89,8 @@ void drbd_md_io_complete(struct bio *bio, int error)
md_io->done = 1;
wake_up(&mdev->misc_wait);
bio_put(bio);
- put_ldev(mdev);
+ if (mdev->ldev) /* special case: drbd_md_read() during drbd_adm_attach() */
+ put_ldev(mdev);
}
/* reads on behalf of the partner,
@@ -1410,7 +1411,7 @@ int w_restart_disk_io(struct drbd_work *w, int cancel)
struct drbd_conf *mdev = w->mdev;
if (bio_data_dir(req->master_bio) == WRITE && req->rq_state & RQ_IN_ACT_LOG)
- drbd_al_begin_io(mdev, &req->i);
+ drbd_al_begin_io(mdev, &req->i, false);
drbd_req_make_private_bio(req, req->master_bio);
req->private_bio->bi_bdev = mdev->ldev->backing_bdev;
@@ -1425,7 +1426,7 @@ static int _drbd_may_sync_now(struct drbd_conf *mdev)
int resync_after;
while (1) {
- if (!odev->ldev)
+ if (!odev->ldev || odev->state.disk == D_DISKLESS)
return 1;
rcu_read_lock();
resync_after = rcu_dereference(odev->ldev->disk_conf)->resync_after;
@@ -1433,7 +1434,7 @@ static int _drbd_may_sync_now(struct drbd_conf *mdev)
if (resync_after == -1)
return 1;
odev = minor_to_mdev(resync_after);
- if (!expect(odev))
+ if (!odev)
return 1;
if ((odev->state.conn >= C_SYNC_SOURCE &&
odev->state.conn <= C_PAUSED_SYNC_T) ||
@@ -1515,7 +1516,7 @@ enum drbd_ret_code drbd_resync_after_valid(struct drbd_conf *mdev, int o_minor)
if (o_minor == -1)
return NO_ERROR;
- if (o_minor < -1 || minor_to_mdev(o_minor) == NULL)
+ if (o_minor < -1 || o_minor > MINORMASK)
return ERR_RESYNC_AFTER;
/* check for loops */
@@ -1524,6 +1525,15 @@ enum drbd_ret_code drbd_resync_after_valid(struct drbd_conf *mdev, int o_minor)
if (odev == mdev)
return ERR_RESYNC_AFTER_CYCLE;
+ /* You are free to depend on diskless, non-existing,
+ * or not yet/no longer existing minors.
+ * We only reject dependency loops.
+ * We cannot follow the dependency chain beyond a detached or
+ * missing minor.
+ */
+ if (!odev || !odev->ldev || odev->state.disk == D_DISKLESS)
+ return NO_ERROR;
+
rcu_read_lock();
resync_after = rcu_dereference(odev->ldev->disk_conf)->resync_after;
rcu_read_unlock();
@@ -1652,7 +1662,9 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
clear_bit(B_RS_H_DONE, &mdev->flags);
write_lock_irq(&global_state_lock);
- if (!get_ldev_if_state(mdev, D_NEGOTIATING)) {
+ /* Did some connection breakage or IO error race with us? */
+ if (mdev->state.conn < C_CONNECTED
+ || !get_ldev_if_state(mdev, D_NEGOTIATING)) {
write_unlock_irq(&global_state_lock);
mutex_unlock(mdev->state_mutex);
return;
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 2ddd64a9ffde..04ceb7e2fadd 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -3601,7 +3601,7 @@ static void __init config_types(void)
pr_cont("\n");
}
-static int floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk, fmode_t mode)
{
int drive = (long)disk->private_data;
@@ -3615,8 +3615,6 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
opened_bdev[drive] = NULL;
mutex_unlock(&open_lock);
mutex_unlock(&floppy_mutex);
-
- return 0;
}
/*
@@ -3777,7 +3775,6 @@ static int __floppy_read_block_0(struct block_device *bdev)
bio_vec.bv_len = size;
bio_vec.bv_offset = 0;
bio.bi_vcnt = 1;
- bio.bi_idx = 0;
bio.bi_size = size;
bio.bi_bdev = bdev;
bio.bi_sector = 0;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index b2955b3f2cbc..d92d50fd84b7 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1518,7 +1518,7 @@ out:
return err;
}
-static int lo_release(struct gendisk *disk, fmode_t mode)
+static void lo_release(struct gendisk *disk, fmode_t mode)
{
struct loop_device *lo = disk->private_data;
int err;
@@ -1535,7 +1535,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
*/
err = loop_clr_fd(lo);
if (!err)
- goto out_unlocked;
+ return;
} else {
/*
* Otherwise keep thread (if running) and config,
@@ -1546,8 +1546,6 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
out:
mutex_unlock(&lo->lo_ctl_mutex);
-out_unlocked:
- return 0;
}
static const struct block_device_operations lo_fops = {
diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c
index 076ae7f1b781..a56cfcd5d648 100644
--- a/drivers/block/mg_disk.c
+++ b/drivers/block/mg_disk.c
@@ -780,6 +780,7 @@ static const struct block_device_operations mg_disk_ops = {
.getgeo = mg_getgeo
};
+#ifdef CONFIG_PM_SLEEP
static int mg_suspend(struct device *dev)
{
struct mg_drv_data *prv_data = dev->platform_data;
@@ -824,6 +825,7 @@ static int mg_resume(struct device *dev)
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(mg_pm, mg_suspend, mg_resume);
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index 32c678028e53..847107ef0cce 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -728,7 +728,10 @@ static void mtip_async_complete(struct mtip_port *port,
atomic_set(&port->commands[tag].active, 0);
release_slot(port, tag);
- up(&port->cmd_slot);
+ if (unlikely(command->unaligned))
+ up(&port->cmd_slot_unal);
+ else
+ up(&port->cmd_slot);
}
/*
@@ -1560,10 +1563,12 @@ static int mtip_get_identify(struct mtip_port *port, void __user *user_buffer)
}
#endif
+#ifdef MTIP_TRIM /* Disabling TRIM support temporarily */
/* Demux ID.DRAT & ID.RZAT to determine trim support */
if (port->identify[69] & (1 << 14) && port->identify[69] & (1 << 5))
port->dd->trim_supp = true;
else
+#endif
port->dd->trim_supp = false;
/* Set the identify buffer as valid. */
@@ -2557,7 +2562,7 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
*/
static void mtip_hw_submit_io(struct driver_data *dd, sector_t sector,
int nsect, int nents, int tag, void *callback,
- void *data, int dir)
+ void *data, int dir, int unaligned)
{
struct host_to_dev_fis *fis;
struct mtip_port *port = dd->port;
@@ -2570,6 +2575,7 @@ static void mtip_hw_submit_io(struct driver_data *dd, sector_t sector,
command->scatter_ents = nents;
+ command->unaligned = unaligned;
/*
* The number of retries for this command before it is
* reported as a failure to the upper layers.
@@ -2598,6 +2604,9 @@ static void mtip_hw_submit_io(struct driver_data *dd, sector_t sector,
fis->res3 = 0;
fill_command_sg(dd, command, nents);
+ if (unaligned)
+ fis->device |= 1 << 7;
+
/* Populate the command header */
command->command_header->opts =
__force_bit2int cpu_to_le32(
@@ -2644,9 +2653,13 @@ static void mtip_hw_submit_io(struct driver_data *dd, sector_t sector,
* return value
* None
*/
-static void mtip_hw_release_scatterlist(struct driver_data *dd, int tag)
+static void mtip_hw_release_scatterlist(struct driver_data *dd, int tag,
+ int unaligned)
{
+ struct semaphore *sem = unaligned ? &dd->port->cmd_slot_unal :
+ &dd->port->cmd_slot;
release_slot(dd->port, tag);
+ up(sem);
}
/*
@@ -2661,22 +2674,25 @@ static void mtip_hw_release_scatterlist(struct driver_data *dd, int tag)
* or NULL if no command slots are available.
*/
static struct scatterlist *mtip_hw_get_scatterlist(struct driver_data *dd,
- int *tag)
+ int *tag, int unaligned)
{
+ struct semaphore *sem = unaligned ? &dd->port->cmd_slot_unal :
+ &dd->port->cmd_slot;
+
/*
* It is possible that, even with this semaphore, a thread
* may think that no command slots are available. Therefore, we
* need to make an attempt to get_slot().
*/
- down(&dd->port->cmd_slot);
+ down(sem);
*tag = get_slot(dd->port);
if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) {
- up(&dd->port->cmd_slot);
+ up(sem);
return NULL;
}
if (unlikely(*tag < 0)) {
- up(&dd->port->cmd_slot);
+ up(sem);
return NULL;
}
@@ -3010,6 +3026,11 @@ static inline void hba_setup(struct driver_data *dd)
dd->mmio + HOST_HSORG);
}
+static int mtip_device_unaligned_constrained(struct driver_data *dd)
+{
+ return (dd->pdev->device == P420M_DEVICE_ID ? 1 : 0);
+}
+
/*
* Detect the details of the product, and store anything needed
* into the driver data structure. This includes product type and
@@ -3232,8 +3253,15 @@ static int mtip_hw_init(struct driver_data *dd)
for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++)
dd->work[i].port = dd->port;
+ /* Enable unaligned IO constraints for some devices */
+ if (mtip_device_unaligned_constrained(dd))
+ dd->unal_qdepth = MTIP_MAX_UNALIGNED_SLOTS;
+ else
+ dd->unal_qdepth = 0;
+
/* Counting semaphore to track command slot usage */
- sema_init(&dd->port->cmd_slot, num_command_slots - 1);
+ sema_init(&dd->port->cmd_slot, num_command_slots - 1 - dd->unal_qdepth);
+ sema_init(&dd->port->cmd_slot_unal, dd->unal_qdepth);
/* Spinlock to prevent concurrent issue */
for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++)
@@ -3836,7 +3864,7 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio)
struct scatterlist *sg;
struct bio_vec *bvec;
int nents = 0;
- int tag = 0;
+ int tag = 0, unaligned = 0;
if (unlikely(dd->dd_flag & MTIP_DDF_STOP_IO)) {
if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
@@ -3872,7 +3900,15 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio)
return;
}
- sg = mtip_hw_get_scatterlist(dd, &tag);
+ if (bio_data_dir(bio) == WRITE && bio_sectors(bio) <= 64 &&
+ dd->unal_qdepth) {
+ if (bio->bi_sector % 8 != 0) /* Unaligned on 4k boundaries */
+ unaligned = 1;
+ else if (bio_sectors(bio) % 8 != 0) /* Aligned but not 4k/8k */
+ unaligned = 1;
+ }
+
+ sg = mtip_hw_get_scatterlist(dd, &tag, unaligned);
if (likely(sg != NULL)) {
blk_queue_bounce(queue, &bio);
@@ -3880,7 +3916,7 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio)
dev_warn(&dd->pdev->dev,
"Maximum number of SGL entries exceeded\n");
bio_io_error(bio);
- mtip_hw_release_scatterlist(dd, tag);
+ mtip_hw_release_scatterlist(dd, tag, unaligned);
return;
}
@@ -3900,7 +3936,8 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio)
tag,
bio_endio,
bio,
- bio_data_dir(bio));
+ bio_data_dir(bio),
+ unaligned);
} else
bio_io_error(bio);
}
@@ -4156,26 +4193,24 @@ static int mtip_block_remove(struct driver_data *dd)
*/
static int mtip_block_shutdown(struct driver_data *dd)
{
- dev_info(&dd->pdev->dev,
- "Shutting down %s ...\n", dd->disk->disk_name);
-
/* Delete our gendisk structure, and cleanup the blk queue. */
if (dd->disk) {
- if (dd->disk->queue)
+ dev_info(&dd->pdev->dev,
+ "Shutting down %s ...\n", dd->disk->disk_name);
+
+ if (dd->disk->queue) {
del_gendisk(dd->disk);
- else
+ blk_cleanup_queue(dd->queue);
+ } else
put_disk(dd->disk);
+ dd->disk = NULL;
+ dd->queue = NULL;
}
-
spin_lock(&rssd_index_lock);
ida_remove(&rssd_index_ida, dd->index);
spin_unlock(&rssd_index_lock);
- blk_cleanup_queue(dd->queue);
- dd->disk = NULL;
- dd->queue = NULL;
-
mtip_hw_shutdown(dd);
return 0;
}
diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h
index 8e8334c9dd0f..3bb8a295fbe4 100644
--- a/drivers/block/mtip32xx/mtip32xx.h
+++ b/drivers/block/mtip32xx/mtip32xx.h
@@ -52,6 +52,9 @@
#define MTIP_FTL_REBUILD_MAGIC 0xED51
#define MTIP_FTL_REBUILD_TIMEOUT_MS 2400000
+/* unaligned IO handling */
+#define MTIP_MAX_UNALIGNED_SLOTS 8
+
/* Macro to extract the tag bit number from a tag value. */
#define MTIP_TAG_BIT(tag) (tag & 0x1F)
@@ -333,6 +336,8 @@ struct mtip_cmd {
int scatter_ents; /* Number of scatter list entries used */
+ int unaligned; /* command is unaligned on 4k boundary */
+
struct scatterlist sg[MTIP_MAX_SG]; /* Scatter list entries */
int retries; /* The number of retries left for this command. */
@@ -452,6 +457,10 @@ struct mtip_port {
* command slots available.
*/
struct semaphore cmd_slot;
+
+ /* Semaphore to control queue depth of unaligned IOs */
+ struct semaphore cmd_slot_unal;
+
/* Spinlock for working around command-issue bug. */
spinlock_t cmd_issue_lock[MTIP_MAX_SLOT_GROUPS];
};
@@ -502,6 +511,8 @@ struct driver_data {
int isr_binding;
+ int unal_qdepth; /* qdepth of unaligned IO queue */
+
struct list_head online_list; /* linkage for online list */
struct list_head remove_list; /* linkage for removing list */
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index ba2b6b5e5910..e76bdc074dbe 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -236,13 +236,12 @@ static int pcd_block_open(struct block_device *bdev, fmode_t mode)
return ret;
}
-static int pcd_block_release(struct gendisk *disk, fmode_t mode)
+static void pcd_block_release(struct gendisk *disk, fmode_t mode)
{
struct pcd_unit *cd = disk->private_data;
mutex_lock(&pcd_mutex);
cdrom_release(&cd->info, mode);
mutex_unlock(&pcd_mutex);
- return 0;
}
static int pcd_block_ioctl(struct block_device *bdev, fmode_t mode,
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index 831e3ac156e6..19ad8f0c83ef 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -783,7 +783,7 @@ static int pd_ioctl(struct block_device *bdev, fmode_t mode,
}
}
-static int pd_release(struct gendisk *p, fmode_t mode)
+static void pd_release(struct gendisk *p, fmode_t mode)
{
struct pd_unit *disk = p->private_data;
@@ -791,8 +791,6 @@ static int pd_release(struct gendisk *p, fmode_t mode)
if (!--disk->access && disk->removable)
pd_special_command(disk, pd_door_unlock);
mutex_unlock(&pd_mutex);
-
- return 0;
}
static unsigned int pd_check_events(struct gendisk *p, unsigned int clearing)
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
index ec8f9ed6326e..f5c86d523ba0 100644
--- a/drivers/block/paride/pf.c
+++ b/drivers/block/paride/pf.c
@@ -211,7 +211,7 @@ static int pf_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg);
static int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo);
-static int pf_release(struct gendisk *disk, fmode_t mode);
+static void pf_release(struct gendisk *disk, fmode_t mode);
static int pf_detect(void);
static void do_pf_read(void);
@@ -360,14 +360,15 @@ static int pf_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, u
return 0;
}
-static int pf_release(struct gendisk *disk, fmode_t mode)
+static void pf_release(struct gendisk *disk, fmode_t mode)
{
struct pf_unit *pf = disk->private_data;
mutex_lock(&pf_mutex);
if (pf->access <= 0) {
mutex_unlock(&pf_mutex);
- return -EINVAL;
+ WARN_ON(1);
+ return;
}
pf->access--;
@@ -376,8 +377,6 @@ static int pf_release(struct gendisk *disk, fmode_t mode)
pf_lock(pf, 0);
mutex_unlock(&pf_mutex);
- return 0;
-
}
static unsigned int pf_check_events(struct gendisk *disk, unsigned int clearing)
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index e0588c6dd86f..3c08983e600a 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -901,7 +901,7 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
pd->iosched.successive_reads += bio->bi_size >> 10;
else {
pd->iosched.successive_reads = 0;
- pd->iosched.last_write = bio->bi_sector + bio_sectors(bio);
+ pd->iosched.last_write = bio_end_sector(bio);
}
if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) {
if (pd->read_speed == pd->write_speed) {
@@ -948,31 +948,6 @@ static int pkt_set_segment_merging(struct pktcdvd_device *pd, struct request_que
}
/*
- * Copy CD_FRAMESIZE bytes from src_bio into a destination page
- */
-static void pkt_copy_bio_data(struct bio *src_bio, int seg, int offs, struct page *dst_page, int dst_offs)
-{
- unsigned int copy_size = CD_FRAMESIZE;
-
- while (copy_size > 0) {
- struct bio_vec *src_bvl = bio_iovec_idx(src_bio, seg);
- void *vfrom = kmap_atomic(src_bvl->bv_page) +
- src_bvl->bv_offset + offs;
- void *vto = page_address(dst_page) + dst_offs;
- int len = min_t(int, copy_size, src_bvl->bv_len - offs);
-
- BUG_ON(len < 0);
- memcpy(vto, vfrom, len);
- kunmap_atomic(vfrom);
-
- seg++;
- offs = 0;
- dst_offs += len;
- copy_size -= len;
- }
-}
-
-/*
* Copy all data for this packet to pkt->pages[], so that
* a) The number of required segments for the write bio is minimized, which
* is necessary for some scsi controllers.
@@ -1181,16 +1156,15 @@ static int pkt_start_recovery(struct packet_data *pkt)
new_sector = new_block * (CD_FRAMESIZE >> 9);
pkt->sector = new_sector;
+ bio_reset(pkt->bio);
+ pkt->bio->bi_bdev = pd->bdev;
+ pkt->bio->bi_rw = REQ_WRITE;
pkt->bio->bi_sector = new_sector;
- pkt->bio->bi_next = NULL;
- pkt->bio->bi_flags = 1 << BIO_UPTODATE;
- pkt->bio->bi_idx = 0;
+ pkt->bio->bi_size = pkt->frames * CD_FRAMESIZE;
+ pkt->bio->bi_vcnt = pkt->frames;
- BUG_ON(pkt->bio->bi_rw != REQ_WRITE);
- BUG_ON(pkt->bio->bi_vcnt != pkt->frames);
- BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE);
- BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write);
- BUG_ON(pkt->bio->bi_private != pkt);
+ pkt->bio->bi_end_io = pkt_end_io_packet_write;
+ pkt->bio->bi_private = pkt;
drop_super(sb);
return 1;
@@ -1325,55 +1299,35 @@ try_next_bio:
*/
static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
{
- struct bio *bio;
int f;
- int frames_write;
struct bio_vec *bvec = pkt->w_bio->bi_io_vec;
+ bio_reset(pkt->w_bio);
+ pkt->w_bio->bi_sector = pkt->sector;
+ pkt->w_bio->bi_bdev = pd->bdev;
+ pkt->w_bio->bi_end_io = pkt_end_io_packet_write;
+ pkt->w_bio->bi_private = pkt;
+
+ /* XXX: locking? */
for (f = 0; f < pkt->frames; f++) {
bvec[f].bv_page = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];
bvec[f].bv_offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
+ if (!bio_add_page(pkt->w_bio, bvec[f].bv_page, CD_FRAMESIZE, bvec[f].bv_offset))
+ BUG();
}
+ VPRINTK(DRIVER_NAME": vcnt=%d\n", pkt->w_bio->bi_vcnt);
/*
* Fill-in bvec with data from orig_bios.
*/
- frames_write = 0;
spin_lock(&pkt->lock);
- bio_list_for_each(bio, &pkt->orig_bios) {
- int segment = bio->bi_idx;
- int src_offs = 0;
- int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);
- int num_frames = bio->bi_size / CD_FRAMESIZE;
- BUG_ON(first_frame < 0);
- BUG_ON(first_frame + num_frames > pkt->frames);
- for (f = first_frame; f < first_frame + num_frames; f++) {
- struct bio_vec *src_bvl = bio_iovec_idx(bio, segment);
-
- while (src_offs >= src_bvl->bv_len) {
- src_offs -= src_bvl->bv_len;
- segment++;
- BUG_ON(segment >= bio->bi_vcnt);
- src_bvl = bio_iovec_idx(bio, segment);
- }
+ bio_copy_data(pkt->w_bio, pkt->orig_bios.head);
- if (src_bvl->bv_len - src_offs >= CD_FRAMESIZE) {
- bvec[f].bv_page = src_bvl->bv_page;
- bvec[f].bv_offset = src_bvl->bv_offset + src_offs;
- } else {
- pkt_copy_bio_data(bio, segment, src_offs,
- bvec[f].bv_page, bvec[f].bv_offset);
- }
- src_offs += CD_FRAMESIZE;
- frames_write++;
- }
- }
pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE);
spin_unlock(&pkt->lock);
VPRINTK("pkt_start_write: Writing %d frames for zone %llx\n",
- frames_write, (unsigned long long)pkt->sector);
- BUG_ON(frames_write != pkt->write_size);
+ pkt->write_size, (unsigned long long)pkt->sector);
if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames)) {
pkt_make_local_copy(pkt, bvec);
@@ -1383,16 +1337,6 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
}
/* Start the write request */
- bio_reset(pkt->w_bio);
- pkt->w_bio->bi_sector = pkt->sector;
- pkt->w_bio->bi_bdev = pd->bdev;
- pkt->w_bio->bi_end_io = pkt_end_io_packet_write;
- pkt->w_bio->bi_private = pkt;
- for (f = 0; f < pkt->frames; f++)
- if (!bio_add_page(pkt->w_bio, bvec[f].bv_page, CD_FRAMESIZE, bvec[f].bv_offset))
- BUG();
- VPRINTK(DRIVER_NAME": vcnt=%d\n", pkt->w_bio->bi_vcnt);
-
atomic_set(&pkt->io_wait, 1);
pkt->w_bio->bi_rw = WRITE;
pkt_queue_bio(pd, pkt->w_bio);
@@ -2376,10 +2320,9 @@ out:
return ret;
}
-static int pkt_close(struct gendisk *disk, fmode_t mode)
+static void pkt_close(struct gendisk *disk, fmode_t mode)
{
struct pktcdvd_device *pd = disk->private_data;
- int ret = 0;
mutex_lock(&pktcdvd_mutex);
mutex_lock(&ctl_mutex);
@@ -2391,7 +2334,6 @@ static int pkt_close(struct gendisk *disk, fmode_t mode)
}
mutex_unlock(&ctl_mutex);
mutex_unlock(&pktcdvd_mutex);
- return ret;
}
@@ -2433,7 +2375,7 @@ static void pkt_make_request(struct request_queue *q, struct bio *bio)
cloned_bio->bi_bdev = pd->bdev;
cloned_bio->bi_private = psd;
cloned_bio->bi_end_io = pkt_end_io_read_cloned;
- pd->stats.secs_r += bio->bi_size >> 9;
+ pd->stats.secs_r += bio_sectors(bio);
pkt_queue_bio(pd, cloned_bio);
return;
}
@@ -2454,7 +2396,7 @@ static void pkt_make_request(struct request_queue *q, struct bio *bio)
zone = ZONE(bio->bi_sector, pd);
VPRINTK("pkt_make_request: start = %6llx stop = %6llx\n",
(unsigned long long)bio->bi_sector,
- (unsigned long long)(bio->bi_sector + bio_sectors(bio)));
+ (unsigned long long)bio_end_sector(bio));
/* Check if we have to split the bio */
{
@@ -2462,7 +2404,7 @@ static void pkt_make_request(struct request_queue *q, struct bio *bio)
sector_t last_zone;
int first_sectors;
- last_zone = ZONE(bio->bi_sector + bio_sectors(bio) - 1, pd);
+ last_zone = ZONE(bio_end_sector(bio) - 1, pd);
if (last_zone != zone) {
BUG_ON(last_zone != zone + pd->settings.size);
first_sectors = last_zone - bio->bi_sector;
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index c2ca1818f335..ca63104136e0 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -460,7 +460,7 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
return 0;
}
-static int rbd_release(struct gendisk *disk, fmode_t mode)
+static void rbd_release(struct gendisk *disk, fmode_t mode)
{
struct rbd_device *rbd_dev = disk->private_data;
unsigned long open_count_before;
@@ -473,8 +473,6 @@ static int rbd_release(struct gendisk *disk, fmode_t mode)
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
put_device(&rbd_dev->dev);
mutex_unlock(&ctl_mutex);
-
- return 0;
}
static const struct block_device_operations rbd_bd_ops = {
@@ -1145,7 +1143,7 @@ static struct bio *bio_clone_range(struct bio *bio_src,
/* Find first affected segment... */
resid = offset;
- __bio_for_each_segment(bv, bio_src, idx, 0) {
+ bio_for_each_segment(bv, bio_src, idx) {
if (resid < bv->bv_len)
break;
resid -= bv->bv_len;
diff --git a/drivers/block/swim.c b/drivers/block/swim.c
index 8766a2257091..2f445b7a174e 100644
--- a/drivers/block/swim.c
+++ b/drivers/block/swim.c
@@ -673,7 +673,7 @@ static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
return ret;
}
-static int floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk, fmode_t mode)
{
struct floppy_state *fs = disk->private_data;
struct swim __iomem *base = fs->swd->base;
@@ -687,8 +687,6 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
if (fs->ref_count == 0)
swim_motor(base, OFF);
mutex_unlock(&swim_mutex);
-
- return 0;
}
static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index 758f2ac878cf..20e061c3e023 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -251,7 +251,7 @@ static int fd_eject(struct floppy_state *fs);
static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long param);
static int floppy_open(struct block_device *bdev, fmode_t mode);
-static int floppy_release(struct gendisk *disk, fmode_t mode);
+static void floppy_release(struct gendisk *disk, fmode_t mode);
static unsigned int floppy_check_events(struct gendisk *disk,
unsigned int clearing);
static int floppy_revalidate(struct gendisk *disk);
@@ -1017,7 +1017,7 @@ static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
return ret;
}
-static int floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk, fmode_t mode)
{
struct floppy_state *fs = disk->private_data;
struct swim3 __iomem *sw = fs->swim3;
@@ -1029,7 +1029,6 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
swim3_select(fs, RELAX);
}
mutex_unlock(&swim3_mutex);
- return 0;
}
static unsigned int floppy_check_events(struct gendisk *disk,
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index a894f88762d8..d89ef86220f4 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -1617,7 +1617,7 @@ out:
return err;
}
-static int blkif_release(struct gendisk *disk, fmode_t mode)
+static void blkif_release(struct gendisk *disk, fmode_t mode)
{
struct blkfront_info *info = disk->private_data;
struct block_device *bdev;
@@ -1658,7 +1658,6 @@ static int blkif_release(struct gendisk *disk, fmode_t mode)
out:
bdput(bdev);
mutex_unlock(&blkfront_mutex);
- return 0;
}
static const struct block_device_operations xlvbd_block_fops =
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
index 1f38643173ca..f8ef15f37c5e 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -915,7 +915,7 @@ static int ace_open(struct block_device *bdev, fmode_t mode)
return 0;
}
-static int ace_release(struct gendisk *disk, fmode_t mode)
+static void ace_release(struct gendisk *disk, fmode_t mode)
{
struct ace_device *ace = disk->private_data;
unsigned long flags;
@@ -932,7 +932,6 @@ static int ace_release(struct gendisk *disk, fmode_t mode)
}
spin_unlock_irqrestore(&ace->lock, flags);
mutex_unlock(&xsysace_mutex);
- return 0;
}
static int ace_getgeo(struct block_device *bdev, struct hd_geometry *geo)
diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c
index a22e3f895947..5a95baf4b104 100644
--- a/drivers/block/z2ram.c
+++ b/drivers/block/z2ram.c
@@ -309,20 +309,18 @@ err_out:
return rc;
}
-static int
+static void
z2_release(struct gendisk *disk, fmode_t mode)
{
mutex_lock(&z2ram_mutex);
if ( current_device == -1 ) {
mutex_unlock(&z2ram_mutex);
- return 0;
+ return;
}
mutex_unlock(&z2ram_mutex);
/*
* FIXME: unmap memory
*/
-
- return 0;
}
static const struct block_device_operations z2_fops =
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 0f51ed687dc8..b05ecab915c4 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -4,6 +4,13 @@
menu "Bus devices"
+config MVEBU_MBUS
+ bool
+ depends on PLAT_ORION
+ help
+ Driver needed for the MBus configuration on Marvell EBU SoCs
+ (Kirkwood, Dove, Orion5x, MV78XX0 and Armada 370/XP).
+
config OMAP_OCP2SCP
tristate "OMAP OCP2SCP DRIVER"
depends on ARCH_OMAP2PLUS
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 45d997c85453..3c7b53c12091 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -2,6 +2,7 @@
# Makefile for the bus drivers.
#
+obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
# Interconnect bus driver for OMAP SoCs.
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
new file mode 100644
index 000000000000..8740f46b4d0d
--- /dev/null
+++ b/drivers/bus/mvebu-mbus.c
@@ -0,0 +1,870 @@
+/*
+ * Address map functions for Marvell EBU SoCs (Kirkwood, Armada
+ * 370/XP, Dove, Orion5x and MV78xx0)
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * The Marvell EBU SoCs have a configurable physical address space:
+ * the physical address at which certain devices (PCIe, NOR, NAND,
+ * etc.) sit can be configured. The configuration takes place through
+ * two sets of registers:
+ *
+ * - One to configure the access of the CPU to the devices. Depending
+ * on the families, there are between 8 and 20 configurable windows,
+ * each can be use to create a physical memory window that maps to a
+ * specific device. Devices are identified by a tuple (target,
+ * attribute).
+ *
+ * - One to configure the access to the CPU to the SDRAM. There are
+ * either 2 (for Dove) or 4 (for other families) windows to map the
+ * SDRAM into the physical address space.
+ *
+ * This driver:
+ *
+ * - Reads out the SDRAM address decoding windows at initialization
+ * time, and fills the mvebu_mbus_dram_info structure with these
+ * informations. The exported function mv_mbus_dram_info() allow
+ * device drivers to get those informations related to the SDRAM
+ * address decoding windows. This is because devices also have their
+ * own windows (configured through registers that are part of each
+ * device register space), and therefore the drivers for Marvell
+ * devices have to configure those device -> SDRAM windows to ensure
+ * that DMA works properly.
+ *
+ * - Provides an API for platform code or device drivers to
+ * dynamically add or remove address decoding windows for the CPU ->
+ * device accesses. This API is mvebu_mbus_add_window(),
+ * mvebu_mbus_add_window_remap_flags() and
+ * mvebu_mbus_del_window(). Since the (target, attribute) values
+ * differ from one SoC family to another, the API uses a 'const char
+ * *' string to identify devices, and this driver is responsible for
+ * knowing the mapping between the name of a device and its
+ * corresponding (target, attribute) in the current SoC family.
+ *
+ * - Provides a debugfs interface in /sys/kernel/debug/mvebu-mbus/ to
+ * see the list of CPU -> SDRAM windows and their configuration
+ * (file 'sdram') and the list of CPU -> devices windows and their
+ * configuration (file 'devices').
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mbus.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/debugfs.h>
+
+/*
+ * DDR target is the same on all platforms.
+ */
+#define TARGET_DDR 0
+
+/*
+ * CPU Address Decode Windows registers
+ */
+#define WIN_CTRL_OFF 0x0000
+#define WIN_CTRL_ENABLE BIT(0)
+#define WIN_CTRL_TGT_MASK 0xf0
+#define WIN_CTRL_TGT_SHIFT 4
+#define WIN_CTRL_ATTR_MASK 0xff00
+#define WIN_CTRL_ATTR_SHIFT 8
+#define WIN_CTRL_SIZE_MASK 0xffff0000
+#define WIN_CTRL_SIZE_SHIFT 16
+#define WIN_BASE_OFF 0x0004
+#define WIN_BASE_LOW 0xffff0000
+#define WIN_BASE_HIGH 0xf
+#define WIN_REMAP_LO_OFF 0x0008
+#define WIN_REMAP_LOW 0xffff0000
+#define WIN_REMAP_HI_OFF 0x000c
+
+#define ATTR_HW_COHERENCY (0x1 << 4)
+
+#define DDR_BASE_CS_OFF(n) (0x0000 + ((n) << 3))
+#define DDR_BASE_CS_HIGH_MASK 0xf
+#define DDR_BASE_CS_LOW_MASK 0xff000000
+#define DDR_SIZE_CS_OFF(n) (0x0004 + ((n) << 3))
+#define DDR_SIZE_ENABLED BIT(0)
+#define DDR_SIZE_CS_MASK 0x1c
+#define DDR_SIZE_CS_SHIFT 2
+#define DDR_SIZE_MASK 0xff000000
+
+#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4)
+
+struct mvebu_mbus_mapping {
+ const char *name;
+ u8 target;
+ u8 attr;
+ u8 attrmask;
+};
+
+/*
+ * Masks used for the 'attrmask' field of mvebu_mbus_mapping. They
+ * allow to get the real attribute value, discarding the special bits
+ * used to select a PCI MEM region or a PCI WA region. This allows the
+ * debugfs code to reverse-match the name of a device from its
+ * target/attr values.
+ *
+ * For all devices except PCI, all bits of 'attr' must be
+ * considered. For most SoCs, only bit 3 should be ignored (it allows
+ * to select between PCI MEM and PCI I/O). On Orion5x however, there
+ * is the special bit 5 to select a PCI WA region.
+ */
+#define MAPDEF_NOMASK 0xff
+#define MAPDEF_PCIMASK 0xf7
+#define MAPDEF_ORIONPCIMASK 0xd7
+
+/* Macro used to define one mvebu_mbus_mapping entry */
+#define MAPDEF(__n, __t, __a, __m) \
+ { .name = __n, .target = __t, .attr = __a, .attrmask = __m }
+
+struct mvebu_mbus_state;
+
+struct mvebu_mbus_soc_data {
+ unsigned int num_wins;
+ unsigned int num_remappable_wins;
+ unsigned int (*win_cfg_offset)(const int win);
+ void (*setup_cpu_target)(struct mvebu_mbus_state *s);
+ int (*show_cpu_target)(struct mvebu_mbus_state *s,
+ struct seq_file *seq, void *v);
+ const struct mvebu_mbus_mapping *map;
+};
+
+struct mvebu_mbus_state {
+ void __iomem *mbuswins_base;
+ void __iomem *sdramwins_base;
+ struct dentry *debugfs_root;
+ struct dentry *debugfs_sdram;
+ struct dentry *debugfs_devs;
+ const struct mvebu_mbus_soc_data *soc;
+ int hw_io_coherency;
+};
+
+static struct mvebu_mbus_state mbus_state;
+
+static struct mbus_dram_target_info mvebu_mbus_dram_info;
+const struct mbus_dram_target_info *mv_mbus_dram_info(void)
+{
+ return &mvebu_mbus_dram_info;
+}
+EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
+
+/*
+ * Functions to manipulate the address decoding windows
+ */
+
+static void mvebu_mbus_read_window(struct mvebu_mbus_state *mbus,
+ int win, int *enabled, u64 *base,
+ u32 *size, u8 *target, u8 *attr,
+ u64 *remap)
+{
+ void __iomem *addr = mbus->mbuswins_base +
+ mbus->soc->win_cfg_offset(win);
+ u32 basereg = readl(addr + WIN_BASE_OFF);
+ u32 ctrlreg = readl(addr + WIN_CTRL_OFF);
+
+ if (!(ctrlreg & WIN_CTRL_ENABLE)) {
+ *enabled = 0;
+ return;
+ }
+
+ *enabled = 1;
+ *base = ((u64)basereg & WIN_BASE_HIGH) << 32;
+ *base |= (basereg & WIN_BASE_LOW);
+ *size = (ctrlreg | ~WIN_CTRL_SIZE_MASK) + 1;
+
+ if (target)
+ *target = (ctrlreg & WIN_CTRL_TGT_MASK) >> WIN_CTRL_TGT_SHIFT;
+
+ if (attr)
+ *attr = (ctrlreg & WIN_CTRL_ATTR_MASK) >> WIN_CTRL_ATTR_SHIFT;
+
+ if (remap) {
+ if (win < mbus->soc->num_remappable_wins) {
+ u32 remap_low = readl(addr + WIN_REMAP_LO_OFF);
+ u32 remap_hi = readl(addr + WIN_REMAP_HI_OFF);
+ *remap = ((u64)remap_hi << 32) | remap_low;
+ } else
+ *remap = 0;
+ }
+}
+
+static void mvebu_mbus_disable_window(struct mvebu_mbus_state *mbus,
+ int win)
+{
+ void __iomem *addr;
+
+ addr = mbus->mbuswins_base + mbus->soc->win_cfg_offset(win);
+
+ writel(0, addr + WIN_BASE_OFF);
+ writel(0, addr + WIN_CTRL_OFF);
+ if (win < mbus->soc->num_remappable_wins) {
+ writel(0, addr + WIN_REMAP_LO_OFF);
+ writel(0, addr + WIN_REMAP_HI_OFF);
+ }
+}
+
+/* Checks whether the given window number is available */
+static int mvebu_mbus_window_is_free(struct mvebu_mbus_state *mbus,
+ const int win)
+{
+ void __iomem *addr = mbus->mbuswins_base +
+ mbus->soc->win_cfg_offset(win);
+ u32 ctrl = readl(addr + WIN_CTRL_OFF);
+ return !(ctrl & WIN_CTRL_ENABLE);
+}
+
+/*
+ * Checks whether the given (base, base+size) area doesn't overlap an
+ * existing region
+ */
+static int mvebu_mbus_window_conflicts(struct mvebu_mbus_state *mbus,
+ phys_addr_t base, size_t size,
+ u8 target, u8 attr)
+{
+ u64 end = (u64)base + size;
+ int win;
+
+ for (win = 0; win < mbus->soc->num_wins; win++) {
+ u64 wbase, wend;
+ u32 wsize;
+ u8 wtarget, wattr;
+ int enabled;
+
+ mvebu_mbus_read_window(mbus, win,
+ &enabled, &wbase, &wsize,
+ &wtarget, &wattr, NULL);
+
+ if (!enabled)
+ continue;
+
+ wend = wbase + wsize;
+
+ /*
+ * Check if the current window overlaps with the
+ * proposed physical range
+ */
+ if ((u64)base < wend && end > wbase)
+ return 0;
+
+ /*
+ * Check if target/attribute conflicts
+ */
+ if (target == wtarget && attr == wattr)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int mvebu_mbus_find_window(struct mvebu_mbus_state *mbus,
+ phys_addr_t base, size_t size)
+{
+ int win;
+
+ for (win = 0; win < mbus->soc->num_wins; win++) {
+ u64 wbase;
+ u32 wsize;
+ int enabled;
+
+ mvebu_mbus_read_window(mbus, win,
+ &enabled, &wbase, &wsize,
+ NULL, NULL, NULL);
+
+ if (!enabled)
+ continue;
+
+ if (base == wbase && size == wsize)
+ return win;
+ }
+
+ return -ENODEV;
+}
+
+static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus,
+ int win, phys_addr_t base, size_t size,
+ phys_addr_t remap, u8 target,
+ u8 attr)
+{
+ void __iomem *addr = mbus->mbuswins_base +
+ mbus->soc->win_cfg_offset(win);
+ u32 ctrl, remap_addr;
+
+ ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) |
+ (attr << WIN_CTRL_ATTR_SHIFT) |
+ (target << WIN_CTRL_TGT_SHIFT) |
+ WIN_CTRL_ENABLE;
+
+ writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF);
+ writel(ctrl, addr + WIN_CTRL_OFF);
+ if (win < mbus->soc->num_remappable_wins) {
+ if (remap == MVEBU_MBUS_NO_REMAP)
+ remap_addr = base;
+ else
+ remap_addr = remap;
+ writel(remap_addr & WIN_REMAP_LOW, addr + WIN_REMAP_LO_OFF);
+ writel(0, addr + WIN_REMAP_HI_OFF);
+ }
+
+ return 0;
+}
+
+static int mvebu_mbus_alloc_window(struct mvebu_mbus_state *mbus,
+ phys_addr_t base, size_t size,
+ phys_addr_t remap, u8 target,
+ u8 attr)
+{
+ int win;
+
+ if (remap == MVEBU_MBUS_NO_REMAP) {
+ for (win = mbus->soc->num_remappable_wins;
+ win < mbus->soc->num_wins; win++)
+ if (mvebu_mbus_window_is_free(mbus, win))
+ return mvebu_mbus_setup_window(mbus, win, base,
+ size, remap,
+ target, attr);
+ }
+
+
+ for (win = 0; win < mbus->soc->num_wins; win++)
+ if (mvebu_mbus_window_is_free(mbus, win))
+ return mvebu_mbus_setup_window(mbus, win, base, size,
+ remap, target, attr);
+
+ return -ENOMEM;
+}
+
+/*
+ * Debugfs debugging
+ */
+
+/* Common function used for Dove, Kirkwood, Armada 370/XP and Orion 5x */
+static int mvebu_sdram_debug_show_orion(struct mvebu_mbus_state *mbus,
+ struct seq_file *seq, void *v)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ u32 basereg = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
+ u32 sizereg = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
+ u64 base;
+ u32 size;
+
+ if (!(sizereg & DDR_SIZE_ENABLED)) {
+ seq_printf(seq, "[%d] disabled\n", i);
+ continue;
+ }
+
+ base = ((u64)basereg & DDR_BASE_CS_HIGH_MASK) << 32;
+ base |= basereg & DDR_BASE_CS_LOW_MASK;
+ size = (sizereg | ~DDR_SIZE_MASK);
+
+ seq_printf(seq, "[%d] %016llx - %016llx : cs%d\n",
+ i, (unsigned long long)base,
+ (unsigned long long)base + size + 1,
+ (sizereg & DDR_SIZE_CS_MASK) >> DDR_SIZE_CS_SHIFT);
+ }
+
+ return 0;
+}
+
+/* Special function for Dove */
+static int mvebu_sdram_debug_show_dove(struct mvebu_mbus_state *mbus,
+ struct seq_file *seq, void *v)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i));
+ u64 base;
+ u32 size;
+
+ if (!(map & 1)) {
+ seq_printf(seq, "[%d] disabled\n", i);
+ continue;
+ }
+
+ base = map & 0xff800000;
+ size = 0x100000 << (((map & 0x000f0000) >> 16) - 4);
+
+ seq_printf(seq, "[%d] %016llx - %016llx : cs%d\n",
+ i, (unsigned long long)base,
+ (unsigned long long)base + size, i);
+ }
+
+ return 0;
+}
+
+static int mvebu_sdram_debug_show(struct seq_file *seq, void *v)
+{
+ struct mvebu_mbus_state *mbus = &mbus_state;
+ return mbus->soc->show_cpu_target(mbus, seq, v);
+}
+
+static int mvebu_sdram_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mvebu_sdram_debug_show, inode->i_private);
+}
+
+static const struct file_operations mvebu_sdram_debug_fops = {
+ .open = mvebu_sdram_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
+{
+ struct mvebu_mbus_state *mbus = &mbus_state;
+ int win;
+
+ for (win = 0; win < mbus->soc->num_wins; win++) {
+ u64 wbase, wremap;
+ u32 wsize;
+ u8 wtarget, wattr;
+ int enabled, i;
+ const char *name;
+
+ mvebu_mbus_read_window(mbus, win,
+ &enabled, &wbase, &wsize,
+ &wtarget, &wattr, &wremap);
+
+ if (!enabled) {
+ seq_printf(seq, "[%02d] disabled\n", win);
+ continue;
+ }
+
+
+ for (i = 0; mbus->soc->map[i].name; i++)
+ if (mbus->soc->map[i].target == wtarget &&
+ mbus->soc->map[i].attr ==
+ (wattr & mbus->soc->map[i].attrmask))
+ break;
+
+ name = mbus->soc->map[i].name ?: "unknown";
+
+ seq_printf(seq, "[%02d] %016llx - %016llx : %s",
+ win, (unsigned long long)wbase,
+ (unsigned long long)(wbase + wsize), name);
+
+ if (win < mbus->soc->num_remappable_wins) {
+ seq_printf(seq, " (remap %016llx)\n",
+ (unsigned long long)wremap);
+ } else
+ seq_printf(seq, "\n");
+ }
+
+ return 0;
+}
+
+static int mvebu_devs_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mvebu_devs_debug_show, inode->i_private);
+}
+
+static const struct file_operations mvebu_devs_debug_fops = {
+ .open = mvebu_devs_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/*
+ * SoC-specific functions and definitions
+ */
+
+static unsigned int orion_mbus_win_offset(int win)
+{
+ return win << 4;
+}
+
+static unsigned int armada_370_xp_mbus_win_offset(int win)
+{
+ /* The register layout is a bit annoying and the below code
+ * tries to cope with it.
+ * - At offset 0x0, there are the registers for the first 8
+ * windows, with 4 registers of 32 bits per window (ctrl,
+ * base, remap low, remap high)
+ * - Then at offset 0x80, there is a hole of 0x10 bytes for
+ * the internal registers base address and internal units
+ * sync barrier register.
+ * - Then at offset 0x90, there the registers for 12
+ * windows, with only 2 registers of 32 bits per window
+ * (ctrl, base).
+ */
+ if (win < 8)
+ return win << 4;
+ else
+ return 0x90 + ((win - 8) << 3);
+}
+
+static unsigned int mv78xx0_mbus_win_offset(int win)
+{
+ if (win < 8)
+ return win << 4;
+ else
+ return 0x900 + ((win - 8) << 4);
+}
+
+static void __init
+mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
+{
+ int i;
+ int cs;
+
+ mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
+
+ for (i = 0, cs = 0; i < 4; i++) {
+ u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
+ u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
+
+ /*
+ * We only take care of entries for which the chip
+ * select is enabled, and that don't have high base
+ * address bits set (devices can only access the first
+ * 32 bits of the memory).
+ */
+ if ((size & DDR_SIZE_ENABLED) &&
+ !(base & DDR_BASE_CS_HIGH_MASK)) {
+ struct mbus_dram_window *w;
+
+ w = &mvebu_mbus_dram_info.cs[cs++];
+ w->cs_index = i;
+ w->mbus_attr = 0xf & ~(1 << i);
+ if (mbus->hw_io_coherency)
+ w->mbus_attr |= ATTR_HW_COHERENCY;
+ w->base = base & DDR_BASE_CS_LOW_MASK;
+ w->size = (size | ~DDR_SIZE_MASK) + 1;
+ }
+ }
+ mvebu_mbus_dram_info.num_cs = cs;
+}
+
+static void __init
+mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
+{
+ int i;
+ int cs;
+
+ mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
+
+ for (i = 0, cs = 0; i < 2; i++) {
+ u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i));
+
+ /*
+ * Chip select enabled?
+ */
+ if (map & 1) {
+ struct mbus_dram_window *w;
+
+ w = &mvebu_mbus_dram_info.cs[cs++];
+ w->cs_index = i;
+ w->mbus_attr = 0; /* CS address decoding done inside */
+ /* the DDR controller, no need to */
+ /* provide attributes */
+ w->base = map & 0xff800000;
+ w->size = 0x100000 << (((map & 0x000f0000) >> 16) - 4);
+ }
+ }
+
+ mvebu_mbus_dram_info.num_cs = cs;
+}
+
+static const struct mvebu_mbus_mapping armada_370_map[] = {
+ MAPDEF("bootrom", 1, 0xe0, MAPDEF_NOMASK),
+ MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK),
+ MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
+ {},
+};
+
+static const struct mvebu_mbus_soc_data armada_370_mbus_data = {
+ .num_wins = 20,
+ .num_remappable_wins = 8,
+ .win_cfg_offset = armada_370_xp_mbus_win_offset,
+ .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
+ .show_cpu_target = mvebu_sdram_debug_show_orion,
+ .map = armada_370_map,
+};
+
+static const struct mvebu_mbus_mapping armada_xp_map[] = {
+ MAPDEF("bootrom", 1, 0x1d, MAPDEF_NOMASK),
+ MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK),
+ MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
+ MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK),
+ MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK),
+ MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK),
+ MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK),
+ MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK),
+ {},
+};
+
+static const struct mvebu_mbus_soc_data armada_xp_mbus_data = {
+ .num_wins = 20,
+ .num_remappable_wins = 8,
+ .win_cfg_offset = armada_370_xp_mbus_win_offset,
+ .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
+ .show_cpu_target = mvebu_sdram_debug_show_orion,
+ .map = armada_xp_map,
+};
+
+static const struct mvebu_mbus_mapping kirkwood_map[] = {
+ MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.0", 4, 0xd0, MAPDEF_PCIMASK),
+ MAPDEF("sram", 3, 0x01, MAPDEF_NOMASK),
+ MAPDEF("nand", 1, 0x2f, MAPDEF_NOMASK),
+ {},
+};
+
+static const struct mvebu_mbus_soc_data kirkwood_mbus_data = {
+ .num_wins = 8,
+ .num_remappable_wins = 4,
+ .win_cfg_offset = orion_mbus_win_offset,
+ .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
+ .show_cpu_target = mvebu_sdram_debug_show_orion,
+ .map = kirkwood_map,
+};
+
+static const struct mvebu_mbus_mapping dove_map[] = {
+ MAPDEF("pcie0.0", 0x4, 0xe0, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.0", 0x8, 0xe0, MAPDEF_PCIMASK),
+ MAPDEF("cesa", 0x3, 0x01, MAPDEF_NOMASK),
+ MAPDEF("bootrom", 0x1, 0xfd, MAPDEF_NOMASK),
+ MAPDEF("scratchpad", 0xd, 0x0, MAPDEF_NOMASK),
+ {},
+};
+
+static const struct mvebu_mbus_soc_data dove_mbus_data = {
+ .num_wins = 8,
+ .num_remappable_wins = 4,
+ .win_cfg_offset = orion_mbus_win_offset,
+ .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target,
+ .show_cpu_target = mvebu_sdram_debug_show_dove,
+ .map = dove_map,
+};
+
+static const struct mvebu_mbus_mapping orion5x_map[] = {
+ MAPDEF("pcie0.0", 4, 0x51, MAPDEF_ORIONPCIMASK),
+ MAPDEF("pci0.0", 3, 0x51, MAPDEF_ORIONPCIMASK),
+ MAPDEF("devbus-boot", 1, 0x0f, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs0", 1, 0x1e, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs1", 1, 0x1d, MAPDEF_NOMASK),
+ MAPDEF("devbus-cs2", 1, 0x1b, MAPDEF_NOMASK),
+ MAPDEF("sram", 0, 0x00, MAPDEF_NOMASK),
+ {},
+};
+
+/*
+ * Some variants of Orion5x have 4 remappable windows, some other have
+ * only two of them.
+ */
+static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = {
+ .num_wins = 8,
+ .num_remappable_wins = 4,
+ .win_cfg_offset = orion_mbus_win_offset,
+ .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
+ .show_cpu_target = mvebu_sdram_debug_show_orion,
+ .map = orion5x_map,
+};
+
+static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = {
+ .num_wins = 8,
+ .num_remappable_wins = 2,
+ .win_cfg_offset = orion_mbus_win_offset,
+ .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
+ .show_cpu_target = mvebu_sdram_debug_show_orion,
+ .map = orion5x_map,
+};
+
+static const struct mvebu_mbus_mapping mv78xx0_map[] = {
+ MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
+ MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK),
+ MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK),
+ MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK),
+ MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK),
+ MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK),
+ MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK),
+ {},
+};
+
+static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
+ .num_wins = 14,
+ .num_remappable_wins = 8,
+ .win_cfg_offset = mv78xx0_mbus_win_offset,
+ .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
+ .show_cpu_target = mvebu_sdram_debug_show_orion,
+ .map = mv78xx0_map,
+};
+
+/*
+ * The driver doesn't yet have a DT binding because the details of
+ * this DT binding still need to be sorted out. However, as a
+ * preparation, we already use of_device_id to match a SoC description
+ * string against the SoC specific details of this driver.
+ */
+static const struct of_device_id of_mvebu_mbus_ids[] = {
+ { .compatible = "marvell,armada370-mbus",
+ .data = &armada_370_mbus_data, },
+ { .compatible = "marvell,armadaxp-mbus",
+ .data = &armada_xp_mbus_data, },
+ { .compatible = "marvell,kirkwood-mbus",
+ .data = &kirkwood_mbus_data, },
+ { .compatible = "marvell,dove-mbus",
+ .data = &dove_mbus_data, },
+ { .compatible = "marvell,orion5x-88f5281-mbus",
+ .data = &orion5x_4win_mbus_data, },
+ { .compatible = "marvell,orion5x-88f5182-mbus",
+ .data = &orion5x_2win_mbus_data, },
+ { .compatible = "marvell,orion5x-88f5181-mbus",
+ .data = &orion5x_2win_mbus_data, },
+ { .compatible = "marvell,orion5x-88f6183-mbus",
+ .data = &orion5x_4win_mbus_data, },
+ { .compatible = "marvell,mv78xx0-mbus",
+ .data = &mv78xx0_mbus_data, },
+ { },
+};
+
+/*
+ * Public API of the driver
+ */
+int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base,
+ size_t size, phys_addr_t remap,
+ unsigned int flags)
+{
+ struct mvebu_mbus_state *s = &mbus_state;
+ u8 target, attr;
+ int i;
+
+ if (!s->soc->map)
+ return -ENODEV;
+
+ for (i = 0; s->soc->map[i].name; i++)
+ if (!strcmp(s->soc->map[i].name, devname))
+ break;
+
+ if (!s->soc->map[i].name) {
+ pr_err("mvebu-mbus: unknown device '%s'\n", devname);
+ return -ENODEV;
+ }
+
+ target = s->soc->map[i].target;
+ attr = s->soc->map[i].attr;
+
+ if (flags == MVEBU_MBUS_PCI_MEM)
+ attr |= 0x8;
+ else if (flags == MVEBU_MBUS_PCI_WA)
+ attr |= 0x28;
+
+ if (!mvebu_mbus_window_conflicts(s, base, size, target, attr)) {
+ pr_err("mvebu-mbus: cannot add window '%s', conflicts with another window\n",
+ devname);
+ return -EINVAL;
+ }
+
+ return mvebu_mbus_alloc_window(s, base, size, remap, target, attr);
+
+}
+
+int mvebu_mbus_add_window(const char *devname, phys_addr_t base, size_t size)
+{
+ return mvebu_mbus_add_window_remap_flags(devname, base, size,
+ MVEBU_MBUS_NO_REMAP, 0);
+}
+
+int mvebu_mbus_del_window(phys_addr_t base, size_t size)
+{
+ int win;
+
+ win = mvebu_mbus_find_window(&mbus_state, base, size);
+ if (win < 0)
+ return win;
+
+ mvebu_mbus_disable_window(&mbus_state, win);
+ return 0;
+}
+
+static __init int mvebu_mbus_debugfs_init(void)
+{
+ struct mvebu_mbus_state *s = &mbus_state;
+
+ /*
+ * If no base has been initialized, doesn't make sense to
+ * register the debugfs entries. We may be on a multiplatform
+ * kernel that isn't running a Marvell EBU SoC.
+ */
+ if (!s->mbuswins_base)
+ return 0;
+
+ s->debugfs_root = debugfs_create_dir("mvebu-mbus", NULL);
+ if (s->debugfs_root) {
+ s->debugfs_sdram = debugfs_create_file("sdram", S_IRUGO,
+ s->debugfs_root, NULL,
+ &mvebu_sdram_debug_fops);
+ s->debugfs_devs = debugfs_create_file("devices", S_IRUGO,
+ s->debugfs_root, NULL,
+ &mvebu_devs_debug_fops);
+ }
+
+ return 0;
+}
+fs_initcall(mvebu_mbus_debugfs_init);
+
+int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
+ size_t mbuswins_size,
+ phys_addr_t sdramwins_phys_base,
+ size_t sdramwins_size)
+{
+ struct mvebu_mbus_state *mbus = &mbus_state;
+ const struct of_device_id *of_id;
+ int win;
+
+ for (of_id = of_mvebu_mbus_ids; of_id->compatible; of_id++)
+ if (!strcmp(of_id->compatible, soc))
+ break;
+
+ if (!of_id->compatible) {
+ pr_err("mvebu-mbus: could not find a matching SoC family\n");
+ return -ENODEV;
+ }
+
+ mbus->soc = of_id->data;
+
+ mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size);
+ if (!mbus->mbuswins_base)
+ return -ENOMEM;
+
+ mbus->sdramwins_base = ioremap(sdramwins_phys_base, sdramwins_size);
+ if (!mbus->sdramwins_base) {
+ iounmap(mbus_state.mbuswins_base);
+ return -ENOMEM;
+ }
+
+ if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"))
+ mbus->hw_io_coherency = 1;
+
+ for (win = 0; win < mbus->soc->num_wins; win++)
+ mvebu_mbus_disable_window(mbus, win);
+
+ mbus->soc->setup_cpu_target(mbus);
+
+ return 0;
+}
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index d59cdcb8fe39..4afcb65cc623 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -503,12 +503,11 @@ static int gdrom_bdops_open(struct block_device *bdev, fmode_t mode)
return ret;
}
-static int gdrom_bdops_release(struct gendisk *disk, fmode_t mode)
+static void gdrom_bdops_release(struct gendisk *disk, fmode_t mode)
{
mutex_lock(&gdrom_mutex);
cdrom_release(gd.cd_info, mode);
mutex_unlock(&gdrom_mutex);
- return 0;
}
static unsigned int gdrom_bdops_check_events(struct gendisk *disk,
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 99fa541bb270..38d3069b7f0a 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -28,6 +28,7 @@
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
+#include <linux/aio.h>
#include <asm/uaccess.h>
@@ -634,6 +635,18 @@ static ssize_t write_null(struct file *file, const char __user *buf,
return count;
}
+static ssize_t aio_read_null(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ return 0;
+}
+
+static ssize_t aio_write_null(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ return iov_length(iov, nr_segs);
+}
+
static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf,
struct splice_desc *sd)
{
@@ -677,6 +690,24 @@ static ssize_t read_zero(struct file *file, char __user *buf,
return written ? written : -EFAULT;
}
+static ssize_t aio_read_zero(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ size_t written = 0;
+ unsigned long i;
+ ssize_t ret;
+
+ for (i = 0; i < nr_segs; i++) {
+ ret = read_zero(iocb->ki_filp, iov[i].iov_base, iov[i].iov_len,
+ &pos);
+ if (ret < 0)
+ break;
+ written += ret;
+ }
+
+ return written ? written : -EFAULT;
+}
+
static int mmap_zero(struct file *file, struct vm_area_struct *vma)
{
#ifndef CONFIG_MMU
@@ -751,6 +782,7 @@ static int open_port(struct inode *inode, struct file *filp)
#define full_lseek null_lseek
#define write_zero write_null
#define read_full read_zero
+#define aio_write_zero aio_write_null
#define open_mem open_port
#define open_kmem open_mem
#define open_oldmem open_mem
@@ -781,6 +813,8 @@ static const struct file_operations null_fops = {
.llseek = null_lseek,
.read = read_null,
.write = write_null,
+ .aio_read = aio_read_null,
+ .aio_write = aio_write_null,
.splice_write = splice_write_null,
};
@@ -797,6 +831,8 @@ static const struct file_operations zero_fops = {
.llseek = zero_lseek,
.read = read_zero,
.write = write_zero,
+ .aio_read = aio_read_zero,
+ .aio_write = aio_write_zero,
.mmap = mmap_zero,
};
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index 71046694d9dd..d0940e69d034 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -16,7 +16,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
-#include <plat/cpu.h>
#include "clk.h"
#include "clk-pll.h"
@@ -910,16 +909,6 @@ struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = {
CLK_IGNORE_UNUSED, 0),
};
-#ifdef CONFIG_OF
-static struct of_device_id exynos4_clk_ids[] __initdata = {
- { .compatible = "samsung,exynos4210-clock",
- .data = (void *)EXYNOS4210, },
- { .compatible = "samsung,exynos4412-clock",
- .data = (void *)EXYNOS4X12, },
- { },
-};
-#endif
-
/*
* The parent of the fin_pll clock is selected by the XOM[0] bit. This bit
* resides in chipid register space, outside of the clock controller memory
@@ -927,33 +916,40 @@ static struct of_device_id exynos4_clk_ids[] __initdata = {
* controller is first remapped and the value of XOM[0] bit is read to
* determine the parent clock.
*/
-static void __init exynos4_clk_register_finpll(void)
+static unsigned long exynos4_get_xom(void)
{
- struct samsung_fixed_rate_clock fclk;
+ unsigned long xom = 0;
+ void __iomem *chipid_base;
struct device_node *np;
- struct clk *clk;
- void __iomem *chipid_base = S5P_VA_CHIPID;
- unsigned long xom, finpll_f = 24000000;
- char *parent_name;
np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid");
- if (np)
+ if (np) {
chipid_base = of_iomap(np, 0);
- if (chipid_base) {
- xom = readl(chipid_base + 8);
- parent_name = xom & 1 ? "xusbxti" : "xxti";
- clk = clk_get(NULL, parent_name);
- if (IS_ERR(clk)) {
- pr_err("%s: failed to lookup parent clock %s, assuming "
- "fin_pll clock frequency is 24MHz\n", __func__,
- parent_name);
- } else {
- finpll_f = clk_get_rate(clk);
- }
+ if (chipid_base)
+ xom = readl(chipid_base + 8);
+
+ iounmap(chipid_base);
+ }
+
+ return xom;
+}
+
+static void __init exynos4_clk_register_finpll(unsigned long xom)
+{
+ struct samsung_fixed_rate_clock fclk;
+ struct clk *clk;
+ unsigned long finpll_f = 24000000;
+ char *parent_name;
+
+ parent_name = xom & 1 ? "xusbxti" : "xxti";
+ clk = clk_get(NULL, parent_name);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to lookup parent clock %s, assuming "
+ "fin_pll clock frequency is 24MHz\n", __func__,
+ parent_name);
} else {
- pr_err("%s: failed to map chipid registers, assuming "
- "fin_pll clock frequency is 24MHz\n", __func__);
+ finpll_f = clk_get_rate(clk);
}
fclk.id = fin_pll;
@@ -963,8 +959,6 @@ static void __init exynos4_clk_register_finpll(void)
fclk.fixed_rate = finpll_f;
samsung_clk_register_fixed_rate(&fclk, 1);
- if (np)
- iounmap(chipid_base);
}
/*
@@ -988,28 +982,14 @@ static __initdata struct of_device_id ext_clk_match[] = {
};
/* register exynos4 clocks */
-void __init exynos4_clk_init(struct device_node *np)
+void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_soc, void __iomem *reg_base, unsigned long xom)
{
- void __iomem *reg_base;
struct clk *apll, *mpll, *epll, *vpll;
- u32 exynos4_soc;
if (np) {
- const struct of_device_id *match;
- match = of_match_node(exynos4_clk_ids, np);
- exynos4_soc = (u32)match->data;
-
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
- } else {
- reg_base = S5P_VA_CMU;
- if (soc_is_exynos4210())
- exynos4_soc = EXYNOS4210;
- else if (soc_is_exynos4212() || soc_is_exynos4412())
- exynos4_soc = EXYNOS4X12;
- else
- panic("%s: unable to determine soc\n", __func__);
}
if (exynos4_soc == EXYNOS4210)
@@ -1026,7 +1006,7 @@ void __init exynos4_clk_init(struct device_node *np)
ARRAY_SIZE(exynos4_fixed_rate_ext_clks),
ext_clk_match);
- exynos4_clk_register_finpll();
+ exynos4_clk_register_finpll(xom);
if (exynos4_soc == EXYNOS4210) {
apll = samsung_clk_register_pll45xx("fout_apll", "fin_pll",
@@ -1087,5 +1067,16 @@ void __init exynos4_clk_init(struct device_node *np)
_get_rate("sclk_epll"), _get_rate("sclk_vpll"),
_get_rate("arm_clk"));
}
-CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4_clk_init);
-CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4_clk_init);
+
+
+static void __init exynos4210_clk_init(struct device_node *np)
+{
+ exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom());
+}
+CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init);
+
+static void __init exynos4412_clk_init(struct device_node *np)
+{
+ exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom());
+}
+CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index bb54606ff035..5c97e75924a8 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -16,7 +16,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
-#include <plat/cpu.h>
#include "clk.h"
#include "clk-pll.h"
diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c
index a0a094c06f19..7d5434167a96 100644
--- a/drivers/clk/samsung/clk-exynos5440.c
+++ b/drivers/clk/samsung/clk-exynos5440.c
@@ -15,7 +15,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
-#include <plat/cpu.h>
#include "clk.h"
#include "clk-pll.h"
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 10b2111f0c0f..e4ad6ea9aa76 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -20,8 +20,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
-#include <mach/map.h>
-
/**
* struct samsung_clock_alias: information about mux clock
* @id: platform specific id of the clock.
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 7bc6e51757ee..f151c6cf27c3 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -65,6 +65,7 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
config ARM_ARCH_TIMER
bool
+ select CLKSRC_OF if OF
config CLKSRC_METAG_GENERIC
def_bool y if METAG
@@ -75,3 +76,12 @@ config CLKSRC_EXYNOS_MCT
def_bool y if ARCH_EXYNOS
help
Support for Multi Core Timer controller on Exynos SoCs.
+
+config CLKSRC_SAMSUNG_PWM
+ bool
+ select CLKSRC_MMIO
+ help
+ This is a new clocksource driver for the PWM timer found in
+ Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver
+ for all devicetree enabled platforms. This driver will be
+ needed only on systems that do not have the Exynos MCT available.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index caacdb63aff9..8d979c72aa94 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
+obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index d7ad425ab9b3..a2b254189782 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -248,14 +248,16 @@ static void __cpuinit arch_timer_stop(struct clock_event_device *clk)
static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
- struct clock_event_device *evt = this_cpu_ptr(arch_timer_evt);
-
+ /*
+ * Grab cpu pointer in each case to avoid spurious
+ * preemptible warnings
+ */
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_STARTING:
- arch_timer_setup(evt);
+ arch_timer_setup(this_cpu_ptr(arch_timer_evt));
break;
case CPU_DYING:
- arch_timer_stop(evt);
+ arch_timer_stop(this_cpu_ptr(arch_timer_evt));
break;
}
@@ -337,22 +339,14 @@ out:
return err;
}
-static const struct of_device_id arch_timer_of_match[] __initconst = {
- { .compatible = "arm,armv7-timer", },
- { .compatible = "arm,armv8-timer", },
- {},
-};
-
-int __init arch_timer_init(void)
+static void __init arch_timer_init(struct device_node *np)
{
- struct device_node *np;
u32 freq;
int i;
- np = of_find_matching_node(NULL, arch_timer_of_match);
- if (!np) {
- pr_err("arch_timer: can't find DT node\n");
- return -ENODEV;
+ if (arch_timer_get_rate()) {
+ pr_warn("arch_timer: multiple nodes in dt, skipping\n");
+ return;
}
/* Try to determine the frequency from the device tree or CNTFRQ */
@@ -378,7 +372,7 @@ int __init arch_timer_init(void)
if (!arch_timer_ppi[PHYS_SECURE_PPI] ||
!arch_timer_ppi[PHYS_NONSECURE_PPI]) {
pr_warn("arch_timer: No interrupt available, giving up\n");
- return -EINVAL;
+ return;
}
}
@@ -387,5 +381,8 @@ int __init arch_timer_init(void)
else
arch_timer_read_counter = arch_counter_get_cntpct;
- return arch_timer_register();
+ arch_timer_register();
+ arch_timer_arch_init();
}
+CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init);
+CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index 661026834b23..662fcc065821 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -24,13 +24,7 @@
#include <linux/of_address.h>
#include <linux/clocksource.h>
-#include <asm/arch_timer.h>
#include <asm/localtimer.h>
-
-#include <plat/cpu.h>
-
-#include <mach/map.h>
-#include <mach/irqs.h>
#include <asm/mach/time.h>
#define EXYNOS4_MCTREG(x) (x)
@@ -511,18 +505,14 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem
#endif /* CONFIG_LOCAL_TIMERS */
}
-void __init mct_init(void)
+void __init mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1)
{
- if (soc_is_exynos4210()) {
- mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0;
- mct_irqs[MCT_L0_IRQ] = EXYNOS4_IRQ_MCT_L0;
- mct_irqs[MCT_L1_IRQ] = EXYNOS4_IRQ_MCT_L1;
- mct_int_type = MCT_INT_SPI;
- } else {
- panic("unable to determine mct controller type\n");
- }
+ mct_irqs[MCT_G0_IRQ] = irq_g0;
+ mct_irqs[MCT_L0_IRQ] = irq_l0;
+ mct_irqs[MCT_L1_IRQ] = irq_l1;
+ mct_int_type = MCT_INT_SPI;
- exynos4_timer_resources(NULL, S5P_VA_SYSTIMER);
+ exynos4_timer_resources(NULL, base);
exynos4_clocksource_init();
exynos4_clockevent_init();
}
diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c
new file mode 100644
index 000000000000..0234c8d2c8f2
--- /dev/null
+++ b/drivers/clocksource/samsung_pwm_timer.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * samsung - Common hr-timer support (s3c and s5p)
+ *
+ * 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/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <clocksource/samsung_pwm.h>
+
+#include <asm/sched_clock.h>
+
+/*
+ * Clocksource driver
+ */
+
+#define REG_TCFG0 0x00
+#define REG_TCFG1 0x04
+#define REG_TCON 0x08
+#define REG_TINT_CSTAT 0x44
+
+#define REG_TCNTB(chan) (0x0c + 12 * (chan))
+#define REG_TCMPB(chan) (0x10 + 12 * (chan))
+
+#define TCFG0_PRESCALER_MASK 0xff
+#define TCFG0_PRESCALER1_SHIFT 8
+
+#define TCFG1_SHIFT(x) ((x) * 4)
+#define TCFG1_MUX_MASK 0xf
+
+#define TCON_START(chan) (1 << (4 * (chan) + 0))
+#define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1))
+#define TCON_INVERT(chan) (1 << (4 * (chan) + 2))
+#define TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3))
+
+DEFINE_SPINLOCK(samsung_pwm_lock);
+EXPORT_SYMBOL(samsung_pwm_lock);
+
+struct samsung_pwm_clocksource {
+ void __iomem *base;
+ unsigned int irq[SAMSUNG_PWM_NUM];
+ struct samsung_pwm_variant variant;
+
+ struct clk *timerclk;
+
+ unsigned int event_id;
+ unsigned int source_id;
+ unsigned int tcnt_max;
+ unsigned int tscaler_div;
+ unsigned int tdiv;
+
+ unsigned long clock_count_per_tick;
+};
+
+static struct samsung_pwm_clocksource pwm;
+
+static void samsung_timer_set_prescale(unsigned int channel, u16 prescale)
+{
+ unsigned long flags;
+ u8 shift = 0;
+ u32 reg;
+
+ if (channel >= 2)
+ shift = TCFG0_PRESCALER1_SHIFT;
+
+ spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ reg = readl(pwm.base + REG_TCFG0);
+ reg &= ~(TCFG0_PRESCALER_MASK << shift);
+ reg |= (prescale - 1) << shift;
+ writel(reg, pwm.base + REG_TCFG0);
+
+ spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+}
+
+static void samsung_timer_set_divisor(unsigned int channel, u8 divisor)
+{
+ u8 shift = TCFG1_SHIFT(channel);
+ unsigned long flags;
+ u32 reg;
+ u8 bits;
+
+ bits = (fls(divisor) - 1) - pwm.variant.div_base;
+
+ spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ reg = readl(pwm.base + REG_TCFG1);
+ reg &= ~(TCFG1_MUX_MASK << shift);
+ reg |= bits << shift;
+ writel(reg, pwm.base + REG_TCFG1);
+
+ spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+}
+
+static void samsung_time_stop(unsigned int channel)
+{
+ unsigned long tcon;
+ unsigned long flags;
+
+ if (channel > 0)
+ ++channel;
+
+ spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ tcon = __raw_readl(pwm.base + REG_TCON);
+ tcon &= ~TCON_START(channel);
+ __raw_writel(tcon, pwm.base + REG_TCON);
+
+ spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+}
+
+static void samsung_time_setup(unsigned int channel, unsigned long tcnt)
+{
+ unsigned long tcon;
+ unsigned long flags;
+ unsigned int tcon_chan = channel;
+
+ if (tcon_chan > 0)
+ ++tcon_chan;
+
+ spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ tcon = __raw_readl(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);
+
+ spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+}
+
+static void samsung_time_start(unsigned int channel, bool periodic)
+{
+ unsigned long tcon;
+ unsigned long flags;
+
+ if (channel > 0)
+ ++channel;
+
+ spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ tcon = __raw_readl(pwm.base + REG_TCON);
+
+ tcon &= ~TCON_MANUALUPDATE(channel);
+ tcon |= TCON_START(channel);
+
+ if (periodic)
+ tcon |= TCON_AUTORELOAD(channel);
+ else
+ tcon &= ~TCON_AUTORELOAD(channel);
+
+ __raw_writel(tcon, pwm.base + REG_TCON);
+
+ spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+}
+
+static int samsung_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ /*
+ * This check is needed to account for internal rounding
+ * errors inside clockevents core, which might result in
+ * passing cycles = 0, which in turn would not generate any
+ * timer interrupt and hang the system.
+ *
+ * Another solution would be to set up the clockevent device
+ * with min_delta = 2, but this would unnecessarily increase
+ * the minimum sleep period.
+ */
+ if (!cycles)
+ cycles = 1;
+
+ samsung_time_setup(pwm.event_id, cycles);
+ samsung_time_start(pwm.event_id, false);
+
+ return 0;
+}
+
+static void samsung_timer_resume(void)
+{
+ /* event timer restart */
+ samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
+ samsung_time_start(pwm.event_id, true);
+
+ /* source timer restart */
+ samsung_time_setup(pwm.source_id, pwm.tcnt_max);
+ samsung_time_start(pwm.source_id, true);
+}
+
+static void samsung_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ samsung_time_stop(pwm.event_id);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
+ samsung_time_start(pwm.event_id, true);
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ break;
+
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ break;
+
+ case CLOCK_EVT_MODE_RESUME:
+ samsung_timer_resume();
+ break;
+ }
+}
+
+static struct clock_event_device time_event_device = {
+ .name = "samsung_event_timer",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 200,
+ .set_next_event = samsung_set_next_event,
+ .set_mode = samsung_set_mode,
+};
+
+static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+
+ if (pwm.variant.has_tint_cstat) {
+ u32 mask = (1 << pwm.event_id);
+ writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
+ }
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction samsung_clock_event_irq = {
+ .name = "samsung_time_irq",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = samsung_clock_event_isr,
+ .dev_id = &time_event_device,
+};
+
+static void __init samsung_clockevent_init(void)
+{
+ unsigned long pclk;
+ unsigned long clock_rate;
+ unsigned int irq_number;
+
+ pclk = clk_get_rate(pwm.timerclk);
+
+ samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div);
+ samsung_timer_set_divisor(pwm.event_id, pwm.tdiv);
+
+ clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
+ pwm.clock_count_per_tick = clock_rate / HZ;
+
+ time_event_device.cpumask = cpumask_of(0);
+ clockevents_config_and_register(&time_event_device,
+ clock_rate, 1, pwm.tcnt_max);
+
+ irq_number = pwm.irq[pwm.event_id];
+ setup_irq(irq_number, &samsung_clock_event_irq);
+
+ if (pwm.variant.has_tint_cstat) {
+ u32 mask = (1 << pwm.event_id);
+ writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
+ }
+}
+
+static void __iomem *samsung_timer_reg(void)
+{
+ switch (pwm.source_id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ return pwm.base + pwm.source_id * 0x0c + 0x14;
+
+ case 4:
+ return pwm.base + 0x40;
+
+ default:
+ BUG();
+ }
+}
+
+/*
+ * Override the global weak sched_clock symbol with this
+ * local implementation which uses the clocksource to get some
+ * better resolution when scheduling the kernel. We accept that
+ * this wraps around for now, since it is just a relative time
+ * stamp. (Inspired by U300 implementation.)
+ */
+static u32 notrace samsung_read_sched_clock(void)
+{
+ void __iomem *reg = samsung_timer_reg();
+
+ if (!reg)
+ return 0;
+
+ return ~__raw_readl(reg);
+}
+
+static void __init samsung_clocksource_init(void)
+{
+ void __iomem *reg = samsung_timer_reg();
+ unsigned long pclk;
+ unsigned long clock_rate;
+ int ret;
+
+ pclk = clk_get_rate(pwm.timerclk);
+
+ samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div);
+ samsung_timer_set_divisor(pwm.source_id, pwm.tdiv);
+
+ clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
+
+ samsung_time_setup(pwm.source_id, pwm.tcnt_max);
+ samsung_time_start(pwm.source_id, true);
+
+ setup_sched_clock(samsung_read_sched_clock,
+ pwm.variant.bits, clock_rate);
+
+ ret = clocksource_mmio_init(reg, "samsung_clocksource_timer",
+ clock_rate, 250, pwm.variant.bits,
+ clocksource_mmio_readl_down);
+ if (ret)
+ panic("samsung_clocksource_timer: can't register clocksource\n");
+}
+
+static void __init samsung_timer_resources(void)
+{
+ pwm.timerclk = clk_get(NULL, "timers");
+ if (IS_ERR(pwm.timerclk))
+ panic("failed to get timers clock for timer");
+
+ clk_prepare_enable(pwm.timerclk);
+
+ pwm.tcnt_max = (1UL << pwm.variant.bits) - 1;
+ if (pwm.variant.bits == 16) {
+ pwm.tscaler_div = 25;
+ pwm.tdiv = 2;
+ } else {
+ pwm.tscaler_div = 2;
+ pwm.tdiv = 1;
+ }
+}
+
+/*
+ * PWM master driver
+ */
+static void __init _samsung_pwm_clocksource_init(void)
+{
+ u8 mask;
+ int channel;
+
+ mask = ~pwm.variant.output_mask & ((1 << SAMSUNG_PWM_NUM) - 1);
+ channel = fls(mask) - 1;
+ if (channel < 0)
+ panic("failed to find PWM channel for clocksource");
+ pwm.source_id = channel;
+
+ mask &= ~(1 << channel);
+ channel = fls(mask) - 1;
+ if (channel < 0)
+ panic("failed to find PWM channel for clock event");
+ pwm.event_id = channel;
+
+ samsung_timer_resources();
+ samsung_clockevent_init();
+ samsung_clocksource_init();
+}
+
+void __init samsung_pwm_clocksource_init(void __iomem *base,
+ unsigned int *irqs, struct samsung_pwm_variant *variant)
+{
+ pwm.base = base;
+ memcpy(&pwm.variant, variant, sizeof(pwm.variant));
+ memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs));
+
+ _samsung_pwm_clocksource_init();
+}
+
+#ifdef CONFIG_CLKSRC_OF
+static void __init samsung_pwm_alloc(struct device_node *np,
+ const struct samsung_pwm_variant *variant)
+{
+ struct resource res;
+ struct property *prop;
+ const __be32 *cur;
+ u32 val;
+ int i;
+
+ memcpy(&pwm.variant, variant, sizeof(pwm.variant));
+ for (i = 0; i < SAMSUNG_PWM_NUM; ++i)
+ pwm.irq[i] = irq_of_parse_and_map(np, i);
+
+ of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) {
+ if (val >= SAMSUNG_PWM_NUM) {
+ pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n",
+ __func__);
+ continue;
+ }
+ pwm.variant.output_mask |= 1 << val;
+ }
+
+ of_address_to_resource(np, 0, &res);
+ if (!request_mem_region(res.start,
+ resource_size(&res), "samsung-pwm")) {
+ pr_err("%s: failed to request IO mem region\n", __func__);
+ return;
+ }
+
+ pwm.base = ioremap(res.start, resource_size(&res));
+ if (!pwm.base) {
+ pr_err("%s: failed to map PWM registers\n", __func__);
+ release_mem_region(res.start, resource_size(&res));
+ return;
+ }
+
+ _samsung_pwm_clocksource_init();
+}
+
+static const struct samsung_pwm_variant s3c24xx_variant = {
+ .bits = 16,
+ .div_base = 1,
+ .has_tint_cstat = false,
+ .tclk_mask = (1 << 4),
+};
+
+static void __init s3c2410_pwm_clocksource_init(struct device_node *np)
+{
+ samsung_pwm_alloc(np, &s3c24xx_variant);
+}
+CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
+
+static const struct samsung_pwm_variant s3c64xx_variant = {
+ .bits = 32,
+ .div_base = 0,
+ .has_tint_cstat = true,
+ .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5),
+};
+
+static void __init s3c64xx_pwm_clocksource_init(struct device_node *np)
+{
+ samsung_pwm_alloc(np, &s3c64xx_variant);
+}
+CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
+
+static const struct samsung_pwm_variant s5p64x0_variant = {
+ .bits = 32,
+ .div_base = 0,
+ .has_tint_cstat = true,
+ .tclk_mask = 0,
+};
+
+static void __init s5p64x0_pwm_clocksource_init(struct device_node *np)
+{
+ samsung_pwm_alloc(np, &s5p64x0_variant);
+}
+CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
+
+static const struct samsung_pwm_variant s5p_variant = {
+ .bits = 32,
+ .div_base = 0,
+ .has_tint_cstat = true,
+ .tclk_mask = (1 << 5),
+};
+
+static void __init s5p_pwm_clocksource_init(struct device_node *np)
+{
+ samsung_pwm_alloc(np, &s5p_variant);
+}
+CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
+#endif
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index 8f6d30d37c45..b48a79c28845 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -27,6 +27,7 @@
#include <linux/stmp_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_dma.h>
#include <asm/irq.h>
@@ -139,6 +140,8 @@ struct mxs_dma_engine {
struct dma_device dma_device;
struct device_dma_parameters dma_parms;
struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS];
+ struct platform_device *pdev;
+ unsigned int nr_channels;
};
struct mxs_dma_type {
@@ -350,10 +353,8 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int ret;
- if (!data)
- return -EINVAL;
-
- mxs_chan->chan_irq = data->chan_irq;
+ if (data)
+ mxs_chan->chan_irq = data->chan_irq;
mxs_chan->ccw = dma_alloc_coherent(mxs_dma->dma_device.dev,
CCW_BLOCK_SIZE, &mxs_chan->ccw_phys,
@@ -665,8 +666,55 @@ err_out:
return ret;
}
+struct mxs_dma_filter_param {
+ struct device_node *of_node;
+ unsigned int chan_id;
+};
+
+static bool mxs_dma_filter_fn(struct dma_chan *chan, void *fn_param)
+{
+ struct mxs_dma_filter_param *param = fn_param;
+ struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
+ struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+ int chan_irq;
+
+ if (mxs_dma->dma_device.dev->of_node != param->of_node)
+ return false;
+
+ if (chan->chan_id != param->chan_id)
+ return false;
+
+ chan_irq = platform_get_irq(mxs_dma->pdev, param->chan_id);
+ if (chan_irq < 0)
+ return false;
+
+ mxs_chan->chan_irq = chan_irq;
+
+ return true;
+}
+
+struct dma_chan *mxs_dma_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct mxs_dma_engine *mxs_dma = ofdma->of_dma_data;
+ dma_cap_mask_t mask = mxs_dma->dma_device.cap_mask;
+ struct mxs_dma_filter_param param;
+
+ if (dma_spec->args_count != 1)
+ return NULL;
+
+ param.of_node = ofdma->of_node;
+ param.chan_id = dma_spec->args[0];
+
+ if (param.chan_id >= mxs_dma->nr_channels)
+ return NULL;
+
+ return dma_request_channel(mask, mxs_dma_filter_fn, &param);
+}
+
static int __init mxs_dma_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
const struct platform_device_id *id_entry;
const struct of_device_id *of_id;
const struct mxs_dma_type *dma_type;
@@ -674,10 +722,16 @@ static int __init mxs_dma_probe(struct platform_device *pdev)
struct resource *iores;
int ret, i;
- mxs_dma = kzalloc(sizeof(*mxs_dma), GFP_KERNEL);
+ mxs_dma = devm_kzalloc(&pdev->dev, sizeof(*mxs_dma), GFP_KERNEL);
if (!mxs_dma)
return -ENOMEM;
+ ret = of_property_read_u32(np, "dma-channels", &mxs_dma->nr_channels);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read dma-channels\n");
+ return ret;
+ }
+
of_id = of_match_device(mxs_dma_dt_ids, &pdev->dev);
if (of_id)
id_entry = of_id->data;
@@ -689,24 +743,13 @@ static int __init mxs_dma_probe(struct platform_device *pdev)
mxs_dma->dev_id = dma_type->id;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mxs_dma->base = devm_ioremap_resource(&pdev->dev, iores);
+ if (IS_ERR(mxs_dma->base))
+ return PTR_ERR(mxs_dma->base);
- if (!request_mem_region(iores->start, resource_size(iores),
- pdev->name)) {
- ret = -EBUSY;
- goto err_request_region;
- }
-
- mxs_dma->base = ioremap(iores->start, resource_size(iores));
- if (!mxs_dma->base) {
- ret = -ENOMEM;
- goto err_ioremap;
- }
-
- mxs_dma->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(mxs_dma->clk)) {
- ret = PTR_ERR(mxs_dma->clk);
- goto err_clk;
- }
+ mxs_dma->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mxs_dma->clk))
+ return PTR_ERR(mxs_dma->clk);
dma_cap_set(DMA_SLAVE, mxs_dma->dma_device.cap_mask);
dma_cap_set(DMA_CYCLIC, mxs_dma->dma_device.cap_mask);
@@ -732,8 +775,9 @@ static int __init mxs_dma_probe(struct platform_device *pdev)
ret = mxs_dma_init(mxs_dma);
if (ret)
- goto err_init;
+ return ret;
+ mxs_dma->pdev = pdev;
mxs_dma->dma_device.dev = &pdev->dev;
/* mxs_dma gets 65535 bytes maximum sg size */
@@ -751,22 +795,19 @@ static int __init mxs_dma_probe(struct platform_device *pdev)
ret = dma_async_device_register(&mxs_dma->dma_device);
if (ret) {
dev_err(mxs_dma->dma_device.dev, "unable to register\n");
- goto err_init;
+ return ret;
+ }
+
+ ret = of_dma_controller_register(np, mxs_dma_xlate, mxs_dma);
+ if (ret) {
+ dev_err(mxs_dma->dma_device.dev,
+ "failed to register controller\n");
+ dma_async_device_unregister(&mxs_dma->dma_device);
}
dev_info(mxs_dma->dma_device.dev, "initialized\n");
return 0;
-
-err_init:
- clk_put(mxs_dma->clk);
-err_clk:
- iounmap(mxs_dma->base);
-err_ioremap:
- release_mem_region(iores->start, resource_size(iores));
-err_request_region:
- kfree(mxs_dma);
- return ret;
}
static struct platform_driver mxs_dma_driver = {
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b166e30b3bc4..c22eed9481e3 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -204,6 +204,12 @@ config GPIO_PXA
help
Say yes here to support the PXA GPIO device
+config GPIO_RCAR
+ tristate "Renesas R-Car GPIO"
+ depends on ARM
+ help
+ Say yes here to support GPIO on Renesas R-Car SoCs.
+
config GPIO_SPEAR_SPICS
bool "ST SPEAr13xx SPI Chip Select as GPIO support"
depends on PLAT_SPEAR
@@ -297,12 +303,21 @@ config GPIO_GE_FPGA
config GPIO_LYNXPOINT
bool "Intel Lynxpoint GPIO support"
- depends on ACPI
+ depends on ACPI && X86
select IRQ_DOMAIN
help
driver for GPIO functionality on Intel Lynxpoint PCH chipset
Requires ACPI device enumeration code to set up a platform device.
+config GPIO_GRGPIO
+ tristate "Aeroflex Gaisler GRGPIO support"
+ depends on OF
+ select GPIO_GENERIC
+ select IRQ_DOMAIN
+ help
+ Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
+ VHDL IP core library.
+
comment "I2C GPIO expanders:"
config GPIO_ARIZONA
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index a274d7df3c8c..0cb2d656ad16 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o
obj-$(CONFIG_GPIO_EM) += gpio-em.o
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
+obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
@@ -57,6 +58,7 @@ obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
+obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
index 464be961f605..721607904d0a 100644
--- a/drivers/gpio/gpio-74x164.c
+++ b/drivers/gpio/gpio-74x164.c
@@ -137,7 +137,7 @@ static int gen_74x164_probe(struct spi_device *spi)
mutex_init(&chip->lock);
- dev_set_drvdata(&spi->dev, chip);
+ spi_set_drvdata(spi, chip);
chip->spi = spi;
@@ -176,7 +176,7 @@ static int gen_74x164_probe(struct spi_device *spi)
return ret;
exit_destroy:
- dev_set_drvdata(&spi->dev, NULL);
+ spi_set_drvdata(spi, NULL);
mutex_destroy(&chip->lock);
return ret;
}
@@ -186,11 +186,11 @@ static int gen_74x164_remove(struct spi_device *spi)
struct gen_74x164_chip *chip;
int ret;
- chip = dev_get_drvdata(&spi->dev);
+ chip = spi_get_drvdata(spi);
if (chip == NULL)
return -ENODEV;
- dev_set_drvdata(&spi->dev, NULL);
+ spi_set_drvdata(spi, NULL);
ret = gpiochip_remove(&chip->gpio_chip);
if (!ret)
diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c
index 8afa95f831b1..f33f78dcadaa 100644
--- a/drivers/gpio/gpio-adp5520.c
+++ b/drivers/gpio/gpio-adp5520.c
@@ -105,7 +105,7 @@ static int adp5520_gpio_probe(struct platform_device *pdev)
return -ENODEV;
}
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&pdev->dev, "failed to alloc memory\n");
return -ENOMEM;
@@ -163,7 +163,6 @@ static int adp5520_gpio_probe(struct platform_device *pdev)
return 0;
err:
- kfree(dev);
return ret;
}
@@ -180,7 +179,6 @@ static int adp5520_gpio_remove(struct platform_device *pdev)
return ret;
}
- kfree(dev);
return 0;
}
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
index deca78f99316..5cba855638bf 100644
--- a/drivers/gpio/gpio-em.c
+++ b/drivers/gpio/gpio-em.c
@@ -231,10 +231,12 @@ static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq,
static struct irq_domain_ops em_gio_irq_domain_ops = {
.map = em_gio_irq_domain_map,
+ .xlate = irq_domain_xlate_twocell,
};
static int em_gio_probe(struct platform_device *pdev)
{
+ struct gpio_em_config pdata_dt;
struct gpio_em_config *pdata = pdev->dev.platform_data;
struct em_gio_priv *p;
struct resource *io[2], *irq[2];
@@ -243,7 +245,7 @@ static int em_gio_probe(struct platform_device *pdev)
const char *name = dev_name(&pdev->dev);
int ret;
- p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
if (!p) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
ret = -ENOMEM;
@@ -259,24 +261,45 @@ static int em_gio_probe(struct platform_device *pdev)
irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
- if (!io[0] || !io[1] || !irq[0] || !irq[1] || !pdata) {
- dev_err(&pdev->dev, "missing IRQ, IOMEM or configuration\n");
+ if (!io[0] || !io[1] || !irq[0] || !irq[1]) {
+ dev_err(&pdev->dev, "missing IRQ or IOMEM\n");
ret = -EINVAL;
- goto err1;
+ goto err0;
}
- p->base0 = ioremap_nocache(io[0]->start, resource_size(io[0]));
+ p->base0 = devm_ioremap_nocache(&pdev->dev, io[0]->start,
+ resource_size(io[0]));
if (!p->base0) {
dev_err(&pdev->dev, "failed to remap low I/O memory\n");
ret = -ENXIO;
- goto err1;
+ goto err0;
}
- p->base1 = ioremap_nocache(io[1]->start, resource_size(io[1]));
+ p->base1 = devm_ioremap_nocache(&pdev->dev, io[1]->start,
+ resource_size(io[1]));
if (!p->base1) {
dev_err(&pdev->dev, "failed to remap high I/O memory\n");
ret = -ENXIO;
- goto err2;
+ goto err0;
+ }
+
+ if (!pdata) {
+ memset(&pdata_dt, 0, sizeof(pdata_dt));
+ pdata = &pdata_dt;
+
+ if (of_property_read_u32(pdev->dev.of_node, "ngpios",
+ &pdata->number_of_pins)) {
+ dev_err(&pdev->dev, "Missing ngpios OF property\n");
+ ret = -EINVAL;
+ goto err0;
+ }
+
+ ret = of_alias_get_id(pdev->dev.of_node, "gpio");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't get OF id\n");
+ goto err0;
+ }
+ pdata->gpio_base = ret * 32; /* 32 GPIOs per instance */
}
gpio_chip = &p->gpio_chip;
@@ -306,40 +329,32 @@ static int em_gio_probe(struct platform_device *pdev)
if (!p->irq_domain) {
ret = -ENXIO;
dev_err(&pdev->dev, "cannot initialize irq domain\n");
- goto err3;
+ goto err0;
}
- if (request_irq(irq[0]->start, em_gio_irq_handler, 0, name, p)) {
+ if (devm_request_irq(&pdev->dev, irq[0]->start,
+ em_gio_irq_handler, 0, name, p)) {
dev_err(&pdev->dev, "failed to request low IRQ\n");
ret = -ENOENT;
- goto err4;
+ goto err1;
}
- if (request_irq(irq[1]->start, em_gio_irq_handler, 0, name, p)) {
+ if (devm_request_irq(&pdev->dev, irq[1]->start,
+ em_gio_irq_handler, 0, name, p)) {
dev_err(&pdev->dev, "failed to request high IRQ\n");
ret = -ENOENT;
- goto err5;
+ goto err1;
}
ret = gpiochip_add(gpio_chip);
if (ret) {
dev_err(&pdev->dev, "failed to add GPIO controller\n");
- goto err6;
+ goto err1;
}
return 0;
-err6:
- free_irq(irq[1]->start, pdev);
-err5:
- free_irq(irq[0]->start, pdev);
-err4:
- irq_domain_remove(p->irq_domain);
-err3:
- iounmap(p->base1);
-err2:
- iounmap(p->base0);
err1:
- kfree(p);
+ irq_domain_remove(p->irq_domain);
err0:
return ret;
}
@@ -347,34 +362,43 @@ err0:
static int em_gio_remove(struct platform_device *pdev)
{
struct em_gio_priv *p = platform_get_drvdata(pdev);
- struct resource *irq[2];
int ret;
ret = gpiochip_remove(&p->gpio_chip);
if (ret)
return ret;
- irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
-
- free_irq(irq[1]->start, pdev);
- free_irq(irq[0]->start, pdev);
irq_domain_remove(p->irq_domain);
- iounmap(p->base1);
- iounmap(p->base0);
- kfree(p);
return 0;
}
+static const struct of_device_id em_gio_dt_ids[] = {
+ { .compatible = "renesas,em-gio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, em_gio_dt_ids);
+
static struct platform_driver em_gio_device_driver = {
.probe = em_gio_probe,
.remove = em_gio_remove,
.driver = {
.name = "em_gio",
+ .of_match_table = em_gio_dt_ids,
+ .owner = THIS_MODULE,
}
};
-module_platform_driver(em_gio_device_driver);
+static int __init em_gio_init(void)
+{
+ return platform_driver_register(&em_gio_device_driver);
+}
+postcore_initcall(em_gio_init);
+
+static void __exit em_gio_exit(void)
+{
+ platform_driver_unregister(&em_gio_device_driver);
+}
+module_exit(em_gio_exit);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("Renesas Emma Mobile GIO Driver");
diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
index 05fcc0f247ca..d2196bf73847 100644
--- a/drivers/gpio/gpio-generic.c
+++ b/drivers/gpio/gpio-generic.c
@@ -104,6 +104,26 @@ static unsigned long bgpio_read64(void __iomem *reg)
}
#endif /* BITS_PER_LONG >= 64 */
+static void bgpio_write16be(void __iomem *reg, unsigned long data)
+{
+ iowrite16be(data, reg);
+}
+
+static unsigned long bgpio_read16be(void __iomem *reg)
+{
+ return ioread16be(reg);
+}
+
+static void bgpio_write32be(void __iomem *reg, unsigned long data)
+{
+ iowrite32be(data, reg);
+}
+
+static unsigned long bgpio_read32be(void __iomem *reg)
+{
+ return ioread32be(reg);
+}
+
static unsigned long bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin)
{
return 1 << pin;
@@ -249,7 +269,8 @@ static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val)
static int bgpio_setup_accessors(struct device *dev,
struct bgpio_chip *bgc,
- bool be)
+ bool bit_be,
+ bool byte_be)
{
switch (bgc->bits) {
@@ -258,17 +279,33 @@ static int bgpio_setup_accessors(struct device *dev,
bgc->write_reg = bgpio_write8;
break;
case 16:
- bgc->read_reg = bgpio_read16;
- bgc->write_reg = bgpio_write16;
+ if (byte_be) {
+ bgc->read_reg = bgpio_read16be;
+ bgc->write_reg = bgpio_write16be;
+ } else {
+ bgc->read_reg = bgpio_read16;
+ bgc->write_reg = bgpio_write16;
+ }
break;
case 32:
- bgc->read_reg = bgpio_read32;
- bgc->write_reg = bgpio_write32;
+ if (byte_be) {
+ bgc->read_reg = bgpio_read32be;
+ bgc->write_reg = bgpio_write32be;
+ } else {
+ bgc->read_reg = bgpio_read32;
+ bgc->write_reg = bgpio_write32;
+ }
break;
#if BITS_PER_LONG >= 64
case 64:
- bgc->read_reg = bgpio_read64;
- bgc->write_reg = bgpio_write64;
+ if (byte_be) {
+ dev_err(dev,
+ "64 bit big endian byte order unsupported\n");
+ return -EINVAL;
+ } else {
+ bgc->read_reg = bgpio_read64;
+ bgc->write_reg = bgpio_write64;
+ }
break;
#endif /* BITS_PER_LONG >= 64 */
default:
@@ -276,7 +313,7 @@ static int bgpio_setup_accessors(struct device *dev,
return -EINVAL;
}
- bgc->pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask;
+ bgc->pin2mask = bit_be ? bgpio_pin2mask_be : bgpio_pin2mask;
return 0;
}
@@ -353,11 +390,7 @@ static int bgpio_setup_direction(struct bgpio_chip *bgc,
int bgpio_remove(struct bgpio_chip *bgc)
{
- int err = gpiochip_remove(&bgc->gc);
-
- kfree(bgc);
-
- return err;
+ return gpiochip_remove(&bgc->gc);
}
EXPORT_SYMBOL_GPL(bgpio_remove);
@@ -385,7 +418,8 @@ int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
if (ret)
return ret;
- ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN);
+ ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN,
+ flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER);
if (ret)
return ret;
diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c
new file mode 100644
index 000000000000..8e08b8647655
--- /dev/null
+++ b/drivers/gpio/gpio-grgpio.c
@@ -0,0 +1,505 @@
+/*
+ * Driver for Aeroflex Gaisler GRGPIO General Purpose I/O cores.
+ *
+ * 2013 (c) Aeroflex Gaisler AB
+ *
+ * This driver supports the GRGPIO GPIO core available in the GRLIB VHDL
+ * IP core library.
+ *
+ * Full documentation of the GRGPIO core can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * See "Documentation/devicetree/bindings/gpio/gpio-grgpio.txt" for
+ * information on open firmware properties.
+ *
+ * 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.
+ *
+ * Contributors: Andreas Larsson <andreas@gaisler.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+
+#define GRGPIO_MAX_NGPIO 32
+
+#define GRGPIO_DATA 0x00
+#define GRGPIO_OUTPUT 0x04
+#define GRGPIO_DIR 0x08
+#define GRGPIO_IMASK 0x0c
+#define GRGPIO_IPOL 0x10
+#define GRGPIO_IEDGE 0x14
+#define GRGPIO_BYPASS 0x18
+#define GRGPIO_IMAP_BASE 0x20
+
+/* Structure for an irq of the core - called an underlying irq */
+struct grgpio_uirq {
+ u8 refcnt; /* Reference counter to manage requesting/freeing of uirq */
+ u8 uirq; /* Underlying irq of the gpio driver */
+};
+
+/*
+ * Structure for an irq of a gpio line handed out by this driver. The index is
+ * used to map to the corresponding underlying irq.
+ */
+struct grgpio_lirq {
+ s8 index; /* Index into struct grgpio_priv's uirqs, or -1 */
+ u8 irq; /* irq for the gpio line */
+};
+
+struct grgpio_priv {
+ struct bgpio_chip bgc;
+ void __iomem *regs;
+ struct device *dev;
+
+ u32 imask; /* irq mask shadow register */
+
+ /*
+ * The grgpio core can have multiple "underlying" irqs. The gpio lines
+ * can be mapped to any one or none of these underlying irqs
+ * independently of each other. This driver sets up an irq domain and
+ * hands out separate irqs to each gpio line
+ */
+ struct irq_domain *domain;
+
+ /*
+ * This array contains information on each underlying irq, each
+ * irq of the grgpio core itself.
+ */
+ struct grgpio_uirq uirqs[GRGPIO_MAX_NGPIO];
+
+ /*
+ * This array contains information for each gpio line on the irqs
+ * obtains from this driver. An index value of -1 for a certain gpio
+ * line indicates that the line has no irq. Otherwise the index connects
+ * the irq to the underlying irq by pointing into the uirqs array.
+ */
+ struct grgpio_lirq lirqs[GRGPIO_MAX_NGPIO];
+};
+
+static inline struct grgpio_priv *grgpio_gc_to_priv(struct gpio_chip *gc)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+ return container_of(bgc, struct grgpio_priv, bgc);
+}
+
+static void grgpio_set_imask(struct grgpio_priv *priv, unsigned int offset,
+ int val)
+{
+ struct bgpio_chip *bgc = &priv->bgc;
+ unsigned long mask = bgc->pin2mask(bgc, offset);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ if (val)
+ priv->imask |= mask;
+ else
+ priv->imask &= ~mask;
+ bgc->write_reg(priv->regs + GRGPIO_IMASK, priv->imask);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static int grgpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct grgpio_priv *priv = grgpio_gc_to_priv(gc);
+
+ if (offset > gc->ngpio)
+ return -ENXIO;
+
+ if (priv->lirqs[offset].index < 0)
+ return -ENXIO;
+
+ return irq_create_mapping(priv->domain, offset);
+}
+
+/* -------------------- IRQ chip functions -------------------- */
+
+static int grgpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct grgpio_priv *priv = irq_data_get_irq_chip_data(d);
+ unsigned long flags;
+ u32 mask = BIT(d->hwirq);
+ u32 ipol;
+ u32 iedge;
+ u32 pol;
+ u32 edge;
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_LOW:
+ pol = 0;
+ edge = 0;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ pol = mask;
+ edge = 0;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ pol = 0;
+ edge = mask;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ pol = mask;
+ edge = mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&priv->bgc.lock, flags);
+
+ ipol = priv->bgc.read_reg(priv->regs + GRGPIO_IPOL) & ~mask;
+ iedge = priv->bgc.read_reg(priv->regs + GRGPIO_IEDGE) & ~mask;
+
+ priv->bgc.write_reg(priv->regs + GRGPIO_IPOL, ipol | pol);
+ priv->bgc.write_reg(priv->regs + GRGPIO_IEDGE, iedge | edge);
+
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+
+ return 0;
+}
+
+static void grgpio_irq_mask(struct irq_data *d)
+{
+ struct grgpio_priv *priv = irq_data_get_irq_chip_data(d);
+ int offset = d->hwirq;
+
+ grgpio_set_imask(priv, offset, 0);
+}
+
+static void grgpio_irq_unmask(struct irq_data *d)
+{
+ struct grgpio_priv *priv = irq_data_get_irq_chip_data(d);
+ int offset = d->hwirq;
+
+ grgpio_set_imask(priv, offset, 1);
+}
+
+static struct irq_chip grgpio_irq_chip = {
+ .name = "grgpio",
+ .irq_mask = grgpio_irq_mask,
+ .irq_unmask = grgpio_irq_unmask,
+ .irq_set_type = grgpio_irq_set_type,
+};
+
+static irqreturn_t grgpio_irq_handler(int irq, void *dev)
+{
+ struct grgpio_priv *priv = dev;
+ int ngpio = priv->bgc.gc.ngpio;
+ unsigned long flags;
+ int i;
+ int match = 0;
+
+ spin_lock_irqsave(&priv->bgc.lock, flags);
+
+ /*
+ * For each gpio line, call its interrupt handler if it its underlying
+ * irq matches the current irq that is handled.
+ */
+ for (i = 0; i < ngpio; i++) {
+ struct grgpio_lirq *lirq = &priv->lirqs[i];
+
+ if (priv->imask & BIT(i) && lirq->index >= 0 &&
+ priv->uirqs[lirq->index].uirq == irq) {
+ generic_handle_irq(lirq->irq);
+ match = 1;
+ }
+ }
+
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+
+ if (!match)
+ dev_warn(priv->dev, "No gpio line matched irq %d\n", irq);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * This function will be called as a consequence of the call to
+ * irq_create_mapping in grgpio_to_irq
+ */
+int grgpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct grgpio_priv *priv = d->host_data;
+ struct grgpio_lirq *lirq;
+ struct grgpio_uirq *uirq;
+ unsigned long flags;
+ int offset = hwirq;
+ int ret = 0;
+
+ if (!priv)
+ return -EINVAL;
+
+ lirq = &priv->lirqs[offset];
+ if (lirq->index < 0)
+ return -EINVAL;
+
+ dev_dbg(priv->dev, "Mapping irq %d for gpio line %d\n",
+ irq, offset);
+
+ spin_lock_irqsave(&priv->bgc.lock, flags);
+
+ /* Request underlying irq if not already requested */
+ lirq->irq = irq;
+ uirq = &priv->uirqs[lirq->index];
+ if (uirq->refcnt == 0) {
+ ret = request_irq(uirq->uirq, grgpio_irq_handler, 0,
+ dev_name(priv->dev), priv);
+ if (ret) {
+ dev_err(priv->dev,
+ "Could not request underlying irq %d\n",
+ uirq->uirq);
+
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+
+ return ret;
+ }
+ }
+ uirq->refcnt++;
+
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+
+ /* Setup irq */
+ irq_set_chip_data(irq, priv);
+ irq_set_chip_and_handler(irq, &grgpio_irq_chip,
+ handle_simple_irq);
+ irq_clear_status_flags(irq, IRQ_NOREQUEST);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+
+ return ret;
+}
+
+void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+ struct grgpio_priv *priv = d->host_data;
+ int index;
+ struct grgpio_lirq *lirq;
+ struct grgpio_uirq *uirq;
+ unsigned long flags;
+ int ngpio = priv->bgc.gc.ngpio;
+ int i;
+
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+
+ spin_lock_irqsave(&priv->bgc.lock, flags);
+
+ /* Free underlying irq if last user unmapped */
+ index = -1;
+ for (i = 0; i < ngpio; i++) {
+ lirq = &priv->lirqs[i];
+ if (lirq->irq == irq) {
+ grgpio_set_imask(priv, i, 0);
+ lirq->irq = 0;
+ index = lirq->index;
+ break;
+ }
+ }
+ WARN_ON(index < 0);
+
+ if (index >= 0) {
+ uirq = &priv->uirqs[lirq->index];
+ uirq->refcnt--;
+ if (uirq->refcnt == 0)
+ free_irq(uirq->uirq, priv);
+ }
+
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+}
+
+static struct irq_domain_ops grgpio_irq_domain_ops = {
+ .map = grgpio_irq_map,
+ .unmap = grgpio_irq_unmap,
+};
+
+/* ------------------------------------------------------------ */
+
+static int grgpio_probe(struct platform_device *ofdev)
+{
+ struct device_node *np = ofdev->dev.of_node;
+ void __iomem *regs;
+ struct gpio_chip *gc;
+ struct bgpio_chip *bgc;
+ struct grgpio_priv *priv;
+ struct resource *res;
+ int err;
+ u32 prop;
+ s32 *irqmap;
+ int size;
+ int i;
+
+ priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&ofdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ bgc = &priv->bgc;
+ err = bgpio_init(bgc, &ofdev->dev, 4, regs + GRGPIO_DATA,
+ regs + GRGPIO_OUTPUT, NULL, regs + GRGPIO_DIR, NULL,
+ BGPIOF_BIG_ENDIAN_BYTE_ORDER);
+ if (err) {
+ dev_err(&ofdev->dev, "bgpio_init() failed\n");
+ return err;
+ }
+
+ priv->regs = regs;
+ priv->imask = bgc->read_reg(regs + GRGPIO_IMASK);
+ priv->dev = &ofdev->dev;
+
+ gc = &bgc->gc;
+ gc->of_node = np;
+ gc->owner = THIS_MODULE;
+ gc->to_irq = grgpio_to_irq;
+ gc->label = np->full_name;
+ gc->base = -1;
+
+ err = of_property_read_u32(np, "nbits", &prop);
+ if (err || prop <= 0 || prop > GRGPIO_MAX_NGPIO) {
+ gc->ngpio = GRGPIO_MAX_NGPIO;
+ dev_dbg(&ofdev->dev,
+ "No or invalid nbits property: assume %d\n", gc->ngpio);
+ } else {
+ gc->ngpio = prop;
+ }
+
+ /*
+ * The irqmap contains the index values indicating which underlying irq,
+ * if anyone, is connected to that line
+ */
+ irqmap = (s32 *)of_get_property(np, "irqmap", &size);
+ if (irqmap) {
+ if (size < gc->ngpio) {
+ dev_err(&ofdev->dev,
+ "irqmap shorter than ngpio (%d < %d)\n",
+ size, gc->ngpio);
+ return -EINVAL;
+ }
+
+ priv->domain = irq_domain_add_linear(np, gc->ngpio,
+ &grgpio_irq_domain_ops,
+ priv);
+ if (!priv->domain) {
+ dev_err(&ofdev->dev, "Could not add irq domain\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < gc->ngpio; i++) {
+ struct grgpio_lirq *lirq;
+ int ret;
+
+ lirq = &priv->lirqs[i];
+ lirq->index = irqmap[i];
+
+ if (lirq->index < 0)
+ continue;
+
+ ret = platform_get_irq(ofdev, lirq->index);
+ if (ret <= 0) {
+ /*
+ * Continue without irq functionality for that
+ * gpio line
+ */
+ dev_err(priv->dev,
+ "Failed to get irq for offset %d\n", i);
+ continue;
+ }
+ priv->uirqs[lirq->index].uirq = ret;
+ }
+ }
+
+ platform_set_drvdata(ofdev, priv);
+
+ err = gpiochip_add(gc);
+ if (err) {
+ dev_err(&ofdev->dev, "Could not add gpiochip\n");
+ return err;
+ }
+
+ dev_info(&ofdev->dev, "regs=0x%p, base=%d, ngpio=%d, irqs=%s\n",
+ priv->regs, gc->base, gc->ngpio, priv->domain ? "on" : "off");
+
+ return 0;
+}
+
+static int grgpio_remove(struct platform_device *ofdev)
+{
+ struct grgpio_priv *priv = platform_get_drvdata(ofdev);
+ unsigned long flags;
+ int i;
+ int ret = 0;
+
+ spin_lock_irqsave(&priv->bgc.lock, flags);
+
+ if (priv->domain) {
+ for (i = 0; i < GRGPIO_MAX_NGPIO; i++) {
+ if (priv->uirqs[i].refcnt != 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+ }
+
+ ret = gpiochip_remove(&priv->bgc.gc);
+ if (ret)
+ goto out;
+
+ if (priv->domain)
+ irq_domain_remove(priv->domain);
+
+out:
+ spin_unlock_irqrestore(&priv->bgc.lock, flags);
+
+ return ret;
+}
+
+static struct of_device_id grgpio_match[] = {
+ {.name = "GAISLER_GPIO"},
+ {.name = "01_01a"},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, grgpio_match);
+
+static struct platform_driver grgpio_driver = {
+ .driver = {
+ .name = "grgpio",
+ .owner = THIS_MODULE,
+ .of_match_table = grgpio_match,
+ },
+ .probe = grgpio_probe,
+ .remove = grgpio_remove,
+};
+module_platform_driver(grgpio_driver);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB.");
+MODULE_DESCRIPTION("Driver for Aeroflex Gaisler GRGPIO");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index de3c317bd3e2..e16d932fd444 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -130,14 +130,11 @@ static int ichx_read_bit(int reg, unsigned nr)
static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr)
{
- return ichx_priv.use_gpio & (1 << (nr / 32));
+ return !!(ichx_priv.use_gpio & (1 << (nr / 32)));
}
static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
{
- if (!ichx_gpio_check_available(gpio, nr))
- return -ENXIO;
-
/*
* Try setting pin as an input and verify it worked since many pins
* are output-only.
@@ -151,9 +148,6 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
int val)
{
- if (!ichx_gpio_check_available(gpio, nr))
- return -ENXIO;
-
/* Set GPIO output value. */
ichx_write_bit(GPIO_LVL, nr, val, 0);
@@ -169,9 +163,6 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
{
- if (!ichx_gpio_check_available(chip, nr))
- return -ENXIO;
-
return ichx_read_bit(GPIO_LVL, nr);
}
@@ -180,9 +171,6 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
unsigned long flags;
u32 data;
- if (!ichx_gpio_check_available(chip, nr))
- return -ENXIO;
-
/*
* GPI 0 - 15 need to be read from the power management registers on
* a ICH6/3100 bridge.
@@ -207,6 +195,9 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr)
{
+ if (!ichx_gpio_check_available(chip, nr))
+ return -ENXIO;
+
/*
* Note we assume the BIOS properly set a bridge's USE value. Some
* chips (eg Intel 3100) have bogus USE values though, so first see if
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index 36d7dee07b28..dda6a756a3d9 100644
--- a/drivers/gpio/gpio-lpc32xx.c
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -533,7 +533,7 @@ static int lpc32xx_of_xlate(struct gpio_chip *gc,
{
/* Is this the correct bank? */
u32 bank = gpiospec->args[0];
- if ((bank > ARRAY_SIZE(lpc32xx_gpiochip) ||
+ if ((bank >= ARRAY_SIZE(lpc32xx_gpiochip) ||
(gc != &lpc32xx_gpiochip[bank].chip)))
return -EINVAL;
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
index 3472b05ac512..86c17de87692 100644
--- a/drivers/gpio/gpio-lynxpoint.c
+++ b/drivers/gpio/gpio-lynxpoint.c
@@ -32,6 +32,7 @@
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/io.h>
/* LynxPoint chipset has support for 94 gpio pins */
diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c
index 4b6b9a04e326..40ab6dfb6021 100644
--- a/drivers/gpio/gpio-max7300.c
+++ b/drivers/gpio/gpio-max7300.c
@@ -41,7 +41,7 @@ static int max7300_probe(struct i2c_client *client,
I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- ts = kzalloc(sizeof(struct max7301), GFP_KERNEL);
+ ts = devm_kzalloc(&client->dev, sizeof(struct max7301), GFP_KERNEL);
if (!ts)
return -ENOMEM;
@@ -50,8 +50,6 @@ static int max7300_probe(struct i2c_client *client,
ts->dev = &client->dev;
ret = __max730x_probe(ts);
- if (ret)
- kfree(ts);
return ret;
}
diff --git a/drivers/gpio/gpio-max7301.c b/drivers/gpio/gpio-max7301.c
index c6c535c1310e..3b16ab701630 100644
--- a/drivers/gpio/gpio-max7301.c
+++ b/drivers/gpio/gpio-max7301.c
@@ -56,12 +56,13 @@ static int max7301_probe(struct spi_device *spi)
int ret;
/* bits_per_word cannot be configured in platform data */
- spi->bits_per_word = 16;
+ if (spi->dev.platform_data)
+ spi->bits_per_word = 16;
ret = spi_setup(spi);
if (ret < 0)
return ret;
- ts = kzalloc(sizeof(struct max7301), GFP_KERNEL);
+ ts = devm_kzalloc(&spi->dev, sizeof(struct max7301), GFP_KERNEL);
if (!ts)
return -ENOMEM;
@@ -70,8 +71,6 @@ static int max7301_probe(struct spi_device *spi)
ts->dev = &spi->dev;
ret = __max730x_probe(ts);
- if (ret)
- kfree(ts);
return ret;
}
diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c
index 1e0467ce4c37..d4b51b163b03 100644
--- a/drivers/gpio/gpio-max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -589,7 +589,8 @@ static int max732x_probe(struct i2c_client *client,
return -EINVAL;
}
- chip = kzalloc(sizeof(struct max732x_chip), GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev, sizeof(struct max732x_chip),
+ GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->client = client;
@@ -647,7 +648,6 @@ static int max732x_probe(struct i2c_client *client,
out_failed:
max732x_irq_teardown(chip);
- kfree(chip);
return ret;
}
@@ -680,7 +680,6 @@ static int max732x_remove(struct i2c_client *client)
if (chip->client_dummy)
i2c_unregister_device(chip->client_dummy);
- kfree(chip);
return 0;
}
diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c
index 6a8fdc26ae6a..63a7a1bfb2d9 100644
--- a/drivers/gpio/gpio-mc33880.c
+++ b/drivers/gpio/gpio-mc33880.c
@@ -101,13 +101,13 @@ static int mc33880_probe(struct spi_device *spi)
if (ret < 0)
return ret;
- mc = kzalloc(sizeof(struct mc33880), GFP_KERNEL);
+ mc = devm_kzalloc(&spi->dev, sizeof(struct mc33880), GFP_KERNEL);
if (!mc)
return -ENOMEM;
mutex_init(&mc->lock);
- dev_set_drvdata(&spi->dev, mc);
+ spi_set_drvdata(spi, mc);
mc->spi = spi;
@@ -130,7 +130,8 @@ static int mc33880_probe(struct spi_device *spi)
ret = mc33880_write_config(mc);
if (ret) {
- printk(KERN_ERR "Failed writing to " DRIVER_NAME ": %d\n", ret);
+ dev_err(&spi->dev, "Failed writing to " DRIVER_NAME ": %d\n",
+ ret);
goto exit_destroy;
}
@@ -141,9 +142,8 @@ static int mc33880_probe(struct spi_device *spi)
return ret;
exit_destroy:
- dev_set_drvdata(&spi->dev, NULL);
+ spi_set_drvdata(spi, NULL);
mutex_destroy(&mc->lock);
- kfree(mc);
return ret;
}
@@ -152,17 +152,16 @@ static int mc33880_remove(struct spi_device *spi)
struct mc33880 *mc;
int ret;
- mc = dev_get_drvdata(&spi->dev);
+ mc = spi_get_drvdata(spi);
if (mc == NULL)
return -ENODEV;
- dev_set_drvdata(&spi->dev, NULL);
+ spi_set_drvdata(spi, NULL);
ret = gpiochip_remove(&mc->chip);
- if (!ret) {
+ if (!ret)
mutex_destroy(&mc->lock);
- kfree(mc);
- } else
+ else
dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n",
ret);
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index 3cea0ea79e80..6a4470b84488 100644
--- a/drivers/gpio/gpio-mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -12,6 +12,8 @@
#include <linux/spi/mcp23s08.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
/**
* MCP types supported by driver
@@ -383,6 +385,10 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
mcp->chip.direction_output = mcp23s08_direction_output;
mcp->chip.set = mcp23s08_set;
mcp->chip.dbg_show = mcp23s08_dbg_show;
+#ifdef CONFIG_OF
+ mcp->chip.of_gpio_n_cells = 2;
+ mcp->chip.of_node = dev->of_node;
+#endif
switch (type) {
#ifdef CONFIG_SPI_MASTER
@@ -473,6 +479,35 @@ fail:
/*----------------------------------------------------------------------*/
+#ifdef CONFIG_OF
+#ifdef CONFIG_SPI_MASTER
+static struct of_device_id mcp23s08_spi_of_match[] = {
+ {
+ .compatible = "mcp,mcp23s08", .data = (void *) MCP_TYPE_S08,
+ },
+ {
+ .compatible = "mcp,mcp23s17", .data = (void *) MCP_TYPE_S17,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mcp23s08_spi_of_match);
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+static struct of_device_id mcp23s08_i2c_of_match[] = {
+ {
+ .compatible = "mcp,mcp23008", .data = (void *) MCP_TYPE_008,
+ },
+ {
+ .compatible = "mcp,mcp23017", .data = (void *) MCP_TYPE_017,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match);
+#endif
+#endif /* CONFIG_OF */
+
+
#if IS_ENABLED(CONFIG_I2C)
static int mcp230xx_probe(struct i2c_client *client,
@@ -480,12 +515,23 @@ static int mcp230xx_probe(struct i2c_client *client,
{
struct mcp23s08_platform_data *pdata;
struct mcp23s08 *mcp;
- int status;
-
- pdata = client->dev.platform_data;
- if (!pdata || !gpio_is_valid(pdata->base)) {
- dev_dbg(&client->dev, "invalid or missing platform data\n");
- return -EINVAL;
+ int status, base, pullups;
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match),
+ &client->dev);
+ if (match) {
+ base = -1;
+ pullups = 0;
+ } else {
+ pdata = client->dev.platform_data;
+ if (!pdata || !gpio_is_valid(pdata->base)) {
+ dev_dbg(&client->dev,
+ "invalid or missing platform data\n");
+ return -EINVAL;
+ }
+ base = pdata->base;
+ pullups = pdata->chip[0].pullups;
}
mcp = kzalloc(sizeof *mcp, GFP_KERNEL);
@@ -493,8 +539,7 @@ static int mcp230xx_probe(struct i2c_client *client,
return -ENOMEM;
status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr,
- id->driver_data, pdata->base,
- pdata->chip[0].pullups);
+ id->driver_data, base, pullups);
if (status)
goto fail;
@@ -531,6 +576,7 @@ static struct i2c_driver mcp230xx_driver = {
.driver = {
.name = "mcp230xx",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mcp23s08_i2c_of_match),
},
.probe = mcp230xx_probe,
.remove = mcp230xx_remove,
@@ -565,28 +611,55 @@ static int mcp23s08_probe(struct spi_device *spi)
unsigned chips = 0;
struct mcp23s08_driver_data *data;
int status, type;
- unsigned base;
-
- type = spi_get_device_id(spi)->driver_data;
-
- pdata = spi->dev.platform_data;
- if (!pdata || !gpio_is_valid(pdata->base)) {
- dev_dbg(&spi->dev, "invalid or missing platform data\n");
- return -EINVAL;
- }
+ unsigned base = -1,
+ ngpio = 0,
+ pullups[ARRAY_SIZE(pdata->chip)];
+ const struct of_device_id *match;
+ u32 spi_present_mask = 0;
+
+ match = of_match_device(of_match_ptr(mcp23s08_spi_of_match), &spi->dev);
+ if (match) {
+ type = (int)match->data;
+ status = of_property_read_u32(spi->dev.of_node,
+ "mcp,spi-present-mask", &spi_present_mask);
+ if (status) {
+ dev_err(&spi->dev, "DT has no spi-present-mask\n");
+ return -ENODEV;
+ }
+ if ((spi_present_mask <= 0) || (spi_present_mask >= 256)) {
+ dev_err(&spi->dev, "invalid spi-present-mask\n");
+ return -ENODEV;
+ }
- for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
- if (!pdata->chip[addr].is_present)
- continue;
- chips++;
- if ((type == MCP_TYPE_S08) && (addr > 3)) {
- dev_err(&spi->dev,
- "mcp23s08 only supports address 0..3\n");
+ for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++)
+ pullups[addr] = 0;
+ } else {
+ type = spi_get_device_id(spi)->driver_data;
+ pdata = spi->dev.platform_data;
+ if (!pdata || !gpio_is_valid(pdata->base)) {
+ dev_dbg(&spi->dev,
+ "invalid or missing platform data\n");
return -EINVAL;
}
+
+ for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
+ if (!pdata->chip[addr].is_present)
+ continue;
+ chips++;
+ if ((type == MCP_TYPE_S08) && (addr > 3)) {
+ dev_err(&spi->dev,
+ "mcp23s08 only supports address 0..3\n");
+ return -EINVAL;
+ }
+ spi_present_mask |= 1 << addr;
+ pullups[addr] = pdata->chip[addr].pullups;
+ }
+
+ if (!chips)
+ return -ENODEV;
+
+ base = pdata->base;
}
- if (!chips)
- return -ENODEV;
data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08),
GFP_KERNEL);
@@ -594,21 +667,22 @@ static int mcp23s08_probe(struct spi_device *spi)
return -ENOMEM;
spi_set_drvdata(spi, data);
- base = pdata->base;
for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
- if (!pdata->chip[addr].is_present)
+ if (!(spi_present_mask & (1 << addr)))
continue;
chips--;
data->mcp[addr] = &data->chip[chips];
status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi,
0x40 | (addr << 1), type, base,
- pdata->chip[addr].pullups);
+ pullups[addr]);
if (status < 0)
goto fail;
- base += (type == MCP_TYPE_S17) ? 16 : 8;
+ if (base != -1)
+ base += (type == MCP_TYPE_S17) ? 16 : 8;
+ ngpio += (type == MCP_TYPE_S17) ? 16 : 8;
}
- data->ngpio = base - pdata->base;
+ data->ngpio = ngpio;
/* NOTE: these chips have a relatively sane IRQ framework, with
* per-signal masking and level/edge triggering. It's not yet
@@ -668,6 +742,7 @@ static struct spi_driver mcp23s08_driver = {
.driver = {
.name = "mcp23s08",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mcp23s08_spi_of_match),
},
};
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index 61a6fde6c089..bf69a7eff370 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -117,7 +117,7 @@ static inline void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvc
{
int cpu;
- switch(mvchip->soc_variant) {
+ switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
case MVEBU_GPIO_SOC_VARIANT_MV78200:
return mvchip->membase + GPIO_EDGE_CAUSE_OFF;
@@ -133,7 +133,7 @@ static inline void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvch
{
int cpu;
- switch(mvchip->soc_variant) {
+ switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
return mvchip->membase + GPIO_EDGE_MASK_OFF;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
@@ -151,7 +151,7 @@ static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip)
{
int cpu;
- switch(mvchip->soc_variant) {
+ switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
return mvchip->membase + GPIO_LEVEL_MASK_OFF;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
@@ -401,7 +401,7 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
/*
* Configure interrupt polarity.
*/
- switch(type) {
+ switch (type) {
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_LEVEL_HIGH:
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
@@ -470,18 +470,76 @@ static void mvebu_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
}
}
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct mvebu_gpio_chip *mvchip =
+ container_of(chip, struct mvebu_gpio_chip, chip);
+ u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk;
+ int i;
+
+ out = readl_relaxed(mvebu_gpioreg_out(mvchip));
+ io_conf = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
+ blink = readl_relaxed(mvebu_gpioreg_blink(mvchip));
+ in_pol = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
+ data_in = readl_relaxed(mvebu_gpioreg_data_in(mvchip));
+ cause = readl_relaxed(mvebu_gpioreg_edge_cause(mvchip));
+ edg_msk = readl_relaxed(mvebu_gpioreg_edge_mask(mvchip));
+ lvl_msk = readl_relaxed(mvebu_gpioreg_level_mask(mvchip));
+
+ for (i = 0; i < chip->ngpio; i++) {
+ const char *label;
+ u32 msk;
+ bool is_out;
+
+ label = gpiochip_is_requested(chip, i);
+ if (!label)
+ continue;
+
+ msk = 1 << i;
+ is_out = !(io_conf & msk);
+
+ seq_printf(s, " gpio-%-3d (%-20.20s)", chip->base + i, label);
+
+ if (is_out) {
+ seq_printf(s, " out %s %s\n",
+ out & msk ? "hi" : "lo",
+ blink & msk ? "(blink )" : "");
+ continue;
+ }
+
+ seq_printf(s, " in %s (act %s) - IRQ",
+ (data_in ^ in_pol) & msk ? "hi" : "lo",
+ in_pol & msk ? "lo" : "hi");
+ if (!((edg_msk | lvl_msk) & msk)) {
+ seq_printf(s, " disabled\n");
+ continue;
+ }
+ if (edg_msk & msk)
+ seq_printf(s, " edge ");
+ if (lvl_msk & msk)
+ seq_printf(s, " level");
+ seq_printf(s, " (%s)\n", cause & msk ? "pending" : "clear ");
+ }
+}
+#else
+#define mvebu_gpio_dbg_show NULL
+#endif
+
static struct of_device_id mvebu_gpio_of_match[] = {
{
.compatible = "marvell,orion-gpio",
- .data = (void*) MVEBU_GPIO_SOC_VARIANT_ORION,
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
},
{
.compatible = "marvell,mv78200-gpio",
- .data = (void*) MVEBU_GPIO_SOC_VARIANT_MV78200,
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_MV78200,
},
{
.compatible = "marvell,armadaxp-gpio",
- .data = (void*) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
},
{
/* sentinel */
@@ -509,13 +567,13 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (! res) {
+ if (!res) {
dev_err(&pdev->dev, "Cannot get memory resource\n");
return -ENODEV;
}
mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), GFP_KERNEL);
- if (! mvchip){
+ if (!mvchip) {
dev_err(&pdev->dev, "Cannot allocate memory\n");
return -ENOMEM;
}
@@ -550,6 +608,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
mvchip->chip.ngpio = ngpios;
mvchip->chip.can_sleep = 0;
mvchip->chip.of_node = np;
+ mvchip->chip.dbg_show = mvebu_gpio_dbg_show;
spin_lock_init(&mvchip->lock);
mvchip->membase = devm_ioremap_resource(&pdev->dev, res);
@@ -560,21 +619,21 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
* per-CPU registers */
if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (! res) {
+ if (!res) {
dev_err(&pdev->dev, "Cannot get memory resource\n");
return -ENODEV;
}
mvchip->percpu_membase = devm_ioremap_resource(&pdev->dev,
res);
- if (IS_ERR(mvchip->percpu_membase))
+ if (IS_ERR(mvchip->percpu_membase))
return PTR_ERR(mvchip->percpu_membase);
}
/*
* Mask and clear GPIO interrupts.
*/
- switch(soc_variant) {
+ switch (soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
@@ -632,7 +691,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase,
mvchip->membase, handle_level_irq);
- if (! gc) {
+ if (!gc) {
dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
return -ENOMEM;
}
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index a612ea1c53cb..2050891d9c65 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -52,7 +52,6 @@ struct gpio_bank {
struct list_head node;
void __iomem *base;
u16 irq;
- int irq_base;
struct irq_domain *domain;
u32 non_wakeup_gpios;
u32 enabled_non_wakeup_gpios;
@@ -88,7 +87,14 @@ struct gpio_bank {
static int irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq)
{
- return gpio_irq - bank->irq_base + bank->chip.base;
+ return bank->chip.base + gpio_irq;
+}
+
+static int omap_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+
+ return irq_find_mapping(bank->domain, offset);
}
static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
@@ -420,13 +426,16 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
int retval;
unsigned long flags;
+ if (WARN_ON(!bank->mod_usage))
+ return -EINVAL;
+
#ifdef CONFIG_ARCH_OMAP1
if (d->irq > IH_MPUIO_BASE)
gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE);
#endif
if (!gpio)
- gpio = irq_to_gpio(bank, d->irq);
+ gpio = irq_to_gpio(bank, d->hwirq);
if (type & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
@@ -579,7 +588,7 @@ static void _reset_gpio(struct gpio_bank *bank, int gpio)
static int gpio_wake_enable(struct irq_data *d, unsigned int enable)
{
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
- unsigned int gpio = irq_to_gpio(bank, d->irq);
+ unsigned int gpio = irq_to_gpio(bank, d->hwirq);
return _set_gpio_wakeup(bank, gpio, enable);
}
@@ -679,7 +688,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
void __iomem *isr_reg = NULL;
u32 isr;
- unsigned int gpio_irq, gpio_index;
+ unsigned int bit;
struct gpio_bank *bank;
int unmasked = 0;
struct irq_chip *chip = irq_desc_get_chip(desc);
@@ -693,7 +702,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
if (WARN_ON(!isr_reg))
goto exit;
- while(1) {
+ while (1) {
u32 isr_saved, level_mask = 0;
u32 enabled;
@@ -720,14 +729,9 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
if (!isr)
break;
- gpio_irq = bank->irq_base;
- for (; isr != 0; isr >>= 1, gpio_irq++) {
- int gpio = irq_to_gpio(bank, gpio_irq);
-
- if (!(isr & 1))
- continue;
-
- gpio_index = GPIO_INDEX(bank, gpio);
+ while (isr) {
+ bit = __ffs(isr);
+ isr &= ~(1 << bit);
/*
* Some chips can't respond to both rising and falling
@@ -736,10 +740,10 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
* to respond to the IRQ for the opposite direction.
* This will be indicated in the bank toggle_mask.
*/
- if (bank->toggle_mask & (1 << gpio_index))
- _toggle_gpio_edge_triggering(bank, gpio_index);
+ if (bank->toggle_mask & (1 << bit))
+ _toggle_gpio_edge_triggering(bank, bit);
- generic_handle_irq(gpio_irq);
+ generic_handle_irq(irq_find_mapping(bank->domain, bit));
}
}
/* if bank has any level sensitive GPIO pin interrupt
@@ -755,7 +759,7 @@ exit:
static void gpio_irq_shutdown(struct irq_data *d)
{
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
- unsigned int gpio = irq_to_gpio(bank, d->irq);
+ unsigned int gpio = irq_to_gpio(bank, d->hwirq);
unsigned long flags;
spin_lock_irqsave(&bank->lock, flags);
@@ -766,7 +770,7 @@ static void gpio_irq_shutdown(struct irq_data *d)
static void gpio_ack_irq(struct irq_data *d)
{
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
- unsigned int gpio = irq_to_gpio(bank, d->irq);
+ unsigned int gpio = irq_to_gpio(bank, d->hwirq);
_clear_gpio_irqstatus(bank, gpio);
}
@@ -774,7 +778,7 @@ static void gpio_ack_irq(struct irq_data *d)
static void gpio_mask_irq(struct irq_data *d)
{
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
- unsigned int gpio = irq_to_gpio(bank, d->irq);
+ unsigned int gpio = irq_to_gpio(bank, d->hwirq);
unsigned long flags;
spin_lock_irqsave(&bank->lock, flags);
@@ -786,7 +790,7 @@ static void gpio_mask_irq(struct irq_data *d)
static void gpio_unmask_irq(struct irq_data *d)
{
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
- unsigned int gpio = irq_to_gpio(bank, d->irq);
+ unsigned int gpio = irq_to_gpio(bank, d->hwirq);
unsigned int irq_mask = GPIO_BIT(bank, gpio);
u32 trigger = irqd_get_trigger_type(d);
unsigned long flags;
@@ -952,14 +956,6 @@ static void gpio_set(struct gpio_chip *chip, unsigned offset, int value)
spin_unlock_irqrestore(&bank->lock, flags);
}
-static int gpio_2irq(struct gpio_chip *chip, unsigned offset)
-{
- struct gpio_bank *bank;
-
- bank = container_of(chip, struct gpio_bank, chip);
- return bank->irq_base + offset;
-}
-
/*---------------------------------------------------------------------*/
static void __init omap_gpio_show_rev(struct gpio_bank *bank)
@@ -1056,7 +1052,7 @@ static void omap_gpio_chip_init(struct gpio_bank *bank)
bank->chip.direction_output = gpio_output;
bank->chip.set_debounce = gpio_debounce;
bank->chip.set = gpio_set;
- bank->chip.to_irq = gpio_2irq;
+ bank->chip.to_irq = omap_gpio_to_irq;
if (bank->is_mpuio) {
bank->chip.label = "mpuio";
if (bank->regs->wkup_en)
@@ -1071,15 +1067,16 @@ static void omap_gpio_chip_init(struct gpio_bank *bank)
gpiochip_add(&bank->chip);
- for (j = bank->irq_base; j < bank->irq_base + bank->width; j++) {
- irq_set_lockdep_class(j, &gpio_lock_class);
- irq_set_chip_data(j, bank);
+ for (j = 0; j < bank->width; j++) {
+ int irq = irq_create_mapping(bank->domain, j);
+ irq_set_lockdep_class(irq, &gpio_lock_class);
+ irq_set_chip_data(irq, bank);
if (bank->is_mpuio) {
- omap_mpuio_alloc_gc(bank, j, bank->width);
+ omap_mpuio_alloc_gc(bank, irq, bank->width);
} else {
- irq_set_chip(j, &gpio_irq_chip);
- irq_set_handler(j, handle_simple_irq);
- set_irq_flags(j, IRQF_VALID);
+ irq_set_chip_and_handler(irq, &gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(irq, IRQF_VALID);
}
}
irq_set_chained_handler(bank->irq, gpio_irq_handler);
@@ -1096,7 +1093,6 @@ static int omap_gpio_probe(struct platform_device *pdev)
const struct omap_gpio_platform_data *pdata;
struct resource *res;
struct gpio_bank *bank;
- int ret = 0;
match = of_match_device(of_match_ptr(omap_gpio_match), dev);
@@ -1123,20 +1119,22 @@ static int omap_gpio_probe(struct platform_device *pdev)
bank->width = pdata->bank_width;
bank->is_mpuio = pdata->is_mpuio;
bank->non_wakeup_gpios = pdata->non_wakeup_gpios;
- bank->loses_context = pdata->loses_context;
bank->regs = pdata->regs;
#ifdef CONFIG_OF_GPIO
bank->chip.of_node = of_node_get(node);
#endif
-
- bank->irq_base = irq_alloc_descs(-1, 0, bank->width, 0);
- if (bank->irq_base < 0) {
- dev_err(dev, "Couldn't allocate IRQ numbers\n");
- return -ENODEV;
+ if (node) {
+ if (!of_property_read_bool(node, "ti,gpio-always-on"))
+ bank->loses_context = true;
+ } else {
+ bank->loses_context = pdata->loses_context;
}
- bank->domain = irq_domain_add_legacy(node, bank->width, bank->irq_base,
- 0, &irq_domain_simple_ops, NULL);
+
+ bank->domain = irq_domain_add_linear(node, bank->width,
+ &irq_domain_simple_ops, NULL);
+ if (!bank->domain)
+ return -ENODEV;
if (bank->regs->set_dataout && bank->regs->clr_dataout)
bank->set_dataout = _set_gpio_dataout_reg;
@@ -1149,18 +1147,21 @@ static int omap_gpio_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!res)) {
dev_err(dev, "Invalid mem resource\n");
+ irq_domain_remove(bank->domain);
return -ENODEV;
}
if (!devm_request_mem_region(dev, res->start, resource_size(res),
pdev->name)) {
dev_err(dev, "Region already claimed\n");
+ irq_domain_remove(bank->domain);
return -EBUSY;
}
bank->base = devm_ioremap(dev, res->start, resource_size(res));
if (!bank->base) {
dev_err(dev, "Could not ioremap\n");
+ irq_domain_remove(bank->domain);
return -ENOMEM;
}
@@ -1184,7 +1185,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
list_add_tail(&bank->node, &omap_gpio_list);
- return ret;
+ return 0;
}
#ifdef CONFIG_ARCH_OMAP2PLUS
@@ -1262,9 +1263,9 @@ static int omap_gpio_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
- int context_lost_cnt_after;
u32 l = 0, gen, gen0, gen1;
unsigned long flags;
+ int c;
spin_lock_irqsave(&bank->lock, flags);
_gpio_dbck_enable(bank);
@@ -1280,14 +1281,17 @@ static int omap_gpio_runtime_resume(struct device *dev)
__raw_writel(bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
- if (bank->get_context_loss_count) {
- context_lost_cnt_after =
- bank->get_context_loss_count(bank->dev);
- if (context_lost_cnt_after != bank->context_loss_count) {
+ if (bank->loses_context) {
+ if (!bank->get_context_loss_count) {
omap_gpio_restore_context(bank);
} else {
- spin_unlock_irqrestore(&bank->lock, flags);
- return 0;
+ c = bank->get_context_loss_count(bank->dev);
+ if (c != bank->context_loss_count) {
+ omap_gpio_restore_context(bank);
+ } else {
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return 0;
+ }
}
}
@@ -1296,10 +1300,6 @@ static int omap_gpio_runtime_resume(struct device *dev)
return 0;
}
- __raw_writel(bank->context.fallingdetect,
- bank->base + bank->regs->fallingdetect);
- __raw_writel(bank->context.risingdetect,
- bank->base + bank->regs->risingdetect);
l = __raw_readl(bank->base + bank->regs->datain);
/*
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 9391cf16e990..426c51dd420c 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -146,8 +146,7 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
ret = i2c_smbus_write_i2c_block_data(chip->client,
(reg << bank_shift) | REG_ADDR_AI,
NBANK(chip), val);
- }
- else {
+ } else {
switch (chip->chip_type) {
case PCA953X_TYPE:
ret = i2c_smbus_write_word_data(chip->client,
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index a19b7457a726..e8faf53f3875 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -45,6 +45,7 @@ static const struct i2c_device_id pcf857x_id[] = {
{ "pca9675", 16 },
{ "max7328", 8 },
{ "max7329", 8 },
+ { "tca9554", 8 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcf857x_id);
@@ -267,7 +268,7 @@ static int pcf857x_probe(struct i2c_client *client,
}
/* Allocate, initialize, and register this gpio_chip. */
- gpio = kzalloc(sizeof *gpio, GFP_KERNEL);
+ gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
@@ -390,7 +391,6 @@ fail:
if (pdata && client->irq)
pcf857x_irq_domain_cleanup(gpio);
- kfree(gpio);
return status;
}
@@ -415,9 +415,7 @@ static int pcf857x_remove(struct i2c_client *client)
pcf857x_irq_domain_cleanup(gpio);
status = gpiochip_remove(&gpio->chip);
- if (status == 0)
- kfree(gpio);
- else
+ if (status)
dev_err(&client->dev, "%s --> %d\n", "remove", status);
return status;
}
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index d7a5c9d75525..df2199dd1499 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -628,7 +628,7 @@ static int pxa_gpio_probe(struct platform_device *pdev)
for_each_gpio_chip(gpio, c) {
writel_relaxed(0, c->regbase + GFER_OFFSET);
writel_relaxed(0, c->regbase + GRER_OFFSET);
- writel_relaxed(~0,c->regbase + GEDR_OFFSET);
+ writel_relaxed(~0, c->regbase + GEDR_OFFSET);
/* unmask GPIO edge detect for AP side */
if (gpio_is_mmp_type(gpio_type))
writel_relaxed(~0, c->regbase + ED_MASK_OFFSET);
@@ -712,7 +712,7 @@ static void pxa_gpio_resume(void)
for_each_gpio_chip(gpio, c) {
/* restore level with set/clear */
- writel_relaxed( c->saved_gplr, c->regbase + GPSR_OFFSET);
+ writel_relaxed(c->saved_gplr, c->regbase + GPSR_OFFSET);
writel_relaxed(~c->saved_gplr, c->regbase + GPCR_OFFSET);
writel_relaxed(c->saved_grer, c->regbase + GRER_OFFSET);
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
new file mode 100644
index 000000000000..b4ca450947b8
--- /dev/null
+++ b/drivers/gpio/gpio-rcar.c
@@ -0,0 +1,396 @@
+/*
+ * Renesas R-Car GPIO Support
+ *
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_data/gpio-rcar.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+struct gpio_rcar_priv {
+ void __iomem *base;
+ spinlock_t lock;
+ struct gpio_rcar_config config;
+ struct platform_device *pdev;
+ struct gpio_chip gpio_chip;
+ struct irq_chip irq_chip;
+ struct irq_domain *irq_domain;
+};
+
+#define IOINTSEL 0x00
+#define INOUTSEL 0x04
+#define OUTDT 0x08
+#define INDT 0x0c
+#define INTDT 0x10
+#define INTCLR 0x14
+#define INTMSK 0x18
+#define MSKCLR 0x1c
+#define POSNEG 0x20
+#define EDGLEVEL 0x24
+#define FILONOFF 0x28
+
+static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs)
+{
+ return ioread32(p->base + offs);
+}
+
+static inline void gpio_rcar_write(struct gpio_rcar_priv *p, int offs,
+ u32 value)
+{
+ iowrite32(value, p->base + offs);
+}
+
+static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, int offs,
+ int bit, bool value)
+{
+ u32 tmp = gpio_rcar_read(p, offs);
+
+ if (value)
+ tmp |= BIT(bit);
+ else
+ tmp &= ~BIT(bit);
+
+ gpio_rcar_write(p, offs, tmp);
+}
+
+static void gpio_rcar_irq_disable(struct irq_data *d)
+{
+ struct gpio_rcar_priv *p = irq_data_get_irq_chip_data(d);
+
+ gpio_rcar_write(p, INTMSK, ~BIT(irqd_to_hwirq(d)));
+}
+
+static void gpio_rcar_irq_enable(struct irq_data *d)
+{
+ struct gpio_rcar_priv *p = irq_data_get_irq_chip_data(d);
+
+ gpio_rcar_write(p, MSKCLR, BIT(irqd_to_hwirq(d)));
+}
+
+static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p,
+ unsigned int hwirq,
+ bool active_high_rising_edge,
+ bool level_trigger)
+{
+ unsigned long flags;
+
+ /* follow steps in the GPIO documentation for
+ * "Setting Edge-Sensitive Interrupt Input Mode" and
+ * "Setting Level-Sensitive Interrupt Input Mode"
+ */
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ /* Configure postive or negative logic in POSNEG */
+ gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge);
+
+ /* Configure edge or level trigger in EDGLEVEL */
+ gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger);
+
+ /* Select "Interrupt Input Mode" in IOINTSEL */
+ gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true);
+
+ /* Write INTCLR in case of edge trigger */
+ if (!level_trigger)
+ gpio_rcar_write(p, INTCLR, BIT(hwirq));
+
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_rcar_priv *p = irq_data_get_irq_chip_data(d);
+ unsigned int hwirq = irqd_to_hwirq(d);
+
+ dev_dbg(&p->pdev->dev, "sense irq = %d, type = %d\n", hwirq, type);
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true);
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_rcar_priv *p = dev_id;
+ u32 pending;
+ unsigned int offset, irqs_handled = 0;
+
+ while ((pending = gpio_rcar_read(p, INTDT))) {
+ offset = __ffs(pending);
+ gpio_rcar_write(p, INTCLR, BIT(offset));
+ generic_handle_irq(irq_find_mapping(p->irq_domain, offset));
+ irqs_handled++;
+ }
+
+ return irqs_handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline struct gpio_rcar_priv *gpio_to_priv(struct gpio_chip *chip)
+{
+ return container_of(chip, struct gpio_rcar_priv, gpio_chip);
+}
+
+static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip,
+ unsigned int gpio,
+ bool output)
+{
+ struct gpio_rcar_priv *p = gpio_to_priv(chip);
+ unsigned long flags;
+
+ /* follow steps in the GPIO documentation for
+ * "Setting General Output Mode" and
+ * "Setting General Input Mode"
+ */
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ /* Configure postive logic in POSNEG */
+ gpio_rcar_modify_bit(p, POSNEG, gpio, false);
+
+ /* Select "General Input/Output Mode" in IOINTSEL */
+ gpio_rcar_modify_bit(p, IOINTSEL, gpio, false);
+
+ /* Select Input Mode or Output Mode in INOUTSEL */
+ gpio_rcar_modify_bit(p, INOUTSEL, gpio, output);
+
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+
+ /* Set the GPIO as an input to ensure that the next GPIO request won't
+ * drive the GPIO pin as an output.
+ */
+ gpio_rcar_config_general_input_output_mode(chip, offset, false);
+}
+
+static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ gpio_rcar_config_general_input_output_mode(chip, offset, false);
+ return 0;
+}
+
+static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset)
+{
+ return (int)(gpio_rcar_read(gpio_to_priv(chip), INDT) & BIT(offset));
+}
+
+static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct gpio_rcar_priv *p = gpio_to_priv(chip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->lock, flags);
+ gpio_rcar_modify_bit(p, OUTDT, offset, value);
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ /* write GPIO value to output before selecting output mode of pin */
+ gpio_rcar_set(chip, offset, value);
+ gpio_rcar_config_general_input_output_mode(chip, offset, true);
+ return 0;
+}
+
+static int gpio_rcar_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset);
+}
+
+static int gpio_rcar_irq_domain_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct gpio_rcar_priv *p = h->host_data;
+
+ dev_dbg(&p->pdev->dev, "map hw irq = %d, virq = %d\n", (int)hw, virq);
+
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
+ set_irq_flags(virq, IRQF_VALID); /* kill me now */
+ return 0;
+}
+
+static struct irq_domain_ops gpio_rcar_irq_domain_ops = {
+ .map = gpio_rcar_irq_domain_map,
+};
+
+static int gpio_rcar_probe(struct platform_device *pdev)
+{
+ struct gpio_rcar_config *pdata = pdev->dev.platform_data;
+ struct gpio_rcar_priv *p;
+ struct resource *io, *irq;
+ struct gpio_chip *gpio_chip;
+ struct irq_chip *irq_chip;
+ const char *name = dev_name(&pdev->dev);
+ int ret;
+
+ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ /* deal with driver instance configuration */
+ if (pdata)
+ p->config = *pdata;
+
+ p->pdev = pdev;
+ platform_set_drvdata(pdev, p);
+ spin_lock_init(&p->lock);
+
+ io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+ if (!io || !irq) {
+ dev_err(&pdev->dev, "missing IRQ or IOMEM\n");
+ ret = -EINVAL;
+ goto err0;
+ }
+
+ p->base = devm_ioremap_nocache(&pdev->dev, io->start,
+ resource_size(io));
+ if (!p->base) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ ret = -ENXIO;
+ goto err0;
+ }
+
+ gpio_chip = &p->gpio_chip;
+ gpio_chip->request = gpio_rcar_request;
+ gpio_chip->free = gpio_rcar_free;
+ gpio_chip->direction_input = gpio_rcar_direction_input;
+ gpio_chip->get = gpio_rcar_get;
+ gpio_chip->direction_output = gpio_rcar_direction_output;
+ gpio_chip->set = gpio_rcar_set;
+ gpio_chip->to_irq = gpio_rcar_to_irq;
+ gpio_chip->label = name;
+ gpio_chip->owner = THIS_MODULE;
+ gpio_chip->base = p->config.gpio_base;
+ gpio_chip->ngpio = p->config.number_of_pins;
+
+ irq_chip = &p->irq_chip;
+ irq_chip->name = name;
+ irq_chip->irq_mask = gpio_rcar_irq_disable;
+ irq_chip->irq_unmask = gpio_rcar_irq_enable;
+ irq_chip->irq_enable = gpio_rcar_irq_enable;
+ irq_chip->irq_disable = gpio_rcar_irq_disable;
+ irq_chip->irq_set_type = gpio_rcar_irq_set_type;
+ irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_SET_TYPE_MASKED;
+
+ p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
+ p->config.number_of_pins,
+ p->config.irq_base,
+ &gpio_rcar_irq_domain_ops, p);
+ if (!p->irq_domain) {
+ ret = -ENXIO;
+ dev_err(&pdev->dev, "cannot initialize irq domain\n");
+ goto err1;
+ }
+
+ if (devm_request_irq(&pdev->dev, irq->start,
+ gpio_rcar_irq_handler, 0, name, p)) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ ret = -ENOENT;
+ goto err1;
+ }
+
+ ret = gpiochip_add(gpio_chip);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add GPIO controller\n");
+ goto err1;
+ }
+
+ dev_info(&pdev->dev, "driving %d GPIOs\n", p->config.number_of_pins);
+
+ /* warn in case of mismatch if irq base is specified */
+ if (p->config.irq_base) {
+ ret = irq_find_mapping(p->irq_domain, 0);
+ if (p->config.irq_base != ret)
+ dev_warn(&pdev->dev, "irq base mismatch (%u/%u)\n",
+ p->config.irq_base, ret);
+ }
+
+ ret = gpiochip_add_pin_range(gpio_chip, p->config.pctl_name, 0,
+ gpio_chip->base, gpio_chip->ngpio);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "failed to add pin range\n");
+
+ return 0;
+
+err1:
+ irq_domain_remove(p->irq_domain);
+err0:
+ return ret;
+}
+
+static int gpio_rcar_remove(struct platform_device *pdev)
+{
+ struct gpio_rcar_priv *p = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = gpiochip_remove(&p->gpio_chip);
+ if (ret)
+ return ret;
+
+ irq_domain_remove(p->irq_domain);
+ return 0;
+}
+
+static struct platform_driver gpio_rcar_device_driver = {
+ .probe = gpio_rcar_probe,
+ .remove = gpio_rcar_remove,
+ .driver = {
+ .name = "gpio_rcar",
+ }
+};
+
+module_platform_driver(gpio_rcar_device_driver);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Renesas R-Car GPIO Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index 99e0fa49fcbd..b22ca7933745 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -3030,6 +3030,7 @@ static __init int samsung_gpiolib_init(void)
{ .compatible = "samsung,exynos4x12-pinctrl", },
{ .compatible = "samsung,exynos5250-pinctrl", },
{ .compatible = "samsung,exynos5440-pinctrl", },
+ { }
};
for_each_matching_node(pctrl_np, exynos_pinctrl_ids)
if (pctrl_np && of_device_is_available(pctrl_np))
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
index edae963f4625..1e4de16ceb41 100644
--- a/drivers/gpio/gpio-sch.c
+++ b/drivers/gpio/gpio-sch.c
@@ -125,13 +125,17 @@ static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
unsigned gpio_num)
{
u8 curr_dirs;
+ unsigned short offset, bit;
spin_lock(&gpio_lock);
- curr_dirs = inb(gpio_ba + RGIO);
+ offset = RGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_dirs = inb(gpio_ba + offset);
- if (!(curr_dirs & (1 << gpio_num)))
- outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO);
+ if (!(curr_dirs & (1 << bit)))
+ outb(curr_dirs | (1 << bit), gpio_ba + offset);
spin_unlock(&gpio_lock);
return 0;
@@ -139,22 +143,31 @@ static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
{
- return !!(inb(gpio_ba + RGLV) & (1 << gpio_num));
+ unsigned short offset, bit;
+
+ offset = RGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ return !!(inb(gpio_ba + offset) & (1 << bit));
}
static void sch_gpio_resume_set(struct gpio_chip *gc,
unsigned gpio_num, int val)
{
u8 curr_vals;
+ unsigned short offset, bit;
spin_lock(&gpio_lock);
- curr_vals = inb(gpio_ba + RGLV);
+ offset = RGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_vals = inb(gpio_ba + offset);
if (val)
- outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV);
+ outb(curr_vals | (1 << bit), gpio_ba + offset);
else
- outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV);
+ outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
spin_unlock(&gpio_lock);
}
@@ -163,14 +176,18 @@ static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
unsigned gpio_num, int val)
{
u8 curr_dirs;
+ unsigned short offset, bit;
sch_gpio_resume_set(gc, gpio_num, val);
+ offset = RGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
spin_lock(&gpio_lock);
- curr_dirs = inb(gpio_ba + RGIO);
- if (curr_dirs & (1 << gpio_num))
- outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO);
+ curr_dirs = inb(gpio_ba + offset);
+ if (curr_dirs & (1 << bit))
+ outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
spin_unlock(&gpio_lock);
return 0;
@@ -204,45 +221,41 @@ static int sch_gpio_probe(struct platform_device *pdev)
gpio_ba = res->start;
switch (id) {
- case PCI_DEVICE_ID_INTEL_SCH_LPC:
- sch_gpio_core.base = 0;
- sch_gpio_core.ngpio = 10;
-
- sch_gpio_resume.base = 10;
- sch_gpio_resume.ngpio = 4;
-
- /*
- * GPIO[6:0] enabled by default
- * GPIO7 is configured by the CMC as SLPIOVR
- * Enable GPIO[9:8] core powered gpios explicitly
- */
- outb(0x3, gpio_ba + CGEN + 1);
- /*
- * SUS_GPIO[2:0] enabled by default
- * Enable SUS_GPIO3 resume powered gpio explicitly
- */
- outb(0x8, gpio_ba + RGEN);
- break;
-
- case PCI_DEVICE_ID_INTEL_ITC_LPC:
- sch_gpio_core.base = 0;
- sch_gpio_core.ngpio = 5;
-
- sch_gpio_resume.base = 5;
- sch_gpio_resume.ngpio = 9;
- break;
-
- case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
- sch_gpio_core.base = 0;
- sch_gpio_core.ngpio = 21;
-
- sch_gpio_resume.base = 21;
- sch_gpio_resume.ngpio = 9;
- break;
-
- default:
- err = -ENODEV;
- goto err_sch_gpio_core;
+ case PCI_DEVICE_ID_INTEL_SCH_LPC:
+ sch_gpio_core.base = 0;
+ sch_gpio_core.ngpio = 10;
+ sch_gpio_resume.base = 10;
+ sch_gpio_resume.ngpio = 4;
+ /*
+ * GPIO[6:0] enabled by default
+ * GPIO7 is configured by the CMC as SLPIOVR
+ * Enable GPIO[9:8] core powered gpios explicitly
+ */
+ outb(0x3, gpio_ba + CGEN + 1);
+ /*
+ * SUS_GPIO[2:0] enabled by default
+ * Enable SUS_GPIO3 resume powered gpio explicitly
+ */
+ outb(0x8, gpio_ba + RGEN);
+ break;
+
+ case PCI_DEVICE_ID_INTEL_ITC_LPC:
+ sch_gpio_core.base = 0;
+ sch_gpio_core.ngpio = 5;
+ sch_gpio_resume.base = 5;
+ sch_gpio_resume.ngpio = 9;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
+ sch_gpio_core.base = 0;
+ sch_gpio_core.ngpio = 21;
+ sch_gpio_resume.base = 21;
+ sch_gpio_resume.ngpio = 9;
+ break;
+
+ default:
+ err = -ENODEV;
+ goto err_sch_gpio_core;
}
sch_gpio_core.dev = &pdev->dev;
diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
index c20e05151212..04882a911b65 100644
--- a/drivers/gpio/gpio-stp-xway.c
+++ b/drivers/gpio/gpio-stp-xway.c
@@ -217,7 +217,7 @@ static int xway_stp_probe(struct platform_device *pdev)
chip->virt = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(chip->virt))
return PTR_ERR(chip->virt);
-
+
chip->gc.dev = &pdev->dev;
chip->gc.label = "stp-xway";
chip->gc.direction_output = xway_stp_dir_out;
diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c
index c0595bbf3268..d34d80dfb083 100644
--- a/drivers/gpio/gpio-tc3589x.c
+++ b/drivers/gpio/gpio-tc3589x.c
@@ -282,9 +282,9 @@ static void tc3589x_gpio_irq_unmap(struct irq_domain *d, unsigned int virq)
}
static struct irq_domain_ops tc3589x_irq_ops = {
- .map = tc3589x_gpio_irq_map,
- .unmap = tc3589x_gpio_irq_unmap,
- .xlate = irq_domain_xlate_twocell,
+ .map = tc3589x_gpio_irq_map,
+ .unmap = tc3589x_gpio_irq_unmap,
+ .xlate = irq_domain_xlate_twocell,
};
static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio,
@@ -344,7 +344,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
tc3589x_gpio->chip.base = (pdata) ? pdata->gpio_base : -1;
#ifdef CONFIG_OF_GPIO
- tc3589x_gpio->chip.of_node = np;
+ tc3589x_gpio->chip.of_node = np;
#endif
tc3589x_gpio->irq_base = tc3589x->irq_base ?
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index dde0656ea951..da4cb5b0cb87 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -414,10 +414,11 @@ static int tegra_gpio_probe(struct platform_device *pdev)
int j;
match = of_match_device(tegra_gpio_of_match, &pdev->dev);
- if (match)
- config = (struct tegra_gpio_soc_config *)match->data;
- else
- config = &tegra20_gpio_config;
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ config = (struct tegra_gpio_soc_config *)match->data;
tegra_gpio_bank_stride = config->bank_stride;
tegra_gpio_upper_offset = config->upper_offset;
@@ -478,9 +479,7 @@ static int tegra_gpio_probe(struct platform_device *pdev)
}
}
-#ifdef CONFIG_OF_GPIO
tegra_gpio_chip.of_node = pdev->dev.of_node;
-#endif
gpiochip_add(&tegra_gpio_chip);
diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c
index 702cca9284f1..43774058b693 100644
--- a/drivers/gpio/gpio-timberdale.c
+++ b/drivers/gpio/gpio-timberdale.c
@@ -167,8 +167,7 @@ static int timbgpio_irq_type(struct irq_data *d, unsigned trigger)
if (ver < 3) {
ret = -EINVAL;
goto out;
- }
- else {
+ } else {
flr |= 1 << offset;
bflr |= 1 << offset;
}
diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c
index 5083825a0348..06146219d9d2 100644
--- a/drivers/gpio/gpio-tps65910.c
+++ b/drivers/gpio/gpio-tps65910.c
@@ -133,7 +133,7 @@ static int tps65910_gpio_probe(struct platform_device *pdev)
tps65910_gpio->gpio_chip.owner = THIS_MODULE;
tps65910_gpio->gpio_chip.label = tps65910->i2c_client->name;
- switch(tps65910_chip_id(tps65910)) {
+ switch (tps65910_chip_id(tps65910)) {
case TPS65910:
tps65910_gpio->gpio_chip.ngpio = TPS65910_NUM_GPIO;
break;
diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c
index 59d72391de26..095ab14cea4d 100644
--- a/drivers/gpio/gpio-viperboard.c
+++ b/drivers/gpio/gpio-viperboard.c
@@ -380,10 +380,6 @@ static int vprbrd_gpiob_direction_output(struct gpio_chip *chip,
struct vprbrd *vb = gpio->vb;
gpio->gpiob_out |= (1 << offset);
- if (value)
- gpio->gpiob_val |= (1 << offset);
- else
- gpio->gpiob_val &= ~(1 << offset);
mutex_lock(&vb->lock);
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index a063eb04b6ce..5c1ef2b3ef18 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -17,6 +17,13 @@
#include <linux/acpi.h>
#include <linux/interrupt.h>
+struct acpi_gpio_evt_pin {
+ struct list_head node;
+ acpi_handle *evt_handle;
+ unsigned int pin;
+ unsigned int irq;
+};
+
static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
{
if (!gc->dev)
@@ -54,7 +61,6 @@ int acpi_get_gpio(char *path, int pin)
}
EXPORT_SYMBOL_GPL(acpi_get_gpio);
-
static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
{
acpi_handle handle = data;
@@ -64,6 +70,27 @@ static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
+static irqreturn_t acpi_gpio_irq_handler_evt(int irq, void *data)
+{
+ struct acpi_gpio_evt_pin *evt_pin = data;
+ struct acpi_object_list args;
+ union acpi_object arg;
+
+ arg.type = ACPI_TYPE_INTEGER;
+ arg.integer.value = evt_pin->pin;
+ args.count = 1;
+ args.pointer = &arg;
+
+ acpi_evaluate_object(evt_pin->evt_handle, NULL, &args, NULL);
+
+ return IRQ_HANDLED;
+}
+
+static void acpi_gpio_evt_dh(acpi_handle handle, void *data)
+{
+ /* The address of this function is used as a key. */
+}
+
/**
* acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events
* @chip: gpio chip
@@ -73,15 +100,13 @@ static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
* chip's interrupt handler. acpi_gpiochip_request_interrupts finds out which
* gpio pins have acpi event methods and assigns interrupt handlers that calls
* the acpi event methods for those pins.
- *
- * Interrupts are automatically freed on driver detach
*/
-
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
{
struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
struct acpi_resource *res;
- acpi_handle handle, ev_handle;
+ acpi_handle handle, evt_handle;
+ struct list_head *evt_pins = NULL;
acpi_status status;
unsigned int pin;
int irq, ret;
@@ -98,13 +123,30 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
if (ACPI_FAILURE(status))
return;
- /* If a gpio interrupt has an acpi event handler method, then
- * set up an interrupt handler that calls the acpi event handler
- */
+ status = acpi_get_handle(handle, "_EVT", &evt_handle);
+ if (ACPI_SUCCESS(status)) {
+ evt_pins = kzalloc(sizeof(*evt_pins), GFP_KERNEL);
+ if (evt_pins) {
+ INIT_LIST_HEAD(evt_pins);
+ status = acpi_attach_data(handle, acpi_gpio_evt_dh,
+ evt_pins);
+ if (ACPI_FAILURE(status)) {
+ kfree(evt_pins);
+ evt_pins = NULL;
+ }
+ }
+ }
+ /*
+ * If a GPIO interrupt has an ACPI event handler method, or _EVT is
+ * present, set up an interrupt handler that calls the ACPI event
+ * handler.
+ */
for (res = buf.pointer;
res && (res->type != ACPI_RESOURCE_TYPE_END_TAG);
res = ACPI_NEXT_RESOURCE(res)) {
+ irq_handler_t handler = NULL;
+ void *data;
if (res->type != ACPI_RESOURCE_TYPE_GPIO ||
res->data.gpio.connection_type !=
@@ -115,23 +157,42 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
if (pin > chip->ngpio)
continue;
- sprintf(ev_name, "_%c%02X",
- res->data.gpio.triggering ? 'E' : 'L', pin);
-
- status = acpi_get_handle(handle, ev_name, &ev_handle);
- if (ACPI_FAILURE(status))
- continue;
-
irq = chip->to_irq(chip, pin);
if (irq < 0)
continue;
+ if (pin <= 255) {
+ acpi_handle ev_handle;
+
+ sprintf(ev_name, "_%c%02X",
+ res->data.gpio.triggering ? 'E' : 'L', pin);
+ status = acpi_get_handle(handle, ev_name, &ev_handle);
+ if (ACPI_SUCCESS(status)) {
+ handler = acpi_gpio_irq_handler;
+ data = ev_handle;
+ }
+ }
+ if (!handler && evt_pins) {
+ struct acpi_gpio_evt_pin *evt_pin;
+
+ evt_pin = kzalloc(sizeof(*evt_pin), GFP_KERNEL);
+ if (!evt_pin)
+ continue;
+
+ list_add_tail(&evt_pin->node, evt_pins);
+ evt_pin->evt_handle = evt_handle;
+ evt_pin->pin = pin;
+ evt_pin->irq = irq;
+ handler = acpi_gpio_irq_handler_evt;
+ data = evt_pin;
+ }
+ if (!handler)
+ continue;
+
/* Assume BIOS sets the triggering, so no flags */
- ret = devm_request_threaded_irq(chip->dev, irq, NULL,
- acpi_gpio_irq_handler,
- 0,
- "GPIO-signaled-ACPI-event",
- ev_handle);
+ ret = devm_request_threaded_irq(chip->dev, irq, NULL, handler,
+ 0, "GPIO-signaled-ACPI-event",
+ data);
if (ret)
dev_err(chip->dev,
"Failed to request IRQ %d ACPI event handler\n",
@@ -139,3 +200,119 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
}
}
EXPORT_SYMBOL(acpi_gpiochip_request_interrupts);
+
+struct acpi_gpio_lookup {
+ struct acpi_gpio_info info;
+ int index;
+ int gpio;
+ int n;
+};
+
+static int acpi_find_gpio(struct acpi_resource *ares, void *data)
+{
+ struct acpi_gpio_lookup *lookup = data;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
+ return 1;
+
+ if (lookup->n++ == lookup->index && lookup->gpio < 0) {
+ const struct acpi_resource_gpio *agpio = &ares->data.gpio;
+
+ lookup->gpio = acpi_get_gpio(agpio->resource_source.string_ptr,
+ agpio->pin_table[0]);
+ lookup->info.gpioint =
+ agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT;
+ }
+
+ return 1;
+}
+
+/**
+ * acpi_get_gpio_by_index() - get a GPIO number from device resources
+ * @dev: pointer to a device to get GPIO from
+ * @index: index of GpioIo/GpioInt resource (starting from %0)
+ * @info: info pointer to fill in (optional)
+ *
+ * Function goes through ACPI resources for @dev and based on @index looks
+ * up a GpioIo/GpioInt resource, translates it to the Linux GPIO number,
+ * and returns it. @index matches GpioIo/GpioInt resources only so if there
+ * are total %3 GPIO resources, the index goes from %0 to %2.
+ *
+ * If the GPIO cannot be translated or there is an error, negative errno is
+ * returned.
+ *
+ * Note: if the GPIO resource has multiple entries in the pin list, this
+ * function only returns the first.
+ */
+int acpi_get_gpio_by_index(struct device *dev, int index,
+ struct acpi_gpio_info *info)
+{
+ struct acpi_gpio_lookup lookup;
+ struct list_head resource_list;
+ struct acpi_device *adev;
+ acpi_handle handle;
+ int ret;
+
+ if (!dev)
+ return -EINVAL;
+
+ handle = ACPI_HANDLE(dev);
+ if (!handle || acpi_bus_get_device(handle, &adev))
+ return -ENODEV;
+
+ memset(&lookup, 0, sizeof(lookup));
+ lookup.index = index;
+ lookup.gpio = -ENODEV;
+
+ INIT_LIST_HEAD(&resource_list);
+ ret = acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio,
+ &lookup);
+ if (ret < 0)
+ return ret;
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ if (lookup.gpio >= 0 && info)
+ *info = lookup.info;
+
+ return lookup.gpio;
+}
+EXPORT_SYMBOL_GPL(acpi_get_gpio_by_index);
+
+/**
+ * acpi_gpiochip_free_interrupts() - Free GPIO _EVT ACPI event interrupts.
+ * @chip: gpio chip
+ *
+ * Free interrupts associated with the _EVT method for the given GPIO chip.
+ *
+ * The remaining ACPI event interrupts associated with the chip are freed
+ * automatically.
+ */
+void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
+{
+ acpi_handle handle;
+ acpi_status status;
+ struct list_head *evt_pins;
+ struct acpi_gpio_evt_pin *evt_pin, *ep;
+
+ if (!chip->dev || !chip->to_irq)
+ return;
+
+ handle = ACPI_HANDLE(chip->dev);
+ if (!handle)
+ return;
+
+ status = acpi_get_data(handle, acpi_gpio_evt_dh, (void **)&evt_pins);
+ if (ACPI_FAILURE(status))
+ return;
+
+ list_for_each_entry_safe_reverse(evt_pin, ep, evt_pins, node) {
+ devm_free_irq(chip->dev, evt_pin->irq, evt_pin);
+ list_del(&evt_pin->node);
+ kfree(evt_pin);
+ }
+
+ acpi_detach_data(handle, acpi_gpio_evt_dh);
+ kfree(evt_pins);
+}
+EXPORT_SYMBOL(acpi_gpiochip_free_interrupts);
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 465f4ca57e80..665f9530c950 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -61,7 +61,7 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data)
* in flags for the GPIO.
*/
int of_get_named_gpio_flags(struct device_node *np, const char *propname,
- int index, enum of_gpio_flags *flags)
+ int index, enum of_gpio_flags *flags)
{
/* Return -EPROBE_DEFER to support probe() functions to be called
* later when the GPIO actually becomes available
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
index c7c3128393d1..70637d23b1f9 100644
--- a/drivers/hwspinlock/Kconfig
+++ b/drivers/hwspinlock/Kconfig
@@ -10,7 +10,7 @@ menu "Hardware Spinlock drivers"
config HWSPINLOCK_OMAP
tristate "OMAP Hardware Spinlock device"
- depends on ARCH_OMAP4
+ depends on ARCH_OMAP4 || SOC_OMAP5
select HWSPINLOCK
help
Say y here to support the OMAP Hardware Spinlock device (firstly
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index c67d89fc6254..2039f230482d 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -31,7 +31,6 @@
#include <linux/of_i2c.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
-#include <linux/fsl/mxs-dma.h>
#define DRIVER_NAME "mxs-i2c"
@@ -118,9 +117,7 @@ struct mxs_i2c_dev {
uint32_t timing1;
/* DMA support components */
- int dma_channel;
struct dma_chan *dmach;
- struct mxs_dma_data dma_data;
uint32_t pio_data[2];
uint32_t addr_data;
struct scatterlist sg_io[2];
@@ -581,21 +578,6 @@ static const struct i2c_algorithm mxs_i2c_algo = {
.functionality = mxs_i2c_func,
};
-static bool mxs_i2c_dma_filter(struct dma_chan *chan, void *param)
-{
- struct mxs_i2c_dev *i2c = param;
-
- if (!mxs_dma_is_apbx(chan))
- return false;
-
- if (chan->chan_id != i2c->dma_channel)
- return false;
-
- chan->private = &i2c->dma_data;
-
- return true;
-}
-
static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, int speed)
{
/* The I2C block clock run at 24MHz */
@@ -640,17 +622,6 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
struct device_node *node = dev->of_node;
int ret;
- /*
- * TODO: This is a temporary solution and should be changed
- * to use generic DMA binding later when the helpers get in.
- */
- ret = of_property_read_u32(node, "fsl,i2c-dma-channel",
- &i2c->dma_channel);
- if (ret) {
- dev_err(dev, "Failed to get DMA channel!\n");
- return -ENODEV;
- }
-
ret = of_property_read_u32(node, "clock-frequency", &speed);
if (ret) {
dev_warn(dev, "No I2C speed selected, using 100kHz\n");
@@ -670,8 +641,7 @@ static int mxs_i2c_probe(struct platform_device *pdev)
struct pinctrl *pinctrl;
struct resource *res;
resource_size_t res_size;
- int err, irq, dmairq;
- dma_cap_mask_t mask;
+ int err, irq;
pinctrl = devm_pinctrl_get_select_default(dev);
if (IS_ERR(pinctrl))
@@ -683,9 +653,8 @@ static int mxs_i2c_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- dmairq = platform_get_irq(pdev, 1);
- if (!res || irq < 0 || dmairq < 0)
+ if (!res || irq < 0)
return -ENOENT;
res_size = resource_size(res);
@@ -711,10 +680,7 @@ static int mxs_i2c_probe(struct platform_device *pdev)
}
/* Setup the DMA */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- i2c->dma_data.chan_irq = dmairq;
- i2c->dmach = dma_request_channel(mask, mxs_i2c_dma_filter, i2c);
+ i2c->dmach = dma_request_slave_channel(dev, "rx-tx");
if (!i2c->dmach) {
dev_err(dev, "Failed to request dma\n");
return -ENODEV;
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index b23113926388..2ff620444930 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -1606,7 +1606,7 @@ out:
return rc;
}
-static int idecd_release(struct gendisk *disk, fmode_t mode)
+static void idecd_release(struct gendisk *disk, fmode_t mode)
{
struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
@@ -1615,8 +1615,6 @@ static int idecd_release(struct gendisk *disk, fmode_t mode)
ide_cd_put(info);
mutex_unlock(&ide_cd_mutex);
-
- return 0;
}
static int idecd_set_spindown(struct cdrom_device_info *cdi, unsigned long arg)
diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c
index 70ea8763567d..de86631e767d 100644
--- a/drivers/ide/ide-gd.c
+++ b/drivers/ide/ide-gd.c
@@ -250,7 +250,7 @@ static int ide_gd_unlocked_open(struct block_device *bdev, fmode_t mode)
}
-static int ide_gd_release(struct gendisk *disk, fmode_t mode)
+static void ide_gd_release(struct gendisk *disk, fmode_t mode)
{
struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj);
ide_drive_t *drive = idkp->drive;
@@ -270,8 +270,6 @@ static int ide_gd_release(struct gendisk *disk, fmode_t mode)
ide_disk_put(idkp);
mutex_unlock(&ide_gd_mutex);
-
- return 0;
}
static int ide_gd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 89f859591bbb..c6c574bd5f59 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -1918,15 +1918,13 @@ static int idetape_open(struct block_device *bdev, fmode_t mode)
return 0;
}
-static int idetape_release(struct gendisk *disk, fmode_t mode)
+static void idetape_release(struct gendisk *disk, fmode_t mode)
{
struct ide_tape_obj *tape = ide_drv_g(disk, ide_tape_obj);
mutex_lock(&ide_tape_mutex);
ide_tape_put(tape);
mutex_unlock(&ide_tape_mutex);
-
- return 0;
}
static int idetape_ioctl(struct block_device *bdev, fmode_t mode,
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c
index 0bb99bb38809..c47c2034ca71 100644
--- a/drivers/infiniband/core/iwcm.c
+++ b/drivers/infiniband/core/iwcm.c
@@ -878,6 +878,8 @@ static void cm_work_handler(struct work_struct *_work)
}
return;
}
+ if (empty)
+ return;
spin_lock_irqsave(&cm_id_priv->lock, flags);
}
spin_unlock_irqrestore(&cm_id_priv->lock, flags);
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index a8fdd3381405..22192deb8828 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -348,7 +348,8 @@ static void __ib_shared_qp_event_handler(struct ib_event *event, void *context)
struct ib_qp *qp = context;
list_for_each_entry(event->element.qp, &qp->open_list, open_list)
- event->element.qp->event_handler(event, event->element.qp->qp_context);
+ if (event->element.qp->event_handler)
+ event->element.qp->event_handler(event, event->element.qp->qp_context);
}
static void __ib_insert_xrcd_qp(struct ib_xrcd *xrcd, struct ib_qp *qp)
diff --git a/drivers/infiniband/hw/cxgb3/cxio_resource.c b/drivers/infiniband/hw/cxgb3/cxio_resource.c
index 31f9201b2980..c40088ecf9f3 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_resource.c
+++ b/drivers/infiniband/hw/cxgb3/cxio_resource.c
@@ -62,13 +62,13 @@ static int __cxio_init_resource_fifo(struct kfifo *fifo,
kfifo_in(fifo, (unsigned char *) &entry, sizeof(u32));
if (random) {
j = 0;
- random_bytes = random32();
+ random_bytes = prandom_u32();
for (i = 0; i < RANDOM_SIZE; i++)
rarray[i] = i + skip_low;
for (i = skip_low + RANDOM_SIZE; i < nr - skip_high; i++) {
if (j >= RANDOM_SIZE) {
j = 0;
- random_bytes = random32();
+ random_bytes = prandom_u32();
}
idx = (random_bytes >> (j * 2)) & 0xF;
kfifo_in(fifo,
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index 9c12da0cbd32..e87f2201b220 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -559,7 +559,7 @@ static int iwch_reregister_phys_mem(struct ib_mr *mr,
__be64 *page_list = NULL;
int shift = 0;
u64 total_size;
- int npages;
+ int npages = 0;
int ret;
PDBG("%s ib_mr %p ib_pd %p\n", __func__, mr, pd);
diff --git a/drivers/infiniband/hw/cxgb4/id_table.c b/drivers/infiniband/hw/cxgb4/id_table.c
index f95e5df30db2..0161ae6ad629 100644
--- a/drivers/infiniband/hw/cxgb4/id_table.c
+++ b/drivers/infiniband/hw/cxgb4/id_table.c
@@ -54,7 +54,7 @@ u32 c4iw_id_alloc(struct c4iw_id_table *alloc)
if (obj < alloc->max) {
if (alloc->flags & C4IW_ID_TABLE_F_RANDOM)
- alloc->last += random32() % RANDOM_SKIP;
+ alloc->last += prandom_u32() % RANDOM_SKIP;
else
alloc->last = obj + 1;
if (alloc->last >= alloc->max)
@@ -88,7 +88,7 @@ int c4iw_id_table_alloc(struct c4iw_id_table *alloc, u32 start, u32 num,
alloc->start = start;
alloc->flags = flags;
if (flags & C4IW_ID_TABLE_F_RANDOM)
- alloc->last = random32() % RANDOM_SKIP;
+ alloc->last = prandom_u32() % RANDOM_SKIP;
else
alloc->last = 0;
alloc->max = num;
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index 5b059e2d80cc..232040447e8a 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -111,6 +111,16 @@ static int alloc_host_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
return 0;
}
+static int alloc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq, int user)
+{
+ int ret = -ENOSYS;
+ if (user)
+ ret = alloc_oc_sq(rdev, sq);
+ if (ret)
+ ret = alloc_host_sq(rdev, sq);
+ return ret;
+}
+
static int destroy_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
struct c4iw_dev_ucontext *uctx)
{
@@ -179,15 +189,9 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
goto free_sw_rq;
}
- if (user) {
- if (alloc_oc_sq(rdev, &wq->sq) && alloc_host_sq(rdev, &wq->sq))
- goto free_hwaddr;
- } else {
- ret = alloc_host_sq(rdev, &wq->sq);
- if (ret)
- goto free_hwaddr;
- }
-
+ ret = alloc_sq(rdev, &wq->sq, user);
+ if (ret)
+ goto free_hwaddr;
memset(wq->sq.queue, 0, wq->sq.memsize);
dma_unmap_addr_set(&wq->sq, mapping, wq->sq.dma_addr);
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index aed8afee56da..6d7f453b4d05 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <linux/highmem.h>
#include <linux/io.h>
+#include <linux/aio.h>
#include <linux/jiffies.h>
#include <linux/cpu.h>
#include <asm/pgtable.h>
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index ea93870266eb..44ea9390417c 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -2187,7 +2187,8 @@ int ipath_register_ib_device(struct ipath_devdata *dd)
if (ret)
goto err_reg;
- if (ipath_verbs_register_sysfs(dev))
+ ret = ipath_verbs_register_sysfs(dev);
+ if (ret)
goto err_class;
enable_timer(dd);
@@ -2327,15 +2328,15 @@ static int ipath_verbs_register_sysfs(struct ib_device *dev)
int i;
int ret;
- for (i = 0; i < ARRAY_SIZE(ipath_class_attributes); ++i)
- if (device_create_file(&dev->dev,
- ipath_class_attributes[i])) {
- ret = 1;
+ for (i = 0; i < ARRAY_SIZE(ipath_class_attributes); ++i) {
+ ret = device_create_file(&dev->dev,
+ ipath_class_attributes[i]);
+ if (ret)
goto bail;
- }
-
- ret = 0;
-
+ }
+ return 0;
bail:
+ for (i = 0; i < ARRAY_SIZE(ipath_class_attributes); ++i)
+ device_remove_file(&dev->dev, ipath_class_attributes[i]);
return ret;
}
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index 73b3a7132587..d5e60f44ba5a 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -33,6 +33,7 @@
#include <linux/mlx4/cq.h>
#include <linux/mlx4/qp.h>
+#include <linux/mlx4/srq.h>
#include <linux/slab.h>
#include "mlx4_ib.h"
@@ -585,6 +586,7 @@ static int mlx4_ib_poll_one(struct mlx4_ib_cq *cq,
struct mlx4_qp *mqp;
struct mlx4_ib_wq *wq;
struct mlx4_ib_srq *srq;
+ struct mlx4_srq *msrq = NULL;
int is_send;
int is_error;
u32 g_mlpath_rqpn;
@@ -653,6 +655,20 @@ repoll:
wc->qp = &(*cur_qp)->ibqp;
+ if (wc->qp->qp_type == IB_QPT_XRC_TGT) {
+ u32 srq_num;
+ g_mlpath_rqpn = be32_to_cpu(cqe->g_mlpath_rqpn);
+ srq_num = g_mlpath_rqpn & 0xffffff;
+ /* SRQ is also in the radix tree */
+ msrq = mlx4_srq_lookup(to_mdev(cq->ibcq.device)->dev,
+ srq_num);
+ if (unlikely(!msrq)) {
+ pr_warn("CQ %06x with entry for unknown SRQN %06x\n",
+ cq->mcq.cqn, srq_num);
+ return -EINVAL;
+ }
+ }
+
if (is_send) {
wq = &(*cur_qp)->sq;
if (!(*cur_qp)->sq_signal_bits) {
@@ -666,6 +682,11 @@ repoll:
wqe_ctr = be16_to_cpu(cqe->wqe_index);
wc->wr_id = srq->wrid[wqe_ctr];
mlx4_ib_free_srq_wqe(srq, wqe_ctr);
+ } else if (msrq) {
+ srq = to_mibsrq(msrq);
+ wqe_ctr = be16_to_cpu(cqe->wqe_index);
+ wc->wr_id = srq->wrid[wqe_ctr];
+ mlx4_ib_free_srq_wqe(srq, wqe_ctr);
} else {
wq = &(*cur_qp)->rq;
tail = wq->tail & (wq->wqe_cnt - 1);
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index 934792c477bc..4d599cedbb0b 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -93,7 +93,7 @@ static void __propagate_pkey_ev(struct mlx4_ib_dev *dev, int port_num,
__be64 mlx4_ib_gen_node_guid(void)
{
#define NODE_GUID_HI ((u64) (((u64)IB_OPENIB_OUI) << 40))
- return cpu_to_be64(NODE_GUID_HI | random32());
+ return cpu_to_be64(NODE_GUID_HI | prandom_u32());
}
__be64 mlx4_ib_get_new_demux_tid(struct mlx4_ib_demux_ctx *ctx)
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 35cced2a4da8..4f10af2905b5 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1292,6 +1292,8 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
if (cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT) {
context->sq_size_stride |= !!qp->sq_no_prefetch << 7;
context->xrcd = cpu_to_be32((u32) qp->xrcdn);
+ if (ibqp->qp_type == IB_QPT_RAW_PACKET)
+ context->param3 |= cpu_to_be32(1 << 30);
}
if (qp->ibqp.uobject)
@@ -1458,6 +1460,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
}
}
+ if (qp->ibqp.qp_type == IB_QPT_RAW_PACKET)
+ context->pri_path.ackto = (context->pri_path.ackto & 0xf8) |
+ MLX4_IB_LINK_TYPE_ETH;
+
if (cur_state == IB_QPS_RTS && new_state == IB_QPS_SQD &&
attr_mask & IB_QP_EN_SQD_ASYNC_NOTIFY && attr->en_sqd_async_notify)
sqd_event = 1;
diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
index 4f7aa301b3b1..b56c9428f3c5 100644
--- a/drivers/infiniband/hw/qib/qib_file_ops.c
+++ b/drivers/infiniband/hw/qib/qib_file_ops.c
@@ -39,7 +39,7 @@
#include <linux/vmalloc.h>
#include <linux/highmem.h>
#include <linux/io.h>
-#include <linux/uio.h>
+#include <linux/aio.h>
#include <linux/jiffies.h>
#include <asm/pgtable.h>
#include <linux/delay.h>
diff --git a/drivers/infiniband/hw/qib/qib_sysfs.c b/drivers/infiniband/hw/qib/qib_sysfs.c
index 034cc821de5c..3c8e4e3caca6 100644
--- a/drivers/infiniband/hw/qib/qib_sysfs.c
+++ b/drivers/infiniband/hw/qib/qib_sysfs.c
@@ -808,10 +808,14 @@ int qib_verbs_register_sysfs(struct qib_devdata *dd)
for (i = 0; i < ARRAY_SIZE(qib_attributes); ++i) {
ret = device_create_file(&dev->dev, qib_attributes[i]);
if (ret)
- return ret;
+ goto bail;
}
return 0;
+bail:
+ for (i = 0; i < ARRAY_SIZE(qib_attributes); ++i)
+ device_remove_file(&dev->dev, qib_attributes[i]);
+ return ret;
}
/*
diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c
index 7c0ab16a2fe2..904c384aa361 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.c
+++ b/drivers/infiniband/hw/qib/qib_verbs.c
@@ -2234,7 +2234,8 @@ int qib_register_ib_device(struct qib_devdata *dd)
if (ret)
goto err_agents;
- if (qib_verbs_register_sysfs(dd))
+ ret = qib_verbs_register_sysfs(dd);
+ if (ret)
goto err_class;
goto bail;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 1ef880de3a41..3eceb61e3532 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -460,7 +460,7 @@ static int ipoib_cm_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even
goto err_qp;
}
- psn = random32() & 0xffffff;
+ psn = prandom_u32() & 0xffffff;
ret = ipoib_cm_modify_rx_qp(dev, cm_id, p->qp, psn);
if (ret)
goto err_modify;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 554b9063da54..b6e049a3c7a8 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -830,7 +830,7 @@ static int ipoib_hard_header(struct sk_buff *skb,
*/
memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
- return 0;
+ return sizeof *header;
}
static void ipoib_set_mcast_list(struct net_device *dev)
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 0ab8c9cc3a78..f19b0998a53c 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -82,10 +82,10 @@ module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
int iser_debug_level = 0;
-MODULE_DESCRIPTION("iSER (iSCSI Extensions for RDMA) Datamover "
- "v" DRV_VER " (" DRV_DATE ")");
+MODULE_DESCRIPTION("iSER (iSCSI Extensions for RDMA) Datamover");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Alex Nezhinsky, Dan Bar Dov, Or Gerlitz");
+MODULE_VERSION(DRV_VER);
module_param_named(debug_level, iser_debug_level, int, 0644);
MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0 (default:disabled)");
@@ -370,8 +370,8 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
/* binds the iSER connection retrieved from the previously
* connected ep_handle to the iSCSI layer connection. exchanges
* connection pointers */
- iser_err("binding iscsi/iser conn %p %p to ib_conn %p\n",
- conn, conn->dd_data, ib_conn);
+ iser_info("binding iscsi/iser conn %p %p to ib_conn %p\n",
+ conn, conn->dd_data, ib_conn);
iser_conn = conn->dd_data;
ib_conn->iser_conn = iser_conn;
iser_conn->ib_conn = ib_conn;
@@ -475,28 +475,28 @@ iscsi_iser_set_param(struct iscsi_cls_conn *cls_conn,
case ISCSI_PARAM_HDRDGST_EN:
sscanf(buf, "%d", &value);
if (value) {
- printk(KERN_ERR "DataDigest wasn't negotiated to None");
+ iser_err("DataDigest wasn't negotiated to None");
return -EPROTO;
}
break;
case ISCSI_PARAM_DATADGST_EN:
sscanf(buf, "%d", &value);
if (value) {
- printk(KERN_ERR "DataDigest wasn't negotiated to None");
+ iser_err("DataDigest wasn't negotiated to None");
return -EPROTO;
}
break;
case ISCSI_PARAM_IFMARKER_EN:
sscanf(buf, "%d", &value);
if (value) {
- printk(KERN_ERR "IFMarker wasn't negotiated to No");
+ iser_err("IFMarker wasn't negotiated to No");
return -EPROTO;
}
break;
case ISCSI_PARAM_OFMARKER_EN:
sscanf(buf, "%d", &value);
if (value) {
- printk(KERN_ERR "OFMarker wasn't negotiated to No");
+ iser_err("OFMarker wasn't negotiated to No");
return -EPROTO;
}
break;
@@ -596,7 +596,7 @@ iscsi_iser_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
ib_conn->state == ISER_CONN_DOWN))
rc = -1;
- iser_err("ib conn %p rc = %d\n", ib_conn, rc);
+ iser_info("ib conn %p rc = %d\n", ib_conn, rc);
if (rc > 0)
return 1; /* success, this is the equivalent of POLLOUT */
@@ -623,7 +623,7 @@ iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
iscsi_suspend_tx(ib_conn->iser_conn->iscsi_conn);
- iser_err("ib conn %p state %d\n",ib_conn, ib_conn->state);
+ iser_info("ib conn %p state %d\n", ib_conn, ib_conn->state);
iser_conn_terminate(ib_conn);
}
@@ -682,7 +682,7 @@ static umode_t iser_attr_is_visible(int param_type, int param)
static struct scsi_host_template iscsi_iser_sht = {
.module = THIS_MODULE,
- .name = "iSCSI Initiator over iSER, v." DRV_VER,
+ .name = "iSCSI Initiator over iSER",
.queuecommand = iscsi_queuecommand,
.change_queue_depth = iscsi_change_queue_depth,
.sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
@@ -740,7 +740,7 @@ static int __init iser_init(void)
iser_dbg("Starting iSER datamover...\n");
if (iscsi_max_lun < 1) {
- printk(KERN_ERR "Invalid max_lun value of %u\n", iscsi_max_lun);
+ iser_err("Invalid max_lun value of %u\n", iscsi_max_lun);
return -EINVAL;
}
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 5babdb35bda7..06f578cde75b 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -42,6 +42,7 @@
#include <linux/types.h>
#include <linux/net.h>
+#include <linux/printk.h>
#include <scsi/libiscsi.h>
#include <scsi/scsi_transport_iscsi.h>
@@ -65,20 +66,26 @@
#define DRV_NAME "iser"
#define PFX DRV_NAME ": "
-#define DRV_VER "0.1"
-#define DRV_DATE "May 7th, 2006"
+#define DRV_VER "1.1"
#define iser_dbg(fmt, arg...) \
do { \
- if (iser_debug_level > 1) \
+ if (iser_debug_level > 2) \
printk(KERN_DEBUG PFX "%s:" fmt,\
__func__ , ## arg); \
} while (0)
#define iser_warn(fmt, arg...) \
do { \
+ if (iser_debug_level > 1) \
+ pr_warn(PFX "%s:" fmt, \
+ __func__ , ## arg); \
+ } while (0)
+
+#define iser_info(fmt, arg...) \
+ do { \
if (iser_debug_level > 0) \
- printk(KERN_DEBUG PFX "%s:" fmt,\
+ pr_info(PFX "%s:" fmt, \
__func__ , ## arg); \
} while (0)
@@ -133,6 +140,15 @@ struct iser_hdr {
__be64 read_va;
} __attribute__((packed));
+
+#define ISER_ZBVA_NOT_SUPPORTED 0x80
+#define ISER_SEND_W_INV_NOT_SUPPORTED 0x40
+
+struct iser_cm_hdr {
+ u8 flags;
+ u8 rsvd[3];
+} __packed;
+
/* Constant PDU lengths calculations */
#define ISER_HEADERS_LEN (sizeof(struct iser_hdr) + sizeof(struct iscsi_hdr))
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index be1edb04b085..68ebb7fe072a 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -416,8 +416,9 @@ int iser_reg_rdma_mem(struct iscsi_iser_task *iser_task,
for (i=0 ; i<ib_conn->page_vec->length ; i++)
iser_err("page_vec[%d] = 0x%llx\n", i,
(unsigned long long) ib_conn->page_vec->pages[i]);
- return err;
}
+ if (err)
+ return err;
}
return 0;
}
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index 4debadc53106..5278916c3103 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -74,8 +74,9 @@ static int iser_create_device_ib_res(struct iser_device *device)
struct iser_cq_desc *cq_desc;
device->cqs_used = min(ISER_MAX_CQ, device->ib_device->num_comp_vectors);
- iser_err("using %d CQs, device %s supports %d vectors\n", device->cqs_used,
- device->ib_device->name, device->ib_device->num_comp_vectors);
+ iser_info("using %d CQs, device %s supports %d vectors\n",
+ device->cqs_used, device->ib_device->name,
+ device->ib_device->num_comp_vectors);
device->cq_desc = kmalloc(sizeof(struct iser_cq_desc) * device->cqs_used,
GFP_KERNEL);
@@ -262,7 +263,7 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
min_index = index;
device->cq_active_qps[min_index]++;
mutex_unlock(&ig.connlist_mutex);
- iser_err("cq index %d used for ib_conn %p\n", min_index, ib_conn);
+ iser_info("cq index %d used for ib_conn %p\n", min_index, ib_conn);
init_attr.event_handler = iser_qp_event_callback;
init_attr.qp_context = (void *)ib_conn;
@@ -280,9 +281,9 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
goto out_err;
ib_conn->qp = ib_conn->cma_id->qp;
- iser_err("setting conn %p cma_id %p: fmr_pool %p qp %p\n",
- ib_conn, ib_conn->cma_id,
- ib_conn->fmr_pool, ib_conn->cma_id->qp);
+ iser_info("setting conn %p cma_id %p: fmr_pool %p qp %p\n",
+ ib_conn, ib_conn->cma_id,
+ ib_conn->fmr_pool, ib_conn->cma_id->qp);
return ret;
out_err:
@@ -299,9 +300,9 @@ static int iser_free_ib_conn_res(struct iser_conn *ib_conn, int can_destroy_id)
int cq_index;
BUG_ON(ib_conn == NULL);
- iser_err("freeing conn %p cma_id %p fmr pool %p qp %p\n",
- ib_conn, ib_conn->cma_id,
- ib_conn->fmr_pool, ib_conn->qp);
+ iser_info("freeing conn %p cma_id %p fmr pool %p qp %p\n",
+ ib_conn, ib_conn->cma_id,
+ ib_conn->fmr_pool, ib_conn->qp);
/* qp is created only once both addr & route are resolved */
if (ib_conn->fmr_pool != NULL)
@@ -379,7 +380,7 @@ static void iser_device_try_release(struct iser_device *device)
{
mutex_lock(&ig.device_list_mutex);
device->refcount--;
- iser_err("device %p refcount %d\n",device,device->refcount);
+ iser_info("device %p refcount %d\n", device, device->refcount);
if (!device->refcount) {
iser_free_device_ib_res(device);
list_del(&device->ig_list);
@@ -498,6 +499,7 @@ static int iser_route_handler(struct rdma_cm_id *cma_id)
{
struct rdma_conn_param conn_param;
int ret;
+ struct iser_cm_hdr req_hdr;
ret = iser_create_ib_conn_res((struct iser_conn *)cma_id->context);
if (ret)
@@ -509,6 +511,12 @@ static int iser_route_handler(struct rdma_cm_id *cma_id)
conn_param.retry_count = 7;
conn_param.rnr_retry_count = 6;
+ memset(&req_hdr, 0, sizeof(req_hdr));
+ req_hdr.flags = (ISER_ZBVA_NOT_SUPPORTED |
+ ISER_SEND_W_INV_NOT_SUPPORTED);
+ conn_param.private_data = (void *)&req_hdr;
+ conn_param.private_data_len = sizeof(struct iser_cm_hdr);
+
ret = rdma_connect(cma_id, &conn_param);
if (ret) {
iser_err("failure connecting: %d\n", ret);
@@ -558,8 +566,8 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
{
int ret = 0;
- iser_err("event %d status %d conn %p id %p\n",
- event->event, event->status, cma_id->context, cma_id);
+ iser_info("event %d status %d conn %p id %p\n",
+ event->event, event->status, cma_id->context, cma_id);
switch (event->event) {
case RDMA_CM_EVENT_ADDR_RESOLVED:
@@ -619,8 +627,8 @@ int iser_connect(struct iser_conn *ib_conn,
/* the device is known only --after-- address resolution */
ib_conn->device = NULL;
- iser_err("connecting to: %pI4, port 0x%x\n",
- &dst_addr->sin_addr, dst_addr->sin_port);
+ iser_info("connecting to: %pI4, port 0x%x\n",
+ &dst_addr->sin_addr, dst_addr->sin_port);
ib_conn->state = ISER_CONN_PENDING;
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index c09d41b1a2ff..b08ca7a9f76b 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1374,7 +1374,7 @@ static int srpt_abort_cmd(struct srpt_send_ioctx *ioctx)
target_put_sess_cmd(ioctx->ch->sess, &ioctx->cmd);
break;
default:
- WARN_ON("ERROR: unexpected command state");
+ WARN(1, "Unexpected command state (%d)", state);
break;
}
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 830183737b0f..21d02b0d907c 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -46,6 +46,7 @@
#include "amd_iommu_proto.h"
#include "amd_iommu_types.h"
#include "irq_remapping.h"
+#include "pci.h"
#define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28))
@@ -263,12 +264,6 @@ static bool check_device(struct device *dev)
return true;
}
-static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
-{
- pci_dev_put(*from);
- *from = to;
-}
-
static struct pci_bus *find_hosted_bus(struct pci_bus *bus)
{
while (!bus->self) {
@@ -701,9 +696,6 @@ retry:
static void iommu_poll_events(struct amd_iommu *iommu)
{
u32 head, tail;
- unsigned long flags;
-
- spin_lock_irqsave(&iommu->lock, flags);
head = readl(iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
tail = readl(iommu->mmio_base + MMIO_EVT_TAIL_OFFSET);
@@ -714,8 +706,6 @@ static void iommu_poll_events(struct amd_iommu *iommu)
}
writel(head, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
-
- spin_unlock_irqrestore(&iommu->lock, flags);
}
static void iommu_handle_ppr_entry(struct amd_iommu *iommu, u64 *raw)
@@ -740,17 +730,11 @@ static void iommu_handle_ppr_entry(struct amd_iommu *iommu, u64 *raw)
static void iommu_poll_ppr_log(struct amd_iommu *iommu)
{
- unsigned long flags;
u32 head, tail;
if (iommu->ppr_log == NULL)
return;
- /* enable ppr interrupts again */
- writel(MMIO_STATUS_PPR_INT_MASK, iommu->mmio_base + MMIO_STATUS_OFFSET);
-
- spin_lock_irqsave(&iommu->lock, flags);
-
head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
@@ -786,34 +770,50 @@ static void iommu_poll_ppr_log(struct amd_iommu *iommu)
head = (head + PPR_ENTRY_SIZE) % PPR_LOG_SIZE;
writel(head, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
- /*
- * Release iommu->lock because ppr-handling might need to
- * re-acquire it
- */
- spin_unlock_irqrestore(&iommu->lock, flags);
-
/* Handle PPR entry */
iommu_handle_ppr_entry(iommu, entry);
- spin_lock_irqsave(&iommu->lock, flags);
-
/* Refresh ring-buffer information */
head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
}
-
- spin_unlock_irqrestore(&iommu->lock, flags);
}
irqreturn_t amd_iommu_int_thread(int irq, void *data)
{
- struct amd_iommu *iommu;
+ struct amd_iommu *iommu = (struct amd_iommu *) data;
+ u32 status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
- for_each_iommu(iommu) {
- iommu_poll_events(iommu);
- iommu_poll_ppr_log(iommu);
- }
+ while (status & (MMIO_STATUS_EVT_INT_MASK | MMIO_STATUS_PPR_INT_MASK)) {
+ /* Enable EVT and PPR interrupts again */
+ writel((MMIO_STATUS_EVT_INT_MASK | MMIO_STATUS_PPR_INT_MASK),
+ iommu->mmio_base + MMIO_STATUS_OFFSET);
+ if (status & MMIO_STATUS_EVT_INT_MASK) {
+ pr_devel("AMD-Vi: Processing IOMMU Event Log\n");
+ iommu_poll_events(iommu);
+ }
+
+ if (status & MMIO_STATUS_PPR_INT_MASK) {
+ pr_devel("AMD-Vi: Processing IOMMU PPR Log\n");
+ iommu_poll_ppr_log(iommu);
+ }
+
+ /*
+ * Hardware bug: ERBT1312
+ * When re-enabling interrupt (by writing 1
+ * to clear the bit), the hardware might also try to set
+ * the interrupt bit in the event status register.
+ * In this scenario, the bit will be set, and disable
+ * subsequent interrupts.
+ *
+ * Workaround: The IOMMU driver should read back the
+ * status register and check if the interrupt bits are cleared.
+ * If not, driver will need to go through the interrupt handler
+ * again and re-clear the bits
+ */
+ status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
+ }
return IRQ_HANDLED;
}
@@ -2839,24 +2839,6 @@ static void unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size,
}
/*
- * This is a special map_sg function which is used if we should map a
- * device which is not handled by an AMD IOMMU in the system.
- */
-static int map_sg_no_iommu(struct device *dev, struct scatterlist *sglist,
- int nelems, int dir)
-{
- struct scatterlist *s;
- int i;
-
- for_each_sg(sglist, s, nelems, i) {
- s->dma_address = (dma_addr_t)sg_phys(s);
- s->dma_length = s->length;
- }
-
- return nelems;
-}
-
-/*
* The exported map_sg function for dma_ops (handles scatter-gather
* lists).
*/
@@ -2875,9 +2857,7 @@ static int map_sg(struct device *dev, struct scatterlist *sglist,
INC_STATS_COUNTER(cnt_map_sg);
domain = get_domain(dev);
- if (PTR_ERR(domain) == -EINVAL)
- return map_sg_no_iommu(dev, sglist, nelems, dir);
- else if (IS_ERR(domain))
+ if (IS_ERR(domain))
return 0;
dma_mask = *dev->dma_mask;
@@ -3410,7 +3390,7 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
}
static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
- unsigned long iova)
+ dma_addr_t iova)
{
struct protection_domain *domain = dom->priv;
unsigned long offset_mask;
@@ -3947,6 +3927,9 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
if (!table)
goto out;
+ /* Initialize table spin-lock */
+ spin_lock_init(&table->lock);
+
if (ioapic)
/* Keep the first 32 indexes free for IOAPIC interrupts */
table->min_index = 32;
@@ -4007,7 +3990,7 @@ static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
c = 0;
if (c == count) {
- struct irq_2_iommu *irte_info;
+ struct irq_2_irte *irte_info;
for (; c != 0; --c)
table->table[index - c + 1] = IRTE_ALLOCATED;
@@ -4015,9 +3998,9 @@ static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
index -= count - 1;
cfg->remapped = 1;
- irte_info = &cfg->irq_2_iommu;
- irte_info->sub_handle = devid;
- irte_info->irte_index = index;
+ irte_info = &cfg->irq_2_irte;
+ irte_info->devid = devid;
+ irte_info->index = index;
goto out;
}
@@ -4098,7 +4081,7 @@ static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry,
struct io_apic_irq_attr *attr)
{
struct irq_remap_table *table;
- struct irq_2_iommu *irte_info;
+ struct irq_2_irte *irte_info;
struct irq_cfg *cfg;
union irte irte;
int ioapic_id;
@@ -4110,7 +4093,7 @@ static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry,
if (!cfg)
return -EINVAL;
- irte_info = &cfg->irq_2_iommu;
+ irte_info = &cfg->irq_2_irte;
ioapic_id = mpc_ioapic_id(attr->ioapic);
devid = get_ioapic_devid(ioapic_id);
@@ -4125,8 +4108,8 @@ static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry,
/* Setup IRQ remapping info */
cfg->remapped = 1;
- irte_info->sub_handle = devid;
- irte_info->irte_index = index;
+ irte_info->devid = devid;
+ irte_info->index = index;
/* Setup IRTE for IOMMU */
irte.val = 0;
@@ -4160,7 +4143,7 @@ static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry,
static int set_affinity(struct irq_data *data, const struct cpumask *mask,
bool force)
{
- struct irq_2_iommu *irte_info;
+ struct irq_2_irte *irte_info;
unsigned int dest, irq;
struct irq_cfg *cfg;
union irte irte;
@@ -4171,12 +4154,12 @@ static int set_affinity(struct irq_data *data, const struct cpumask *mask,
cfg = data->chip_data;
irq = data->irq;
- irte_info = &cfg->irq_2_iommu;
+ irte_info = &cfg->irq_2_irte;
if (!cpumask_intersects(mask, cpu_online_mask))
return -EINVAL;
- if (get_irte(irte_info->sub_handle, irte_info->irte_index, &irte))
+ if (get_irte(irte_info->devid, irte_info->index, &irte))
return -EBUSY;
if (assign_irq_vector(irq, cfg, mask))
@@ -4192,7 +4175,7 @@ static int set_affinity(struct irq_data *data, const struct cpumask *mask,
irte.fields.vector = cfg->vector;
irte.fields.destination = dest;
- modify_irte(irte_info->sub_handle, irte_info->irte_index, irte);
+ modify_irte(irte_info->devid, irte_info->index, irte);
if (cfg->move_in_progress)
send_cleanup_vector(cfg);
@@ -4204,16 +4187,16 @@ static int set_affinity(struct irq_data *data, const struct cpumask *mask,
static int free_irq(int irq)
{
- struct irq_2_iommu *irte_info;
+ struct irq_2_irte *irte_info;
struct irq_cfg *cfg;
cfg = irq_get_chip_data(irq);
if (!cfg)
return -EINVAL;
- irte_info = &cfg->irq_2_iommu;
+ irte_info = &cfg->irq_2_irte;
- free_irte(irte_info->sub_handle, irte_info->irte_index);
+ free_irte(irte_info->devid, irte_info->index);
return 0;
}
@@ -4222,7 +4205,7 @@ static void compose_msi_msg(struct pci_dev *pdev,
unsigned int irq, unsigned int dest,
struct msi_msg *msg, u8 hpet_id)
{
- struct irq_2_iommu *irte_info;
+ struct irq_2_irte *irte_info;
struct irq_cfg *cfg;
union irte irte;
@@ -4230,7 +4213,7 @@ static void compose_msi_msg(struct pci_dev *pdev,
if (!cfg)
return;
- irte_info = &cfg->irq_2_iommu;
+ irte_info = &cfg->irq_2_irte;
irte.val = 0;
irte.fields.vector = cfg->vector;
@@ -4239,11 +4222,11 @@ static void compose_msi_msg(struct pci_dev *pdev,
irte.fields.dm = apic->irq_dest_mode;
irte.fields.valid = 1;
- modify_irte(irte_info->sub_handle, irte_info->irte_index, irte);
+ modify_irte(irte_info->devid, irte_info->index, irte);
msg->address_hi = MSI_ADDR_BASE_HI;
msg->address_lo = MSI_ADDR_BASE_LO;
- msg->data = irte_info->irte_index;
+ msg->data = irte_info->index;
}
static int msi_alloc_irq(struct pci_dev *pdev, int irq, int nvec)
@@ -4268,7 +4251,7 @@ static int msi_alloc_irq(struct pci_dev *pdev, int irq, int nvec)
static int msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
int index, int offset)
{
- struct irq_2_iommu *irte_info;
+ struct irq_2_irte *irte_info;
struct irq_cfg *cfg;
u16 devid;
@@ -4283,18 +4266,18 @@ static int msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
return 0;
devid = get_device_id(&pdev->dev);
- irte_info = &cfg->irq_2_iommu;
+ irte_info = &cfg->irq_2_irte;
cfg->remapped = 1;
- irte_info->sub_handle = devid;
- irte_info->irte_index = index + offset;
+ irte_info->devid = devid;
+ irte_info->index = index + offset;
return 0;
}
static int setup_hpet_msi(unsigned int irq, unsigned int id)
{
- struct irq_2_iommu *irte_info;
+ struct irq_2_irte *irte_info;
struct irq_cfg *cfg;
int index, devid;
@@ -4302,7 +4285,7 @@ static int setup_hpet_msi(unsigned int irq, unsigned int id)
if (!cfg)
return -EINVAL;
- irte_info = &cfg->irq_2_iommu;
+ irte_info = &cfg->irq_2_irte;
devid = get_hpet_devid(id);
if (devid < 0)
return devid;
@@ -4312,8 +4295,8 @@ static int setup_hpet_msi(unsigned int irq, unsigned int id)
return index;
cfg->remapped = 1;
- irte_info->sub_handle = devid;
- irte_info->irte_index = index;
+ irte_info->devid = devid;
+ irte_info->index = index;
return 0;
}
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 2f46881256a2..bf51abb78dee 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -213,6 +213,14 @@ enum iommu_init_state {
IOMMU_INIT_ERROR,
};
+/* Early ioapic and hpet maps from kernel command line */
+#define EARLY_MAP_SIZE 4
+static struct devid_map __initdata early_ioapic_map[EARLY_MAP_SIZE];
+static struct devid_map __initdata early_hpet_map[EARLY_MAP_SIZE];
+static int __initdata early_ioapic_map_size;
+static int __initdata early_hpet_map_size;
+static bool __initdata cmdline_maps;
+
static enum iommu_init_state init_state = IOMMU_START_STATE;
static int amd_iommu_enable_interrupts(void);
@@ -703,31 +711,66 @@ static void __init set_dev_entry_from_acpi(struct amd_iommu *iommu,
set_iommu_for_device(iommu, devid);
}
-static int add_special_device(u8 type, u8 id, u16 devid)
+static int __init add_special_device(u8 type, u8 id, u16 devid, bool cmd_line)
{
struct devid_map *entry;
struct list_head *list;
- if (type != IVHD_SPECIAL_IOAPIC && type != IVHD_SPECIAL_HPET)
+ if (type == IVHD_SPECIAL_IOAPIC)
+ list = &ioapic_map;
+ else if (type == IVHD_SPECIAL_HPET)
+ list = &hpet_map;
+ else
return -EINVAL;
+ list_for_each_entry(entry, list, list) {
+ if (!(entry->id == id && entry->cmd_line))
+ continue;
+
+ pr_info("AMD-Vi: Command-line override present for %s id %d - ignoring\n",
+ type == IVHD_SPECIAL_IOAPIC ? "IOAPIC" : "HPET", id);
+
+ return 0;
+ }
+
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
- entry->id = id;
- entry->devid = devid;
-
- if (type == IVHD_SPECIAL_IOAPIC)
- list = &ioapic_map;
- else
- list = &hpet_map;
+ entry->id = id;
+ entry->devid = devid;
+ entry->cmd_line = cmd_line;
list_add_tail(&entry->list, list);
return 0;
}
+static int __init add_early_maps(void)
+{
+ int i, ret;
+
+ for (i = 0; i < early_ioapic_map_size; ++i) {
+ ret = add_special_device(IVHD_SPECIAL_IOAPIC,
+ early_ioapic_map[i].id,
+ early_ioapic_map[i].devid,
+ early_ioapic_map[i].cmd_line);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < early_hpet_map_size; ++i) {
+ ret = add_special_device(IVHD_SPECIAL_HPET,
+ early_hpet_map[i].id,
+ early_hpet_map[i].devid,
+ early_hpet_map[i].cmd_line);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
/*
* Reads the device exclusion range from ACPI and initializes the IOMMU with
* it
@@ -764,6 +807,12 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,
u32 dev_i, ext_flags = 0;
bool alias = false;
struct ivhd_entry *e;
+ int ret;
+
+
+ ret = add_early_maps();
+ if (ret)
+ return ret;
/*
* First save the recommended feature enable bits from ACPI
@@ -929,7 +978,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,
PCI_FUNC(devid));
set_dev_entry_from_acpi(iommu, devid, e->flags, 0);
- ret = add_special_device(type, handle, devid);
+ ret = add_special_device(type, handle, devid, false);
if (ret)
return ret;
break;
@@ -1275,7 +1324,7 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
amd_iommu_int_handler,
amd_iommu_int_thread,
0, "AMD-Vi",
- iommu->dev);
+ iommu);
if (r) {
pci_disable_msi(iommu->dev);
@@ -1638,18 +1687,28 @@ static void __init free_on_init_error(void)
static bool __init check_ioapic_information(void)
{
+ const char *fw_bug = FW_BUG;
bool ret, has_sb_ioapic;
int idx;
has_sb_ioapic = false;
ret = false;
+ /*
+ * If we have map overrides on the kernel command line the
+ * messages in this function might not describe firmware bugs
+ * anymore - so be careful
+ */
+ if (cmdline_maps)
+ fw_bug = "";
+
for (idx = 0; idx < nr_ioapics; idx++) {
int devid, id = mpc_ioapic_id(idx);
devid = get_ioapic_devid(id);
if (devid < 0) {
- pr_err(FW_BUG "AMD-Vi: IOAPIC[%d] not in IVRS table\n", id);
+ pr_err("%sAMD-Vi: IOAPIC[%d] not in IVRS table\n",
+ fw_bug, id);
ret = false;
} else if (devid == IOAPIC_SB_DEVID) {
has_sb_ioapic = true;
@@ -1666,11 +1725,11 @@ static bool __init check_ioapic_information(void)
* when the BIOS is buggy and provides us the wrong
* device id for the IOAPIC in the system.
*/
- pr_err(FW_BUG "AMD-Vi: No southbridge IOAPIC found in IVRS table\n");
+ pr_err("%sAMD-Vi: No southbridge IOAPIC found\n", fw_bug);
}
if (!ret)
- pr_err("AMD-Vi: Disabling interrupt remapping due to BIOS Bug(s)\n");
+ pr_err("AMD-Vi: Disabling interrupt remapping\n");
return ret;
}
@@ -1801,6 +1860,7 @@ static int __init early_amd_iommu_init(void)
* Interrupt remapping enabled, create kmem_cache for the
* remapping tables.
*/
+ ret = -ENOMEM;
amd_iommu_irq_cache = kmem_cache_create("irq_remap_cache",
MAX_IRQS_PER_TABLE * sizeof(u32),
IRQ_TABLE_ALIGNMENT,
@@ -2097,8 +2157,70 @@ static int __init parse_amd_iommu_options(char *str)
return 1;
}
-__setup("amd_iommu_dump", parse_amd_iommu_dump);
-__setup("amd_iommu=", parse_amd_iommu_options);
+static int __init parse_ivrs_ioapic(char *str)
+{
+ unsigned int bus, dev, fn;
+ int ret, id, i;
+ u16 devid;
+
+ ret = sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn);
+
+ if (ret != 4) {
+ pr_err("AMD-Vi: Invalid command line: ivrs_ioapic%s\n", str);
+ return 1;
+ }
+
+ if (early_ioapic_map_size == EARLY_MAP_SIZE) {
+ pr_err("AMD-Vi: Early IOAPIC map overflow - ignoring ivrs_ioapic%s\n",
+ str);
+ return 1;
+ }
+
+ devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7);
+
+ cmdline_maps = true;
+ i = early_ioapic_map_size++;
+ early_ioapic_map[i].id = id;
+ early_ioapic_map[i].devid = devid;
+ early_ioapic_map[i].cmd_line = true;
+
+ return 1;
+}
+
+static int __init parse_ivrs_hpet(char *str)
+{
+ unsigned int bus, dev, fn;
+ int ret, id, i;
+ u16 devid;
+
+ ret = sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn);
+
+ if (ret != 4) {
+ pr_err("AMD-Vi: Invalid command line: ivrs_hpet%s\n", str);
+ return 1;
+ }
+
+ if (early_hpet_map_size == EARLY_MAP_SIZE) {
+ pr_err("AMD-Vi: Early HPET map overflow - ignoring ivrs_hpet%s\n",
+ str);
+ return 1;
+ }
+
+ devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7);
+
+ cmdline_maps = true;
+ i = early_hpet_map_size++;
+ early_hpet_map[i].id = id;
+ early_hpet_map[i].devid = devid;
+ early_hpet_map[i].cmd_line = true;
+
+ return 1;
+}
+
+__setup("amd_iommu_dump", parse_amd_iommu_dump);
+__setup("amd_iommu=", parse_amd_iommu_options);
+__setup("ivrs_ioapic", parse_ivrs_ioapic);
+__setup("ivrs_hpet", parse_ivrs_hpet);
IOMMU_INIT_FINISH(amd_iommu_detect,
gart_iommu_hole_init,
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index ec36cf63e0ca..0285a215df16 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -100,6 +100,7 @@
#define PASID_MASK 0x000fffff
/* MMIO status bits */
+#define MMIO_STATUS_EVT_INT_MASK (1 << 1)
#define MMIO_STATUS_COM_WAIT_INT_MASK (1 << 2)
#define MMIO_STATUS_PPR_INT_MASK (1 << 6)
@@ -589,6 +590,7 @@ struct devid_map {
struct list_head list;
u8 id;
u16 devid;
+ bool cmd_line;
};
/* Map HPET and IOAPIC ids to the devid used by the IOMMU */
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index b8008f679bc7..a7967ceb79e6 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -646,7 +646,7 @@ out:
int alloc_iommu(struct dmar_drhd_unit *drhd)
{
struct intel_iommu *iommu;
- u32 ver;
+ u32 ver, sts;
static int iommu_allocated = 0;
int agaw = 0;
int msagaw = 0;
@@ -696,6 +696,15 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
(unsigned long long)iommu->cap,
(unsigned long long)iommu->ecap);
+ /* Reflect status in gcmd */
+ sts = readl(iommu->reg + DMAR_GSTS_REG);
+ if (sts & DMA_GSTS_IRES)
+ iommu->gcmd |= DMA_GCMD_IRE;
+ if (sts & DMA_GSTS_TES)
+ iommu->gcmd |= DMA_GCMD_TE;
+ if (sts & DMA_GSTS_QIES)
+ iommu->gcmd |= DMA_GCMD_QIE;
+
raw_spin_lock_init(&iommu->register_lock);
drhd->iommu = iommu;
@@ -1205,7 +1214,7 @@ irqreturn_t dmar_fault(int irq, void *dev_id)
/* TBD: ignore advanced fault log currently */
if (!(fault_status & DMA_FSTS_PPF))
- goto clear_rest;
+ goto unlock_exit;
fault_index = dma_fsts_fault_record_index(fault_status);
reg = cap_fault_reg_offset(iommu->cap);
@@ -1246,11 +1255,10 @@ irqreturn_t dmar_fault(int irq, void *dev_id)
fault_index = 0;
raw_spin_lock_irqsave(&iommu->register_lock, flag);
}
-clear_rest:
- /* clear all the other faults */
- fault_status = readl(iommu->reg + DMAR_FSTS_REG);
- writel(fault_status, iommu->reg + DMAR_FSTS_REG);
+ writel(DMA_FSTS_PFO | DMA_FSTS_PPF, iommu->reg + DMAR_FSTS_REG);
+
+unlock_exit:
raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
return IRQ_HANDLED;
}
@@ -1298,6 +1306,7 @@ int __init enable_drhd_fault_handling(void)
for_each_drhd_unit(drhd) {
int ret;
struct intel_iommu *iommu = drhd->iommu;
+ u32 fault_status;
ret = dmar_set_interrupt(iommu);
if (ret) {
@@ -1310,6 +1319,8 @@ int __init enable_drhd_fault_handling(void)
* Clear any previous faults.
*/
dmar_fault(iommu->irq, iommu);
+ fault_status = readl(iommu->reg + DMAR_FSTS_REG);
+ writel(fault_status, iommu->reg + DMAR_FSTS_REG);
}
return 0;
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 238a3caa949a..3f32d64ab87a 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -1027,7 +1027,7 @@ done:
}
static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long iova)
+ dma_addr_t iova)
{
struct exynos_iommu_domain *priv = domain->priv;
unsigned long *entry;
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 0099667a397e..b4f0e28dfa41 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -47,6 +47,7 @@
#include <asm/iommu.h>
#include "irq_remapping.h"
+#include "pci.h"
#define ROOT_SIZE VTD_PAGE_SIZE
#define CONTEXT_SIZE VTD_PAGE_SIZE
@@ -3665,6 +3666,7 @@ static struct notifier_block device_nb = {
int __init intel_iommu_init(void)
{
int ret = 0;
+ struct dmar_drhd_unit *drhd;
/* VT-d is required for a TXT/tboot launch, so enforce that */
force_on = tboot_force_iommu();
@@ -3675,6 +3677,20 @@ int __init intel_iommu_init(void)
return -ENODEV;
}
+ /*
+ * Disable translation if already enabled prior to OS handover.
+ */
+ for_each_drhd_unit(drhd) {
+ struct intel_iommu *iommu;
+
+ if (drhd->ignored)
+ continue;
+
+ iommu = drhd->iommu;
+ if (iommu->gcmd & DMA_GCMD_TE)
+ iommu_disable_translation(iommu);
+ }
+
if (dmar_dev_scope_init() < 0) {
if (force_on)
panic("tboot: Failed to initialize DMAR device scope\n");
@@ -4111,7 +4127,7 @@ static size_t intel_iommu_unmap(struct iommu_domain *domain,
}
static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long iova)
+ dma_addr_t iova)
{
struct dmar_domain *dmar_domain = domain->priv;
struct dma_pte *pte;
@@ -4137,12 +4153,6 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain,
return 0;
}
-static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
-{
- pci_dev_put(*from);
- *from = to;
-}
-
#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
static int intel_iommu_add_device(struct device *dev)
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index f3b8f23b5d8f..5b19b2d6ec2d 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -524,6 +524,16 @@ static int __init intel_irq_remapping_supported(void)
if (disable_irq_remap)
return 0;
+ if (irq_remap_broken) {
+ WARN_TAINT(1, TAINT_FIRMWARE_WORKAROUND,
+ "This system BIOS has enabled interrupt remapping\n"
+ "on a chipset that contains an erratum making that\n"
+ "feature unstable. To maintain system stability\n"
+ "interrupt remapping is being disabled. Please\n"
+ "contact your BIOS vendor for an update\n");
+ disable_irq_remap = 1;
+ return 0;
+ }
if (!dmar_ir_support())
return 0;
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b972d430d92b..d8f98b14e2fe 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -204,6 +204,35 @@ again:
}
EXPORT_SYMBOL_GPL(iommu_group_alloc);
+struct iommu_group *iommu_group_get_by_id(int id)
+{
+ struct kobject *group_kobj;
+ struct iommu_group *group;
+ const char *name;
+
+ if (!iommu_group_kset)
+ return NULL;
+
+ name = kasprintf(GFP_KERNEL, "%d", id);
+ if (!name)
+ return NULL;
+
+ group_kobj = kset_find_obj(iommu_group_kset, name);
+ kfree(name);
+
+ if (!group_kobj)
+ return NULL;
+
+ group = container_of(group_kobj, struct iommu_group, kobj);
+ BUG_ON(group->id != id);
+
+ kobject_get(group->devices_kobj);
+ kobject_put(&group->kobj);
+
+ return group;
+}
+EXPORT_SYMBOL_GPL(iommu_group_get_by_id);
+
/**
* iommu_group_get_iommudata - retrieve iommu_data registered for a group
* @group: the group
@@ -706,8 +735,7 @@ void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
}
EXPORT_SYMBOL_GPL(iommu_detach_group);
-phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long iova)
+phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
{
if (unlikely(domain->ops->iova_to_phys == NULL))
return 0;
@@ -854,12 +882,13 @@ EXPORT_SYMBOL_GPL(iommu_unmap);
int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
- phys_addr_t paddr, u64 size)
+ phys_addr_t paddr, u64 size, int prot)
{
if (unlikely(domain->ops->domain_window_enable == NULL))
return -ENODEV;
- return domain->ops->domain_window_enable(domain, wnd_nr, paddr, size);
+ return domain->ops->domain_window_enable(domain, wnd_nr, paddr, size,
+ prot);
}
EXPORT_SYMBOL_GPL(iommu_domain_window_enable);
diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c
index 7c11ff368d07..dcfea4e39be7 100644
--- a/drivers/iommu/irq_remapping.c
+++ b/drivers/iommu/irq_remapping.c
@@ -18,6 +18,7 @@
int irq_remapping_enabled;
int disable_irq_remap;
+int irq_remap_broken;
int disable_sourceid_checking;
int no_x2apic_optout;
@@ -210,6 +211,11 @@ void __init setup_irq_remapping_ops(void)
#endif
}
+void set_irq_remapping_broken(void)
+{
+ irq_remap_broken = 1;
+}
+
int irq_remapping_supported(void)
{
if (disable_irq_remap)
diff --git a/drivers/iommu/irq_remapping.h b/drivers/iommu/irq_remapping.h
index ecb637670405..90c4dae5a46b 100644
--- a/drivers/iommu/irq_remapping.h
+++ b/drivers/iommu/irq_remapping.h
@@ -32,6 +32,7 @@ struct pci_dev;
struct msi_msg;
extern int disable_irq_remap;
+extern int irq_remap_broken;
extern int disable_sourceid_checking;
extern int no_x2apic_optout;
extern int irq_remapping_enabled;
@@ -89,6 +90,7 @@ extern struct irq_remap_ops amd_iommu_irq_ops;
#define irq_remapping_enabled 0
#define disable_irq_remap 1
+#define irq_remap_broken 0
#endif /* CONFIG_IRQ_REMAP */
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 6a8870a31668..8ab4f41090af 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -554,7 +554,7 @@ fail:
}
static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long va)
+ dma_addr_t va)
{
struct msm_priv *priv;
struct msm_iommu_drvdata *iommu_drvdata;
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index 6ac02fa5910f..e02e5d71745b 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -1219,7 +1219,7 @@ static void omap_iommu_domain_destroy(struct iommu_domain *domain)
}
static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long da)
+ dma_addr_t da)
{
struct omap_iommu_domain *omap_domain = domain->priv;
struct omap_iommu *oiommu = omap_domain->iommu_dev;
diff --git a/drivers/iommu/pci.h b/drivers/iommu/pci.h
new file mode 100644
index 000000000000..352d80ae7443
--- /dev/null
+++ b/drivers/iommu/pci.h
@@ -0,0 +1,29 @@
+/*
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ */
+#ifndef __IOMMU_PCI_H
+#define __IOMMU_PCI_H
+
+/* Helper function for swapping pci device reference */
+static inline void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
+{
+ pci_dev_put(*from);
+ *from = to;
+}
+
+#endif /* __IOMMU_PCI_H */
diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c
index b6e8b57cf0a8..d572863dfccd 100644
--- a/drivers/iommu/shmobile-iommu.c
+++ b/drivers/iommu/shmobile-iommu.c
@@ -296,7 +296,7 @@ done:
}
static phys_addr_t shmobile_iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long iova)
+ dma_addr_t iova)
{
struct shmobile_iommu_domain *sh_domain = domain->priv;
uint32_t l1entry = 0, l2entry = 0;
diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c
index 86437575f94d..108c0e9c24d9 100644
--- a/drivers/iommu/tegra-gart.c
+++ b/drivers/iommu/tegra-gart.c
@@ -279,7 +279,7 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
}
static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long iova)
+ dma_addr_t iova)
{
struct gart_device *gart = domain->priv;
unsigned long pte;
@@ -295,7 +295,8 @@ static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,
pa = (pte & GART_PAGE_MASK);
if (!pfn_valid(__phys_to_pfn(pa))) {
- dev_err(gart->dev, "No entry for %08lx:%08x\n", iova, pa);
+ dev_err(gart->dev, "No entry for %08llx:%08x\n",
+ (unsigned long long)iova, pa);
gart_dump_table(gart);
return -EINVAL;
}
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index b34e5fd7fd9e..f6f120e25409 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -757,7 +757,7 @@ static size_t smmu_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
}
static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long iova)
+ dma_addr_t iova)
{
struct smmu_as *as = domain->priv;
unsigned long *pte;
@@ -772,7 +772,8 @@ static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain,
pfn = *pte & SMMU_PFN_MASK;
WARN_ON(!pfn_valid(pfn));
dev_dbg(as->smmu->dev,
- "iova:%08lx pfn:%08lx asid:%d\n", iova, pfn, as->asid);
+ "iova:%08llx pfn:%08lx asid:%d\n", (unsigned long long)iova,
+ pfn, as->asid);
spin_unlock_irqrestore(&as->lock, flags);
return PFN_PHYS(pfn);
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c28fcccf4a0d..cda4cb5f7327 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_MVEBU) += irq-armada-370-xp.o
obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
obj-$(CONFIG_METAG) += irq-metag-ext.o
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index 02492ab20d22..a9d2b2fa4afd 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -12,13 +12,16 @@
#include <linux/export.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/mach/irq.h>
+#ifdef CONFIG_EXYNOS_ATAGS
#include <plat/cpu.h>
+#endif
#include "irqchip.h"
@@ -26,17 +29,18 @@
#define COMBINER_ENABLE_CLEAR 0x4
#define COMBINER_INT_STATUS 0xC
+#define IRQ_IN_COMBINER 8
+
static DEFINE_SPINLOCK(irq_controller_lock);
struct combiner_chip_data {
- unsigned int irq_offset;
+ unsigned int hwirq_offset;
unsigned int irq_mask;
void __iomem *base;
unsigned int parent_irq;
};
static struct irq_domain *combiner_irq_domain;
-static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
static inline void __iomem *combiner_base(struct irq_data *data)
{
@@ -77,11 +81,11 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
if (status == 0)
goto out;
- combiner_irq = __ffs(status);
+ combiner_irq = chip_data->hwirq_offset + __ffs(status);
+ cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq);
- cascade_irq = combiner_irq + (chip_data->irq_offset & ~31);
- if (unlikely(cascade_irq >= NR_IRQS))
- do_bad_IRQ(cascade_irq, desc);
+ if (unlikely(!cascade_irq))
+ do_bad_IRQ(irq, desc);
else
generic_handle_irq(cascade_irq);
@@ -113,40 +117,25 @@ static struct irq_chip combiner_chip = {
#endif
};
-static unsigned int max_combiner_nr(void)
-{
- if (soc_is_exynos5250())
- return EXYNOS5_MAX_COMBINER_NR;
- else if (soc_is_exynos4412())
- return EXYNOS4412_MAX_COMBINER_NR;
- else if (soc_is_exynos4212())
- return EXYNOS4212_MAX_COMBINER_NR;
- else
- return EXYNOS4210_MAX_COMBINER_NR;
-}
-
-static void __init combiner_cascade_irq(unsigned int combiner_nr,
+static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data,
unsigned int irq)
{
- if (combiner_nr >= max_combiner_nr())
- BUG();
- if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0)
+ if (irq_set_handler_data(irq, combiner_data) != 0)
BUG();
irq_set_chained_handler(irq, combiner_handle_cascade_irq);
}
-static void __init combiner_init_one(unsigned int combiner_nr,
+static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
+ unsigned int combiner_nr,
void __iomem *base, unsigned int irq)
{
- combiner_data[combiner_nr].base = base;
- combiner_data[combiner_nr].irq_offset = irq_find_mapping(
- combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER);
- combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3);
- combiner_data[combiner_nr].parent_irq = irq;
+ combiner_data->base = base;
+ combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER;
+ combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3);
+ combiner_data->parent_irq = irq;
/* Disable all interrupts */
- __raw_writel(combiner_data[combiner_nr].irq_mask,
- base + COMBINER_ENABLE_CLEAR);
+ __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
}
#ifdef CONFIG_OF
@@ -162,7 +151,7 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
if (intsize < 2)
return -EINVAL;
- *out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1];
+ *out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1];
*out_type = 0;
return 0;
@@ -181,6 +170,8 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
+ struct combiner_chip_data *combiner_data = d->host_data;
+
irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
irq_set_chip_data(irq, &combiner_data[hw >> 3]);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
@@ -193,8 +184,12 @@ static struct irq_domain_ops combiner_irq_domain_ops = {
.map = combiner_irq_domain_map,
};
-static unsigned int exynos4x12_combiner_extra_irq(int group)
+static unsigned int combiner_lookup_irq(int group)
{
+#ifdef CONFIG_EXYNOS_ATAGS
+ if (group < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
+ return IRQ_SPI(group);
+
switch (group) {
case 16:
return IRQ_SPI(107);
@@ -204,53 +199,46 @@ static unsigned int exynos4x12_combiner_extra_irq(int group)
return IRQ_SPI(48);
case 19:
return IRQ_SPI(42);
- default:
- return 0;
}
+#endif
+ return 0;
}
void __init combiner_init(void __iomem *combiner_base,
- struct device_node *np)
+ struct device_node *np,
+ unsigned int max_nr,
+ int irq_base)
{
- int i, irq, irq_base;
- unsigned int max_nr, nr_irq;
+ int i, irq;
+ unsigned int nr_irq;
+ struct combiner_chip_data *combiner_data;
- max_nr = max_combiner_nr();
+ nr_irq = max_nr * IRQ_IN_COMBINER;
- if (np) {
- if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
- pr_info("%s: number of combiners not specified, "
- "setting default as %d.\n",
- __func__, max_nr);
- }
- }
-
- nr_irq = max_nr * MAX_IRQ_IN_COMBINER;
-
- irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0);
- if (IS_ERR_VALUE(irq_base)) {
- irq_base = COMBINER_IRQ(0, 0);
- pr_warning("%s: irq desc alloc failed. Continuing with %d as linux irq base\n", __func__, irq_base);
+ combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL);
+ if (!combiner_data) {
+ pr_warning("%s: could not allocate combiner data\n", __func__);
+ return;
}
- combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0,
- &combiner_irq_domain_ops, &combiner_data);
+ combiner_irq_domain = irq_domain_add_simple(np, nr_irq, irq_base,
+ &combiner_irq_domain_ops, combiner_data);
if (WARN_ON(!combiner_irq_domain)) {
pr_warning("%s: irq domain init failed\n", __func__);
return;
}
for (i = 0; i < max_nr; i++) {
- if (i < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
- irq = IRQ_SPI(i);
- else
- irq = exynos4x12_combiner_extra_irq(i);
#ifdef CONFIG_OF
if (np)
irq = irq_of_parse_and_map(np, i);
+ else
#endif
- combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq);
- combiner_cascade_irq(i, irq);
+ irq = combiner_lookup_irq(i);
+
+ combiner_init_one(&combiner_data[i], i,
+ combiner_base + (i >> 2) * 0x10, irq);
+ combiner_cascade_irq(&combiner_data[i], irq);
}
}
@@ -259,6 +247,8 @@ static int __init combiner_of_init(struct device_node *np,
struct device_node *parent)
{
void __iomem *combiner_base;
+ unsigned int max_nr = 20;
+ int irq_base = -1;
combiner_base = of_iomap(np, 0);
if (!combiner_base) {
@@ -266,7 +256,20 @@ static int __init combiner_of_init(struct device_node *np,
return -ENXIO;
}
- combiner_init(combiner_base, np);
+ if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
+ pr_info("%s: number of combiners not specified, "
+ "setting default as %d.\n",
+ __func__, max_nr);
+ }
+
+ /*
+ * FIXME: This is a hardwired COMBINER_IRQ(0,0). Once all devices
+ * get their IRQ from DT, remove this in order to get dynamic
+ * allocation.
+ */
+ irq_base = 160;
+
+ combiner_init(combiner_base, np, max_nr, irq_base);
return 0;
}
diff --git a/arch/arm/mach-mvebu/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 830139a3e2ba..bb328a366122 100644
--- a/arch/arm/mach-mvebu/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -25,7 +25,9 @@
#include <asm/mach/arch.h>
#include <asm/exception.h>
#include <asm/smp_plat.h>
-#include <asm/hardware/cache-l2x0.h>
+#include <asm/mach/irq.h>
+
+#include "irqchip.h"
/* Interrupt Controller Registers Map */
#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
@@ -46,7 +48,9 @@
#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5)
-#define ACTIVE_DOORBELLS (8)
+#define IPI_DOORBELL_START (0)
+#define IPI_DOORBELL_END (8)
+#define IPI_DOORBELL_MASK 0xFF
static DEFINE_RAW_SPINLOCK(irq_controller_lock);
@@ -184,7 +188,7 @@ void armada_xp_mpic_smp_cpu_init(void)
writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
/* Enable first 8 IPIs */
- writel((1 << ACTIVE_DOORBELLS) - 1, per_cpu_int_base +
+ writel(IPI_DOORBELL_MASK, per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
/* Unmask IPI interrupt */
@@ -197,46 +201,8 @@ static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
.xlate = irq_domain_xlate_onecell,
};
-static int __init armada_370_xp_mpic_of_init(struct device_node *node,
- struct device_node *parent)
-{
- u32 control;
-
- main_int_base = of_iomap(node, 0);
- per_cpu_int_base = of_iomap(node, 1);
-
- BUG_ON(!main_int_base);
- BUG_ON(!per_cpu_int_base);
-
- control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
-
- armada_370_xp_mpic_domain =
- irq_domain_add_linear(node, (control >> 2) & 0x3ff,
- &armada_370_xp_mpic_irq_ops, NULL);
-
- if (!armada_370_xp_mpic_domain)
- panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n");
-
- irq_set_default_host(armada_370_xp_mpic_domain);
-
-#ifdef CONFIG_SMP
- armada_xp_mpic_smp_cpu_init();
-
- /*
- * Set the default affinity from all CPUs to the boot cpu.
- * This is required since the MPIC doesn't limit several CPUs
- * from acknowledging the same interrupt.
- */
- cpumask_clear(irq_default_affinity);
- cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
-
-#endif
-
- return 0;
-}
-
-asmlinkage void __exception_irq_entry armada_370_xp_handle_irq(struct pt_regs
- *regs)
+static asmlinkage void __exception_irq_entry
+armada_370_xp_handle_irq(struct pt_regs *regs)
{
u32 irqstat, irqnr;
@@ -261,13 +227,14 @@ asmlinkage void __exception_irq_entry armada_370_xp_handle_irq(struct pt_regs
ipimask = readl_relaxed(per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
- & 0xFF;
+ & IPI_DOORBELL_MASK;
- writel(0x0, per_cpu_int_base +
+ writel(~IPI_DOORBELL_MASK, per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
/* Handle all pending doorbells */
- for (ipinr = 0; ipinr < ACTIVE_DOORBELLS; ipinr++) {
+ for (ipinr = IPI_DOORBELL_START;
+ ipinr < IPI_DOORBELL_END; ipinr++) {
if (ipimask & (0x1 << ipinr))
handle_IPI(ipinr, regs);
}
@@ -278,15 +245,44 @@ asmlinkage void __exception_irq_entry armada_370_xp_handle_irq(struct pt_regs
} while (1);
}
-static const struct of_device_id mpic_of_match[] __initconst = {
- {.compatible = "marvell,mpic", .data = armada_370_xp_mpic_of_init},
- {},
-};
-
-void __init armada_370_xp_init_irq(void)
+static int __init armada_370_xp_mpic_of_init(struct device_node *node,
+ struct device_node *parent)
{
- of_irq_init(mpic_of_match);
-#ifdef CONFIG_CACHE_L2X0
- l2x0_of_init(0, ~0UL);
+ u32 control;
+
+ main_int_base = of_iomap(node, 0);
+ per_cpu_int_base = of_iomap(node, 1);
+
+ BUG_ON(!main_int_base);
+ BUG_ON(!per_cpu_int_base);
+
+ control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
+
+ armada_370_xp_mpic_domain =
+ irq_domain_add_linear(node, (control >> 2) & 0x3ff,
+ &armada_370_xp_mpic_irq_ops, NULL);
+
+ if (!armada_370_xp_mpic_domain)
+ panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n");
+
+ irq_set_default_host(armada_370_xp_mpic_domain);
+
+#ifdef CONFIG_SMP
+ armada_xp_mpic_smp_cpu_init();
+
+ /*
+ * Set the default affinity from all CPUs to the boot cpu.
+ * This is required since the MPIC doesn't limit several CPUs
+ * from acknowledging the same interrupt.
+ */
+ cpumask_clear(irq_default_affinity);
+ cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
+
#endif
+
+ set_handle_irq(armada_370_xp_handle_irq);
+
+ return 0;
}
+
+IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index bf957a358a0d..d44806d41b44 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -194,8 +194,8 @@ config LEDS_LP3944
module will be called leds-lp3944.
config LEDS_LP55XX_COMMON
- tristate "Common Driver for TI/National LP5521 and LP5523/55231"
- depends on LEDS_LP5521 || LEDS_LP5523
+ tristate "Common Driver for TI/National LP5521, LP5523/55231 and LP5562"
+ depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562
select FW_LOADER
help
This option supports common operations for LP5521 and LP5523/55231
@@ -222,6 +222,16 @@ config LEDS_LP5523
Driver provides direct control via LED class and interface for
programming the engines.
+config LEDS_LP5562
+ tristate "LED Support for TI LP5562 LED driver chip"
+ depends on LEDS_CLASS && I2C
+ select LEDS_LP55XX_COMMON
+ help
+ If you say yes here you get support for TI LP5562 LED driver.
+ It is 4 channels chip with programmable engines.
+ Driver provides direct control via LED class and interface for
+ programming the engines.
+
config LEDS_LP8788
tristate "LED support for the TI LP8788 PMIC"
depends on LEDS_CLASS
@@ -469,112 +479,7 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED.
-config LEDS_TRIGGERS
- bool "LED Trigger support"
- depends on LEDS_CLASS
- help
- This option enables trigger support for the leds class.
- These triggers allow kernel events to drive the LEDs and can
- be configured via sysfs. If unsure, say Y.
-
comment "LED Triggers"
-
-config LEDS_TRIGGER_TIMER
- tristate "LED Timer Trigger"
- depends on LEDS_TRIGGERS
- help
- This allows LEDs to be controlled by a programmable timer
- via sysfs. Some LED hardware can be programmed to start
- blinking the LED without any further software interaction.
- For more details read Documentation/leds/leds-class.txt.
-
- If unsure, say Y.
-
-config LEDS_TRIGGER_ONESHOT
- tristate "LED One-shot Trigger"
- depends on LEDS_TRIGGERS
- help
- This allows LEDs to blink in one-shot pulses with parameters
- controlled via sysfs. It's useful to notify the user on
- sporadic events, when there are no clear begin and end trap points,
- or on dense events, where this blinks the LED at constant rate if
- rearmed continuously.
-
- It also shows how to use the led_blink_set_oneshot() function.
-
- If unsure, say Y.
-
-config LEDS_TRIGGER_IDE_DISK
- bool "LED IDE Disk Trigger"
- depends on IDE_GD_ATA
- depends on LEDS_TRIGGERS
- help
- This allows LEDs to be controlled by IDE disk activity.
- If unsure, say Y.
-
-config LEDS_TRIGGER_HEARTBEAT
- tristate "LED Heartbeat Trigger"
- depends on LEDS_TRIGGERS
- help
- This allows LEDs to be controlled by a CPU load average.
- The flash frequency is a hyperbolic function of the 1-minute
- load average.
- If unsure, say Y.
-
-config LEDS_TRIGGER_BACKLIGHT
- tristate "LED backlight Trigger"
- depends on LEDS_TRIGGERS
- help
- This allows LEDs to be controlled as a backlight device: they
- turn off and on when the display is blanked and unblanked.
-
- If unsure, say N.
-
-config LEDS_TRIGGER_CPU
- bool "LED CPU Trigger"
- depends on LEDS_TRIGGERS
- help
- This allows LEDs to be controlled by active CPUs. This shows
- the active CPUs across an array of LEDs so you can see which
- CPUs are active on the system at any given moment.
-
- If unsure, say N.
-
-config LEDS_TRIGGER_GPIO
- tristate "LED GPIO Trigger"
- depends on LEDS_TRIGGERS
- depends on GPIOLIB
- help
- This allows LEDs to be controlled by gpio events. It's good
- when using gpios as switches and triggering the needed LEDs
- from there. One use case is n810's keypad LEDs that could
- be triggered by this trigger when user slides up to show
- keypad.
-
- If unsure, say N.
-
-config LEDS_TRIGGER_DEFAULT_ON
- tristate "LED Default ON Trigger"
- depends on LEDS_TRIGGERS
- help
- This allows LEDs to be initialised in the ON state.
- If unsure, say Y.
-
-config LEDS_TRIGGER_SLEEP
- tristate "LED Sleep Mode Trigger"
- depends on LEDS_TRIGGERS && HAS_EARLYSUSPEND
- help
- This turns LEDs on when the screen is off but the cpu still running.
-
-comment "iptables trigger is under Netfilter config (LED target)"
- depends on LEDS_TRIGGERS
-
-config LEDS_TRIGGER_TRANSIENT
- tristate "LED Transient Trigger"
- depends on LEDS_TRIGGERS
- help
- This allows one time activation of a transient state on
- GPIO/PWM based hardware.
- If unsure, say Y.
+source "drivers/leds/trigger/Kconfig"
endif # NEW_LEDS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 9424400ab678..ecc1bfe103bf 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o
obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
+obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
@@ -57,13 +58,5 @@ obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
# LED Triggers
-obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
-obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o
-obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
-obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
-obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
-obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
-obj-$(CONFIG_LEDS_TRIGGER_CPU) += ledtrig-cpu.o
-obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
-obj-$(CONFIG_LEDS_TRIGGER_SLEEP) += ledtrig-sleep.o
-obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
+obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
+
diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c
index b474745e001b..cf9efe421c2b 100644
--- a/drivers/leds/leds-asic3.c
+++ b/drivers/leds/leds-asic3.c
@@ -134,6 +134,7 @@ static int asic3_led_remove(struct platform_device *pdev)
return mfd_cell_disable(pdev);
}
+#ifdef CONFIG_PM_SLEEP
static int asic3_led_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -159,11 +160,9 @@ static int asic3_led_resume(struct device *dev)
return ret;
}
+#endif
-static const struct dev_pm_ops asic3_led_pm_ops = {
- .suspend = asic3_led_suspend,
- .resume = asic3_led_resume,
-};
+static SIMPLE_DEV_PM_OPS(asic3_led_pm_ops, asic3_led_suspend, asic3_led_resume);
static struct platform_driver asic3_led_driver = {
.probe = asic3_led_probe,
diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c
index 386773532d95..8a39c5b20f76 100644
--- a/drivers/leds/leds-atmel-pwm.c
+++ b/drivers/leds/leds-atmel-pwm.c
@@ -113,7 +113,7 @@ err:
return status;
}
-static int __exit pwmled_remove(struct platform_device *pdev)
+static int pwmled_remove(struct platform_device *pdev)
{
const struct gpio_led_platform_data *pdata;
struct pwmled *leds;
@@ -140,7 +140,7 @@ static struct platform_driver pwmled_driver = {
},
/* REVISIT add suspend() and resume() methods */
.probe = pwmled_probe,
- .remove = __exit_p(pwmled_remove),
+ .remove = pwmled_remove,
};
module_platform_driver(pwmled_driver);
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
index 851517030cc1..2db04231a792 100644
--- a/drivers/leds/leds-bd2802.c
+++ b/drivers/leds/leds-bd2802.c
@@ -732,7 +732,7 @@ failed_unregister_dev_file:
return ret;
}
-static int __exit bd2802_remove(struct i2c_client *client)
+static int bd2802_remove(struct i2c_client *client)
{
struct bd2802_led *led = i2c_get_clientdata(client);
int i;
@@ -747,8 +747,7 @@ static int __exit bd2802_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-
+#ifdef CONFIG_PM_SLEEP
static void bd2802_restore_state(struct bd2802_led *led)
{
int i;
@@ -785,12 +784,9 @@ static int bd2802_resume(struct device *dev)
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume);
-#define BD2802_PM (&bd2802_pm)
-#else /* CONFIG_PM */
-#define BD2802_PM NULL
-#endif
static const struct i2c_device_id bd2802_id[] = {
{ "BD2802", 0 },
@@ -801,10 +797,10 @@ MODULE_DEVICE_TABLE(i2c, bd2802_id);
static struct i2c_driver bd2802_i2c_driver = {
.driver = {
.name = "BD2802",
- .pm = BD2802_PM,
+ .pm = &bd2802_pm,
},
.probe = bd2802_probe,
- .remove = __exit_p(bd2802_remove),
+ .remove = bd2802_remove,
.id_table = bd2802_id,
};
diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c
index 4117235ba618..d81a8e7afd6c 100644
--- a/drivers/leds/leds-lm355x.c
+++ b/drivers/leds/leds-lm355x.c
@@ -477,6 +477,7 @@ static int lm355x_probe(struct i2c_client *client,
chip->cdev_flash.name = "flash";
chip->cdev_flash.max_brightness = 16;
chip->cdev_flash.brightness_set = lm355x_strobe_brightness_set;
+ chip->cdev_flash.default_trigger = "flash";
err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_flash);
if (err < 0)
@@ -486,6 +487,7 @@ static int lm355x_probe(struct i2c_client *client,
chip->cdev_torch.name = "torch";
chip->cdev_torch.max_brightness = 8;
chip->cdev_torch.brightness_set = lm355x_torch_brightness_set;
+ chip->cdev_torch.default_trigger = "torch";
err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_torch);
if (err < 0)
diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c
index 9f428d9dfe91..f361bbef2dec 100644
--- a/drivers/leds/leds-lm3642.c
+++ b/drivers/leds/leds-lm3642.c
@@ -363,6 +363,7 @@ static int lm3642_probe(struct i2c_client *client,
chip->cdev_flash.name = "flash";
chip->cdev_flash.max_brightness = 16;
chip->cdev_flash.brightness_set = lm3642_strobe_brightness_set;
+ chip->cdev_flash.default_trigger = "flash";
err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_flash);
if (err < 0) {
@@ -380,6 +381,7 @@ static int lm3642_probe(struct i2c_client *client,
chip->cdev_torch.name = "torch";
chip->cdev_torch.max_brightness = 8;
chip->cdev_torch.brightness_set = lm3642_torch_brightness_set;
+ chip->cdev_torch.default_trigger = "torch";
err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_torch);
if (err < 0) {
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 1001347ba70b..19752c928aa2 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -68,6 +68,18 @@
#define LP5521_ENABLE_RUN_PROGRAM \
(LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN)
+/* CONFIG register */
+#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
+#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
+#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
+#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
+#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
+#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
+#define LP5521_R_TO_BATT 0x04 /* R out: 0 = CP, 1 = Vbat */
+#define LP5521_CLK_INT 0x01 /* Internal clock */
+#define LP5521_DEFAULT_CFG \
+ (LP5521_PWM_HF | LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO)
+
/* Status */
#define LP5521_EXT_CLK_USED 0x08
@@ -296,8 +308,11 @@ static int lp5521_post_init_device(struct lp55xx_chip *chip)
/* Set all PWMs to direct control mode */
ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
- val = chip->pdata->update_config ?
- : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
+ /* Update configuration for the clock setting */
+ val = LP5521_DEFAULT_CFG;
+ if (!lp55xx_is_extclk_used(chip))
+ val |= LP5521_CLK_INT;
+
ret = lp55xx_write(chip, LP5521_REG_CONFIG, val);
if (ret)
return ret;
@@ -360,7 +375,8 @@ static ssize_t lp5521_selftest(struct device *dev,
mutex_lock(&chip->lock);
ret = lp5521_run_selftest(chip, buf);
mutex_unlock(&chip->lock);
- return sprintf(buf, "%s\n", ret ? "FAIL" : "OK");
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", ret ? "FAIL" : "OK");
}
/* device attributes */
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
new file mode 100644
index 000000000000..513f2390ca2d
--- /dev/null
+++ b/drivers/leds/leds-lp5562.c
@@ -0,0 +1,599 @@
+/*
+ * LP5562 LED driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.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/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/leds-lp55xx.h>
+#include <linux/slab.h>
+
+#include "leds-lp55xx-common.h"
+
+#define LP5562_PROGRAM_LENGTH 32
+#define LP5562_MAX_LEDS 4
+
+/* ENABLE Register 00h */
+#define LP5562_REG_ENABLE 0x00
+#define LP5562_EXEC_ENG1_M 0x30
+#define LP5562_EXEC_ENG2_M 0x0C
+#define LP5562_EXEC_ENG3_M 0x03
+#define LP5562_EXEC_M 0x3F
+#define LP5562_MASTER_ENABLE 0x40 /* Chip master enable */
+#define LP5562_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */
+#define LP5562_EXEC_RUN 0x2A
+#define LP5562_ENABLE_DEFAULT \
+ (LP5562_MASTER_ENABLE | LP5562_LOGARITHMIC_PWM)
+#define LP5562_ENABLE_RUN_PROGRAM \
+ (LP5562_ENABLE_DEFAULT | LP5562_EXEC_RUN)
+
+/* OPMODE Register 01h */
+#define LP5562_REG_OP_MODE 0x01
+#define LP5562_MODE_ENG1_M 0x30
+#define LP5562_MODE_ENG2_M 0x0C
+#define LP5562_MODE_ENG3_M 0x03
+#define LP5562_LOAD_ENG1 0x10
+#define LP5562_LOAD_ENG2 0x04
+#define LP5562_LOAD_ENG3 0x01
+#define LP5562_RUN_ENG1 0x20
+#define LP5562_RUN_ENG2 0x08
+#define LP5562_RUN_ENG3 0x02
+#define LP5562_ENG1_IS_LOADING(mode) \
+ ((mode & LP5562_MODE_ENG1_M) == LP5562_LOAD_ENG1)
+#define LP5562_ENG2_IS_LOADING(mode) \
+ ((mode & LP5562_MODE_ENG2_M) == LP5562_LOAD_ENG2)
+#define LP5562_ENG3_IS_LOADING(mode) \
+ ((mode & LP5562_MODE_ENG3_M) == LP5562_LOAD_ENG3)
+
+/* BRIGHTNESS Registers */
+#define LP5562_REG_R_PWM 0x04
+#define LP5562_REG_G_PWM 0x03
+#define LP5562_REG_B_PWM 0x02
+#define LP5562_REG_W_PWM 0x0E
+
+/* CURRENT Registers */
+#define LP5562_REG_R_CURRENT 0x07
+#define LP5562_REG_G_CURRENT 0x06
+#define LP5562_REG_B_CURRENT 0x05
+#define LP5562_REG_W_CURRENT 0x0F
+
+/* CONFIG Register 08h */
+#define LP5562_REG_CONFIG 0x08
+#define LP5562_PWM_HF 0x40
+#define LP5562_PWRSAVE_EN 0x20
+#define LP5562_CLK_INT 0x01 /* Internal clock */
+#define LP5562_DEFAULT_CFG (LP5562_PWM_HF | LP5562_PWRSAVE_EN)
+
+/* RESET Register 0Dh */
+#define LP5562_REG_RESET 0x0D
+#define LP5562_RESET 0xFF
+
+/* PROGRAM ENGINE Registers */
+#define LP5562_REG_PROG_MEM_ENG1 0x10
+#define LP5562_REG_PROG_MEM_ENG2 0x30
+#define LP5562_REG_PROG_MEM_ENG3 0x50
+
+/* LEDMAP Register 70h */
+#define LP5562_REG_ENG_SEL 0x70
+#define LP5562_ENG_SEL_PWM 0
+#define LP5562_ENG_FOR_RGB_M 0x3F
+#define LP5562_ENG_SEL_RGB 0x1B /* R:ENG1, G:ENG2, B:ENG3 */
+#define LP5562_ENG_FOR_W_M 0xC0
+#define LP5562_ENG1_FOR_W 0x40 /* W:ENG1 */
+#define LP5562_ENG2_FOR_W 0x80 /* W:ENG2 */
+#define LP5562_ENG3_FOR_W 0xC0 /* W:ENG3 */
+
+/* Program Commands */
+#define LP5562_CMD_DISABLE 0x00
+#define LP5562_CMD_LOAD 0x15
+#define LP5562_CMD_RUN 0x2A
+#define LP5562_CMD_DIRECT 0x3F
+#define LP5562_PATTERN_OFF 0
+
+static inline void lp5562_wait_opmode_done(void)
+{
+ /* operation mode change needs to be longer than 153 us */
+ usleep_range(200, 300);
+}
+
+static inline void lp5562_wait_enable_done(void)
+{
+ /* it takes more 488 us to update ENABLE register */
+ usleep_range(500, 600);
+}
+
+static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
+{
+ u8 addr[] = {
+ LP5562_REG_R_CURRENT,
+ LP5562_REG_G_CURRENT,
+ LP5562_REG_B_CURRENT,
+ LP5562_REG_W_CURRENT,
+ };
+
+ led->led_current = led_current;
+ lp55xx_write(led->chip, addr[led->chan_nr], led_current);
+}
+
+static void lp5562_load_engine(struct lp55xx_chip *chip)
+{
+ enum lp55xx_engine_index idx = chip->engine_idx;
+ u8 mask[] = {
+ [LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M,
+ [LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M,
+ [LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M,
+ };
+
+ u8 val[] = {
+ [LP55XX_ENGINE_1] = LP5562_LOAD_ENG1,
+ [LP55XX_ENGINE_2] = LP5562_LOAD_ENG2,
+ [LP55XX_ENGINE_3] = LP5562_LOAD_ENG3,
+ };
+
+ lp55xx_update_bits(chip, LP5562_REG_OP_MODE, mask[idx], val[idx]);
+
+ lp5562_wait_opmode_done();
+}
+
+static void lp5562_stop_engine(struct lp55xx_chip *chip)
+{
+ lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DISABLE);
+ lp5562_wait_opmode_done();
+}
+
+static void lp5562_run_engine(struct lp55xx_chip *chip, bool start)
+{
+ int ret;
+ u8 mode;
+ u8 exec;
+
+ /* stop engine */
+ if (!start) {
+ lp55xx_write(chip, LP5562_REG_ENABLE, LP5562_ENABLE_DEFAULT);
+ lp5562_wait_enable_done();
+ lp5562_stop_engine(chip);
+ lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
+ lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
+ lp5562_wait_opmode_done();
+ return;
+ }
+
+ /*
+ * To run the engine,
+ * operation mode and enable register should updated at the same time
+ */
+
+ ret = lp55xx_read(chip, LP5562_REG_OP_MODE, &mode);
+ if (ret)
+ return;
+
+ ret = lp55xx_read(chip, LP5562_REG_ENABLE, &exec);
+ if (ret)
+ return;
+
+ /* change operation mode to RUN only when each engine is loading */
+ if (LP5562_ENG1_IS_LOADING(mode)) {
+ mode = (mode & ~LP5562_MODE_ENG1_M) | LP5562_RUN_ENG1;
+ exec = (exec & ~LP5562_EXEC_ENG1_M) | LP5562_RUN_ENG1;
+ }
+
+ if (LP5562_ENG2_IS_LOADING(mode)) {
+ mode = (mode & ~LP5562_MODE_ENG2_M) | LP5562_RUN_ENG2;
+ exec = (exec & ~LP5562_EXEC_ENG2_M) | LP5562_RUN_ENG2;
+ }
+
+ if (LP5562_ENG3_IS_LOADING(mode)) {
+ mode = (mode & ~LP5562_MODE_ENG3_M) | LP5562_RUN_ENG3;
+ exec = (exec & ~LP5562_EXEC_ENG3_M) | LP5562_RUN_ENG3;
+ }
+
+ lp55xx_write(chip, LP5562_REG_OP_MODE, mode);
+ lp5562_wait_opmode_done();
+
+ lp55xx_update_bits(chip, LP5562_REG_ENABLE, LP5562_EXEC_M, exec);
+ lp5562_wait_enable_done();
+}
+
+static int lp5562_update_firmware(struct lp55xx_chip *chip,
+ const u8 *data, size_t size)
+{
+ enum lp55xx_engine_index idx = chip->engine_idx;
+ u8 pattern[LP5562_PROGRAM_LENGTH] = {0};
+ u8 addr[] = {
+ [LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1,
+ [LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2,
+ [LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3,
+ };
+ unsigned cmd;
+ char c[3];
+ int program_size;
+ int nrchars;
+ int offset = 0;
+ int ret;
+ int i;
+
+ /* clear program memory before updating */
+ for (i = 0; i < LP5562_PROGRAM_LENGTH; i++)
+ lp55xx_write(chip, addr[idx] + i, 0);
+
+ i = 0;
+ while ((offset < size - 1) && (i < LP5562_PROGRAM_LENGTH)) {
+ /* separate sscanfs because length is working only for %s */
+ ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
+ if (ret != 1)
+ goto err;
+
+ ret = sscanf(c, "%2x", &cmd);
+ if (ret != 1)
+ goto err;
+
+ pattern[i] = (u8)cmd;
+ offset += nrchars;
+ i++;
+ }
+
+ /* Each instruction is 16bit long. Check that length is even */
+ if (i % 2)
+ goto err;
+
+ program_size = i;
+ for (i = 0; i < program_size; i++)
+ lp55xx_write(chip, addr[idx] + i, pattern[i]);
+
+ return 0;
+
+err:
+ dev_err(&chip->cl->dev, "wrong pattern format\n");
+ return -EINVAL;
+}
+
+static void lp5562_firmware_loaded(struct lp55xx_chip *chip)
+{
+ const struct firmware *fw = chip->fw;
+
+ if (fw->size > LP5562_PROGRAM_LENGTH) {
+ dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
+ fw->size);
+ return;
+ }
+
+ /*
+ * Program momery sequence
+ * 1) set engine mode to "LOAD"
+ * 2) write firmware data into program memory
+ */
+
+ lp5562_load_engine(chip);
+ lp5562_update_firmware(chip, fw->data, fw->size);
+}
+
+static int lp5562_post_init_device(struct lp55xx_chip *chip)
+{
+ int ret;
+ u8 cfg = LP5562_DEFAULT_CFG;
+
+ /* Set all PWMs to direct control mode */
+ ret = lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
+ if (ret)
+ return ret;
+
+ lp5562_wait_opmode_done();
+
+ /* Update configuration for the clock setting */
+ if (!lp55xx_is_extclk_used(chip))
+ cfg |= LP5562_CLK_INT;
+
+ ret = lp55xx_write(chip, LP5562_REG_CONFIG, cfg);
+ if (ret)
+ return ret;
+
+ /* Initialize all channels PWM to zero -> leds off */
+ lp55xx_write(chip, LP5562_REG_R_PWM, 0);
+ lp55xx_write(chip, LP5562_REG_G_PWM, 0);
+ lp55xx_write(chip, LP5562_REG_B_PWM, 0);
+ lp55xx_write(chip, LP5562_REG_W_PWM, 0);
+
+ /* Set LED map as register PWM by default */
+ lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
+
+ return 0;
+}
+
+static void lp5562_led_brightness_work(struct work_struct *work)
+{
+ struct lp55xx_led *led = container_of(work, struct lp55xx_led,
+ brightness_work);
+ struct lp55xx_chip *chip = led->chip;
+ u8 addr[] = {
+ LP5562_REG_R_PWM,
+ LP5562_REG_G_PWM,
+ LP5562_REG_B_PWM,
+ LP5562_REG_W_PWM,
+ };
+
+ mutex_lock(&chip->lock);
+ lp55xx_write(chip, addr[led->chan_nr], led->brightness);
+ mutex_unlock(&chip->lock);
+}
+
+static void lp5562_write_program_memory(struct lp55xx_chip *chip,
+ u8 base, const u8 *rgb, int size)
+{
+ int i;
+
+ if (!rgb || size <= 0)
+ return;
+
+ for (i = 0; i < size; i++)
+ lp55xx_write(chip, base + i, *(rgb + i));
+
+ lp55xx_write(chip, base + i, 0);
+ lp55xx_write(chip, base + i + 1, 0);
+}
+
+/* check the size of program count */
+static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn)
+{
+ return (ptn->size_r >= LP5562_PROGRAM_LENGTH ||
+ ptn->size_g >= LP5562_PROGRAM_LENGTH ||
+ ptn->size_b >= LP5562_PROGRAM_LENGTH);
+}
+
+static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
+{
+ struct lp55xx_predef_pattern *ptn;
+ int i;
+
+ if (mode == LP5562_PATTERN_OFF) {
+ lp5562_run_engine(chip, false);
+ return 0;
+ }
+
+ ptn = chip->pdata->patterns + (mode - 1);
+ if (!ptn || _is_pc_overflow(ptn)) {
+ dev_err(&chip->cl->dev, "invalid pattern data\n");
+ return -EINVAL;
+ }
+
+ lp5562_stop_engine(chip);
+
+ /* Set LED map as RGB */
+ lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_RGB);
+
+ /* Load engines */
+ for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
+ chip->engine_idx = i;
+ lp5562_load_engine(chip);
+ }
+
+ /* Clear program registers */
+ lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1, 0);
+ lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1 + 1, 0);
+ lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2, 0);
+ lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2 + 1, 0);
+ lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3, 0);
+ lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3 + 1, 0);
+
+ /* Program engines */
+ lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG1,
+ ptn->r, ptn->size_r);
+ lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG2,
+ ptn->g, ptn->size_g);
+ lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG3,
+ ptn->b, ptn->size_b);
+
+ /* Run engines */
+ lp5562_run_engine(chip, true);
+
+ return 0;
+}
+
+static ssize_t lp5562_store_pattern(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ struct lp55xx_predef_pattern *ptn = chip->pdata->patterns;
+ int num_patterns = chip->pdata->num_patterns;
+ unsigned long mode;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &mode);
+ if (ret)
+ return ret;
+
+ if (mode > num_patterns || !ptn)
+ return -EINVAL;
+
+ mutex_lock(&chip->lock);
+ ret = lp5562_run_predef_led_pattern(chip, mode);
+ mutex_unlock(&chip->lock);
+
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t lp5562_store_engine_mux(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ u8 mask;
+ u8 val;
+
+ /* LED map
+ * R ... Engine 1 (fixed)
+ * G ... Engine 2 (fixed)
+ * B ... Engine 3 (fixed)
+ * W ... Engine 1 or 2 or 3
+ */
+
+ if (sysfs_streq(buf, "RGB")) {
+ mask = LP5562_ENG_FOR_RGB_M;
+ val = LP5562_ENG_SEL_RGB;
+ } else if (sysfs_streq(buf, "W")) {
+ enum lp55xx_engine_index idx = chip->engine_idx;
+
+ mask = LP5562_ENG_FOR_W_M;
+ switch (idx) {
+ case LP55XX_ENGINE_1:
+ val = LP5562_ENG1_FOR_W;
+ break;
+ case LP55XX_ENGINE_2:
+ val = LP5562_ENG2_FOR_W;
+ break;
+ case LP55XX_ENGINE_3:
+ val = LP5562_ENG3_FOR_W;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ } else {
+ dev_err(dev, "choose RGB or W\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ lp55xx_update_bits(chip, LP5562_REG_ENG_SEL, mask, val);
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+
+static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern);
+static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux);
+
+static struct attribute *lp5562_attributes[] = {
+ &dev_attr_led_pattern.attr,
+ &dev_attr_engine_mux.attr,
+ NULL,
+};
+
+static const struct attribute_group lp5562_group = {
+ .attrs = lp5562_attributes,
+};
+
+/* Chip specific configurations */
+static struct lp55xx_device_config lp5562_cfg = {
+ .max_channel = LP5562_MAX_LEDS,
+ .reset = {
+ .addr = LP5562_REG_RESET,
+ .val = LP5562_RESET,
+ },
+ .enable = {
+ .addr = LP5562_REG_ENABLE,
+ .val = LP5562_ENABLE_DEFAULT,
+ },
+ .post_init_device = lp5562_post_init_device,
+ .set_led_current = lp5562_set_led_current,
+ .brightness_work_fn = lp5562_led_brightness_work,
+ .run_engine = lp5562_run_engine,
+ .firmware_cb = lp5562_firmware_loaded,
+ .dev_attr_group = &lp5562_group,
+};
+
+static int lp5562_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct lp55xx_chip *chip;
+ struct lp55xx_led *led;
+ struct lp55xx_platform_data *pdata = client->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ led = devm_kzalloc(&client->dev,
+ sizeof(*led) * pdata->num_channels, GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ chip->cl = client;
+ chip->pdata = pdata;
+ chip->cfg = &lp5562_cfg;
+
+ mutex_init(&chip->lock);
+
+ i2c_set_clientdata(client, led);
+
+ ret = lp55xx_init_device(chip);
+ if (ret)
+ goto err_init;
+
+ ret = lp55xx_register_leds(led, chip);
+ if (ret)
+ goto err_register_leds;
+
+ ret = lp55xx_register_sysfs(chip);
+ if (ret) {
+ dev_err(&client->dev, "registering sysfs failed\n");
+ goto err_register_sysfs;
+ }
+
+ return 0;
+
+err_register_sysfs:
+ lp55xx_unregister_leds(led, chip);
+err_register_leds:
+ lp55xx_deinit_device(chip);
+err_init:
+ return ret;
+}
+
+static int lp5562_remove(struct i2c_client *client)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(client);
+ struct lp55xx_chip *chip = led->chip;
+
+ lp5562_stop_engine(chip);
+
+ lp55xx_unregister_sysfs(chip);
+ lp55xx_unregister_leds(led, chip);
+ lp55xx_deinit_device(chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id lp5562_id[] = {
+ { "lp5562", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lp5562_id);
+
+static struct i2c_driver lp5562_driver = {
+ .driver = {
+ .name = "lp5562",
+ },
+ .probe = lp5562_probe,
+ .remove = lp5562_remove,
+ .id_table = lp5562_id,
+};
+
+module_i2c_driver(lp5562_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP5562 LED Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
index d9eb84157423..ba34199dc3d9 100644
--- a/drivers/leds/leds-lp55xx-common.c
+++ b/drivers/leds/leds-lp55xx-common.c
@@ -1,5 +1,5 @@
/*
- * LP5521/LP5523/LP55231 Common Driver
+ * LP5521/LP5523/LP55231/LP5562 Common Driver
*
* Copyright 2012 Texas Instruments
*
@@ -12,6 +12,7 @@
* Derived from leds-lp5521.c, leds-lp5523.c
*/
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
@@ -21,6 +22,9 @@
#include "leds-lp55xx-common.h"
+/* External clock rate */
+#define LP55XX_CLK_32K 32768
+
static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
{
return container_of(cdev, struct lp55xx_led, cdev);
@@ -80,7 +84,7 @@ static ssize_t lp55xx_show_current(struct device *dev,
{
struct lp55xx_led *led = dev_to_lp55xx_led(dev);
- return sprintf(buf, "%d\n", led->led_current);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", led->led_current);
}
static ssize_t lp55xx_store_current(struct device *dev,
@@ -113,7 +117,7 @@ static ssize_t lp55xx_show_max_current(struct device *dev,
{
struct lp55xx_led *led = dev_to_lp55xx_led(dev);
- return sprintf(buf, "%d\n", led->max_current);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", led->max_current);
}
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current,
@@ -357,6 +361,35 @@ int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val)
}
EXPORT_SYMBOL_GPL(lp55xx_update_bits);
+bool lp55xx_is_extclk_used(struct lp55xx_chip *chip)
+{
+ struct clk *clk;
+ int err;
+
+ clk = devm_clk_get(&chip->cl->dev, "32k_clk");
+ if (IS_ERR(clk))
+ goto use_internal_clk;
+
+ err = clk_prepare_enable(clk);
+ if (err)
+ goto use_internal_clk;
+
+ if (clk_get_rate(clk) != LP55XX_CLK_32K) {
+ clk_disable_unprepare(clk);
+ goto use_internal_clk;
+ }
+
+ dev_info(&chip->cl->dev, "%dHz external clock used\n", LP55XX_CLK_32K);
+
+ chip->clk = clk;
+ return true;
+
+use_internal_clk:
+ dev_info(&chip->cl->dev, "internal clock used\n");
+ return false;
+}
+EXPORT_SYMBOL_GPL(lp55xx_is_extclk_used);
+
int lp55xx_init_device(struct lp55xx_chip *chip)
{
struct lp55xx_platform_data *pdata;
@@ -421,6 +454,9 @@ void lp55xx_deinit_device(struct lp55xx_chip *chip)
{
struct lp55xx_platform_data *pdata = chip->pdata;
+ if (chip->clk)
+ clk_disable_unprepare(chip->clk);
+
if (pdata->enable)
pdata->enable(0);
diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h
index ece4761a1302..fa6a078bf547 100644
--- a/drivers/leds/leds-lp55xx-common.h
+++ b/drivers/leds/leds-lp55xx-common.h
@@ -83,6 +83,7 @@ struct lp55xx_device_config {
*/
struct lp55xx_chip {
struct i2c_client *cl;
+ struct clk *clk;
struct lp55xx_platform_data *pdata;
struct mutex lock; /* lock for user-space interface */
int num_leds;
@@ -117,6 +118,9 @@ extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val);
extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg,
u8 mask, u8 val);
+/* external clock detection */
+extern bool lp55xx_is_extclk_used(struct lp55xx_chip *chip);
+
/* common device init/deinit functions */
extern int lp55xx_init_device(struct lp55xx_chip *chip);
extern void lp55xx_deinit_device(struct lp55xx_chip *chip);
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
index c9b9e1fec587..ca48a7d5502d 100644
--- a/drivers/leds/leds-lt3593.c
+++ b/drivers/leds/leds-lt3593.c
@@ -106,8 +106,9 @@ static int create_lt3593_led(const struct gpio_led *template,
if (!template->retain_state_suspended)
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
- ret = devm_gpio_request_one(parent, template->gpio,
- GPIOF_DIR_OUT | state, template->name);
+ ret = devm_gpio_request_one(parent, template->gpio, state ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
+ template->name);
if (ret < 0)
return ret;
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c
index d978171c25b4..70137b1eecf5 100644
--- a/drivers/leds/leds-ns2.c
+++ b/drivers/leds/leds-ns2.c
@@ -193,7 +193,8 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
enum ns2_led_modes mode;
ret = devm_gpio_request_one(&pdev->dev, template->cmd,
- GPIOF_DIR_OUT | gpio_get_value(template->cmd),
+ gpio_get_value(template->cmd) ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
template->name);
if (ret) {
dev_err(&pdev->dev, "%s: failed to setup command GPIO\n",
@@ -202,7 +203,8 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
}
ret = devm_gpio_request_one(&pdev->dev, template->slow,
- GPIOF_DIR_OUT | gpio_get_value(template->slow),
+ gpio_get_value(template->slow) ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
template->name);
if (ret) {
dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n",
@@ -306,10 +308,21 @@ static const struct of_device_id of_ns2_leds_match[] = {
};
#endif /* CONFIG_OF_GPIO */
+struct ns2_led_priv {
+ int num_leds;
+ struct ns2_led_data leds_data[];
+};
+
+static inline int sizeof_ns2_led_priv(int num_leds)
+{
+ return sizeof(struct ns2_led_priv) +
+ (sizeof(struct ns2_led_data) * num_leds);
+}
+
static int ns2_led_probe(struct platform_device *pdev)
{
struct ns2_led_platform_data *pdata = pdev->dev.platform_data;
- struct ns2_led_data *leds_data;
+ struct ns2_led_priv *priv;
int i;
int ret;
@@ -330,21 +343,23 @@ static int ns2_led_probe(struct platform_device *pdev)
return -EINVAL;
#endif /* CONFIG_OF_GPIO */
- leds_data = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_data) *
- pdata->num_leds, GFP_KERNEL);
- if (!leds_data)
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
+ priv->num_leds = pdata->num_leds;
- for (i = 0; i < pdata->num_leds; i++) {
- ret = create_ns2_led(pdev, &leds_data[i], &pdata->leds[i]);
+ for (i = 0; i < priv->num_leds; i++) {
+ ret = create_ns2_led(pdev, &priv->leds_data[i],
+ &pdata->leds[i]);
if (ret < 0) {
for (i = i - 1; i >= 0; i--)
- delete_ns2_led(&leds_data[i]);
+ delete_ns2_led(&priv->leds_data[i]);
return ret;
}
}
- platform_set_drvdata(pdev, leds_data);
+ platform_set_drvdata(pdev, priv);
return 0;
}
@@ -352,13 +367,12 @@ static int ns2_led_probe(struct platform_device *pdev)
static int ns2_led_remove(struct platform_device *pdev)
{
int i;
- struct ns2_led_platform_data *pdata = pdev->dev.platform_data;
- struct ns2_led_data *leds_data;
+ struct ns2_led_priv *priv;
- leds_data = platform_get_drvdata(pdev);
+ priv = platform_get_drvdata(pdev);
- for (i = 0; i < pdata->num_leds; i++)
- delete_ns2_led(&leds_data[i]);
+ for (i = 0; i < priv->num_leds; i++)
+ delete_ns2_led(&priv->leds_data[i]);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index a1ea5f6a8d39..faf52c005e8c 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -23,12 +23,16 @@
#include <linux/pwm.h>
#include <linux/leds_pwm.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
struct led_pwm_data {
struct led_classdev cdev;
struct pwm_device *pwm;
+ struct work_struct work;
unsigned int active_low;
unsigned int period;
+ int duty;
+ bool can_sleep;
};
struct led_pwm_priv {
@@ -36,6 +40,26 @@ struct led_pwm_priv {
struct led_pwm_data leds[0];
};
+static void __led_pwm_set(struct led_pwm_data *led_dat)
+{
+ int new_duty = led_dat->duty;
+
+ pwm_config(led_dat->pwm, new_duty, led_dat->period);
+
+ if (new_duty == 0)
+ pwm_disable(led_dat->pwm);
+ else
+ pwm_enable(led_dat->pwm);
+}
+
+static void led_pwm_work(struct work_struct *work)
+{
+ struct led_pwm_data *led_dat =
+ container_of(work, struct led_pwm_data, work);
+
+ __led_pwm_set(led_dat);
+}
+
static void led_pwm_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
@@ -44,13 +68,12 @@ static void led_pwm_set(struct led_classdev *led_cdev,
unsigned int max = led_dat->cdev.max_brightness;
unsigned int period = led_dat->period;
- if (brightness == 0) {
- pwm_config(led_dat->pwm, 0, period);
- pwm_disable(led_dat->pwm);
- } else {
- pwm_config(led_dat->pwm, brightness * period / max, period);
- pwm_enable(led_dat->pwm);
- }
+ led_dat->duty = brightness * period / max;
+
+ if (led_dat->can_sleep)
+ schedule_work(&led_dat->work);
+ else
+ __led_pwm_set(led_dat);
}
static inline size_t sizeof_pwm_leds_priv(int num_leds)
@@ -100,6 +123,10 @@ static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev)
led_dat->cdev.brightness = LED_OFF;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+ led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
+ if (led_dat->can_sleep)
+ INIT_WORK(&led_dat->work, led_pwm_work);
+
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register for %s\n",
@@ -153,6 +180,10 @@ static int led_pwm_probe(struct platform_device *pdev)
led_dat->cdev.max_brightness = cur_led->max_brightness;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+ led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
+ if (led_dat->can_sleep)
+ INIT_WORK(&led_dat->work, led_pwm_work);
+
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
if (ret < 0)
goto err;
@@ -180,8 +211,11 @@ static int led_pwm_remove(struct platform_device *pdev)
struct led_pwm_priv *priv = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < priv->num_leds; i++)
+ for (i = 0; i < priv->num_leds; i++) {
led_classdev_unregister(&priv->leds[i].cdev);
+ if (priv->leds[i].can_sleep)
+ cancel_work_sync(&priv->leds[i].work);
+ }
return 0;
}
diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c
index d3c2b7e68fbc..9483f1c1078d 100644
--- a/drivers/leds/leds-renesas-tpu.c
+++ b/drivers/leds/leds-renesas-tpu.c
@@ -205,7 +205,8 @@ static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state,
gpio_free(cfg->pin_gpio_fn);
if (new_state == R_TPU_PIN_GPIO)
- gpio_request_one(cfg->pin_gpio, GPIOF_DIR_OUT | !!brightness,
+ gpio_request_one(cfg->pin_gpio, !!brightness ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
cfg->name);
if (new_state == R_TPU_PIN_GPIO_FN)
diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c
index 070ba0741b21..98fe021ba276 100644
--- a/drivers/leds/leds-tca6507.c
+++ b/drivers/leds/leds-tca6507.c
@@ -85,6 +85,7 @@
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/leds-tca6507.h>
+#include <linux/of.h>
/* LED select registers determine the source that drives LED outputs */
#define TCA6507_LS_LED_OFF 0x0 /* Output HI-Z (off) */
@@ -724,7 +725,6 @@ tca6507_led_dt_init(struct i2c_client *client)
return ERR_PTR(-ENODEV);
}
-#define of_tca6507_leds_match NULL
#endif
static int tca6507_probe(struct i2c_client *client,
@@ -813,7 +813,7 @@ static struct i2c_driver tca6507_driver = {
.driver = {
.name = "leds-tca6507",
.owner = THIS_MODULE,
- .of_match_table = of_tca6507_leds_match,
+ .of_match_table = of_match_ptr(of_tca6507_leds_match),
},
.probe = tca6507_probe,
.remove = tca6507_remove,
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
index ed15157c8f6c..8a181d56602d 100644
--- a/drivers/leds/leds-wm8350.c
+++ b/drivers/leds/leds-wm8350.c
@@ -129,7 +129,10 @@ static void wm8350_led_disable(struct wm8350_led *led)
ret = regulator_disable(led->isink);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
- regulator_enable(led->dcdc);
+ ret = regulator_enable(led->dcdc);
+ if (ret != 0)
+ dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n",
+ ret);
return;
}
diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
new file mode 100644
index 000000000000..5c8971f4c99d
--- /dev/null
+++ b/drivers/leds/trigger/Kconfig
@@ -0,0 +1,117 @@
+menuconfig LEDS_TRIGGERS
+ bool "LED Trigger support"
+ depends on LEDS_CLASS
+ help
+ This option enables trigger support for the leds class.
+ These triggers allow kernel events to drive the LEDs and can
+ be configured via sysfs. If unsure, say Y.
+
+if LEDS_TRIGGERS
+
+config LEDS_TRIGGER_TIMER
+ tristate "LED Timer Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled by a programmable timer
+ via sysfs. Some LED hardware can be programmed to start
+ blinking the LED without any further software interaction.
+ For more details read Documentation/leds/leds-class.txt.
+
+ If unsure, say Y.
+
+config LEDS_TRIGGER_ONESHOT
+ tristate "LED One-shot Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to blink in one-shot pulses with parameters
+ controlled via sysfs. It's useful to notify the user on
+ sporadic events, when there are no clear begin and end trap points,
+ or on dense events, where this blinks the LED at constant rate if
+ rearmed continuously.
+
+ It also shows how to use the led_blink_set_oneshot() function.
+
+ If unsure, say Y.
+
+config LEDS_TRIGGER_IDE_DISK
+ bool "LED IDE Disk Trigger"
+ depends on IDE_GD_ATA
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled by IDE disk activity.
+ If unsure, say Y.
+
+config LEDS_TRIGGER_HEARTBEAT
+ tristate "LED Heartbeat Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled by a CPU load average.
+ The flash frequency is a hyperbolic function of the 1-minute
+ load average.
+ If unsure, say Y.
+
+config LEDS_TRIGGER_BACKLIGHT
+ tristate "LED backlight Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled as a backlight device: they
+ turn off and on when the display is blanked and unblanked.
+
+ If unsure, say N.
+
+config LEDS_TRIGGER_CPU
+ bool "LED CPU Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled by active CPUs. This shows
+ the active CPUs across an array of LEDs so you can see which
+ CPUs are active on the system at any given moment.
+
+ If unsure, say N.
+
+config LEDS_TRIGGER_GPIO
+ tristate "LED GPIO Trigger"
+ depends on LEDS_TRIGGERS
+ depends on GPIOLIB
+ help
+ This allows LEDs to be controlled by gpio events. It's good
+ when using gpios as switches and triggering the needed LEDs
+ from there. One use case is n810's keypad LEDs that could
+ be triggered by this trigger when user slides up to show
+ keypad.
+
+ If unsure, say N.
+
+config LEDS_TRIGGER_DEFAULT_ON
+ tristate "LED Default ON Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be initialised in the ON state.
+ If unsure, say Y.
+
+comment "iptables trigger is under Netfilter config (LED target)"
+ depends on LEDS_TRIGGERS
+
+config LEDS_TRIGGER_SLEEP
+ tristate "LED Sleep Mode Trigger"
+ depends on LEDS_TRIGGERS && HAS_EARLYSUSPEND
+ help
+ This turns LEDs on when the screen is off but the cpu still running.
+
+config LEDS_TRIGGER_TRANSIENT
+ tristate "LED Transient Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows one time activation of a transient state on
+ GPIO/PWM based hardware.
+ If unsure, say Y.
+
+config LEDS_TRIGGER_CAMERA
+ tristate "LED Camera Flash/Torch Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled as a camera flash/torch device.
+ This enables direct flash/torch on/off by the driver, kernel space.
+ If unsure, say Y.
+
+endif # LEDS_TRIGGERS
diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
new file mode 100644
index 000000000000..77b38ab0fe3c
--- /dev/null
+++ b/drivers/leds/trigger/Makefile
@@ -0,0 +1,12 @@
+obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
+obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o
+obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
+obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
+obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
+obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
+obj-$(CONFIG_LEDS_TRIGGER_CPU) += ledtrig-cpu.o
+obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
+obj-$(CONFIG_LEDS_TRIGGER_SLEEP) += ledtrig-sleep.o
+obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
+obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
+
diff --git a/drivers/leds/ledtrig-backlight.c b/drivers/leds/trigger/ledtrig-backlight.c
index 027a2b15d7d8..3c9c88a07eb8 100644
--- a/drivers/leds/ledtrig-backlight.c
+++ b/drivers/leds/trigger/ledtrig-backlight.c
@@ -16,7 +16,7 @@
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/leds.h>
-#include "leds.h"
+#include "../leds.h"
#define BLANK 1
#define UNBLANK 0
diff --git a/drivers/leds/trigger/ledtrig-camera.c b/drivers/leds/trigger/ledtrig-camera.c
new file mode 100644
index 000000000000..9bd73a8bad5c
--- /dev/null
+++ b/drivers/leds/trigger/ledtrig-camera.c
@@ -0,0 +1,57 @@
+/*
+ * Camera Flash and Torch On/Off Trigger
+ *
+ * based on ledtrig-ide-disk.c
+ *
+ * Copyright 2013 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+
+DEFINE_LED_TRIGGER(ledtrig_flash);
+DEFINE_LED_TRIGGER(ledtrig_torch);
+
+void ledtrig_flash_ctrl(bool on)
+{
+ enum led_brightness brt = on ? LED_FULL : LED_OFF;
+
+ led_trigger_event(ledtrig_flash, brt);
+}
+EXPORT_SYMBOL_GPL(ledtrig_flash_ctrl);
+
+void ledtrig_torch_ctrl(bool on)
+{
+ enum led_brightness brt = on ? LED_FULL : LED_OFF;
+
+ led_trigger_event(ledtrig_torch, brt);
+}
+EXPORT_SYMBOL_GPL(ledtrig_torch_ctrl);
+
+static int __init ledtrig_camera_init(void)
+{
+ led_trigger_register_simple("flash", &ledtrig_flash);
+ led_trigger_register_simple("torch", &ledtrig_torch);
+ return 0;
+}
+module_init(ledtrig_camera_init);
+
+static void __exit ledtrig_camera_exit(void)
+{
+ led_trigger_unregister_simple(ledtrig_torch);
+ led_trigger_unregister_simple(ledtrig_flash);
+}
+module_exit(ledtrig_camera_exit);
+
+MODULE_DESCRIPTION("LED Trigger for Camera Flash/Torch Control");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c
index 4239b3955ff0..118335eccc56 100644
--- a/drivers/leds/ledtrig-cpu.c
+++ b/drivers/leds/trigger/ledtrig-cpu.c
@@ -26,7 +26,7 @@
#include <linux/percpu.h>
#include <linux/syscore_ops.h>
#include <linux/rwsem.h>
-#include "leds.h"
+#include "../leds.h"
#define MAX_NAME_LEN 8
diff --git a/drivers/leds/ledtrig-default-on.c b/drivers/leds/trigger/ledtrig-default-on.c
index eac1f1b1adac..81a91be8e18d 100644
--- a/drivers/leds/ledtrig-default-on.c
+++ b/drivers/leds/trigger/ledtrig-default-on.c
@@ -15,7 +15,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/leds.h>
-#include "leds.h"
+#include "../leds.h"
static void defon_trig_activate(struct led_classdev *led_cdev)
{
diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c
index 72e3ebfc281f..35812e3a37f2 100644
--- a/drivers/leds/ledtrig-gpio.c
+++ b/drivers/leds/trigger/ledtrig-gpio.c
@@ -17,7 +17,7 @@
#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/slab.h>
-#include "leds.h"
+#include "../leds.h"
struct gpio_trig_data {
struct led_classdev *led;
diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c
index 1edc7463ce83..5c8464a33172 100644
--- a/drivers/leds/ledtrig-heartbeat.c
+++ b/drivers/leds/trigger/ledtrig-heartbeat.c
@@ -19,7 +19,7 @@
#include <linux/sched.h>
#include <linux/leds.h>
#include <linux/reboot.h>
-#include "leds.h"
+#include "../leds.h"
static int panic_heartbeats;
diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/trigger/ledtrig-ide-disk.c
index 2cd7c0cf5924..2cd7c0cf5924 100644
--- a/drivers/leds/ledtrig-ide-disk.c
+++ b/drivers/leds/trigger/ledtrig-ide-disk.c
diff --git a/drivers/leds/ledtrig-oneshot.c b/drivers/leds/trigger/ledtrig-oneshot.c
index 2c029aa5c4f1..cb4c7466692a 100644
--- a/drivers/leds/ledtrig-oneshot.c
+++ b/drivers/leds/trigger/ledtrig-oneshot.c
@@ -18,7 +18,7 @@
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/leds.h>
-#include "leds.h"
+#include "../leds.h"
#define DEFAULT_DELAY 100
diff --git a/drivers/leds/ledtrig-sleep.c b/drivers/leds/trigger/ledtrig-sleep.c
index f16404212152..f16404212152 100644
--- a/drivers/leds/ledtrig-sleep.c
+++ b/drivers/leds/trigger/ledtrig-sleep.c
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/trigger/ledtrig-timer.c
index f774d0592204..8d09327b5719 100644
--- a/drivers/leds/ledtrig-timer.c
+++ b/drivers/leds/trigger/ledtrig-timer.c
@@ -17,7 +17,6 @@
#include <linux/device.h>
#include <linux/ctype.h>
#include <linux/leds.h>
-#include "leds.h"
static ssize_t led_delay_on_show(struct device *dev,
struct device_attribute *attr, char *buf)
diff --git a/drivers/leds/ledtrig-transient.c b/drivers/leds/trigger/ledtrig-transient.c
index 398f1042c43e..e5abc00bb00c 100644
--- a/drivers/leds/ledtrig-transient.c
+++ b/drivers/leds/trigger/ledtrig-transient.c
@@ -25,7 +25,7 @@
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/leds.h>
-#include "leds.h"
+#include "../leds.h"
struct transient_trig_data {
int activate;
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 4d8d90b4fe78..3bfc8f1da9fe 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -174,6 +174,8 @@ config MD_FAULTY
In unsure, say N.
+source "drivers/md/bcache/Kconfig"
+
config BLK_DEV_DM
tristate "Device mapper support"
---help---
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 7ceeaefc0e95..1439fd4ad9b1 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_MD_RAID10) += raid10.o
obj-$(CONFIG_MD_RAID456) += raid456.o
obj-$(CONFIG_MD_MULTIPATH) += multipath.o
obj-$(CONFIG_MD_FAULTY) += faulty.o
+obj-$(CONFIG_BCACHE) += bcache/
obj-$(CONFIG_BLK_DEV_MD) += md-mod.o
obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
obj-$(CONFIG_DM_BUFIO) += dm-bufio.o
diff --git a/drivers/md/bcache/Kconfig b/drivers/md/bcache/Kconfig
new file mode 100644
index 000000000000..05c220d05e23
--- /dev/null
+++ b/drivers/md/bcache/Kconfig
@@ -0,0 +1,42 @@
+
+config BCACHE
+ tristate "Block device as cache"
+ select CLOSURES
+ ---help---
+ Allows a block device to be used as cache for other devices; uses
+ a btree for indexing and the layout is optimized for SSDs.
+
+ See Documentation/bcache.txt for details.
+
+config BCACHE_DEBUG
+ bool "Bcache debugging"
+ depends on BCACHE
+ ---help---
+ Don't select this option unless you're a developer
+
+ Enables extra debugging tools (primarily a fuzz tester)
+
+config BCACHE_EDEBUG
+ bool "Extended runtime checks"
+ depends on BCACHE
+ ---help---
+ Don't select this option unless you're a developer
+
+ Enables extra runtime checks which significantly affect performance
+
+config BCACHE_CLOSURES_DEBUG
+ bool "Debug closures"
+ depends on BCACHE
+ select DEBUG_FS
+ ---help---
+ Keeps all active closures in a linked list and provides a debugfs
+ interface to list them, which makes it possible to see asynchronous
+ operations that get stuck.
+
+# cgroup code needs to be updated:
+#
+#config CGROUP_BCACHE
+# bool "Cgroup controls for bcache"
+# depends on BCACHE && BLK_CGROUP
+# ---help---
+# TODO
diff --git a/drivers/md/bcache/Makefile b/drivers/md/bcache/Makefile
new file mode 100644
index 000000000000..0e9c82523be6
--- /dev/null
+++ b/drivers/md/bcache/Makefile
@@ -0,0 +1,7 @@
+
+obj-$(CONFIG_BCACHE) += bcache.o
+
+bcache-y := alloc.o btree.o bset.o io.o journal.o writeback.o\
+ movinggc.o request.o super.o sysfs.o debug.o util.o trace.o stats.o closure.o
+
+CFLAGS_request.o += -Iblock
diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
new file mode 100644
index 000000000000..048f2947e08b
--- /dev/null
+++ b/drivers/md/bcache/alloc.c
@@ -0,0 +1,599 @@
+/*
+ * Primary bucket allocation code
+ *
+ * Copyright 2012 Google, Inc.
+ *
+ * Allocation in bcache is done in terms of buckets:
+ *
+ * Each bucket has associated an 8 bit gen; this gen corresponds to the gen in
+ * btree pointers - they must match for the pointer to be considered valid.
+ *
+ * Thus (assuming a bucket has no dirty data or metadata in it) we can reuse a
+ * bucket simply by incrementing its gen.
+ *
+ * The gens (along with the priorities; it's really the gens are important but
+ * the code is named as if it's the priorities) are written in an arbitrary list
+ * of buckets on disk, with a pointer to them in the journal header.
+ *
+ * When we invalidate a bucket, we have to write its new gen to disk and wait
+ * for that write to complete before we use it - otherwise after a crash we
+ * could have pointers that appeared to be good but pointed to data that had
+ * been overwritten.
+ *
+ * Since the gens and priorities are all stored contiguously on disk, we can
+ * batch this up: We fill up the free_inc list with freshly invalidated buckets,
+ * call prio_write(), and when prio_write() finishes we pull buckets off the
+ * free_inc list and optionally discard them.
+ *
+ * free_inc isn't the only freelist - if it was, we'd often to sleep while
+ * priorities and gens were being written before we could allocate. c->free is a
+ * smaller freelist, and buckets on that list are always ready to be used.
+ *
+ * If we've got discards enabled, that happens when a bucket moves from the
+ * free_inc list to the free list.
+ *
+ * There is another freelist, because sometimes we have buckets that we know
+ * have nothing pointing into them - these we can reuse without waiting for
+ * priorities to be rewritten. These come from freed btree nodes and buckets
+ * that garbage collection discovered no longer had valid keys pointing into
+ * them (because they were overwritten). That's the unused list - buckets on the
+ * unused list move to the free list, optionally being discarded in the process.
+ *
+ * It's also important to ensure that gens don't wrap around - with respect to
+ * either the oldest gen in the btree or the gen on disk. This is quite
+ * difficult to do in practice, but we explicitly guard against it anyways - if
+ * a bucket is in danger of wrapping around we simply skip invalidating it that
+ * time around, and we garbage collect or rewrite the priorities sooner than we
+ * would have otherwise.
+ *
+ * bch_bucket_alloc() allocates a single bucket from a specific cache.
+ *
+ * bch_bucket_alloc_set() allocates one or more buckets from different caches
+ * out of a cache set.
+ *
+ * free_some_buckets() drives all the processes described above. It's called
+ * from bch_bucket_alloc() and a few other places that need to make sure free
+ * buckets are ready.
+ *
+ * invalidate_buckets_(lru|fifo)() find buckets that are available to be
+ * invalidated, and then invalidate them and stick them on the free_inc list -
+ * in either lru or fifo order.
+ */
+
+#include "bcache.h"
+#include "btree.h"
+
+#include <linux/random.h>
+
+#define MAX_IN_FLIGHT_DISCARDS 8U
+
+/* Bucket heap / gen */
+
+uint8_t bch_inc_gen(struct cache *ca, struct bucket *b)
+{
+ uint8_t ret = ++b->gen;
+
+ ca->set->need_gc = max(ca->set->need_gc, bucket_gc_gen(b));
+ WARN_ON_ONCE(ca->set->need_gc > BUCKET_GC_GEN_MAX);
+
+ if (CACHE_SYNC(&ca->set->sb)) {
+ ca->need_save_prio = max(ca->need_save_prio,
+ bucket_disk_gen(b));
+ WARN_ON_ONCE(ca->need_save_prio > BUCKET_DISK_GEN_MAX);
+ }
+
+ return ret;
+}
+
+void bch_rescale_priorities(struct cache_set *c, int sectors)
+{
+ struct cache *ca;
+ struct bucket *b;
+ unsigned next = c->nbuckets * c->sb.bucket_size / 1024;
+ unsigned i;
+ int r;
+
+ atomic_sub(sectors, &c->rescale);
+
+ do {
+ r = atomic_read(&c->rescale);
+
+ if (r >= 0)
+ return;
+ } while (atomic_cmpxchg(&c->rescale, r, r + next) != r);
+
+ mutex_lock(&c->bucket_lock);
+
+ c->min_prio = USHRT_MAX;
+
+ for_each_cache(ca, c, i)
+ for_each_bucket(b, ca)
+ if (b->prio &&
+ b->prio != BTREE_PRIO &&
+ !atomic_read(&b->pin)) {
+ b->prio--;
+ c->min_prio = min(c->min_prio, b->prio);
+ }
+
+ mutex_unlock(&c->bucket_lock);
+}
+
+/* Discard/TRIM */
+
+struct discard {
+ struct list_head list;
+ struct work_struct work;
+ struct cache *ca;
+ long bucket;
+
+ struct bio bio;
+ struct bio_vec bv;
+};
+
+static void discard_finish(struct work_struct *w)
+{
+ struct discard *d = container_of(w, struct discard, work);
+ struct cache *ca = d->ca;
+ char buf[BDEVNAME_SIZE];
+
+ if (!test_bit(BIO_UPTODATE, &d->bio.bi_flags)) {
+ pr_notice("discard error on %s, disabling",
+ bdevname(ca->bdev, buf));
+ d->ca->discard = 0;
+ }
+
+ mutex_lock(&ca->set->bucket_lock);
+
+ fifo_push(&ca->free, d->bucket);
+ list_add(&d->list, &ca->discards);
+ atomic_dec(&ca->discards_in_flight);
+
+ mutex_unlock(&ca->set->bucket_lock);
+
+ closure_wake_up(&ca->set->bucket_wait);
+ wake_up(&ca->set->alloc_wait);
+
+ closure_put(&ca->set->cl);
+}
+
+static void discard_endio(struct bio *bio, int error)
+{
+ struct discard *d = container_of(bio, struct discard, bio);
+ schedule_work(&d->work);
+}
+
+static void do_discard(struct cache *ca, long bucket)
+{
+ struct discard *d = list_first_entry(&ca->discards,
+ struct discard, list);
+
+ list_del(&d->list);
+ d->bucket = bucket;
+
+ atomic_inc(&ca->discards_in_flight);
+ closure_get(&ca->set->cl);
+
+ bio_init(&d->bio);
+
+ d->bio.bi_sector = bucket_to_sector(ca->set, d->bucket);
+ d->bio.bi_bdev = ca->bdev;
+ d->bio.bi_rw = REQ_WRITE|REQ_DISCARD;
+ d->bio.bi_max_vecs = 1;
+ d->bio.bi_io_vec = d->bio.bi_inline_vecs;
+ d->bio.bi_size = bucket_bytes(ca);
+ d->bio.bi_end_io = discard_endio;
+ bio_set_prio(&d->bio, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
+
+ submit_bio(0, &d->bio);
+}
+
+/* Allocation */
+
+static inline bool can_inc_bucket_gen(struct bucket *b)
+{
+ return bucket_gc_gen(b) < BUCKET_GC_GEN_MAX &&
+ bucket_disk_gen(b) < BUCKET_DISK_GEN_MAX;
+}
+
+bool bch_bucket_add_unused(struct cache *ca, struct bucket *b)
+{
+ BUG_ON(GC_MARK(b) || GC_SECTORS_USED(b));
+
+ if (fifo_used(&ca->free) > ca->watermark[WATERMARK_MOVINGGC] &&
+ CACHE_REPLACEMENT(&ca->sb) == CACHE_REPLACEMENT_FIFO)
+ return false;
+
+ b->prio = 0;
+
+ if (can_inc_bucket_gen(b) &&
+ fifo_push(&ca->unused, b - ca->buckets)) {
+ atomic_inc(&b->pin);
+ return true;
+ }
+
+ return false;
+}
+
+static bool can_invalidate_bucket(struct cache *ca, struct bucket *b)
+{
+ return GC_MARK(b) == GC_MARK_RECLAIMABLE &&
+ !atomic_read(&b->pin) &&
+ can_inc_bucket_gen(b);
+}
+
+static void invalidate_one_bucket(struct cache *ca, struct bucket *b)
+{
+ bch_inc_gen(ca, b);
+ b->prio = INITIAL_PRIO;
+ atomic_inc(&b->pin);
+ fifo_push(&ca->free_inc, b - ca->buckets);
+}
+
+#define bucket_prio(b) \
+ (((unsigned) (b->prio - ca->set->min_prio)) * GC_SECTORS_USED(b))
+
+#define bucket_max_cmp(l, r) (bucket_prio(l) < bucket_prio(r))
+#define bucket_min_cmp(l, r) (bucket_prio(l) > bucket_prio(r))
+
+static void invalidate_buckets_lru(struct cache *ca)
+{
+ struct bucket *b;
+ ssize_t i;
+
+ ca->heap.used = 0;
+
+ for_each_bucket(b, ca) {
+ /*
+ * If we fill up the unused list, if we then return before
+ * adding anything to the free_inc list we'll skip writing
+ * prios/gens and just go back to allocating from the unused
+ * list:
+ */
+ if (fifo_full(&ca->unused))
+ return;
+
+ if (!can_invalidate_bucket(ca, b))
+ continue;
+
+ if (!GC_SECTORS_USED(b) &&
+ bch_bucket_add_unused(ca, b))
+ continue;
+
+ if (!heap_full(&ca->heap))
+ heap_add(&ca->heap, b, bucket_max_cmp);
+ else if (bucket_max_cmp(b, heap_peek(&ca->heap))) {
+ ca->heap.data[0] = b;
+ heap_sift(&ca->heap, 0, bucket_max_cmp);
+ }
+ }
+
+ for (i = ca->heap.used / 2 - 1; i >= 0; --i)
+ heap_sift(&ca->heap, i, bucket_min_cmp);
+
+ while (!fifo_full(&ca->free_inc)) {
+ if (!heap_pop(&ca->heap, b, bucket_min_cmp)) {
+ /*
+ * We don't want to be calling invalidate_buckets()
+ * multiple times when it can't do anything
+ */
+ ca->invalidate_needs_gc = 1;
+ bch_queue_gc(ca->set);
+ return;
+ }
+
+ invalidate_one_bucket(ca, b);
+ }
+}
+
+static void invalidate_buckets_fifo(struct cache *ca)
+{
+ struct bucket *b;
+ size_t checked = 0;
+
+ while (!fifo_full(&ca->free_inc)) {
+ if (ca->fifo_last_bucket < ca->sb.first_bucket ||
+ ca->fifo_last_bucket >= ca->sb.nbuckets)
+ ca->fifo_last_bucket = ca->sb.first_bucket;
+
+ b = ca->buckets + ca->fifo_last_bucket++;
+
+ if (can_invalidate_bucket(ca, b))
+ invalidate_one_bucket(ca, b);
+
+ if (++checked >= ca->sb.nbuckets) {
+ ca->invalidate_needs_gc = 1;
+ bch_queue_gc(ca->set);
+ return;
+ }
+ }
+}
+
+static void invalidate_buckets_random(struct cache *ca)
+{
+ struct bucket *b;
+ size_t checked = 0;
+
+ while (!fifo_full(&ca->free_inc)) {
+ size_t n;
+ get_random_bytes(&n, sizeof(n));
+
+ n %= (size_t) (ca->sb.nbuckets - ca->sb.first_bucket);
+ n += ca->sb.first_bucket;
+
+ b = ca->buckets + n;
+
+ if (can_invalidate_bucket(ca, b))
+ invalidate_one_bucket(ca, b);
+
+ if (++checked >= ca->sb.nbuckets / 2) {
+ ca->invalidate_needs_gc = 1;
+ bch_queue_gc(ca->set);
+ return;
+ }
+ }
+}
+
+static void invalidate_buckets(struct cache *ca)
+{
+ if (ca->invalidate_needs_gc)
+ return;
+
+ switch (CACHE_REPLACEMENT(&ca->sb)) {
+ case CACHE_REPLACEMENT_LRU:
+ invalidate_buckets_lru(ca);
+ break;
+ case CACHE_REPLACEMENT_FIFO:
+ invalidate_buckets_fifo(ca);
+ break;
+ case CACHE_REPLACEMENT_RANDOM:
+ invalidate_buckets_random(ca);
+ break;
+ }
+
+ pr_debug("free %zu/%zu free_inc %zu/%zu unused %zu/%zu",
+ fifo_used(&ca->free), ca->free.size,
+ fifo_used(&ca->free_inc), ca->free_inc.size,
+ fifo_used(&ca->unused), ca->unused.size);
+}
+
+#define allocator_wait(ca, cond) \
+do { \
+ DEFINE_WAIT(__wait); \
+ \
+ while (1) { \
+ prepare_to_wait(&ca->set->alloc_wait, \
+ &__wait, TASK_INTERRUPTIBLE); \
+ if (cond) \
+ break; \
+ \
+ mutex_unlock(&(ca)->set->bucket_lock); \
+ if (test_bit(CACHE_SET_STOPPING_2, &ca->set->flags)) { \
+ finish_wait(&ca->set->alloc_wait, &__wait); \
+ closure_return(cl); \
+ } \
+ \
+ schedule(); \
+ mutex_lock(&(ca)->set->bucket_lock); \
+ } \
+ \
+ finish_wait(&ca->set->alloc_wait, &__wait); \
+} while (0)
+
+void bch_allocator_thread(struct closure *cl)
+{
+ struct cache *ca = container_of(cl, struct cache, alloc);
+
+ mutex_lock(&ca->set->bucket_lock);
+
+ while (1) {
+ /*
+ * First, we pull buckets off of the unused and free_inc lists,
+ * possibly issue discards to them, then we add the bucket to
+ * the free list:
+ */
+ while (1) {
+ long bucket;
+
+ if ((!atomic_read(&ca->set->prio_blocked) ||
+ !CACHE_SYNC(&ca->set->sb)) &&
+ !fifo_empty(&ca->unused))
+ fifo_pop(&ca->unused, bucket);
+ else if (!fifo_empty(&ca->free_inc))
+ fifo_pop(&ca->free_inc, bucket);
+ else
+ break;
+
+ allocator_wait(ca, (int) fifo_free(&ca->free) >
+ atomic_read(&ca->discards_in_flight));
+
+ if (ca->discard) {
+ allocator_wait(ca, !list_empty(&ca->discards));
+ do_discard(ca, bucket);
+ } else {
+ fifo_push(&ca->free, bucket);
+ closure_wake_up(&ca->set->bucket_wait);
+ }
+ }
+
+ /*
+ * We've run out of free buckets, we need to find some buckets
+ * we can invalidate. First, invalidate them in memory and add
+ * them to the free_inc list:
+ */
+
+ allocator_wait(ca, ca->set->gc_mark_valid &&
+ (ca->need_save_prio > 64 ||
+ !ca->invalidate_needs_gc));
+ invalidate_buckets(ca);
+
+ /*
+ * Now, we write their new gens to disk so we can start writing
+ * new stuff to them:
+ */
+ allocator_wait(ca, !atomic_read(&ca->set->prio_blocked));
+ if (CACHE_SYNC(&ca->set->sb) &&
+ (!fifo_empty(&ca->free_inc) ||
+ ca->need_save_prio > 64))
+ bch_prio_write(ca);
+ }
+}
+
+long bch_bucket_alloc(struct cache *ca, unsigned watermark, struct closure *cl)
+{
+ long r = -1;
+again:
+ wake_up(&ca->set->alloc_wait);
+
+ if (fifo_used(&ca->free) > ca->watermark[watermark] &&
+ fifo_pop(&ca->free, r)) {
+ struct bucket *b = ca->buckets + r;
+#ifdef CONFIG_BCACHE_EDEBUG
+ size_t iter;
+ long i;
+
+ for (iter = 0; iter < prio_buckets(ca) * 2; iter++)
+ BUG_ON(ca->prio_buckets[iter] == (uint64_t) r);
+
+ fifo_for_each(i, &ca->free, iter)
+ BUG_ON(i == r);
+ fifo_for_each(i, &ca->free_inc, iter)
+ BUG_ON(i == r);
+ fifo_for_each(i, &ca->unused, iter)
+ BUG_ON(i == r);
+#endif
+ BUG_ON(atomic_read(&b->pin) != 1);
+
+ SET_GC_SECTORS_USED(b, ca->sb.bucket_size);
+
+ if (watermark <= WATERMARK_METADATA) {
+ SET_GC_MARK(b, GC_MARK_METADATA);
+ b->prio = BTREE_PRIO;
+ } else {
+ SET_GC_MARK(b, GC_MARK_RECLAIMABLE);
+ b->prio = INITIAL_PRIO;
+ }
+
+ return r;
+ }
+
+ pr_debug("alloc failure: blocked %i free %zu free_inc %zu unused %zu",
+ atomic_read(&ca->set->prio_blocked), fifo_used(&ca->free),
+ fifo_used(&ca->free_inc), fifo_used(&ca->unused));
+
+ if (cl) {
+ closure_wait(&ca->set->bucket_wait, cl);
+
+ if (closure_blocking(cl)) {
+ mutex_unlock(&ca->set->bucket_lock);
+ closure_sync(cl);
+ mutex_lock(&ca->set->bucket_lock);
+ goto again;
+ }
+ }
+
+ return -1;
+}
+
+void bch_bucket_free(struct cache_set *c, struct bkey *k)
+{
+ unsigned i;
+
+ for (i = 0; i < KEY_PTRS(k); i++) {
+ struct bucket *b = PTR_BUCKET(c, k, i);
+
+ SET_GC_MARK(b, GC_MARK_RECLAIMABLE);
+ SET_GC_SECTORS_USED(b, 0);
+ bch_bucket_add_unused(PTR_CACHE(c, k, i), b);
+ }
+}
+
+int __bch_bucket_alloc_set(struct cache_set *c, unsigned watermark,
+ struct bkey *k, int n, struct closure *cl)
+{
+ int i;
+
+ lockdep_assert_held(&c->bucket_lock);
+ BUG_ON(!n || n > c->caches_loaded || n > 8);
+
+ bkey_init(k);
+
+ /* sort by free space/prio of oldest data in caches */
+
+ for (i = 0; i < n; i++) {
+ struct cache *ca = c->cache_by_alloc[i];
+ long b = bch_bucket_alloc(ca, watermark, cl);
+
+ if (b == -1)
+ goto err;
+
+ k->ptr[i] = PTR(ca->buckets[b].gen,
+ bucket_to_sector(c, b),
+ ca->sb.nr_this_dev);
+
+ SET_KEY_PTRS(k, i + 1);
+ }
+
+ return 0;
+err:
+ bch_bucket_free(c, k);
+ __bkey_put(c, k);
+ return -1;
+}
+
+int bch_bucket_alloc_set(struct cache_set *c, unsigned watermark,
+ struct bkey *k, int n, struct closure *cl)
+{
+ int ret;
+ mutex_lock(&c->bucket_lock);
+ ret = __bch_bucket_alloc_set(c, watermark, k, n, cl);
+ mutex_unlock(&c->bucket_lock);
+ return ret;
+}
+
+/* Init */
+
+void bch_cache_allocator_exit(struct cache *ca)
+{
+ struct discard *d;
+
+ while (!list_empty(&ca->discards)) {
+ d = list_first_entry(&ca->discards, struct discard, list);
+ cancel_work_sync(&d->work);
+ list_del(&d->list);
+ kfree(d);
+ }
+}
+
+int bch_cache_allocator_init(struct cache *ca)
+{
+ unsigned i;
+
+ /*
+ * Reserve:
+ * Prio/gen writes first
+ * Then 8 for btree allocations
+ * Then half for the moving garbage collector
+ */
+
+ ca->watermark[WATERMARK_PRIO] = 0;
+
+ ca->watermark[WATERMARK_METADATA] = prio_buckets(ca);
+
+ ca->watermark[WATERMARK_MOVINGGC] = 8 +
+ ca->watermark[WATERMARK_METADATA];
+
+ ca->watermark[WATERMARK_NONE] = ca->free.size / 2 +
+ ca->watermark[WATERMARK_MOVINGGC];
+
+ for (i = 0; i < MAX_IN_FLIGHT_DISCARDS; i++) {
+ struct discard *d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ d->ca = ca;
+ INIT_WORK(&d->work, discard_finish);
+ list_add(&d->list, &ca->discards);
+ }
+
+ return 0;
+}
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
new file mode 100644
index 000000000000..340146d7c17f
--- /dev/null
+++ b/drivers/md/bcache/bcache.h
@@ -0,0 +1,1259 @@
+#ifndef _BCACHE_H
+#define _BCACHE_H
+
+/*
+ * SOME HIGH LEVEL CODE DOCUMENTATION:
+ *
+ * Bcache mostly works with cache sets, cache devices, and backing devices.
+ *
+ * Support for multiple cache devices hasn't quite been finished off yet, but
+ * it's about 95% plumbed through. A cache set and its cache devices is sort of
+ * like a md raid array and its component devices. Most of the code doesn't care
+ * about individual cache devices, the main abstraction is the cache set.
+ *
+ * Multiple cache devices is intended to give us the ability to mirror dirty
+ * cached data and metadata, without mirroring clean cached data.
+ *
+ * Backing devices are different, in that they have a lifetime independent of a
+ * cache set. When you register a newly formatted backing device it'll come up
+ * in passthrough mode, and then you can attach and detach a backing device from
+ * a cache set at runtime - while it's mounted and in use. Detaching implicitly
+ * invalidates any cached data for that backing device.
+ *
+ * A cache set can have multiple (many) backing devices attached to it.
+ *
+ * There's also flash only volumes - this is the reason for the distinction
+ * between struct cached_dev and struct bcache_device. A flash only volume
+ * works much like a bcache device that has a backing device, except the
+ * "cached" data is always dirty. The end result is that we get thin
+ * provisioning with very little additional code.
+ *
+ * Flash only volumes work but they're not production ready because the moving
+ * garbage collector needs more work. More on that later.
+ *
+ * BUCKETS/ALLOCATION:
+ *
+ * Bcache is primarily designed for caching, which means that in normal
+ * operation all of our available space will be allocated. Thus, we need an
+ * efficient way of deleting things from the cache so we can write new things to
+ * it.
+ *
+ * To do this, we first divide the cache device up into buckets. A bucket is the
+ * unit of allocation; they're typically around 1 mb - anywhere from 128k to 2M+
+ * works efficiently.
+ *
+ * Each bucket has a 16 bit priority, and an 8 bit generation associated with
+ * it. The gens and priorities for all the buckets are stored contiguously and
+ * packed on disk (in a linked list of buckets - aside from the superblock, all
+ * of bcache's metadata is stored in buckets).
+ *
+ * The priority is used to implement an LRU. We reset a bucket's priority when
+ * we allocate it or on cache it, and every so often we decrement the priority
+ * of each bucket. It could be used to implement something more sophisticated,
+ * if anyone ever gets around to it.
+ *
+ * The generation is used for invalidating buckets. Each pointer also has an 8
+ * bit generation embedded in it; for a pointer to be considered valid, its gen
+ * must match the gen of the bucket it points into. Thus, to reuse a bucket all
+ * we have to do is increment its gen (and write its new gen to disk; we batch
+ * this up).
+ *
+ * Bcache is entirely COW - we never write twice to a bucket, even buckets that
+ * contain metadata (including btree nodes).
+ *
+ * THE BTREE:
+ *
+ * Bcache is in large part design around the btree.
+ *
+ * At a high level, the btree is just an index of key -> ptr tuples.
+ *
+ * Keys represent extents, and thus have a size field. Keys also have a variable
+ * number of pointers attached to them (potentially zero, which is handy for
+ * invalidating the cache).
+ *
+ * The key itself is an inode:offset pair. The inode number corresponds to a
+ * backing device or a flash only volume. The offset is the ending offset of the
+ * extent within the inode - not the starting offset; this makes lookups
+ * slightly more convenient.
+ *
+ * Pointers contain the cache device id, the offset on that device, and an 8 bit
+ * generation number. More on the gen later.
+ *
+ * Index lookups are not fully abstracted - cache lookups in particular are
+ * still somewhat mixed in with the btree code, but things are headed in that
+ * direction.
+ *
+ * Updates are fairly well abstracted, though. There are two different ways of
+ * updating the btree; insert and replace.
+ *
+ * BTREE_INSERT will just take a list of keys and insert them into the btree -
+ * overwriting (possibly only partially) any extents they overlap with. This is
+ * used to update the index after a write.
+ *
+ * BTREE_REPLACE is really cmpxchg(); it inserts a key into the btree iff it is
+ * overwriting a key that matches another given key. This is used for inserting
+ * data into the cache after a cache miss, and for background writeback, and for
+ * the moving garbage collector.
+ *
+ * There is no "delete" operation; deleting things from the index is
+ * accomplished by either by invalidating pointers (by incrementing a bucket's
+ * gen) or by inserting a key with 0 pointers - which will overwrite anything
+ * previously present at that location in the index.
+ *
+ * This means that there are always stale/invalid keys in the btree. They're
+ * filtered out by the code that iterates through a btree node, and removed when
+ * a btree node is rewritten.
+ *
+ * BTREE NODES:
+ *
+ * Our unit of allocation is a bucket, and we we can't arbitrarily allocate and
+ * free smaller than a bucket - so, that's how big our btree nodes are.
+ *
+ * (If buckets are really big we'll only use part of the bucket for a btree node
+ * - no less than 1/4th - but a bucket still contains no more than a single
+ * btree node. I'd actually like to change this, but for now we rely on the
+ * bucket's gen for deleting btree nodes when we rewrite/split a node.)
+ *
+ * Anyways, btree nodes are big - big enough to be inefficient with a textbook
+ * btree implementation.
+ *
+ * The way this is solved is that btree nodes are internally log structured; we
+ * can append new keys to an existing btree node without rewriting it. This
+ * means each set of keys we write is sorted, but the node is not.
+ *
+ * We maintain this log structure in memory - keeping 1Mb of keys sorted would
+ * be expensive, and we have to distinguish between the keys we have written and
+ * the keys we haven't. So to do a lookup in a btree node, we have to search
+ * each sorted set. But we do merge written sets together lazily, so the cost of
+ * these extra searches is quite low (normally most of the keys in a btree node
+ * will be in one big set, and then there'll be one or two sets that are much
+ * smaller).
+ *
+ * This log structure makes bcache's btree more of a hybrid between a
+ * conventional btree and a compacting data structure, with some of the
+ * advantages of both.
+ *
+ * GARBAGE COLLECTION:
+ *
+ * We can't just invalidate any bucket - it might contain dirty data or
+ * metadata. If it once contained dirty data, other writes might overwrite it
+ * later, leaving no valid pointers into that bucket in the index.
+ *
+ * Thus, the primary purpose of garbage collection is to find buckets to reuse.
+ * It also counts how much valid data it each bucket currently contains, so that
+ * allocation can reuse buckets sooner when they've been mostly overwritten.
+ *
+ * It also does some things that are really internal to the btree
+ * implementation. If a btree node contains pointers that are stale by more than
+ * some threshold, it rewrites the btree node to avoid the bucket's generation
+ * wrapping around. It also merges adjacent btree nodes if they're empty enough.
+ *
+ * THE JOURNAL:
+ *
+ * Bcache's journal is not necessary for consistency; we always strictly
+ * order metadata writes so that the btree and everything else is consistent on
+ * disk in the event of an unclean shutdown, and in fact bcache had writeback
+ * caching (with recovery from unclean shutdown) before journalling was
+ * implemented.
+ *
+ * Rather, the journal is purely a performance optimization; we can't complete a
+ * write until we've updated the index on disk, otherwise the cache would be
+ * inconsistent in the event of an unclean shutdown. This means that without the
+ * journal, on random write workloads we constantly have to update all the leaf
+ * nodes in the btree, and those writes will be mostly empty (appending at most
+ * a few keys each) - highly inefficient in terms of amount of metadata writes,
+ * and it puts more strain on the various btree resorting/compacting code.
+ *
+ * The journal is just a log of keys we've inserted; on startup we just reinsert
+ * all the keys in the open journal entries. That means that when we're updating
+ * a node in the btree, we can wait until a 4k block of keys fills up before
+ * writing them out.
+ *
+ * For simplicity, we only journal updates to leaf nodes; updates to parent
+ * nodes are rare enough (since our leaf nodes are huge) that it wasn't worth
+ * the complexity to deal with journalling them (in particular, journal replay)
+ * - updates to non leaf nodes just happen synchronously (see btree_split()).
+ */
+
+#define pr_fmt(fmt) "bcache: %s() " fmt "\n", __func__
+
+#include <linux/bio.h>
+#include <linux/blktrace_api.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "util.h"
+#include "closure.h"
+
+struct bucket {
+ atomic_t pin;
+ uint16_t prio;
+ uint8_t gen;
+ uint8_t disk_gen;
+ uint8_t last_gc; /* Most out of date gen in the btree */
+ uint8_t gc_gen;
+ uint16_t gc_mark;
+};
+
+/*
+ * I'd use bitfields for these, but I don't trust the compiler not to screw me
+ * as multiple threads touch struct bucket without locking
+ */
+
+BITMASK(GC_MARK, struct bucket, gc_mark, 0, 2);
+#define GC_MARK_RECLAIMABLE 0
+#define GC_MARK_DIRTY 1
+#define GC_MARK_METADATA 2
+BITMASK(GC_SECTORS_USED, struct bucket, gc_mark, 2, 14);
+
+struct bkey {
+ uint64_t high;
+ uint64_t low;
+ uint64_t ptr[];
+};
+
+/* Enough for a key with 6 pointers */
+#define BKEY_PAD 8
+
+#define BKEY_PADDED(key) \
+ union { struct bkey key; uint64_t key ## _pad[BKEY_PAD]; }
+
+/* Version 0: Cache device
+ * Version 1: Backing device
+ * Version 2: Seed pointer into btree node checksum
+ * Version 3: Cache device with new UUID format
+ * Version 4: Backing device with data offset
+ */
+#define BCACHE_SB_VERSION_CDEV 0
+#define BCACHE_SB_VERSION_BDEV 1
+#define BCACHE_SB_VERSION_CDEV_WITH_UUID 3
+#define BCACHE_SB_VERSION_BDEV_WITH_OFFSET 4
+#define BCACHE_SB_MAX_VERSION 4
+
+#define SB_SECTOR 8
+#define SB_SIZE 4096
+#define SB_LABEL_SIZE 32
+#define SB_JOURNAL_BUCKETS 256U
+/* SB_JOURNAL_BUCKETS must be divisible by BITS_PER_LONG */
+#define MAX_CACHES_PER_SET 8
+
+#define BDEV_DATA_START_DEFAULT 16 /* sectors */
+
+struct cache_sb {
+ uint64_t csum;
+ uint64_t offset; /* sector where this sb was written */
+ uint64_t version;
+
+ uint8_t magic[16];
+
+ uint8_t uuid[16];
+ union {
+ uint8_t set_uuid[16];
+ uint64_t set_magic;
+ };
+ uint8_t label[SB_LABEL_SIZE];
+
+ uint64_t flags;
+ uint64_t seq;
+ uint64_t pad[8];
+
+ union {
+ struct {
+ /* Cache devices */
+ uint64_t nbuckets; /* device size */
+
+ uint16_t block_size; /* sectors */
+ uint16_t bucket_size; /* sectors */
+
+ uint16_t nr_in_set;
+ uint16_t nr_this_dev;
+ };
+ struct {
+ /* Backing devices */
+ uint64_t data_offset;
+
+ /*
+ * block_size from the cache device section is still used by
+ * backing devices, so don't add anything here until we fix
+ * things to not need it for backing devices anymore
+ */
+ };
+ };
+
+ uint32_t last_mount; /* time_t */
+
+ uint16_t first_bucket;
+ union {
+ uint16_t njournal_buckets;
+ uint16_t keys;
+ };
+ uint64_t d[SB_JOURNAL_BUCKETS]; /* journal buckets */
+};
+
+BITMASK(CACHE_SYNC, struct cache_sb, flags, 0, 1);
+BITMASK(CACHE_DISCARD, struct cache_sb, flags, 1, 1);
+BITMASK(CACHE_REPLACEMENT, struct cache_sb, flags, 2, 3);
+#define CACHE_REPLACEMENT_LRU 0U
+#define CACHE_REPLACEMENT_FIFO 1U
+#define CACHE_REPLACEMENT_RANDOM 2U
+
+BITMASK(BDEV_CACHE_MODE, struct cache_sb, flags, 0, 4);
+#define CACHE_MODE_WRITETHROUGH 0U
+#define CACHE_MODE_WRITEBACK 1U
+#define CACHE_MODE_WRITEAROUND 2U
+#define CACHE_MODE_NONE 3U
+BITMASK(BDEV_STATE, struct cache_sb, flags, 61, 2);
+#define BDEV_STATE_NONE 0U
+#define BDEV_STATE_CLEAN 1U
+#define BDEV_STATE_DIRTY 2U
+#define BDEV_STATE_STALE 3U
+
+/* Version 1: Seed pointer into btree node checksum
+ */
+#define BCACHE_BSET_VERSION 1
+
+/*
+ * This is the on disk format for btree nodes - a btree node on disk is a list
+ * of these; within each set the keys are sorted
+ */
+struct bset {
+ uint64_t csum;
+ uint64_t magic;
+ uint64_t seq;
+ uint32_t version;
+ uint32_t keys;
+
+ union {
+ struct bkey start[0];
+ uint64_t d[0];
+ };
+};
+
+/*
+ * On disk format for priorities and gens - see super.c near prio_write() for
+ * more.
+ */
+struct prio_set {
+ uint64_t csum;
+ uint64_t magic;
+ uint64_t seq;
+ uint32_t version;
+ uint32_t pad;
+
+ uint64_t next_bucket;
+
+ struct bucket_disk {
+ uint16_t prio;
+ uint8_t gen;
+ } __attribute((packed)) data[];
+};
+
+struct uuid_entry {
+ union {
+ struct {
+ uint8_t uuid[16];
+ uint8_t label[32];
+ uint32_t first_reg;
+ uint32_t last_reg;
+ uint32_t invalidated;
+
+ uint32_t flags;
+ /* Size of flash only volumes */
+ uint64_t sectors;
+ };
+
+ uint8_t pad[128];
+ };
+};
+
+BITMASK(UUID_FLASH_ONLY, struct uuid_entry, flags, 0, 1);
+
+#include "journal.h"
+#include "stats.h"
+struct search;
+struct btree;
+struct keybuf;
+
+struct keybuf_key {
+ struct rb_node node;
+ BKEY_PADDED(key);
+ void *private;
+};
+
+typedef bool (keybuf_pred_fn)(struct keybuf *, struct bkey *);
+
+struct keybuf {
+ keybuf_pred_fn *key_predicate;
+
+ struct bkey last_scanned;
+ spinlock_t lock;
+
+ /*
+ * Beginning and end of range in rb tree - so that we can skip taking
+ * lock and checking the rb tree when we need to check for overlapping
+ * keys.
+ */
+ struct bkey start;
+ struct bkey end;
+
+ struct rb_root keys;
+
+#define KEYBUF_NR 100
+ DECLARE_ARRAY_ALLOCATOR(struct keybuf_key, freelist, KEYBUF_NR);
+};
+
+struct bio_split_pool {
+ struct bio_set *bio_split;
+ mempool_t *bio_split_hook;
+};
+
+struct bio_split_hook {
+ struct closure cl;
+ struct bio_split_pool *p;
+ struct bio *bio;
+ bio_end_io_t *bi_end_io;
+ void *bi_private;
+};
+
+struct bcache_device {
+ struct closure cl;
+
+ struct kobject kobj;
+
+ struct cache_set *c;
+ unsigned id;
+#define BCACHEDEVNAME_SIZE 12
+ char name[BCACHEDEVNAME_SIZE];
+
+ struct gendisk *disk;
+
+ /* If nonzero, we're closing */
+ atomic_t closing;
+
+ /* If nonzero, we're detaching/unregistering from cache set */
+ atomic_t detaching;
+
+ atomic_long_t sectors_dirty;
+ unsigned long sectors_dirty_gc;
+ unsigned long sectors_dirty_last;
+ long sectors_dirty_derivative;
+
+ mempool_t *unaligned_bvec;
+ struct bio_set *bio_split;
+
+ unsigned data_csum:1;
+
+ int (*cache_miss)(struct btree *, struct search *,
+ struct bio *, unsigned);
+ int (*ioctl) (struct bcache_device *, fmode_t, unsigned, unsigned long);
+
+ struct bio_split_pool bio_split_hook;
+};
+
+struct io {
+ /* Used to track sequential IO so it can be skipped */
+ struct hlist_node hash;
+ struct list_head lru;
+
+ unsigned long jiffies;
+ unsigned sequential;
+ sector_t last;
+};
+
+struct cached_dev {
+ struct list_head list;
+ struct bcache_device disk;
+ struct block_device *bdev;
+
+ struct cache_sb sb;
+ struct bio sb_bio;
+ struct bio_vec sb_bv[1];
+ struct closure_with_waitlist sb_write;
+
+ /* Refcount on the cache set. Always nonzero when we're caching. */
+ atomic_t count;
+ struct work_struct detach;
+
+ /*
+ * Device might not be running if it's dirty and the cache set hasn't
+ * showed up yet.
+ */
+ atomic_t running;
+
+ /*
+ * Writes take a shared lock from start to finish; scanning for dirty
+ * data to refill the rb tree requires an exclusive lock.
+ */
+ struct rw_semaphore writeback_lock;
+
+ /*
+ * Nonzero, and writeback has a refcount (d->count), iff there is dirty
+ * data in the cache. Protected by writeback_lock; must have an
+ * shared lock to set and exclusive lock to clear.
+ */
+ atomic_t has_dirty;
+
+ struct ratelimit writeback_rate;
+ struct delayed_work writeback_rate_update;
+
+ /*
+ * Internal to the writeback code, so read_dirty() can keep track of
+ * where it's at.
+ */
+ sector_t last_read;
+
+ /* Number of writeback bios in flight */
+ atomic_t in_flight;
+ struct closure_with_timer writeback;
+ struct closure_waitlist writeback_wait;
+
+ struct keybuf writeback_keys;
+
+ /* For tracking sequential IO */
+#define RECENT_IO_BITS 7
+#define RECENT_IO (1 << RECENT_IO_BITS)
+ struct io io[RECENT_IO];
+ struct hlist_head io_hash[RECENT_IO + 1];
+ struct list_head io_lru;
+ spinlock_t io_lock;
+
+ struct cache_accounting accounting;
+
+ /* The rest of this all shows up in sysfs */
+ unsigned sequential_cutoff;
+ unsigned readahead;
+
+ unsigned sequential_merge:1;
+ unsigned verify:1;
+
+ unsigned writeback_metadata:1;
+ unsigned writeback_running:1;
+ unsigned char writeback_percent;
+ unsigned writeback_delay;
+
+ int writeback_rate_change;
+ int64_t writeback_rate_derivative;
+ uint64_t writeback_rate_target;
+
+ unsigned writeback_rate_update_seconds;
+ unsigned writeback_rate_d_term;
+ unsigned writeback_rate_p_term_inverse;
+ unsigned writeback_rate_d_smooth;
+};
+
+enum alloc_watermarks {
+ WATERMARK_PRIO,
+ WATERMARK_METADATA,
+ WATERMARK_MOVINGGC,
+ WATERMARK_NONE,
+ WATERMARK_MAX
+};
+
+struct cache {
+ struct cache_set *set;
+ struct cache_sb sb;
+ struct bio sb_bio;
+ struct bio_vec sb_bv[1];
+
+ struct kobject kobj;
+ struct block_device *bdev;
+
+ unsigned watermark[WATERMARK_MAX];
+
+ struct closure alloc;
+ struct workqueue_struct *alloc_workqueue;
+
+ struct closure prio;
+ struct prio_set *disk_buckets;
+
+ /*
+ * When allocating new buckets, prio_write() gets first dibs - since we
+ * may not be allocate at all without writing priorities and gens.
+ * prio_buckets[] contains the last buckets we wrote priorities to (so
+ * gc can mark them as metadata), prio_next[] contains the buckets
+ * allocated for the next prio write.
+ */
+ uint64_t *prio_buckets;
+ uint64_t *prio_last_buckets;
+
+ /*
+ * free: Buckets that are ready to be used
+ *
+ * free_inc: Incoming buckets - these are buckets that currently have
+ * cached data in them, and we can't reuse them until after we write
+ * their new gen to disk. After prio_write() finishes writing the new
+ * gens/prios, they'll be moved to the free list (and possibly discarded
+ * in the process)
+ *
+ * unused: GC found nothing pointing into these buckets (possibly
+ * because all the data they contained was overwritten), so we only
+ * need to discard them before they can be moved to the free list.
+ */
+ DECLARE_FIFO(long, free);
+ DECLARE_FIFO(long, free_inc);
+ DECLARE_FIFO(long, unused);
+
+ size_t fifo_last_bucket;
+
+ /* Allocation stuff: */
+ struct bucket *buckets;
+
+ DECLARE_HEAP(struct bucket *, heap);
+
+ /*
+ * max(gen - disk_gen) for all buckets. When it gets too big we have to
+ * call prio_write() to keep gens from wrapping.
+ */
+ uint8_t need_save_prio;
+ unsigned gc_move_threshold;
+
+ /*
+ * If nonzero, we know we aren't going to find any buckets to invalidate
+ * until a gc finishes - otherwise we could pointlessly burn a ton of
+ * cpu
+ */
+ unsigned invalidate_needs_gc:1;
+
+ bool discard; /* Get rid of? */
+
+ /*
+ * We preallocate structs for issuing discards to buckets, and keep them
+ * on this list when they're not in use; do_discard() issues discards
+ * whenever there's work to do and is called by free_some_buckets() and
+ * when a discard finishes.
+ */
+ atomic_t discards_in_flight;
+ struct list_head discards;
+
+ struct journal_device journal;
+
+ /* The rest of this all shows up in sysfs */
+#define IO_ERROR_SHIFT 20
+ atomic_t io_errors;
+ atomic_t io_count;
+
+ atomic_long_t meta_sectors_written;
+ atomic_long_t btree_sectors_written;
+ atomic_long_t sectors_written;
+
+ struct bio_split_pool bio_split_hook;
+};
+
+struct gc_stat {
+ size_t nodes;
+ size_t key_bytes;
+
+ size_t nkeys;
+ uint64_t data; /* sectors */
+ uint64_t dirty; /* sectors */
+ unsigned in_use; /* percent */
+};
+
+/*
+ * Flag bits, for how the cache set is shutting down, and what phase it's at:
+ *
+ * CACHE_SET_UNREGISTERING means we're not just shutting down, we're detaching
+ * all the backing devices first (their cached data gets invalidated, and they
+ * won't automatically reattach).
+ *
+ * CACHE_SET_STOPPING always gets set first when we're closing down a cache set;
+ * we'll continue to run normally for awhile with CACHE_SET_STOPPING set (i.e.
+ * flushing dirty data).
+ *
+ * CACHE_SET_STOPPING_2 gets set at the last phase, when it's time to shut down
+ * the allocation thread.
+ */
+#define CACHE_SET_UNREGISTERING 0
+#define CACHE_SET_STOPPING 1
+#define CACHE_SET_STOPPING_2 2
+
+struct cache_set {
+ struct closure cl;
+
+ struct list_head list;
+ struct kobject kobj;
+ struct kobject internal;
+ struct dentry *debug;
+ struct cache_accounting accounting;
+
+ unsigned long flags;
+
+ struct cache_sb sb;
+
+ struct cache *cache[MAX_CACHES_PER_SET];
+ struct cache *cache_by_alloc[MAX_CACHES_PER_SET];
+ int caches_loaded;
+
+ struct bcache_device **devices;
+ struct list_head cached_devs;
+ uint64_t cached_dev_sectors;
+ struct closure caching;
+
+ struct closure_with_waitlist sb_write;
+
+ mempool_t *search;
+ mempool_t *bio_meta;
+ struct bio_set *bio_split;
+
+ /* For the btree cache */
+ struct shrinker shrink;
+
+ /* For the allocator itself */
+ wait_queue_head_t alloc_wait;
+
+ /* For the btree cache and anything allocation related */
+ struct mutex bucket_lock;
+
+ /* log2(bucket_size), in sectors */
+ unsigned short bucket_bits;
+
+ /* log2(block_size), in sectors */
+ unsigned short block_bits;
+
+ /*
+ * Default number of pages for a new btree node - may be less than a
+ * full bucket
+ */
+ unsigned btree_pages;
+
+ /*
+ * Lists of struct btrees; lru is the list for structs that have memory
+ * allocated for actual btree node, freed is for structs that do not.
+ *
+ * We never free a struct btree, except on shutdown - we just put it on
+ * the btree_cache_freed list and reuse it later. This simplifies the
+ * code, and it doesn't cost us much memory as the memory usage is
+ * dominated by buffers that hold the actual btree node data and those
+ * can be freed - and the number of struct btrees allocated is
+ * effectively bounded.
+ *
+ * btree_cache_freeable effectively is a small cache - we use it because
+ * high order page allocations can be rather expensive, and it's quite
+ * common to delete and allocate btree nodes in quick succession. It
+ * should never grow past ~2-3 nodes in practice.
+ */
+ struct list_head btree_cache;
+ struct list_head btree_cache_freeable;
+ struct list_head btree_cache_freed;
+
+ /* Number of elements in btree_cache + btree_cache_freeable lists */
+ unsigned bucket_cache_used;
+
+ /*
+ * If we need to allocate memory for a new btree node and that
+ * allocation fails, we can cannibalize another node in the btree cache
+ * to satisfy the allocation. However, only one thread can be doing this
+ * at a time, for obvious reasons - try_harder and try_wait are
+ * basically a lock for this that we can wait on asynchronously. The
+ * btree_root() macro releases the lock when it returns.
+ */
+ struct closure *try_harder;
+ struct closure_waitlist try_wait;
+ uint64_t try_harder_start;
+
+ /*
+ * When we free a btree node, we increment the gen of the bucket the
+ * node is in - but we can't rewrite the prios and gens until we
+ * finished whatever it is we were doing, otherwise after a crash the
+ * btree node would be freed but for say a split, we might not have the
+ * pointers to the new nodes inserted into the btree yet.
+ *
+ * This is a refcount that blocks prio_write() until the new keys are
+ * written.
+ */
+ atomic_t prio_blocked;
+ struct closure_waitlist bucket_wait;
+
+ /*
+ * For any bio we don't skip we subtract the number of sectors from
+ * rescale; when it hits 0 we rescale all the bucket priorities.
+ */
+ atomic_t rescale;
+ /*
+ * When we invalidate buckets, we use both the priority and the amount
+ * of good data to determine which buckets to reuse first - to weight
+ * those together consistently we keep track of the smallest nonzero
+ * priority of any bucket.
+ */
+ uint16_t min_prio;
+
+ /*
+ * max(gen - gc_gen) for all buckets. When it gets too big we have to gc
+ * to keep gens from wrapping around.
+ */
+ uint8_t need_gc;
+ struct gc_stat gc_stats;
+ size_t nbuckets;
+
+ struct closure_with_waitlist gc;
+ /* Where in the btree gc currently is */
+ struct bkey gc_done;
+
+ /*
+ * The allocation code needs gc_mark in struct bucket to be correct, but
+ * it's not while a gc is in progress. Protected by bucket_lock.
+ */
+ int gc_mark_valid;
+
+ /* Counts how many sectors bio_insert has added to the cache */
+ atomic_t sectors_to_gc;
+
+ struct closure moving_gc;
+ struct closure_waitlist moving_gc_wait;
+ struct keybuf moving_gc_keys;
+ /* Number of moving GC bios in flight */
+ atomic_t in_flight;
+
+ struct btree *root;
+
+#ifdef CONFIG_BCACHE_DEBUG
+ struct btree *verify_data;
+ struct mutex verify_lock;
+#endif
+
+ unsigned nr_uuids;
+ struct uuid_entry *uuids;
+ BKEY_PADDED(uuid_bucket);
+ struct closure_with_waitlist uuid_write;
+
+ /*
+ * A btree node on disk could have too many bsets for an iterator to fit
+ * on the stack - this is a single element mempool for btree_read_work()
+ */
+ struct mutex fill_lock;
+ struct btree_iter *fill_iter;
+
+ /*
+ * btree_sort() is a merge sort and requires temporary space - single
+ * element mempool
+ */
+ struct mutex sort_lock;
+ struct bset *sort;
+
+ /* List of buckets we're currently writing data to */
+ struct list_head data_buckets;
+ spinlock_t data_bucket_lock;
+
+ struct journal journal;
+
+#define CONGESTED_MAX 1024
+ unsigned congested_last_us;
+ atomic_t congested;
+
+ /* The rest of this all shows up in sysfs */
+ unsigned congested_read_threshold_us;
+ unsigned congested_write_threshold_us;
+
+ spinlock_t sort_time_lock;
+ struct time_stats sort_time;
+ struct time_stats btree_gc_time;
+ struct time_stats btree_split_time;
+ spinlock_t btree_read_time_lock;
+ struct time_stats btree_read_time;
+ struct time_stats try_harder_time;
+
+ atomic_long_t cache_read_races;
+ atomic_long_t writeback_keys_done;
+ atomic_long_t writeback_keys_failed;
+ unsigned error_limit;
+ unsigned error_decay;
+ unsigned short journal_delay_ms;
+ unsigned verify:1;
+ unsigned key_merging_disabled:1;
+ unsigned gc_always_rewrite:1;
+ unsigned shrinker_disabled:1;
+ unsigned copy_gc_enabled:1;
+
+#define BUCKET_HASH_BITS 12
+ struct hlist_head bucket_hash[1 << BUCKET_HASH_BITS];
+};
+
+static inline bool key_merging_disabled(struct cache_set *c)
+{
+#ifdef CONFIG_BCACHE_DEBUG
+ return c->key_merging_disabled;
+#else
+ return 0;
+#endif
+}
+
+static inline bool SB_IS_BDEV(const struct cache_sb *sb)
+{
+ return sb->version == BCACHE_SB_VERSION_BDEV
+ || sb->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET;
+}
+
+struct bbio {
+ unsigned submit_time_us;
+ union {
+ struct bkey key;
+ uint64_t _pad[3];
+ /*
+ * We only need pad = 3 here because we only ever carry around a
+ * single pointer - i.e. the pointer we're doing io to/from.
+ */
+ };
+ struct bio bio;
+};
+
+static inline unsigned local_clock_us(void)
+{
+ return local_clock() >> 10;
+}
+
+#define MAX_BSETS 4U
+
+#define BTREE_PRIO USHRT_MAX
+#define INITIAL_PRIO 32768
+
+#define btree_bytes(c) ((c)->btree_pages * PAGE_SIZE)
+#define btree_blocks(b) \
+ ((unsigned) (KEY_SIZE(&b->key) >> (b)->c->block_bits))
+
+#define btree_default_blocks(c) \
+ ((unsigned) ((PAGE_SECTORS * (c)->btree_pages) >> (c)->block_bits))
+
+#define bucket_pages(c) ((c)->sb.bucket_size / PAGE_SECTORS)
+#define bucket_bytes(c) ((c)->sb.bucket_size << 9)
+#define block_bytes(c) ((c)->sb.block_size << 9)
+
+#define __set_bytes(i, k) (sizeof(*(i)) + (k) * sizeof(uint64_t))
+#define set_bytes(i) __set_bytes(i, i->keys)
+
+#define __set_blocks(i, k, c) DIV_ROUND_UP(__set_bytes(i, k), block_bytes(c))
+#define set_blocks(i, c) __set_blocks(i, (i)->keys, c)
+
+#define node(i, j) ((struct bkey *) ((i)->d + (j)))
+#define end(i) node(i, (i)->keys)
+
+#define index(i, b) \
+ ((size_t) (((void *) i - (void *) (b)->sets[0].data) / \
+ block_bytes(b->c)))
+
+#define btree_data_space(b) (PAGE_SIZE << (b)->page_order)
+
+#define prios_per_bucket(c) \
+ ((bucket_bytes(c) - sizeof(struct prio_set)) / \
+ sizeof(struct bucket_disk))
+#define prio_buckets(c) \
+ DIV_ROUND_UP((size_t) (c)->sb.nbuckets, prios_per_bucket(c))
+
+#define JSET_MAGIC 0x245235c1a3625032ULL
+#define PSET_MAGIC 0x6750e15f87337f91ULL
+#define BSET_MAGIC 0x90135c78b99e07f5ULL
+
+#define jset_magic(c) ((c)->sb.set_magic ^ JSET_MAGIC)
+#define pset_magic(c) ((c)->sb.set_magic ^ PSET_MAGIC)
+#define bset_magic(c) ((c)->sb.set_magic ^ BSET_MAGIC)
+
+/* Bkey fields: all units are in sectors */
+
+#define KEY_FIELD(name, field, offset, size) \
+ BITMASK(name, struct bkey, field, offset, size)
+
+#define PTR_FIELD(name, offset, size) \
+ static inline uint64_t name(const struct bkey *k, unsigned i) \
+ { return (k->ptr[i] >> offset) & ~(((uint64_t) ~0) << size); } \
+ \
+ static inline void SET_##name(struct bkey *k, unsigned i, uint64_t v)\
+ { \
+ k->ptr[i] &= ~(~((uint64_t) ~0 << size) << offset); \
+ k->ptr[i] |= v << offset; \
+ }
+
+KEY_FIELD(KEY_PTRS, high, 60, 3)
+KEY_FIELD(HEADER_SIZE, high, 58, 2)
+KEY_FIELD(KEY_CSUM, high, 56, 2)
+KEY_FIELD(KEY_PINNED, high, 55, 1)
+KEY_FIELD(KEY_DIRTY, high, 36, 1)
+
+KEY_FIELD(KEY_SIZE, high, 20, 16)
+KEY_FIELD(KEY_INODE, high, 0, 20)
+
+/* Next time I change the on disk format, KEY_OFFSET() won't be 64 bits */
+
+static inline uint64_t KEY_OFFSET(const struct bkey *k)
+{
+ return k->low;
+}
+
+static inline void SET_KEY_OFFSET(struct bkey *k, uint64_t v)
+{
+ k->low = v;
+}
+
+PTR_FIELD(PTR_DEV, 51, 12)
+PTR_FIELD(PTR_OFFSET, 8, 43)
+PTR_FIELD(PTR_GEN, 0, 8)
+
+#define PTR_CHECK_DEV ((1 << 12) - 1)
+
+#define PTR(gen, offset, dev) \
+ ((((uint64_t) dev) << 51) | ((uint64_t) offset) << 8 | gen)
+
+static inline size_t sector_to_bucket(struct cache_set *c, sector_t s)
+{
+ return s >> c->bucket_bits;
+}
+
+static inline sector_t bucket_to_sector(struct cache_set *c, size_t b)
+{
+ return ((sector_t) b) << c->bucket_bits;
+}
+
+static inline sector_t bucket_remainder(struct cache_set *c, sector_t s)
+{
+ return s & (c->sb.bucket_size - 1);
+}
+
+static inline struct cache *PTR_CACHE(struct cache_set *c,
+ const struct bkey *k,
+ unsigned ptr)
+{
+ return c->cache[PTR_DEV(k, ptr)];
+}
+
+static inline size_t PTR_BUCKET_NR(struct cache_set *c,
+ const struct bkey *k,
+ unsigned ptr)
+{
+ return sector_to_bucket(c, PTR_OFFSET(k, ptr));
+}
+
+static inline struct bucket *PTR_BUCKET(struct cache_set *c,
+ const struct bkey *k,
+ unsigned ptr)
+{
+ return PTR_CACHE(c, k, ptr)->buckets + PTR_BUCKET_NR(c, k, ptr);
+}
+
+/* Btree key macros */
+
+/*
+ * The high bit being set is a relic from when we used it to do binary
+ * searches - it told you where a key started. It's not used anymore,
+ * and can probably be safely dropped.
+ */
+#define KEY(dev, sector, len) \
+((struct bkey) { \
+ .high = (1ULL << 63) | ((uint64_t) (len) << 20) | (dev), \
+ .low = (sector) \
+})
+
+static inline void bkey_init(struct bkey *k)
+{
+ *k = KEY(0, 0, 0);
+}
+
+#define KEY_START(k) (KEY_OFFSET(k) - KEY_SIZE(k))
+#define START_KEY(k) KEY(KEY_INODE(k), KEY_START(k), 0)
+#define MAX_KEY KEY(~(~0 << 20), ((uint64_t) ~0) >> 1, 0)
+#define ZERO_KEY KEY(0, 0, 0)
+
+/*
+ * This is used for various on disk data structures - cache_sb, prio_set, bset,
+ * jset: The checksum is _always_ the first 8 bytes of these structs
+ */
+#define csum_set(i) \
+ bch_crc64(((void *) (i)) + sizeof(uint64_t), \
+ ((void *) end(i)) - (((void *) (i)) + sizeof(uint64_t)))
+
+/* Error handling macros */
+
+#define btree_bug(b, ...) \
+do { \
+ if (bch_cache_set_error((b)->c, __VA_ARGS__)) \
+ dump_stack(); \
+} while (0)
+
+#define cache_bug(c, ...) \
+do { \
+ if (bch_cache_set_error(c, __VA_ARGS__)) \
+ dump_stack(); \
+} while (0)
+
+#define btree_bug_on(cond, b, ...) \
+do { \
+ if (cond) \
+ btree_bug(b, __VA_ARGS__); \
+} while (0)
+
+#define cache_bug_on(cond, c, ...) \
+do { \
+ if (cond) \
+ cache_bug(c, __VA_ARGS__); \
+} while (0)
+
+#define cache_set_err_on(cond, c, ...) \
+do { \
+ if (cond) \
+ bch_cache_set_error(c, __VA_ARGS__); \
+} while (0)
+
+/* Looping macros */
+
+#define for_each_cache(ca, cs, iter) \
+ for (iter = 0; ca = cs->cache[iter], iter < (cs)->sb.nr_in_set; iter++)
+
+#define for_each_bucket(b, ca) \
+ for (b = (ca)->buckets + (ca)->sb.first_bucket; \
+ b < (ca)->buckets + (ca)->sb.nbuckets; b++)
+
+static inline void __bkey_put(struct cache_set *c, struct bkey *k)
+{
+ unsigned i;
+
+ for (i = 0; i < KEY_PTRS(k); i++)
+ atomic_dec_bug(&PTR_BUCKET(c, k, i)->pin);
+}
+
+/* Blktrace macros */
+
+#define blktrace_msg(c, fmt, ...) \
+do { \
+ struct request_queue *q = bdev_get_queue(c->bdev); \
+ if (q) \
+ blk_add_trace_msg(q, fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define blktrace_msg_all(s, fmt, ...) \
+do { \
+ struct cache *_c; \
+ unsigned i; \
+ for_each_cache(_c, (s), i) \
+ blktrace_msg(_c, fmt, ##__VA_ARGS__); \
+} while (0)
+
+static inline void cached_dev_put(struct cached_dev *dc)
+{
+ if (atomic_dec_and_test(&dc->count))
+ schedule_work(&dc->detach);
+}
+
+static inline bool cached_dev_get(struct cached_dev *dc)
+{
+ if (!atomic_inc_not_zero(&dc->count))
+ return false;
+
+ /* Paired with the mb in cached_dev_attach */
+ smp_mb__after_atomic_inc();
+ return true;
+}
+
+/*
+ * bucket_gc_gen() returns the difference between the bucket's current gen and
+ * the oldest gen of any pointer into that bucket in the btree (last_gc).
+ *
+ * bucket_disk_gen() returns the difference between the current gen and the gen
+ * on disk; they're both used to make sure gens don't wrap around.
+ */
+
+static inline uint8_t bucket_gc_gen(struct bucket *b)
+{
+ return b->gen - b->last_gc;
+}
+
+static inline uint8_t bucket_disk_gen(struct bucket *b)
+{
+ return b->gen - b->disk_gen;
+}
+
+#define BUCKET_GC_GEN_MAX 96U
+#define BUCKET_DISK_GEN_MAX 64U
+
+#define kobj_attribute_write(n, fn) \
+ static struct kobj_attribute ksysfs_##n = __ATTR(n, S_IWUSR, NULL, fn)
+
+#define kobj_attribute_rw(n, show, store) \
+ static struct kobj_attribute ksysfs_##n = \
+ __ATTR(n, S_IWUSR|S_IRUSR, show, store)
+
+/* Forward declarations */
+
+void bch_writeback_queue(struct cached_dev *);
+void bch_writeback_add(struct cached_dev *, unsigned);
+
+void bch_count_io_errors(struct cache *, int, const char *);
+void bch_bbio_count_io_errors(struct cache_set *, struct bio *,
+ int, const char *);
+void bch_bbio_endio(struct cache_set *, struct bio *, int, const char *);
+void bch_bbio_free(struct bio *, struct cache_set *);
+struct bio *bch_bbio_alloc(struct cache_set *);
+
+struct bio *bch_bio_split(struct bio *, int, gfp_t, struct bio_set *);
+void bch_generic_make_request(struct bio *, struct bio_split_pool *);
+void __bch_submit_bbio(struct bio *, struct cache_set *);
+void bch_submit_bbio(struct bio *, struct cache_set *, struct bkey *, unsigned);
+
+uint8_t bch_inc_gen(struct cache *, struct bucket *);
+void bch_rescale_priorities(struct cache_set *, int);
+bool bch_bucket_add_unused(struct cache *, struct bucket *);
+void bch_allocator_thread(struct closure *);
+
+long bch_bucket_alloc(struct cache *, unsigned, struct closure *);
+void bch_bucket_free(struct cache_set *, struct bkey *);
+
+int __bch_bucket_alloc_set(struct cache_set *, unsigned,
+ struct bkey *, int, struct closure *);
+int bch_bucket_alloc_set(struct cache_set *, unsigned,
+ struct bkey *, int, struct closure *);
+
+__printf(2, 3)
+bool bch_cache_set_error(struct cache_set *, const char *, ...);
+
+void bch_prio_write(struct cache *);
+void bch_write_bdev_super(struct cached_dev *, struct closure *);
+
+extern struct workqueue_struct *bcache_wq, *bch_gc_wq;
+extern const char * const bch_cache_modes[];
+extern struct mutex bch_register_lock;
+extern struct list_head bch_cache_sets;
+
+extern struct kobj_type bch_cached_dev_ktype;
+extern struct kobj_type bch_flash_dev_ktype;
+extern struct kobj_type bch_cache_set_ktype;
+extern struct kobj_type bch_cache_set_internal_ktype;
+extern struct kobj_type bch_cache_ktype;
+
+void bch_cached_dev_release(struct kobject *);
+void bch_flash_dev_release(struct kobject *);
+void bch_cache_set_release(struct kobject *);
+void bch_cache_release(struct kobject *);
+
+int bch_uuid_write(struct cache_set *);
+void bcache_write_super(struct cache_set *);
+
+int bch_flash_dev_create(struct cache_set *c, uint64_t size);
+
+int bch_cached_dev_attach(struct cached_dev *, struct cache_set *);
+void bch_cached_dev_detach(struct cached_dev *);
+void bch_cached_dev_run(struct cached_dev *);
+void bcache_device_stop(struct bcache_device *);
+
+void bch_cache_set_unregister(struct cache_set *);
+void bch_cache_set_stop(struct cache_set *);
+
+struct cache_set *bch_cache_set_alloc(struct cache_sb *);
+void bch_btree_cache_free(struct cache_set *);
+int bch_btree_cache_alloc(struct cache_set *);
+void bch_writeback_init_cached_dev(struct cached_dev *);
+void bch_moving_init_cache_set(struct cache_set *);
+
+void bch_cache_allocator_exit(struct cache *ca);
+int bch_cache_allocator_init(struct cache *ca);
+
+void bch_debug_exit(void);
+int bch_debug_init(struct kobject *);
+void bch_writeback_exit(void);
+int bch_writeback_init(void);
+void bch_request_exit(void);
+int bch_request_init(void);
+void bch_btree_exit(void);
+int bch_btree_init(void);
+
+#endif /* _BCACHE_H */
diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c
new file mode 100644
index 000000000000..cb4578a327b9
--- /dev/null
+++ b/drivers/md/bcache/bset.c
@@ -0,0 +1,1192 @@
+/*
+ * Code for working with individual keys, and sorted sets of keys with in a
+ * btree node
+ *
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "btree.h"
+#include "debug.h"
+
+#include <linux/random.h>
+#include <linux/prefetch.h>
+
+/* Keylists */
+
+void bch_keylist_copy(struct keylist *dest, struct keylist *src)
+{
+ *dest = *src;
+
+ if (src->list == src->d) {
+ size_t n = (uint64_t *) src->top - src->d;
+ dest->top = (struct bkey *) &dest->d[n];
+ dest->list = dest->d;
+ }
+}
+
+int bch_keylist_realloc(struct keylist *l, int nptrs, struct cache_set *c)
+{
+ unsigned oldsize = (uint64_t *) l->top - l->list;
+ unsigned newsize = oldsize + 2 + nptrs;
+ uint64_t *new;
+
+ /* The journalling code doesn't handle the case where the keys to insert
+ * is bigger than an empty write: If we just return -ENOMEM here,
+ * bio_insert() and bio_invalidate() will insert the keys created so far
+ * and finish the rest when the keylist is empty.
+ */
+ if (newsize * sizeof(uint64_t) > block_bytes(c) - sizeof(struct jset))
+ return -ENOMEM;
+
+ newsize = roundup_pow_of_two(newsize);
+
+ if (newsize <= KEYLIST_INLINE ||
+ roundup_pow_of_two(oldsize) == newsize)
+ return 0;
+
+ new = krealloc(l->list == l->d ? NULL : l->list,
+ sizeof(uint64_t) * newsize, GFP_NOIO);
+
+ if (!new)
+ return -ENOMEM;
+
+ if (l->list == l->d)
+ memcpy(new, l->list, sizeof(uint64_t) * KEYLIST_INLINE);
+
+ l->list = new;
+ l->top = (struct bkey *) (&l->list[oldsize]);
+
+ return 0;
+}
+
+struct bkey *bch_keylist_pop(struct keylist *l)
+{
+ struct bkey *k = l->bottom;
+
+ if (k == l->top)
+ return NULL;
+
+ while (bkey_next(k) != l->top)
+ k = bkey_next(k);
+
+ return l->top = k;
+}
+
+/* Pointer validation */
+
+bool __bch_ptr_invalid(struct cache_set *c, int level, const struct bkey *k)
+{
+ unsigned i;
+
+ if (level && (!KEY_PTRS(k) || !KEY_SIZE(k) || KEY_DIRTY(k)))
+ goto bad;
+
+ if (!level && KEY_SIZE(k) > KEY_OFFSET(k))
+ goto bad;
+
+ if (!KEY_SIZE(k))
+ return true;
+
+ for (i = 0; i < KEY_PTRS(k); i++)
+ if (ptr_available(c, k, i)) {
+ struct cache *ca = PTR_CACHE(c, k, i);
+ size_t bucket = PTR_BUCKET_NR(c, k, i);
+ size_t r = bucket_remainder(c, PTR_OFFSET(k, i));
+
+ if (KEY_SIZE(k) + r > c->sb.bucket_size ||
+ bucket < ca->sb.first_bucket ||
+ bucket >= ca->sb.nbuckets)
+ goto bad;
+ }
+
+ return false;
+bad:
+ cache_bug(c, "spotted bad key %s: %s", pkey(k), bch_ptr_status(c, k));
+ return true;
+}
+
+bool bch_ptr_bad(struct btree *b, const struct bkey *k)
+{
+ struct bucket *g;
+ unsigned i, stale;
+
+ if (!bkey_cmp(k, &ZERO_KEY) ||
+ !KEY_PTRS(k) ||
+ bch_ptr_invalid(b, k))
+ return true;
+
+ if (KEY_PTRS(k) && PTR_DEV(k, 0) == PTR_CHECK_DEV)
+ return true;
+
+ for (i = 0; i < KEY_PTRS(k); i++)
+ if (ptr_available(b->c, k, i)) {
+ g = PTR_BUCKET(b->c, k, i);
+ stale = ptr_stale(b->c, k, i);
+
+ btree_bug_on(stale > 96, b,
+ "key too stale: %i, need_gc %u",
+ stale, b->c->need_gc);
+
+ btree_bug_on(stale && KEY_DIRTY(k) && KEY_SIZE(k),
+ b, "stale dirty pointer");
+
+ if (stale)
+ return true;
+
+#ifdef CONFIG_BCACHE_EDEBUG
+ if (!mutex_trylock(&b->c->bucket_lock))
+ continue;
+
+ if (b->level) {
+ if (KEY_DIRTY(k) ||
+ g->prio != BTREE_PRIO ||
+ (b->c->gc_mark_valid &&
+ GC_MARK(g) != GC_MARK_METADATA))
+ goto bug;
+
+ } else {
+ if (g->prio == BTREE_PRIO)
+ goto bug;
+
+ if (KEY_DIRTY(k) &&
+ b->c->gc_mark_valid &&
+ GC_MARK(g) != GC_MARK_DIRTY)
+ goto bug;
+ }
+ mutex_unlock(&b->c->bucket_lock);
+#endif
+ }
+
+ return false;
+#ifdef CONFIG_BCACHE_EDEBUG
+bug:
+ mutex_unlock(&b->c->bucket_lock);
+ btree_bug(b,
+"inconsistent pointer %s: bucket %zu pin %i prio %i gen %i last_gc %i mark %llu gc_gen %i",
+ pkey(k), PTR_BUCKET_NR(b->c, k, i), atomic_read(&g->pin),
+ g->prio, g->gen, g->last_gc, GC_MARK(g), g->gc_gen);
+ return true;
+#endif
+}
+
+/* Key/pointer manipulation */
+
+void bch_bkey_copy_single_ptr(struct bkey *dest, const struct bkey *src,
+ unsigned i)
+{
+ BUG_ON(i > KEY_PTRS(src));
+
+ /* Only copy the header, key, and one pointer. */
+ memcpy(dest, src, 2 * sizeof(uint64_t));
+ dest->ptr[0] = src->ptr[i];
+ SET_KEY_PTRS(dest, 1);
+ /* We didn't copy the checksum so clear that bit. */
+ SET_KEY_CSUM(dest, 0);
+}
+
+bool __bch_cut_front(const struct bkey *where, struct bkey *k)
+{
+ unsigned i, len = 0;
+
+ if (bkey_cmp(where, &START_KEY(k)) <= 0)
+ return false;
+
+ if (bkey_cmp(where, k) < 0)
+ len = KEY_OFFSET(k) - KEY_OFFSET(where);
+ else
+ bkey_copy_key(k, where);
+
+ for (i = 0; i < KEY_PTRS(k); i++)
+ SET_PTR_OFFSET(k, i, PTR_OFFSET(k, i) + KEY_SIZE(k) - len);
+
+ BUG_ON(len > KEY_SIZE(k));
+ SET_KEY_SIZE(k, len);
+ return true;
+}
+
+bool __bch_cut_back(const struct bkey *where, struct bkey *k)
+{
+ unsigned len = 0;
+
+ if (bkey_cmp(where, k) >= 0)
+ return false;
+
+ BUG_ON(KEY_INODE(where) != KEY_INODE(k));
+
+ if (bkey_cmp(where, &START_KEY(k)) > 0)
+ len = KEY_OFFSET(where) - KEY_START(k);
+
+ bkey_copy_key(k, where);
+
+ BUG_ON(len > KEY_SIZE(k));
+ SET_KEY_SIZE(k, len);
+ return true;
+}
+
+static uint64_t merge_chksums(struct bkey *l, struct bkey *r)
+{
+ return (l->ptr[KEY_PTRS(l)] + r->ptr[KEY_PTRS(r)]) &
+ ~((uint64_t)1 << 63);
+}
+
+/* Tries to merge l and r: l should be lower than r
+ * Returns true if we were able to merge. If we did merge, l will be the merged
+ * key, r will be untouched.
+ */
+bool bch_bkey_try_merge(struct btree *b, struct bkey *l, struct bkey *r)
+{
+ unsigned i;
+
+ if (key_merging_disabled(b->c))
+ return false;
+
+ if (KEY_PTRS(l) != KEY_PTRS(r) ||
+ KEY_DIRTY(l) != KEY_DIRTY(r) ||
+ bkey_cmp(l, &START_KEY(r)))
+ return false;
+
+ for (i = 0; i < KEY_PTRS(l); i++)
+ if (l->ptr[i] + PTR(0, KEY_SIZE(l), 0) != r->ptr[i] ||
+ PTR_BUCKET_NR(b->c, l, i) != PTR_BUCKET_NR(b->c, r, i))
+ return false;
+
+ /* Keys with no pointers aren't restricted to one bucket and could
+ * overflow KEY_SIZE
+ */
+ if (KEY_SIZE(l) + KEY_SIZE(r) > USHRT_MAX) {
+ SET_KEY_OFFSET(l, KEY_OFFSET(l) + USHRT_MAX - KEY_SIZE(l));
+ SET_KEY_SIZE(l, USHRT_MAX);
+
+ bch_cut_front(l, r);
+ return false;
+ }
+
+ if (KEY_CSUM(l)) {
+ if (KEY_CSUM(r))
+ l->ptr[KEY_PTRS(l)] = merge_chksums(l, r);
+ else
+ SET_KEY_CSUM(l, 0);
+ }
+
+ SET_KEY_OFFSET(l, KEY_OFFSET(l) + KEY_SIZE(r));
+ SET_KEY_SIZE(l, KEY_SIZE(l) + KEY_SIZE(r));
+
+ return true;
+}
+
+/* Binary tree stuff for auxiliary search trees */
+
+static unsigned inorder_next(unsigned j, unsigned size)
+{
+ if (j * 2 + 1 < size) {
+ j = j * 2 + 1;
+
+ while (j * 2 < size)
+ j *= 2;
+ } else
+ j >>= ffz(j) + 1;
+
+ return j;
+}
+
+static unsigned inorder_prev(unsigned j, unsigned size)
+{
+ if (j * 2 < size) {
+ j = j * 2;
+
+ while (j * 2 + 1 < size)
+ j = j * 2 + 1;
+ } else
+ j >>= ffs(j);
+
+ return j;
+}
+
+/* I have no idea why this code works... and I'm the one who wrote it
+ *
+ * However, I do know what it does:
+ * Given a binary tree constructed in an array (i.e. how you normally implement
+ * a heap), it converts a node in the tree - referenced by array index - to the
+ * index it would have if you did an inorder traversal.
+ *
+ * Also tested for every j, size up to size somewhere around 6 million.
+ *
+ * The binary tree starts at array index 1, not 0
+ * extra is a function of size:
+ * extra = (size - rounddown_pow_of_two(size - 1)) << 1;
+ */
+static unsigned __to_inorder(unsigned j, unsigned size, unsigned extra)
+{
+ unsigned b = fls(j);
+ unsigned shift = fls(size - 1) - b;
+
+ j ^= 1U << (b - 1);
+ j <<= 1;
+ j |= 1;
+ j <<= shift;
+
+ if (j > extra)
+ j -= (j - extra) >> 1;
+
+ return j;
+}
+
+static unsigned to_inorder(unsigned j, struct bset_tree *t)
+{
+ return __to_inorder(j, t->size, t->extra);
+}
+
+static unsigned __inorder_to_tree(unsigned j, unsigned size, unsigned extra)
+{
+ unsigned shift;
+
+ if (j > extra)
+ j += j - extra;
+
+ shift = ffs(j);
+
+ j >>= shift;
+ j |= roundup_pow_of_two(size) >> shift;
+
+ return j;
+}
+
+static unsigned inorder_to_tree(unsigned j, struct bset_tree *t)
+{
+ return __inorder_to_tree(j, t->size, t->extra);
+}
+
+#if 0
+void inorder_test(void)
+{
+ unsigned long done = 0;
+ ktime_t start = ktime_get();
+
+ for (unsigned size = 2;
+ size < 65536000;
+ size++) {
+ unsigned extra = (size - rounddown_pow_of_two(size - 1)) << 1;
+ unsigned i = 1, j = rounddown_pow_of_two(size - 1);
+
+ if (!(size % 4096))
+ printk(KERN_NOTICE "loop %u, %llu per us\n", size,
+ done / ktime_us_delta(ktime_get(), start));
+
+ while (1) {
+ if (__inorder_to_tree(i, size, extra) != j)
+ panic("size %10u j %10u i %10u", size, j, i);
+
+ if (__to_inorder(j, size, extra) != i)
+ panic("size %10u j %10u i %10u", size, j, i);
+
+ if (j == rounddown_pow_of_two(size) - 1)
+ break;
+
+ BUG_ON(inorder_prev(inorder_next(j, size), size) != j);
+
+ j = inorder_next(j, size);
+ i++;
+ }
+
+ done += size - 1;
+ }
+}
+#endif
+
+/*
+ * Cacheline/offset <-> bkey pointer arithmatic:
+ *
+ * t->tree is a binary search tree in an array; each node corresponds to a key
+ * in one cacheline in t->set (BSET_CACHELINE bytes).
+ *
+ * This means we don't have to store the full index of the key that a node in
+ * the binary tree points to; to_inorder() gives us the cacheline, and then
+ * bkey_float->m gives us the offset within that cacheline, in units of 8 bytes.
+ *
+ * cacheline_to_bkey() and friends abstract out all the pointer arithmatic to
+ * make this work.
+ *
+ * To construct the bfloat for an arbitrary key we need to know what the key
+ * immediately preceding it is: we have to check if the two keys differ in the
+ * bits we're going to store in bkey_float->mantissa. t->prev[j] stores the size
+ * of the previous key so we can walk backwards to it from t->tree[j]'s key.
+ */
+
+static struct bkey *cacheline_to_bkey(struct bset_tree *t, unsigned cacheline,
+ unsigned offset)
+{
+ return ((void *) t->data) + cacheline * BSET_CACHELINE + offset * 8;
+}
+
+static unsigned bkey_to_cacheline(struct bset_tree *t, struct bkey *k)
+{
+ return ((void *) k - (void *) t->data) / BSET_CACHELINE;
+}
+
+static unsigned bkey_to_cacheline_offset(struct bkey *k)
+{
+ return ((size_t) k & (BSET_CACHELINE - 1)) / sizeof(uint64_t);
+}
+
+static struct bkey *tree_to_bkey(struct bset_tree *t, unsigned j)
+{
+ return cacheline_to_bkey(t, to_inorder(j, t), t->tree[j].m);
+}
+
+static struct bkey *tree_to_prev_bkey(struct bset_tree *t, unsigned j)
+{
+ return (void *) (((uint64_t *) tree_to_bkey(t, j)) - t->prev[j]);
+}
+
+/*
+ * For the write set - the one we're currently inserting keys into - we don't
+ * maintain a full search tree, we just keep a simple lookup table in t->prev.
+ */
+static struct bkey *table_to_bkey(struct bset_tree *t, unsigned cacheline)
+{
+ return cacheline_to_bkey(t, cacheline, t->prev[cacheline]);
+}
+
+static inline uint64_t shrd128(uint64_t high, uint64_t low, uint8_t shift)
+{
+#ifdef CONFIG_X86_64
+ asm("shrd %[shift],%[high],%[low]"
+ : [low] "+Rm" (low)
+ : [high] "R" (high),
+ [shift] "ci" (shift)
+ : "cc");
+#else
+ low >>= shift;
+ low |= (high << 1) << (63U - shift);
+#endif
+ return low;
+}
+
+static inline unsigned bfloat_mantissa(const struct bkey *k,
+ struct bkey_float *f)
+{
+ const uint64_t *p = &k->low - (f->exponent >> 6);
+ return shrd128(p[-1], p[0], f->exponent & 63) & BKEY_MANTISSA_MASK;
+}
+
+static void make_bfloat(struct bset_tree *t, unsigned j)
+{
+ struct bkey_float *f = &t->tree[j];
+ struct bkey *m = tree_to_bkey(t, j);
+ struct bkey *p = tree_to_prev_bkey(t, j);
+
+ struct bkey *l = is_power_of_2(j)
+ ? t->data->start
+ : tree_to_prev_bkey(t, j >> ffs(j));
+
+ struct bkey *r = is_power_of_2(j + 1)
+ ? node(t->data, t->data->keys - bkey_u64s(&t->end))
+ : tree_to_bkey(t, j >> (ffz(j) + 1));
+
+ BUG_ON(m < l || m > r);
+ BUG_ON(bkey_next(p) != m);
+
+ if (KEY_INODE(l) != KEY_INODE(r))
+ f->exponent = fls64(KEY_INODE(r) ^ KEY_INODE(l)) + 64;
+ else
+ f->exponent = fls64(r->low ^ l->low);
+
+ f->exponent = max_t(int, f->exponent - BKEY_MANTISSA_BITS, 0);
+
+ /*
+ * Setting f->exponent = 127 flags this node as failed, and causes the
+ * lookup code to fall back to comparing against the original key.
+ */
+
+ if (bfloat_mantissa(m, f) != bfloat_mantissa(p, f))
+ f->mantissa = bfloat_mantissa(m, f) - 1;
+ else
+ f->exponent = 127;
+}
+
+static void bset_alloc_tree(struct btree *b, struct bset_tree *t)
+{
+ if (t != b->sets) {
+ unsigned j = roundup(t[-1].size,
+ 64 / sizeof(struct bkey_float));
+
+ t->tree = t[-1].tree + j;
+ t->prev = t[-1].prev + j;
+ }
+
+ while (t < b->sets + MAX_BSETS)
+ t++->size = 0;
+}
+
+static void bset_build_unwritten_tree(struct btree *b)
+{
+ struct bset_tree *t = b->sets + b->nsets;
+
+ bset_alloc_tree(b, t);
+
+ if (t->tree != b->sets->tree + bset_tree_space(b)) {
+ t->prev[0] = bkey_to_cacheline_offset(t->data->start);
+ t->size = 1;
+ }
+}
+
+static void bset_build_written_tree(struct btree *b)
+{
+ struct bset_tree *t = b->sets + b->nsets;
+ struct bkey *k = t->data->start;
+ unsigned j, cacheline = 1;
+
+ bset_alloc_tree(b, t);
+
+ t->size = min_t(unsigned,
+ bkey_to_cacheline(t, end(t->data)),
+ b->sets->tree + bset_tree_space(b) - t->tree);
+
+ if (t->size < 2) {
+ t->size = 0;
+ return;
+ }
+
+ t->extra = (t->size - rounddown_pow_of_two(t->size - 1)) << 1;
+
+ /* First we figure out where the first key in each cacheline is */
+ for (j = inorder_next(0, t->size);
+ j;
+ j = inorder_next(j, t->size)) {
+ while (bkey_to_cacheline(t, k) != cacheline)
+ k = bkey_next(k);
+
+ t->prev[j] = bkey_u64s(k);
+ k = bkey_next(k);
+ cacheline++;
+ t->tree[j].m = bkey_to_cacheline_offset(k);
+ }
+
+ while (bkey_next(k) != end(t->data))
+ k = bkey_next(k);
+
+ t->end = *k;
+
+ /* Then we build the tree */
+ for (j = inorder_next(0, t->size);
+ j;
+ j = inorder_next(j, t->size))
+ make_bfloat(t, j);
+}
+
+void bch_bset_fix_invalidated_key(struct btree *b, struct bkey *k)
+{
+ struct bset_tree *t;
+ unsigned inorder, j = 1;
+
+ for (t = b->sets; t <= &b->sets[b->nsets]; t++)
+ if (k < end(t->data))
+ goto found_set;
+
+ BUG();
+found_set:
+ if (!t->size || !bset_written(b, t))
+ return;
+
+ inorder = bkey_to_cacheline(t, k);
+
+ if (k == t->data->start)
+ goto fix_left;
+
+ if (bkey_next(k) == end(t->data)) {
+ t->end = *k;
+ goto fix_right;
+ }
+
+ j = inorder_to_tree(inorder, t);
+
+ if (j &&
+ j < t->size &&
+ k == tree_to_bkey(t, j))
+fix_left: do {
+ make_bfloat(t, j);
+ j = j * 2;
+ } while (j < t->size);
+
+ j = inorder_to_tree(inorder + 1, t);
+
+ if (j &&
+ j < t->size &&
+ k == tree_to_prev_bkey(t, j))
+fix_right: do {
+ make_bfloat(t, j);
+ j = j * 2 + 1;
+ } while (j < t->size);
+}
+
+void bch_bset_fix_lookup_table(struct btree *b, struct bkey *k)
+{
+ struct bset_tree *t = &b->sets[b->nsets];
+ unsigned shift = bkey_u64s(k);
+ unsigned j = bkey_to_cacheline(t, k);
+
+ /* We're getting called from btree_split() or btree_gc, just bail out */
+ if (!t->size)
+ return;
+
+ /* k is the key we just inserted; we need to find the entry in the
+ * lookup table for the first key that is strictly greater than k:
+ * it's either k's cacheline or the next one
+ */
+ if (j < t->size &&
+ table_to_bkey(t, j) <= k)
+ j++;
+
+ /* Adjust all the lookup table entries, and find a new key for any that
+ * have gotten too big
+ */
+ for (; j < t->size; j++) {
+ t->prev[j] += shift;
+
+ if (t->prev[j] > 7) {
+ k = table_to_bkey(t, j - 1);
+
+ while (k < cacheline_to_bkey(t, j, 0))
+ k = bkey_next(k);
+
+ t->prev[j] = bkey_to_cacheline_offset(k);
+ }
+ }
+
+ if (t->size == b->sets->tree + bset_tree_space(b) - t->tree)
+ return;
+
+ /* Possibly add a new entry to the end of the lookup table */
+
+ for (k = table_to_bkey(t, t->size - 1);
+ k != end(t->data);
+ k = bkey_next(k))
+ if (t->size == bkey_to_cacheline(t, k)) {
+ t->prev[t->size] = bkey_to_cacheline_offset(k);
+ t->size++;
+ }
+}
+
+void bch_bset_init_next(struct btree *b)
+{
+ struct bset *i = write_block(b);
+
+ if (i != b->sets[0].data) {
+ b->sets[++b->nsets].data = i;
+ i->seq = b->sets[0].data->seq;
+ } else
+ get_random_bytes(&i->seq, sizeof(uint64_t));
+
+ i->magic = bset_magic(b->c);
+ i->version = 0;
+ i->keys = 0;
+
+ bset_build_unwritten_tree(b);
+}
+
+struct bset_search_iter {
+ struct bkey *l, *r;
+};
+
+static struct bset_search_iter bset_search_write_set(struct btree *b,
+ struct bset_tree *t,
+ const struct bkey *search)
+{
+ unsigned li = 0, ri = t->size;
+
+ BUG_ON(!b->nsets &&
+ t->size < bkey_to_cacheline(t, end(t->data)));
+
+ while (li + 1 != ri) {
+ unsigned m = (li + ri) >> 1;
+
+ if (bkey_cmp(table_to_bkey(t, m), search) > 0)
+ ri = m;
+ else
+ li = m;
+ }
+
+ return (struct bset_search_iter) {
+ table_to_bkey(t, li),
+ ri < t->size ? table_to_bkey(t, ri) : end(t->data)
+ };
+}
+
+static struct bset_search_iter bset_search_tree(struct btree *b,
+ struct bset_tree *t,
+ const struct bkey *search)
+{
+ struct bkey *l, *r;
+ struct bkey_float *f;
+ unsigned inorder, j, n = 1;
+
+ do {
+ unsigned p = n << 4;
+ p &= ((int) (p - t->size)) >> 31;
+
+ prefetch(&t->tree[p]);
+
+ j = n;
+ f = &t->tree[j];
+
+ /*
+ * n = (f->mantissa > bfloat_mantissa())
+ * ? j * 2
+ * : j * 2 + 1;
+ *
+ * We need to subtract 1 from f->mantissa for the sign bit trick
+ * to work - that's done in make_bfloat()
+ */
+ if (likely(f->exponent != 127))
+ n = j * 2 + (((unsigned)
+ (f->mantissa -
+ bfloat_mantissa(search, f))) >> 31);
+ else
+ n = (bkey_cmp(tree_to_bkey(t, j), search) > 0)
+ ? j * 2
+ : j * 2 + 1;
+ } while (n < t->size);
+
+ inorder = to_inorder(j, t);
+
+ /*
+ * n would have been the node we recursed to - the low bit tells us if
+ * we recursed left or recursed right.
+ */
+ if (n & 1) {
+ l = cacheline_to_bkey(t, inorder, f->m);
+
+ if (++inorder != t->size) {
+ f = &t->tree[inorder_next(j, t->size)];
+ r = cacheline_to_bkey(t, inorder, f->m);
+ } else
+ r = end(t->data);
+ } else {
+ r = cacheline_to_bkey(t, inorder, f->m);
+
+ if (--inorder) {
+ f = &t->tree[inorder_prev(j, t->size)];
+ l = cacheline_to_bkey(t, inorder, f->m);
+ } else
+ l = t->data->start;
+ }
+
+ return (struct bset_search_iter) {l, r};
+}
+
+struct bkey *__bch_bset_search(struct btree *b, struct bset_tree *t,
+ const struct bkey *search)
+{
+ struct bset_search_iter i;
+
+ /*
+ * First, we search for a cacheline, then lastly we do a linear search
+ * within that cacheline.
+ *
+ * To search for the cacheline, there's three different possibilities:
+ * * The set is too small to have a search tree, so we just do a linear
+ * search over the whole set.
+ * * The set is the one we're currently inserting into; keeping a full
+ * auxiliary search tree up to date would be too expensive, so we
+ * use a much simpler lookup table to do a binary search -
+ * bset_search_write_set().
+ * * Or we use the auxiliary search tree we constructed earlier -
+ * bset_search_tree()
+ */
+
+ if (unlikely(!t->size)) {
+ i.l = t->data->start;
+ i.r = end(t->data);
+ } else if (bset_written(b, t)) {
+ /*
+ * Each node in the auxiliary search tree covers a certain range
+ * of bits, and keys above and below the set it covers might
+ * differ outside those bits - so we have to special case the
+ * start and end - handle that here:
+ */
+
+ if (unlikely(bkey_cmp(search, &t->end) >= 0))
+ return end(t->data);
+
+ if (unlikely(bkey_cmp(search, t->data->start) < 0))
+ return t->data->start;
+
+ i = bset_search_tree(b, t, search);
+ } else
+ i = bset_search_write_set(b, t, search);
+
+#ifdef CONFIG_BCACHE_EDEBUG
+ BUG_ON(bset_written(b, t) &&
+ i.l != t->data->start &&
+ bkey_cmp(tree_to_prev_bkey(t,
+ inorder_to_tree(bkey_to_cacheline(t, i.l), t)),
+ search) > 0);
+
+ BUG_ON(i.r != end(t->data) &&
+ bkey_cmp(i.r, search) <= 0);
+#endif
+
+ while (likely(i.l != i.r) &&
+ bkey_cmp(i.l, search) <= 0)
+ i.l = bkey_next(i.l);
+
+ return i.l;
+}
+
+/* Btree iterator */
+
+static inline bool btree_iter_cmp(struct btree_iter_set l,
+ struct btree_iter_set r)
+{
+ int64_t c = bkey_cmp(&START_KEY(l.k), &START_KEY(r.k));
+
+ return c ? c > 0 : l.k < r.k;
+}
+
+static inline bool btree_iter_end(struct btree_iter *iter)
+{
+ return !iter->used;
+}
+
+void bch_btree_iter_push(struct btree_iter *iter, struct bkey *k,
+ struct bkey *end)
+{
+ if (k != end)
+ BUG_ON(!heap_add(iter,
+ ((struct btree_iter_set) { k, end }),
+ btree_iter_cmp));
+}
+
+struct bkey *__bch_btree_iter_init(struct btree *b, struct btree_iter *iter,
+ struct bkey *search, struct bset_tree *start)
+{
+ struct bkey *ret = NULL;
+ iter->size = ARRAY_SIZE(iter->data);
+ iter->used = 0;
+
+ for (; start <= &b->sets[b->nsets]; start++) {
+ ret = bch_bset_search(b, start, search);
+ bch_btree_iter_push(iter, ret, end(start->data));
+ }
+
+ return ret;
+}
+
+struct bkey *bch_btree_iter_next(struct btree_iter *iter)
+{
+ struct btree_iter_set unused;
+ struct bkey *ret = NULL;
+
+ if (!btree_iter_end(iter)) {
+ ret = iter->data->k;
+ iter->data->k = bkey_next(iter->data->k);
+
+ if (iter->data->k > iter->data->end) {
+ WARN_ONCE(1, "bset was corrupt!\n");
+ iter->data->k = iter->data->end;
+ }
+
+ if (iter->data->k == iter->data->end)
+ heap_pop(iter, unused, btree_iter_cmp);
+ else
+ heap_sift(iter, 0, btree_iter_cmp);
+ }
+
+ return ret;
+}
+
+struct bkey *bch_btree_iter_next_filter(struct btree_iter *iter,
+ struct btree *b, ptr_filter_fn fn)
+{
+ struct bkey *ret;
+
+ do {
+ ret = bch_btree_iter_next(iter);
+ } while (ret && fn(b, ret));
+
+ return ret;
+}
+
+struct bkey *bch_next_recurse_key(struct btree *b, struct bkey *search)
+{
+ struct btree_iter iter;
+
+ bch_btree_iter_init(b, &iter, search);
+ return bch_btree_iter_next_filter(&iter, b, bch_ptr_bad);
+}
+
+/* Mergesort */
+
+static void btree_sort_fixup(struct btree_iter *iter)
+{
+ while (iter->used > 1) {
+ struct btree_iter_set *top = iter->data, *i = top + 1;
+ struct bkey *k;
+
+ if (iter->used > 2 &&
+ btree_iter_cmp(i[0], i[1]))
+ i++;
+
+ for (k = i->k;
+ k != i->end && bkey_cmp(top->k, &START_KEY(k)) > 0;
+ k = bkey_next(k))
+ if (top->k > i->k)
+ __bch_cut_front(top->k, k);
+ else if (KEY_SIZE(k))
+ bch_cut_back(&START_KEY(k), top->k);
+
+ if (top->k < i->k || k == i->k)
+ break;
+
+ heap_sift(iter, i - top, btree_iter_cmp);
+ }
+}
+
+static void btree_mergesort(struct btree *b, struct bset *out,
+ struct btree_iter *iter,
+ bool fixup, bool remove_stale)
+{
+ struct bkey *k, *last = NULL;
+ bool (*bad)(struct btree *, const struct bkey *) = remove_stale
+ ? bch_ptr_bad
+ : bch_ptr_invalid;
+
+ while (!btree_iter_end(iter)) {
+ if (fixup && !b->level)
+ btree_sort_fixup(iter);
+
+ k = bch_btree_iter_next(iter);
+ if (bad(b, k))
+ continue;
+
+ if (!last) {
+ last = out->start;
+ bkey_copy(last, k);
+ } else if (b->level ||
+ !bch_bkey_try_merge(b, last, k)) {
+ last = bkey_next(last);
+ bkey_copy(last, k);
+ }
+ }
+
+ out->keys = last ? (uint64_t *) bkey_next(last) - out->d : 0;
+
+ pr_debug("sorted %i keys", out->keys);
+ bch_check_key_order(b, out);
+}
+
+static void __btree_sort(struct btree *b, struct btree_iter *iter,
+ unsigned start, unsigned order, bool fixup)
+{
+ uint64_t start_time;
+ bool remove_stale = !b->written;
+ struct bset *out = (void *) __get_free_pages(__GFP_NOWARN|GFP_NOIO,
+ order);
+ if (!out) {
+ mutex_lock(&b->c->sort_lock);
+ out = b->c->sort;
+ order = ilog2(bucket_pages(b->c));
+ }
+
+ start_time = local_clock();
+
+ btree_mergesort(b, out, iter, fixup, remove_stale);
+ b->nsets = start;
+
+ if (!fixup && !start && b->written)
+ bch_btree_verify(b, out);
+
+ if (!start && order == b->page_order) {
+ /*
+ * Our temporary buffer is the same size as the btree node's
+ * buffer, we can just swap buffers instead of doing a big
+ * memcpy()
+ */
+
+ out->magic = bset_magic(b->c);
+ out->seq = b->sets[0].data->seq;
+ out->version = b->sets[0].data->version;
+ swap(out, b->sets[0].data);
+
+ if (b->c->sort == b->sets[0].data)
+ b->c->sort = out;
+ } else {
+ b->sets[start].data->keys = out->keys;
+ memcpy(b->sets[start].data->start, out->start,
+ (void *) end(out) - (void *) out->start);
+ }
+
+ if (out == b->c->sort)
+ mutex_unlock(&b->c->sort_lock);
+ else
+ free_pages((unsigned long) out, order);
+
+ if (b->written)
+ bset_build_written_tree(b);
+
+ if (!start) {
+ spin_lock(&b->c->sort_time_lock);
+ bch_time_stats_update(&b->c->sort_time, start_time);
+ spin_unlock(&b->c->sort_time_lock);
+ }
+}
+
+void bch_btree_sort_partial(struct btree *b, unsigned start)
+{
+ size_t oldsize = 0, order = b->page_order, keys = 0;
+ struct btree_iter iter;
+ __bch_btree_iter_init(b, &iter, NULL, &b->sets[start]);
+
+ BUG_ON(b->sets[b->nsets].data == write_block(b) &&
+ (b->sets[b->nsets].size || b->nsets));
+
+ if (b->written)
+ oldsize = bch_count_data(b);
+
+ if (start) {
+ unsigned i;
+
+ for (i = start; i <= b->nsets; i++)
+ keys += b->sets[i].data->keys;
+
+ order = roundup_pow_of_two(__set_bytes(b->sets->data,
+ keys)) / PAGE_SIZE;
+ if (order)
+ order = ilog2(order);
+ }
+
+ __btree_sort(b, &iter, start, order, false);
+
+ EBUG_ON(b->written && bch_count_data(b) != oldsize);
+}
+
+void bch_btree_sort_and_fix_extents(struct btree *b, struct btree_iter *iter)
+{
+ BUG_ON(!b->written);
+ __btree_sort(b, iter, 0, b->page_order, true);
+}
+
+void bch_btree_sort_into(struct btree *b, struct btree *new)
+{
+ uint64_t start_time = local_clock();
+
+ struct btree_iter iter;
+ bch_btree_iter_init(b, &iter, NULL);
+
+ btree_mergesort(b, new->sets->data, &iter, false, true);
+
+ spin_lock(&b->c->sort_time_lock);
+ bch_time_stats_update(&b->c->sort_time, start_time);
+ spin_unlock(&b->c->sort_time_lock);
+
+ bkey_copy_key(&new->key, &b->key);
+ new->sets->size = 0;
+}
+
+void bch_btree_sort_lazy(struct btree *b)
+{
+ if (b->nsets) {
+ unsigned i, j, keys = 0, total;
+
+ for (i = 0; i <= b->nsets; i++)
+ keys += b->sets[i].data->keys;
+
+ total = keys;
+
+ for (j = 0; j < b->nsets; j++) {
+ if (keys * 2 < total ||
+ keys < 1000) {
+ bch_btree_sort_partial(b, j);
+ return;
+ }
+
+ keys -= b->sets[j].data->keys;
+ }
+
+ /* Must sort if b->nsets == 3 or we'll overflow */
+ if (b->nsets >= (MAX_BSETS - 1) - b->level) {
+ bch_btree_sort(b);
+ return;
+ }
+ }
+
+ bset_build_written_tree(b);
+}
+
+/* Sysfs stuff */
+
+struct bset_stats {
+ size_t nodes;
+ size_t sets_written, sets_unwritten;
+ size_t bytes_written, bytes_unwritten;
+ size_t floats, failed;
+};
+
+static int bch_btree_bset_stats(struct btree *b, struct btree_op *op,
+ struct bset_stats *stats)
+{
+ struct bkey *k;
+ unsigned i;
+
+ stats->nodes++;
+
+ for (i = 0; i <= b->nsets; i++) {
+ struct bset_tree *t = &b->sets[i];
+ size_t bytes = t->data->keys * sizeof(uint64_t);
+ size_t j;
+
+ if (bset_written(b, t)) {
+ stats->sets_written++;
+ stats->bytes_written += bytes;
+
+ stats->floats += t->size - 1;
+
+ for (j = 1; j < t->size; j++)
+ if (t->tree[j].exponent == 127)
+ stats->failed++;
+ } else {
+ stats->sets_unwritten++;
+ stats->bytes_unwritten += bytes;
+ }
+ }
+
+ if (b->level) {
+ struct btree_iter iter;
+
+ for_each_key_filter(b, k, &iter, bch_ptr_bad) {
+ int ret = btree(bset_stats, k, b, op, stats);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int bch_bset_print_stats(struct cache_set *c, char *buf)
+{
+ struct btree_op op;
+ struct bset_stats t;
+ int ret;
+
+ bch_btree_op_init_stack(&op);
+ memset(&t, 0, sizeof(struct bset_stats));
+
+ ret = btree_root(bset_stats, c, &op, &t);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE,
+ "btree nodes: %zu\n"
+ "written sets: %zu\n"
+ "unwritten sets: %zu\n"
+ "written key bytes: %zu\n"
+ "unwritten key bytes: %zu\n"
+ "floats: %zu\n"
+ "failed: %zu\n",
+ t.nodes,
+ t.sets_written, t.sets_unwritten,
+ t.bytes_written, t.bytes_unwritten,
+ t.floats, t.failed);
+}
diff --git a/drivers/md/bcache/bset.h b/drivers/md/bcache/bset.h
new file mode 100644
index 000000000000..57a9cff41546
--- /dev/null
+++ b/drivers/md/bcache/bset.h
@@ -0,0 +1,379 @@
+#ifndef _BCACHE_BSET_H
+#define _BCACHE_BSET_H
+
+/*
+ * BKEYS:
+ *
+ * A bkey contains a key, a size field, a variable number of pointers, and some
+ * ancillary flag bits.
+ *
+ * We use two different functions for validating bkeys, bch_ptr_invalid and
+ * bch_ptr_bad().
+ *
+ * bch_ptr_invalid() primarily filters out keys and pointers that would be
+ * invalid due to some sort of bug, whereas bch_ptr_bad() filters out keys and
+ * pointer that occur in normal practice but don't point to real data.
+ *
+ * The one exception to the rule that ptr_invalid() filters out invalid keys is
+ * that it also filters out keys of size 0 - these are keys that have been
+ * completely overwritten. It'd be safe to delete these in memory while leaving
+ * them on disk, just unnecessary work - so we filter them out when resorting
+ * instead.
+ *
+ * We can't filter out stale keys when we're resorting, because garbage
+ * collection needs to find them to ensure bucket gens don't wrap around -
+ * unless we're rewriting the btree node those stale keys still exist on disk.
+ *
+ * We also implement functions here for removing some number of sectors from the
+ * front or the back of a bkey - this is mainly used for fixing overlapping
+ * extents, by removing the overlapping sectors from the older key.
+ *
+ * BSETS:
+ *
+ * A bset is an array of bkeys laid out contiguously in memory in sorted order,
+ * along with a header. A btree node is made up of a number of these, written at
+ * different times.
+ *
+ * There could be many of them on disk, but we never allow there to be more than
+ * 4 in memory - we lazily resort as needed.
+ *
+ * We implement code here for creating and maintaining auxiliary search trees
+ * (described below) for searching an individial bset, and on top of that we
+ * implement a btree iterator.
+ *
+ * BTREE ITERATOR:
+ *
+ * Most of the code in bcache doesn't care about an individual bset - it needs
+ * to search entire btree nodes and iterate over them in sorted order.
+ *
+ * The btree iterator code serves both functions; it iterates through the keys
+ * in a btree node in sorted order, starting from either keys after a specific
+ * point (if you pass it a search key) or the start of the btree node.
+ *
+ * AUXILIARY SEARCH TREES:
+ *
+ * Since keys are variable length, we can't use a binary search on a bset - we
+ * wouldn't be able to find the start of the next key. But binary searches are
+ * slow anyways, due to terrible cache behaviour; bcache originally used binary
+ * searches and that code topped out at under 50k lookups/second.
+ *
+ * So we need to construct some sort of lookup table. Since we only insert keys
+ * into the last (unwritten) set, most of the keys within a given btree node are
+ * usually in sets that are mostly constant. We use two different types of
+ * lookup tables to take advantage of this.
+ *
+ * Both lookup tables share in common that they don't index every key in the
+ * set; they index one key every BSET_CACHELINE bytes, and then a linear search
+ * is used for the rest.
+ *
+ * For sets that have been written to disk and are no longer being inserted
+ * into, we construct a binary search tree in an array - traversing a binary
+ * search tree in an array gives excellent locality of reference and is very
+ * fast, since both children of any node are adjacent to each other in memory
+ * (and their grandchildren, and great grandchildren...) - this means
+ * prefetching can be used to great effect.
+ *
+ * It's quite useful performance wise to keep these nodes small - not just
+ * because they're more likely to be in L2, but also because we can prefetch
+ * more nodes on a single cacheline and thus prefetch more iterations in advance
+ * when traversing this tree.
+ *
+ * Nodes in the auxiliary search tree must contain both a key to compare against
+ * (we don't want to fetch the key from the set, that would defeat the purpose),
+ * and a pointer to the key. We use a few tricks to compress both of these.
+ *
+ * To compress the pointer, we take advantage of the fact that one node in the
+ * search tree corresponds to precisely BSET_CACHELINE bytes in the set. We have
+ * a function (to_inorder()) that takes the index of a node in a binary tree and
+ * returns what its index would be in an inorder traversal, so we only have to
+ * store the low bits of the offset.
+ *
+ * The key is 84 bits (KEY_DEV + key->key, the offset on the device). To
+ * compress that, we take advantage of the fact that when we're traversing the
+ * search tree at every iteration we know that both our search key and the key
+ * we're looking for lie within some range - bounded by our previous
+ * comparisons. (We special case the start of a search so that this is true even
+ * at the root of the tree).
+ *
+ * So we know the key we're looking for is between a and b, and a and b don't
+ * differ higher than bit 50, we don't need to check anything higher than bit
+ * 50.
+ *
+ * We don't usually need the rest of the bits, either; we only need enough bits
+ * to partition the key range we're currently checking. Consider key n - the
+ * key our auxiliary search tree node corresponds to, and key p, the key
+ * immediately preceding n. The lowest bit we need to store in the auxiliary
+ * search tree is the highest bit that differs between n and p.
+ *
+ * Note that this could be bit 0 - we might sometimes need all 80 bits to do the
+ * comparison. But we'd really like our nodes in the auxiliary search tree to be
+ * of fixed size.
+ *
+ * The solution is to make them fixed size, and when we're constructing a node
+ * check if p and n differed in the bits we needed them to. If they don't we
+ * flag that node, and when doing lookups we fallback to comparing against the
+ * real key. As long as this doesn't happen to often (and it seems to reliably
+ * happen a bit less than 1% of the time), we win - even on failures, that key
+ * is then more likely to be in cache than if we were doing binary searches all
+ * the way, since we're touching so much less memory.
+ *
+ * The keys in the auxiliary search tree are stored in (software) floating
+ * point, with an exponent and a mantissa. The exponent needs to be big enough
+ * to address all the bits in the original key, but the number of bits in the
+ * mantissa is somewhat arbitrary; more bits just gets us fewer failures.
+ *
+ * We need 7 bits for the exponent and 3 bits for the key's offset (since keys
+ * are 8 byte aligned); using 22 bits for the mantissa means a node is 4 bytes.
+ * We need one node per 128 bytes in the btree node, which means the auxiliary
+ * search trees take up 3% as much memory as the btree itself.
+ *
+ * Constructing these auxiliary search trees is moderately expensive, and we
+ * don't want to be constantly rebuilding the search tree for the last set
+ * whenever we insert another key into it. For the unwritten set, we use a much
+ * simpler lookup table - it's just a flat array, so index i in the lookup table
+ * corresponds to the i range of BSET_CACHELINE bytes in the set. Indexing
+ * within each byte range works the same as with the auxiliary search trees.
+ *
+ * These are much easier to keep up to date when we insert a key - we do it
+ * somewhat lazily; when we shift a key up we usually just increment the pointer
+ * to it, only when it would overflow do we go to the trouble of finding the
+ * first key in that range of bytes again.
+ */
+
+/* Btree key comparison/iteration */
+
+struct btree_iter {
+ size_t size, used;
+ struct btree_iter_set {
+ struct bkey *k, *end;
+ } data[MAX_BSETS];
+};
+
+struct bset_tree {
+ /*
+ * We construct a binary tree in an array as if the array
+ * started at 1, so that things line up on the same cachelines
+ * better: see comments in bset.c at cacheline_to_bkey() for
+ * details
+ */
+
+ /* size of the binary tree and prev array */
+ unsigned size;
+
+ /* function of size - precalculated for to_inorder() */
+ unsigned extra;
+
+ /* copy of the last key in the set */
+ struct bkey end;
+ struct bkey_float *tree;
+
+ /*
+ * The nodes in the bset tree point to specific keys - this
+ * array holds the sizes of the previous key.
+ *
+ * Conceptually it's a member of struct bkey_float, but we want
+ * to keep bkey_float to 4 bytes and prev isn't used in the fast
+ * path.
+ */
+ uint8_t *prev;
+
+ /* The actual btree node, with pointers to each sorted set */
+ struct bset *data;
+};
+
+static __always_inline int64_t bkey_cmp(const struct bkey *l,
+ const struct bkey *r)
+{
+ return unlikely(KEY_INODE(l) != KEY_INODE(r))
+ ? (int64_t) KEY_INODE(l) - (int64_t) KEY_INODE(r)
+ : (int64_t) KEY_OFFSET(l) - (int64_t) KEY_OFFSET(r);
+}
+
+static inline size_t bkey_u64s(const struct bkey *k)
+{
+ BUG_ON(KEY_CSUM(k) > 1);
+ return 2 + KEY_PTRS(k) + (KEY_CSUM(k) ? 1 : 0);
+}
+
+static inline size_t bkey_bytes(const struct bkey *k)
+{
+ return bkey_u64s(k) * sizeof(uint64_t);
+}
+
+static inline void bkey_copy(struct bkey *dest, const struct bkey *src)
+{
+ memcpy(dest, src, bkey_bytes(src));
+}
+
+static inline void bkey_copy_key(struct bkey *dest, const struct bkey *src)
+{
+ if (!src)
+ src = &KEY(0, 0, 0);
+
+ SET_KEY_INODE(dest, KEY_INODE(src));
+ SET_KEY_OFFSET(dest, KEY_OFFSET(src));
+}
+
+static inline struct bkey *bkey_next(const struct bkey *k)
+{
+ uint64_t *d = (void *) k;
+ return (struct bkey *) (d + bkey_u64s(k));
+}
+
+/* Keylists */
+
+struct keylist {
+ struct bkey *top;
+ union {
+ uint64_t *list;
+ struct bkey *bottom;
+ };
+
+ /* Enough room for btree_split's keys without realloc */
+#define KEYLIST_INLINE 16
+ uint64_t d[KEYLIST_INLINE];
+};
+
+static inline void bch_keylist_init(struct keylist *l)
+{
+ l->top = (void *) (l->list = l->d);
+}
+
+static inline void bch_keylist_push(struct keylist *l)
+{
+ l->top = bkey_next(l->top);
+}
+
+static inline void bch_keylist_add(struct keylist *l, struct bkey *k)
+{
+ bkey_copy(l->top, k);
+ bch_keylist_push(l);
+}
+
+static inline bool bch_keylist_empty(struct keylist *l)
+{
+ return l->top == (void *) l->list;
+}
+
+static inline void bch_keylist_free(struct keylist *l)
+{
+ if (l->list != l->d)
+ kfree(l->list);
+}
+
+void bch_keylist_copy(struct keylist *, struct keylist *);
+struct bkey *bch_keylist_pop(struct keylist *);
+int bch_keylist_realloc(struct keylist *, int, struct cache_set *);
+
+void bch_bkey_copy_single_ptr(struct bkey *, const struct bkey *,
+ unsigned);
+bool __bch_cut_front(const struct bkey *, struct bkey *);
+bool __bch_cut_back(const struct bkey *, struct bkey *);
+
+static inline bool bch_cut_front(const struct bkey *where, struct bkey *k)
+{
+ BUG_ON(bkey_cmp(where, k) > 0);
+ return __bch_cut_front(where, k);
+}
+
+static inline bool bch_cut_back(const struct bkey *where, struct bkey *k)
+{
+ BUG_ON(bkey_cmp(where, &START_KEY(k)) < 0);
+ return __bch_cut_back(where, k);
+}
+
+const char *bch_ptr_status(struct cache_set *, const struct bkey *);
+bool __bch_ptr_invalid(struct cache_set *, int level, const struct bkey *);
+bool bch_ptr_bad(struct btree *, const struct bkey *);
+
+static inline uint8_t gen_after(uint8_t a, uint8_t b)
+{
+ uint8_t r = a - b;
+ return r > 128U ? 0 : r;
+}
+
+static inline uint8_t ptr_stale(struct cache_set *c, const struct bkey *k,
+ unsigned i)
+{
+ return gen_after(PTR_BUCKET(c, k, i)->gen, PTR_GEN(k, i));
+}
+
+static inline bool ptr_available(struct cache_set *c, const struct bkey *k,
+ unsigned i)
+{
+ return (PTR_DEV(k, i) < MAX_CACHES_PER_SET) && PTR_CACHE(c, k, i);
+}
+
+
+typedef bool (*ptr_filter_fn)(struct btree *, const struct bkey *);
+
+struct bkey *bch_next_recurse_key(struct btree *, struct bkey *);
+struct bkey *bch_btree_iter_next(struct btree_iter *);
+struct bkey *bch_btree_iter_next_filter(struct btree_iter *,
+ struct btree *, ptr_filter_fn);
+
+void bch_btree_iter_push(struct btree_iter *, struct bkey *, struct bkey *);
+struct bkey *__bch_btree_iter_init(struct btree *, struct btree_iter *,
+ struct bkey *, struct bset_tree *);
+
+/* 32 bits total: */
+#define BKEY_MID_BITS 3
+#define BKEY_EXPONENT_BITS 7
+#define BKEY_MANTISSA_BITS 22
+#define BKEY_MANTISSA_MASK ((1 << BKEY_MANTISSA_BITS) - 1)
+
+struct bkey_float {
+ unsigned exponent:BKEY_EXPONENT_BITS;
+ unsigned m:BKEY_MID_BITS;
+ unsigned mantissa:BKEY_MANTISSA_BITS;
+} __packed;
+
+/*
+ * BSET_CACHELINE was originally intended to match the hardware cacheline size -
+ * it used to be 64, but I realized the lookup code would touch slightly less
+ * memory if it was 128.
+ *
+ * It definites the number of bytes (in struct bset) per struct bkey_float in
+ * the auxiliar search tree - when we're done searching the bset_float tree we
+ * have this many bytes left that we do a linear search over.
+ *
+ * Since (after level 5) every level of the bset_tree is on a new cacheline,
+ * we're touching one fewer cacheline in the bset tree in exchange for one more
+ * cacheline in the linear search - but the linear search might stop before it
+ * gets to the second cacheline.
+ */
+
+#define BSET_CACHELINE 128
+#define bset_tree_space(b) (btree_data_space(b) / BSET_CACHELINE)
+
+#define bset_tree_bytes(b) (bset_tree_space(b) * sizeof(struct bkey_float))
+#define bset_prev_bytes(b) (bset_tree_space(b) * sizeof(uint8_t))
+
+void bch_bset_init_next(struct btree *);
+
+void bch_bset_fix_invalidated_key(struct btree *, struct bkey *);
+void bch_bset_fix_lookup_table(struct btree *, struct bkey *);
+
+struct bkey *__bch_bset_search(struct btree *, struct bset_tree *,
+ const struct bkey *);
+
+static inline struct bkey *bch_bset_search(struct btree *b, struct bset_tree *t,
+ const struct bkey *search)
+{
+ return search ? __bch_bset_search(b, t, search) : t->data->start;
+}
+
+bool bch_bkey_try_merge(struct btree *, struct bkey *, struct bkey *);
+void bch_btree_sort_lazy(struct btree *);
+void bch_btree_sort_into(struct btree *, struct btree *);
+void bch_btree_sort_and_fix_extents(struct btree *, struct btree_iter *);
+void bch_btree_sort_partial(struct btree *, unsigned);
+
+static inline void bch_btree_sort(struct btree *b)
+{
+ bch_btree_sort_partial(b, 0);
+}
+
+int bch_bset_print_stats(struct cache_set *, char *);
+
+#endif
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
new file mode 100644
index 000000000000..7a5658f04e62
--- /dev/null
+++ b/drivers/md/bcache/btree.c
@@ -0,0 +1,2503 @@
+/*
+ * Copyright (C) 2010 Kent Overstreet <kent.overstreet@gmail.com>
+ *
+ * Uses a block device as cache for other block devices; optimized for SSDs.
+ * All allocation is done in buckets, which should match the erase block size
+ * of the device.
+ *
+ * Buckets containing cached data are kept on a heap sorted by priority;
+ * bucket priority is increased on cache hit, and periodically all the buckets
+ * on the heap have their priority scaled down. This currently is just used as
+ * an LRU but in the future should allow for more intelligent heuristics.
+ *
+ * Buckets have an 8 bit counter; freeing is accomplished by incrementing the
+ * counter. Garbage collection is used to remove stale pointers.
+ *
+ * Indexing is done via a btree; nodes are not necessarily fully sorted, rather
+ * as keys are inserted we only sort the pages that have not yet been written.
+ * When garbage collection is run, we resort the entire node.
+ *
+ * All configuration is done via sysfs; see Documentation/bcache.txt.
+ */
+
+#include "bcache.h"
+#include "btree.h"
+#include "debug.h"
+#include "request.h"
+
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/hash.h>
+#include <linux/prefetch.h>
+#include <linux/random.h>
+#include <linux/rcupdate.h>
+#include <trace/events/bcache.h>
+
+/*
+ * Todo:
+ * register_bcache: Return errors out to userspace correctly
+ *
+ * Writeback: don't undirty key until after a cache flush
+ *
+ * Create an iterator for key pointers
+ *
+ * On btree write error, mark bucket such that it won't be freed from the cache
+ *
+ * Journalling:
+ * Check for bad keys in replay
+ * Propagate barriers
+ * Refcount journal entries in journal_replay
+ *
+ * Garbage collection:
+ * Finish incremental gc
+ * Gc should free old UUIDs, data for invalid UUIDs
+ *
+ * Provide a way to list backing device UUIDs we have data cached for, and
+ * probably how long it's been since we've seen them, and a way to invalidate
+ * dirty data for devices that will never be attached again
+ *
+ * Keep 1 min/5 min/15 min statistics of how busy a block device has been, so
+ * that based on that and how much dirty data we have we can keep writeback
+ * from being starved
+ *
+ * Add a tracepoint or somesuch to watch for writeback starvation
+ *
+ * When btree depth > 1 and splitting an interior node, we have to make sure
+ * alloc_bucket() cannot fail. This should be true but is not completely
+ * obvious.
+ *
+ * Make sure all allocations get charged to the root cgroup
+ *
+ * Plugging?
+ *
+ * If data write is less than hard sector size of ssd, round up offset in open
+ * bucket to the next whole sector
+ *
+ * Also lookup by cgroup in get_open_bucket()
+ *
+ * Superblock needs to be fleshed out for multiple cache devices
+ *
+ * Add a sysfs tunable for the number of writeback IOs in flight
+ *
+ * Add a sysfs tunable for the number of open data buckets
+ *
+ * IO tracking: Can we track when one process is doing io on behalf of another?
+ * IO tracking: Don't use just an average, weigh more recent stuff higher
+ *
+ * Test module load/unload
+ */
+
+static const char * const op_types[] = {
+ "insert", "replace"
+};
+
+static const char *op_type(struct btree_op *op)
+{
+ return op_types[op->type];
+}
+
+#define MAX_NEED_GC 64
+#define MAX_SAVE_PRIO 72
+
+#define PTR_DIRTY_BIT (((uint64_t) 1 << 36))
+
+#define PTR_HASH(c, k) \
+ (((k)->ptr[0] >> c->bucket_bits) | PTR_GEN(k, 0))
+
+struct workqueue_struct *bch_gc_wq;
+static struct workqueue_struct *btree_io_wq;
+
+void bch_btree_op_init_stack(struct btree_op *op)
+{
+ memset(op, 0, sizeof(struct btree_op));
+ closure_init_stack(&op->cl);
+ op->lock = -1;
+ bch_keylist_init(&op->keys);
+}
+
+/* Btree key manipulation */
+
+static void bkey_put(struct cache_set *c, struct bkey *k, int level)
+{
+ if ((level && KEY_OFFSET(k)) || !level)
+ __bkey_put(c, k);
+}
+
+/* Btree IO */
+
+static uint64_t btree_csum_set(struct btree *b, struct bset *i)
+{
+ uint64_t crc = b->key.ptr[0];
+ void *data = (void *) i + 8, *end = end(i);
+
+ crc = bch_crc64_update(crc, data, end - data);
+ return crc ^ 0xffffffffffffffffULL;
+}
+
+static void btree_bio_endio(struct bio *bio, int error)
+{
+ struct closure *cl = bio->bi_private;
+ struct btree *b = container_of(cl, struct btree, io.cl);
+
+ if (error)
+ set_btree_node_io_error(b);
+
+ bch_bbio_count_io_errors(b->c, bio, error, (bio->bi_rw & WRITE)
+ ? "writing btree" : "reading btree");
+ closure_put(cl);
+}
+
+static void btree_bio_init(struct btree *b)
+{
+ BUG_ON(b->bio);
+ b->bio = bch_bbio_alloc(b->c);
+
+ b->bio->bi_end_io = btree_bio_endio;
+ b->bio->bi_private = &b->io.cl;
+}
+
+void bch_btree_read_done(struct closure *cl)
+{
+ struct btree *b = container_of(cl, struct btree, io.cl);
+ struct bset *i = b->sets[0].data;
+ struct btree_iter *iter = b->c->fill_iter;
+ const char *err = "bad btree header";
+ BUG_ON(b->nsets || b->written);
+
+ bch_bbio_free(b->bio, b->c);
+ b->bio = NULL;
+
+ mutex_lock(&b->c->fill_lock);
+ iter->used = 0;
+
+ if (btree_node_io_error(b) ||
+ !i->seq)
+ goto err;
+
+ for (;
+ b->written < btree_blocks(b) && i->seq == b->sets[0].data->seq;
+ i = write_block(b)) {
+ err = "unsupported bset version";
+ if (i->version > BCACHE_BSET_VERSION)
+ goto err;
+
+ err = "bad btree header";
+ if (b->written + set_blocks(i, b->c) > btree_blocks(b))
+ goto err;
+
+ err = "bad magic";
+ if (i->magic != bset_magic(b->c))
+ goto err;
+
+ err = "bad checksum";
+ switch (i->version) {
+ case 0:
+ if (i->csum != csum_set(i))
+ goto err;
+ break;
+ case BCACHE_BSET_VERSION:
+ if (i->csum != btree_csum_set(b, i))
+ goto err;
+ break;
+ }
+
+ err = "empty set";
+ if (i != b->sets[0].data && !i->keys)
+ goto err;
+
+ bch_btree_iter_push(iter, i->start, end(i));
+
+ b->written += set_blocks(i, b->c);
+ }
+
+ err = "corrupted btree";
+ for (i = write_block(b);
+ index(i, b) < btree_blocks(b);
+ i = ((void *) i) + block_bytes(b->c))
+ if (i->seq == b->sets[0].data->seq)
+ goto err;
+
+ bch_btree_sort_and_fix_extents(b, iter);
+
+ i = b->sets[0].data;
+ err = "short btree key";
+ if (b->sets[0].size &&
+ bkey_cmp(&b->key, &b->sets[0].end) < 0)
+ goto err;
+
+ if (b->written < btree_blocks(b))
+ bch_bset_init_next(b);
+out:
+
+ mutex_unlock(&b->c->fill_lock);
+
+ spin_lock(&b->c->btree_read_time_lock);
+ bch_time_stats_update(&b->c->btree_read_time, b->io_start_time);
+ spin_unlock(&b->c->btree_read_time_lock);
+
+ smp_wmb(); /* read_done is our write lock */
+ set_btree_node_read_done(b);
+
+ closure_return(cl);
+err:
+ set_btree_node_io_error(b);
+ bch_cache_set_error(b->c, "%s at bucket %zu, block %zu, %u keys",
+ err, PTR_BUCKET_NR(b->c, &b->key, 0),
+ index(i, b), i->keys);
+ goto out;
+}
+
+void bch_btree_read(struct btree *b)
+{
+ BUG_ON(b->nsets || b->written);
+
+ if (!closure_trylock(&b->io.cl, &b->c->cl))
+ BUG();
+
+ b->io_start_time = local_clock();
+
+ btree_bio_init(b);
+ b->bio->bi_rw = REQ_META|READ_SYNC;
+ b->bio->bi_size = KEY_SIZE(&b->key) << 9;
+
+ bch_bio_map(b->bio, b->sets[0].data);
+
+ pr_debug("%s", pbtree(b));
+ trace_bcache_btree_read(b->bio);
+ bch_submit_bbio(b->bio, b->c, &b->key, 0);
+
+ continue_at(&b->io.cl, bch_btree_read_done, system_wq);
+}
+
+static void btree_complete_write(struct btree *b, struct btree_write *w)
+{
+ if (w->prio_blocked &&
+ !atomic_sub_return(w->prio_blocked, &b->c->prio_blocked))
+ wake_up(&b->c->alloc_wait);
+
+ if (w->journal) {
+ atomic_dec_bug(w->journal);
+ __closure_wake_up(&b->c->journal.wait);
+ }
+
+ if (w->owner)
+ closure_put(w->owner);
+
+ w->prio_blocked = 0;
+ w->journal = NULL;
+ w->owner = NULL;
+}
+
+static void __btree_write_done(struct closure *cl)
+{
+ struct btree *b = container_of(cl, struct btree, io.cl);
+ struct btree_write *w = btree_prev_write(b);
+
+ bch_bbio_free(b->bio, b->c);
+ b->bio = NULL;
+ btree_complete_write(b, w);
+
+ if (btree_node_dirty(b))
+ queue_delayed_work(btree_io_wq, &b->work,
+ msecs_to_jiffies(30000));
+
+ closure_return(cl);
+}
+
+static void btree_write_done(struct closure *cl)
+{
+ struct btree *b = container_of(cl, struct btree, io.cl);
+ struct bio_vec *bv;
+ int n;
+
+ __bio_for_each_segment(bv, b->bio, n, 0)
+ __free_page(bv->bv_page);
+
+ __btree_write_done(cl);
+}
+
+static void do_btree_write(struct btree *b)
+{
+ struct closure *cl = &b->io.cl;
+ struct bset *i = b->sets[b->nsets].data;
+ BKEY_PADDED(key) k;
+
+ i->version = BCACHE_BSET_VERSION;
+ i->csum = btree_csum_set(b, i);
+
+ btree_bio_init(b);
+ b->bio->bi_rw = REQ_META|WRITE_SYNC;
+ b->bio->bi_size = set_blocks(i, b->c) * block_bytes(b->c);
+ bch_bio_map(b->bio, i);
+
+ bkey_copy(&k.key, &b->key);
+ SET_PTR_OFFSET(&k.key, 0, PTR_OFFSET(&k.key, 0) + bset_offset(b, i));
+
+ if (!bch_bio_alloc_pages(b->bio, GFP_NOIO)) {
+ int j;
+ struct bio_vec *bv;
+ void *base = (void *) ((unsigned long) i & ~(PAGE_SIZE - 1));
+
+ bio_for_each_segment(bv, b->bio, j)
+ memcpy(page_address(bv->bv_page),
+ base + j * PAGE_SIZE, PAGE_SIZE);
+
+ trace_bcache_btree_write(b->bio);
+ bch_submit_bbio(b->bio, b->c, &k.key, 0);
+
+ continue_at(cl, btree_write_done, NULL);
+ } else {
+ b->bio->bi_vcnt = 0;
+ bch_bio_map(b->bio, i);
+
+ trace_bcache_btree_write(b->bio);
+ bch_submit_bbio(b->bio, b->c, &k.key, 0);
+
+ closure_sync(cl);
+ __btree_write_done(cl);
+ }
+}
+
+static void __btree_write(struct btree *b)
+{
+ struct bset *i = b->sets[b->nsets].data;
+
+ BUG_ON(current->bio_list);
+
+ closure_lock(&b->io, &b->c->cl);
+ cancel_delayed_work(&b->work);
+
+ clear_bit(BTREE_NODE_dirty, &b->flags);
+ change_bit(BTREE_NODE_write_idx, &b->flags);
+
+ bch_check_key_order(b, i);
+ BUG_ON(b->written && !i->keys);
+
+ do_btree_write(b);
+
+ pr_debug("%s block %i keys %i", pbtree(b), b->written, i->keys);
+
+ b->written += set_blocks(i, b->c);
+ atomic_long_add(set_blocks(i, b->c) * b->c->sb.block_size,
+ &PTR_CACHE(b->c, &b->key, 0)->btree_sectors_written);
+
+ bch_btree_sort_lazy(b);
+
+ if (b->written < btree_blocks(b))
+ bch_bset_init_next(b);
+}
+
+static void btree_write_work(struct work_struct *w)
+{
+ struct btree *b = container_of(to_delayed_work(w), struct btree, work);
+
+ down_write(&b->lock);
+
+ if (btree_node_dirty(b))
+ __btree_write(b);
+ up_write(&b->lock);
+}
+
+void bch_btree_write(struct btree *b, bool now, struct btree_op *op)
+{
+ struct bset *i = b->sets[b->nsets].data;
+ struct btree_write *w = btree_current_write(b);
+
+ BUG_ON(b->written &&
+ (b->written >= btree_blocks(b) ||
+ i->seq != b->sets[0].data->seq ||
+ !i->keys));
+
+ if (!btree_node_dirty(b)) {
+ set_btree_node_dirty(b);
+ queue_delayed_work(btree_io_wq, &b->work,
+ msecs_to_jiffies(30000));
+ }
+
+ w->prio_blocked += b->prio_blocked;
+ b->prio_blocked = 0;
+
+ if (op && op->journal && !b->level) {
+ if (w->journal &&
+ journal_pin_cmp(b->c, w, op)) {
+ atomic_dec_bug(w->journal);
+ w->journal = NULL;
+ }
+
+ if (!w->journal) {
+ w->journal = op->journal;
+ atomic_inc(w->journal);
+ }
+ }
+
+ if (current->bio_list)
+ return;
+
+ /* Force write if set is too big */
+ if (now ||
+ b->level ||
+ set_bytes(i) > PAGE_SIZE - 48) {
+ if (op && now) {
+ /* Must wait on multiple writes */
+ BUG_ON(w->owner);
+ w->owner = &op->cl;
+ closure_get(&op->cl);
+ }
+
+ __btree_write(b);
+ }
+ BUG_ON(!b->written);
+}
+
+/*
+ * Btree in memory cache - allocation/freeing
+ * mca -> memory cache
+ */
+
+static void mca_reinit(struct btree *b)
+{
+ unsigned i;
+
+ b->flags = 0;
+ b->written = 0;
+ b->nsets = 0;
+
+ for (i = 0; i < MAX_BSETS; i++)
+ b->sets[i].size = 0;
+ /*
+ * Second loop starts at 1 because b->sets[0]->data is the memory we
+ * allocated
+ */
+ for (i = 1; i < MAX_BSETS; i++)
+ b->sets[i].data = NULL;
+}
+
+#define mca_reserve(c) (((c->root && c->root->level) \
+ ? c->root->level : 1) * 8 + 16)
+#define mca_can_free(c) \
+ max_t(int, 0, c->bucket_cache_used - mca_reserve(c))
+
+static void mca_data_free(struct btree *b)
+{
+ struct bset_tree *t = b->sets;
+ BUG_ON(!closure_is_unlocked(&b->io.cl));
+
+ if (bset_prev_bytes(b) < PAGE_SIZE)
+ kfree(t->prev);
+ else
+ free_pages((unsigned long) t->prev,
+ get_order(bset_prev_bytes(b)));
+
+ if (bset_tree_bytes(b) < PAGE_SIZE)
+ kfree(t->tree);
+ else
+ free_pages((unsigned long) t->tree,
+ get_order(bset_tree_bytes(b)));
+
+ free_pages((unsigned long) t->data, b->page_order);
+
+ t->prev = NULL;
+ t->tree = NULL;
+ t->data = NULL;
+ list_move(&b->list, &b->c->btree_cache_freed);
+ b->c->bucket_cache_used--;
+}
+
+static void mca_bucket_free(struct btree *b)
+{
+ BUG_ON(btree_node_dirty(b));
+
+ b->key.ptr[0] = 0;
+ hlist_del_init_rcu(&b->hash);
+ list_move(&b->list, &b->c->btree_cache_freeable);
+}
+
+static unsigned btree_order(struct bkey *k)
+{
+ return ilog2(KEY_SIZE(k) / PAGE_SECTORS ?: 1);
+}
+
+static void mca_data_alloc(struct btree *b, struct bkey *k, gfp_t gfp)
+{
+ struct bset_tree *t = b->sets;
+ BUG_ON(t->data);
+
+ b->page_order = max_t(unsigned,
+ ilog2(b->c->btree_pages),
+ btree_order(k));
+
+ t->data = (void *) __get_free_pages(gfp, b->page_order);
+ if (!t->data)
+ goto err;
+
+ t->tree = bset_tree_bytes(b) < PAGE_SIZE
+ ? kmalloc(bset_tree_bytes(b), gfp)
+ : (void *) __get_free_pages(gfp, get_order(bset_tree_bytes(b)));
+ if (!t->tree)
+ goto err;
+
+ t->prev = bset_prev_bytes(b) < PAGE_SIZE
+ ? kmalloc(bset_prev_bytes(b), gfp)
+ : (void *) __get_free_pages(gfp, get_order(bset_prev_bytes(b)));
+ if (!t->prev)
+ goto err;
+
+ list_move(&b->list, &b->c->btree_cache);
+ b->c->bucket_cache_used++;
+ return;
+err:
+ mca_data_free(b);
+}
+
+static struct btree *mca_bucket_alloc(struct cache_set *c,
+ struct bkey *k, gfp_t gfp)
+{
+ struct btree *b = kzalloc(sizeof(struct btree), gfp);
+ if (!b)
+ return NULL;
+
+ init_rwsem(&b->lock);
+ lockdep_set_novalidate_class(&b->lock);
+ INIT_LIST_HEAD(&b->list);
+ INIT_DELAYED_WORK(&b->work, btree_write_work);
+ b->c = c;
+ closure_init_unlocked(&b->io);
+
+ mca_data_alloc(b, k, gfp);
+ return b;
+}
+
+static int mca_reap(struct btree *b, struct closure *cl, unsigned min_order)
+{
+ lockdep_assert_held(&b->c->bucket_lock);
+
+ if (!down_write_trylock(&b->lock))
+ return -ENOMEM;
+
+ if (b->page_order < min_order) {
+ rw_unlock(true, b);
+ return -ENOMEM;
+ }
+
+ BUG_ON(btree_node_dirty(b) && !b->sets[0].data);
+
+ if (cl && btree_node_dirty(b))
+ bch_btree_write(b, true, NULL);
+
+ if (cl)
+ closure_wait_event_async(&b->io.wait, cl,
+ atomic_read(&b->io.cl.remaining) == -1);
+
+ if (btree_node_dirty(b) ||
+ !closure_is_unlocked(&b->io.cl) ||
+ work_pending(&b->work.work)) {
+ rw_unlock(true, b);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int bch_mca_shrink(struct shrinker *shrink, struct shrink_control *sc)
+{
+ struct cache_set *c = container_of(shrink, struct cache_set, shrink);
+ struct btree *b, *t;
+ unsigned long i, nr = sc->nr_to_scan;
+
+ if (c->shrinker_disabled)
+ return 0;
+
+ if (c->try_harder)
+ return 0;
+
+ /*
+ * If nr == 0, we're supposed to return the number of items we have
+ * cached. Not allowed to return -1.
+ */
+ if (!nr)
+ return mca_can_free(c) * c->btree_pages;
+
+ /* Return -1 if we can't do anything right now */
+ if (sc->gfp_mask & __GFP_WAIT)
+ mutex_lock(&c->bucket_lock);
+ else if (!mutex_trylock(&c->bucket_lock))
+ return -1;
+
+ nr /= c->btree_pages;
+ nr = min_t(unsigned long, nr, mca_can_free(c));
+
+ i = 0;
+ list_for_each_entry_safe(b, t, &c->btree_cache_freeable, list) {
+ if (!nr)
+ break;
+
+ if (++i > 3 &&
+ !mca_reap(b, NULL, 0)) {
+ mca_data_free(b);
+ rw_unlock(true, b);
+ --nr;
+ }
+ }
+
+ /*
+ * Can happen right when we first start up, before we've read in any
+ * btree nodes
+ */
+ if (list_empty(&c->btree_cache))
+ goto out;
+
+ for (i = 0; nr && i < c->bucket_cache_used; i++) {
+ b = list_first_entry(&c->btree_cache, struct btree, list);
+ list_rotate_left(&c->btree_cache);
+
+ if (!b->accessed &&
+ !mca_reap(b, NULL, 0)) {
+ mca_bucket_free(b);
+ mca_data_free(b);
+ rw_unlock(true, b);
+ --nr;
+ } else
+ b->accessed = 0;
+ }
+out:
+ nr = mca_can_free(c) * c->btree_pages;
+ mutex_unlock(&c->bucket_lock);
+ return nr;
+}
+
+void bch_btree_cache_free(struct cache_set *c)
+{
+ struct btree *b;
+ struct closure cl;
+ closure_init_stack(&cl);
+
+ if (c->shrink.list.next)
+ unregister_shrinker(&c->shrink);
+
+ mutex_lock(&c->bucket_lock);
+
+#ifdef CONFIG_BCACHE_DEBUG
+ if (c->verify_data)
+ list_move(&c->verify_data->list, &c->btree_cache);
+#endif
+
+ list_splice(&c->btree_cache_freeable,
+ &c->btree_cache);
+
+ while (!list_empty(&c->btree_cache)) {
+ b = list_first_entry(&c->btree_cache, struct btree, list);
+
+ if (btree_node_dirty(b))
+ btree_complete_write(b, btree_current_write(b));
+ clear_bit(BTREE_NODE_dirty, &b->flags);
+
+ mca_data_free(b);
+ }
+
+ while (!list_empty(&c->btree_cache_freed)) {
+ b = list_first_entry(&c->btree_cache_freed,
+ struct btree, list);
+ list_del(&b->list);
+ cancel_delayed_work_sync(&b->work);
+ kfree(b);
+ }
+
+ mutex_unlock(&c->bucket_lock);
+}
+
+int bch_btree_cache_alloc(struct cache_set *c)
+{
+ unsigned i;
+
+ /* XXX: doesn't check for errors */
+
+ closure_init_unlocked(&c->gc);
+
+ for (i = 0; i < mca_reserve(c); i++)
+ mca_bucket_alloc(c, &ZERO_KEY, GFP_KERNEL);
+
+ list_splice_init(&c->btree_cache,
+ &c->btree_cache_freeable);
+
+#ifdef CONFIG_BCACHE_DEBUG
+ mutex_init(&c->verify_lock);
+
+ c->verify_data = mca_bucket_alloc(c, &ZERO_KEY, GFP_KERNEL);
+
+ if (c->verify_data &&
+ c->verify_data->sets[0].data)
+ list_del_init(&c->verify_data->list);
+ else
+ c->verify_data = NULL;
+#endif
+
+ c->shrink.shrink = bch_mca_shrink;
+ c->shrink.seeks = 4;
+ c->shrink.batch = c->btree_pages * 2;
+ register_shrinker(&c->shrink);
+
+ return 0;
+}
+
+/* Btree in memory cache - hash table */
+
+static struct hlist_head *mca_hash(struct cache_set *c, struct bkey *k)
+{
+ return &c->bucket_hash[hash_32(PTR_HASH(c, k), BUCKET_HASH_BITS)];
+}
+
+static struct btree *mca_find(struct cache_set *c, struct bkey *k)
+{
+ struct btree *b;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(b, mca_hash(c, k), hash)
+ if (PTR_HASH(c, &b->key) == PTR_HASH(c, k))
+ goto out;
+ b = NULL;
+out:
+ rcu_read_unlock();
+ return b;
+}
+
+static struct btree *mca_cannibalize(struct cache_set *c, struct bkey *k,
+ int level, struct closure *cl)
+{
+ int ret = -ENOMEM;
+ struct btree *i;
+
+ if (!cl)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * Trying to free up some memory - i.e. reuse some btree nodes - may
+ * require initiating IO to flush the dirty part of the node. If we're
+ * running under generic_make_request(), that IO will never finish and
+ * we would deadlock. Returning -EAGAIN causes the cache lookup code to
+ * punt to workqueue and retry.
+ */
+ if (current->bio_list)
+ return ERR_PTR(-EAGAIN);
+
+ if (c->try_harder && c->try_harder != cl) {
+ closure_wait_event_async(&c->try_wait, cl, !c->try_harder);
+ return ERR_PTR(-EAGAIN);
+ }
+
+ /* XXX: tracepoint */
+ c->try_harder = cl;
+ c->try_harder_start = local_clock();
+retry:
+ list_for_each_entry_reverse(i, &c->btree_cache, list) {
+ int r = mca_reap(i, cl, btree_order(k));
+ if (!r)
+ return i;
+ if (r != -ENOMEM)
+ ret = r;
+ }
+
+ if (ret == -EAGAIN &&
+ closure_blocking(cl)) {
+ mutex_unlock(&c->bucket_lock);
+ closure_sync(cl);
+ mutex_lock(&c->bucket_lock);
+ goto retry;
+ }
+
+ return ERR_PTR(ret);
+}
+
+/*
+ * We can only have one thread cannibalizing other cached btree nodes at a time,
+ * or we'll deadlock. We use an open coded mutex to ensure that, which a
+ * cannibalize_bucket() will take. This means every time we unlock the root of
+ * the btree, we need to release this lock if we have it held.
+ */
+void bch_cannibalize_unlock(struct cache_set *c, struct closure *cl)
+{
+ if (c->try_harder == cl) {
+ bch_time_stats_update(&c->try_harder_time, c->try_harder_start);
+ c->try_harder = NULL;
+ __closure_wake_up(&c->try_wait);
+ }
+}
+
+static struct btree *mca_alloc(struct cache_set *c, struct bkey *k,
+ int level, struct closure *cl)
+{
+ struct btree *b;
+
+ lockdep_assert_held(&c->bucket_lock);
+
+ if (mca_find(c, k))
+ return NULL;
+
+ /* btree_free() doesn't free memory; it sticks the node on the end of
+ * the list. Check if there's any freed nodes there:
+ */
+ list_for_each_entry(b, &c->btree_cache_freeable, list)
+ if (!mca_reap(b, NULL, btree_order(k)))
+ goto out;
+
+ /* We never free struct btree itself, just the memory that holds the on
+ * disk node. Check the freed list before allocating a new one:
+ */
+ list_for_each_entry(b, &c->btree_cache_freed, list)
+ if (!mca_reap(b, NULL, 0)) {
+ mca_data_alloc(b, k, __GFP_NOWARN|GFP_NOIO);
+ if (!b->sets[0].data)
+ goto err;
+ else
+ goto out;
+ }
+
+ b = mca_bucket_alloc(c, k, __GFP_NOWARN|GFP_NOIO);
+ if (!b)
+ goto err;
+
+ BUG_ON(!down_write_trylock(&b->lock));
+ if (!b->sets->data)
+ goto err;
+out:
+ BUG_ON(!closure_is_unlocked(&b->io.cl));
+
+ bkey_copy(&b->key, k);
+ list_move(&b->list, &c->btree_cache);
+ hlist_del_init_rcu(&b->hash);
+ hlist_add_head_rcu(&b->hash, mca_hash(c, k));
+
+ lock_set_subclass(&b->lock.dep_map, level + 1, _THIS_IP_);
+ b->level = level;
+
+ mca_reinit(b);
+
+ return b;
+err:
+ if (b)
+ rw_unlock(true, b);
+
+ b = mca_cannibalize(c, k, level, cl);
+ if (!IS_ERR(b))
+ goto out;
+
+ return b;
+}
+
+/**
+ * bch_btree_node_get - find a btree node in the cache and lock it, reading it
+ * in from disk if necessary.
+ *
+ * If IO is necessary, it uses the closure embedded in struct btree_op to wait;
+ * if that closure is in non blocking mode, will return -EAGAIN.
+ *
+ * The btree node will have either a read or a write lock held, depending on
+ * level and op->lock.
+ */
+struct btree *bch_btree_node_get(struct cache_set *c, struct bkey *k,
+ int level, struct btree_op *op)
+{
+ int i = 0;
+ bool write = level <= op->lock;
+ struct btree *b;
+
+ BUG_ON(level < 0);
+retry:
+ b = mca_find(c, k);
+
+ if (!b) {
+ mutex_lock(&c->bucket_lock);
+ b = mca_alloc(c, k, level, &op->cl);
+ mutex_unlock(&c->bucket_lock);
+
+ if (!b)
+ goto retry;
+ if (IS_ERR(b))
+ return b;
+
+ bch_btree_read(b);
+
+ if (!write)
+ downgrade_write(&b->lock);
+ } else {
+ rw_lock(write, b, level);
+ if (PTR_HASH(c, &b->key) != PTR_HASH(c, k)) {
+ rw_unlock(write, b);
+ goto retry;
+ }
+ BUG_ON(b->level != level);
+ }
+
+ b->accessed = 1;
+
+ for (; i <= b->nsets && b->sets[i].size; i++) {
+ prefetch(b->sets[i].tree);
+ prefetch(b->sets[i].data);
+ }
+
+ for (; i <= b->nsets; i++)
+ prefetch(b->sets[i].data);
+
+ if (!closure_wait_event(&b->io.wait, &op->cl,
+ btree_node_read_done(b))) {
+ rw_unlock(write, b);
+ b = ERR_PTR(-EAGAIN);
+ } else if (btree_node_io_error(b)) {
+ rw_unlock(write, b);
+ b = ERR_PTR(-EIO);
+ } else
+ BUG_ON(!b->written);
+
+ return b;
+}
+
+static void btree_node_prefetch(struct cache_set *c, struct bkey *k, int level)
+{
+ struct btree *b;
+
+ mutex_lock(&c->bucket_lock);
+ b = mca_alloc(c, k, level, NULL);
+ mutex_unlock(&c->bucket_lock);
+
+ if (!IS_ERR_OR_NULL(b)) {
+ bch_btree_read(b);
+ rw_unlock(true, b);
+ }
+}
+
+/* Btree alloc */
+
+static void btree_node_free(struct btree *b, struct btree_op *op)
+{
+ unsigned i;
+
+ /*
+ * The BUG_ON() in btree_node_get() implies that we must have a write
+ * lock on parent to free or even invalidate a node
+ */
+ BUG_ON(op->lock <= b->level);
+ BUG_ON(b == b->c->root);
+ pr_debug("bucket %s", pbtree(b));
+
+ if (btree_node_dirty(b))
+ btree_complete_write(b, btree_current_write(b));
+ clear_bit(BTREE_NODE_dirty, &b->flags);
+
+ if (b->prio_blocked &&
+ !atomic_sub_return(b->prio_blocked, &b->c->prio_blocked))
+ wake_up(&b->c->alloc_wait);
+
+ b->prio_blocked = 0;
+
+ cancel_delayed_work(&b->work);
+
+ mutex_lock(&b->c->bucket_lock);
+
+ for (i = 0; i < KEY_PTRS(&b->key); i++) {
+ BUG_ON(atomic_read(&PTR_BUCKET(b->c, &b->key, i)->pin));
+
+ bch_inc_gen(PTR_CACHE(b->c, &b->key, i),
+ PTR_BUCKET(b->c, &b->key, i));
+ }
+
+ bch_bucket_free(b->c, &b->key);
+ mca_bucket_free(b);
+ mutex_unlock(&b->c->bucket_lock);
+}
+
+struct btree *bch_btree_node_alloc(struct cache_set *c, int level,
+ struct closure *cl)
+{
+ BKEY_PADDED(key) k;
+ struct btree *b = ERR_PTR(-EAGAIN);
+
+ mutex_lock(&c->bucket_lock);
+retry:
+ if (__bch_bucket_alloc_set(c, WATERMARK_METADATA, &k.key, 1, cl))
+ goto err;
+
+ SET_KEY_SIZE(&k.key, c->btree_pages * PAGE_SECTORS);
+
+ b = mca_alloc(c, &k.key, level, cl);
+ if (IS_ERR(b))
+ goto err_free;
+
+ if (!b) {
+ cache_bug(c,
+ "Tried to allocate bucket that was in btree cache");
+ __bkey_put(c, &k.key);
+ goto retry;
+ }
+
+ set_btree_node_read_done(b);
+ b->accessed = 1;
+ bch_bset_init_next(b);
+
+ mutex_unlock(&c->bucket_lock);
+ return b;
+err_free:
+ bch_bucket_free(c, &k.key);
+ __bkey_put(c, &k.key);
+err:
+ mutex_unlock(&c->bucket_lock);
+ return b;
+}
+
+static struct btree *btree_node_alloc_replacement(struct btree *b,
+ struct closure *cl)
+{
+ struct btree *n = bch_btree_node_alloc(b->c, b->level, cl);
+ if (!IS_ERR_OR_NULL(n))
+ bch_btree_sort_into(b, n);
+
+ return n;
+}
+
+/* Garbage collection */
+
+uint8_t __bch_btree_mark_key(struct cache_set *c, int level, struct bkey *k)
+{
+ uint8_t stale = 0;
+ unsigned i;
+ struct bucket *g;
+
+ /*
+ * ptr_invalid() can't return true for the keys that mark btree nodes as
+ * freed, but since ptr_bad() returns true we'll never actually use them
+ * for anything and thus we don't want mark their pointers here
+ */
+ if (!bkey_cmp(k, &ZERO_KEY))
+ return stale;
+
+ for (i = 0; i < KEY_PTRS(k); i++) {
+ if (!ptr_available(c, k, i))
+ continue;
+
+ g = PTR_BUCKET(c, k, i);
+
+ if (gen_after(g->gc_gen, PTR_GEN(k, i)))
+ g->gc_gen = PTR_GEN(k, i);
+
+ if (ptr_stale(c, k, i)) {
+ stale = max(stale, ptr_stale(c, k, i));
+ continue;
+ }
+
+ cache_bug_on(GC_MARK(g) &&
+ (GC_MARK(g) == GC_MARK_METADATA) != (level != 0),
+ c, "inconsistent ptrs: mark = %llu, level = %i",
+ GC_MARK(g), level);
+
+ if (level)
+ SET_GC_MARK(g, GC_MARK_METADATA);
+ else if (KEY_DIRTY(k))
+ SET_GC_MARK(g, GC_MARK_DIRTY);
+
+ /* guard against overflow */
+ SET_GC_SECTORS_USED(g, min_t(unsigned,
+ GC_SECTORS_USED(g) + KEY_SIZE(k),
+ (1 << 14) - 1));
+
+ BUG_ON(!GC_SECTORS_USED(g));
+ }
+
+ return stale;
+}
+
+#define btree_mark_key(b, k) __bch_btree_mark_key(b->c, b->level, k)
+
+static int btree_gc_mark_node(struct btree *b, unsigned *keys,
+ struct gc_stat *gc)
+{
+ uint8_t stale = 0;
+ unsigned last_dev = -1;
+ struct bcache_device *d = NULL;
+ struct bkey *k;
+ struct btree_iter iter;
+ struct bset_tree *t;
+
+ gc->nodes++;
+
+ for_each_key_filter(b, k, &iter, bch_ptr_invalid) {
+ if (last_dev != KEY_INODE(k)) {
+ last_dev = KEY_INODE(k);
+
+ d = KEY_INODE(k) < b->c->nr_uuids
+ ? b->c->devices[last_dev]
+ : NULL;
+ }
+
+ stale = max(stale, btree_mark_key(b, k));
+
+ if (bch_ptr_bad(b, k))
+ continue;
+
+ *keys += bkey_u64s(k);
+
+ gc->key_bytes += bkey_u64s(k);
+ gc->nkeys++;
+
+ gc->data += KEY_SIZE(k);
+ if (KEY_DIRTY(k)) {
+ gc->dirty += KEY_SIZE(k);
+ if (d)
+ d->sectors_dirty_gc += KEY_SIZE(k);
+ }
+ }
+
+ for (t = b->sets; t <= &b->sets[b->nsets]; t++)
+ btree_bug_on(t->size &&
+ bset_written(b, t) &&
+ bkey_cmp(&b->key, &t->end) < 0,
+ b, "found short btree key in gc");
+
+ return stale;
+}
+
+static struct btree *btree_gc_alloc(struct btree *b, struct bkey *k,
+ struct btree_op *op)
+{
+ /*
+ * We block priorities from being written for the duration of garbage
+ * collection, so we can't sleep in btree_alloc() ->
+ * bch_bucket_alloc_set(), or we'd risk deadlock - so we don't pass it
+ * our closure.
+ */
+ struct btree *n = btree_node_alloc_replacement(b, NULL);
+
+ if (!IS_ERR_OR_NULL(n)) {
+ swap(b, n);
+
+ memcpy(k->ptr, b->key.ptr,
+ sizeof(uint64_t) * KEY_PTRS(&b->key));
+
+ __bkey_put(b->c, &b->key);
+ atomic_inc(&b->c->prio_blocked);
+ b->prio_blocked++;
+
+ btree_node_free(n, op);
+ up_write(&n->lock);
+ }
+
+ return b;
+}
+
+/*
+ * Leaving this at 2 until we've got incremental garbage collection done; it
+ * could be higher (and has been tested with 4) except that garbage collection
+ * could take much longer, adversely affecting latency.
+ */
+#define GC_MERGE_NODES 2U
+
+struct gc_merge_info {
+ struct btree *b;
+ struct bkey *k;
+ unsigned keys;
+};
+
+static void btree_gc_coalesce(struct btree *b, struct btree_op *op,
+ struct gc_stat *gc, struct gc_merge_info *r)
+{
+ unsigned nodes = 0, keys = 0, blocks;
+ int i;
+
+ while (nodes < GC_MERGE_NODES && r[nodes].b)
+ keys += r[nodes++].keys;
+
+ blocks = btree_default_blocks(b->c) * 2 / 3;
+
+ if (nodes < 2 ||
+ __set_blocks(b->sets[0].data, keys, b->c) > blocks * (nodes - 1))
+ return;
+
+ for (i = nodes - 1; i >= 0; --i) {
+ if (r[i].b->written)
+ r[i].b = btree_gc_alloc(r[i].b, r[i].k, op);
+
+ if (r[i].b->written)
+ return;
+ }
+
+ for (i = nodes - 1; i > 0; --i) {
+ struct bset *n1 = r[i].b->sets->data;
+ struct bset *n2 = r[i - 1].b->sets->data;
+ struct bkey *k, *last = NULL;
+
+ keys = 0;
+
+ if (i == 1) {
+ /*
+ * Last node we're not getting rid of - we're getting
+ * rid of the node at r[0]. Have to try and fit all of
+ * the remaining keys into this node; we can't ensure
+ * they will always fit due to rounding and variable
+ * length keys (shouldn't be possible in practice,
+ * though)
+ */
+ if (__set_blocks(n1, n1->keys + r->keys,
+ b->c) > btree_blocks(r[i].b))
+ return;
+
+ keys = n2->keys;
+ last = &r->b->key;
+ } else
+ for (k = n2->start;
+ k < end(n2);
+ k = bkey_next(k)) {
+ if (__set_blocks(n1, n1->keys + keys +
+ bkey_u64s(k), b->c) > blocks)
+ break;
+
+ last = k;
+ keys += bkey_u64s(k);
+ }
+
+ BUG_ON(__set_blocks(n1, n1->keys + keys,
+ b->c) > btree_blocks(r[i].b));
+
+ if (last) {
+ bkey_copy_key(&r[i].b->key, last);
+ bkey_copy_key(r[i].k, last);
+ }
+
+ memcpy(end(n1),
+ n2->start,
+ (void *) node(n2, keys) - (void *) n2->start);
+
+ n1->keys += keys;
+
+ memmove(n2->start,
+ node(n2, keys),
+ (void *) end(n2) - (void *) node(n2, keys));
+
+ n2->keys -= keys;
+
+ r[i].keys = n1->keys;
+ r[i - 1].keys = n2->keys;
+ }
+
+ btree_node_free(r->b, op);
+ up_write(&r->b->lock);
+
+ pr_debug("coalesced %u nodes", nodes);
+
+ gc->nodes--;
+ nodes--;
+
+ memmove(&r[0], &r[1], sizeof(struct gc_merge_info) * nodes);
+ memset(&r[nodes], 0, sizeof(struct gc_merge_info));
+}
+
+static int btree_gc_recurse(struct btree *b, struct btree_op *op,
+ struct closure *writes, struct gc_stat *gc)
+{
+ void write(struct btree *r)
+ {
+ if (!r->written)
+ bch_btree_write(r, true, op);
+ else if (btree_node_dirty(r)) {
+ BUG_ON(btree_current_write(r)->owner);
+ btree_current_write(r)->owner = writes;
+ closure_get(writes);
+
+ bch_btree_write(r, true, NULL);
+ }
+
+ up_write(&r->lock);
+ }
+
+ int ret = 0, stale;
+ unsigned i;
+ struct gc_merge_info r[GC_MERGE_NODES];
+
+ memset(r, 0, sizeof(r));
+
+ while ((r->k = bch_next_recurse_key(b, &b->c->gc_done))) {
+ r->b = bch_btree_node_get(b->c, r->k, b->level - 1, op);
+
+ if (IS_ERR(r->b)) {
+ ret = PTR_ERR(r->b);
+ break;
+ }
+
+ r->keys = 0;
+ stale = btree_gc_mark_node(r->b, &r->keys, gc);
+
+ if (!b->written &&
+ (r->b->level || stale > 10 ||
+ b->c->gc_always_rewrite))
+ r->b = btree_gc_alloc(r->b, r->k, op);
+
+ if (r->b->level)
+ ret = btree_gc_recurse(r->b, op, writes, gc);
+
+ if (ret) {
+ write(r->b);
+ break;
+ }
+
+ bkey_copy_key(&b->c->gc_done, r->k);
+
+ if (!b->written)
+ btree_gc_coalesce(b, op, gc, r);
+
+ if (r[GC_MERGE_NODES - 1].b)
+ write(r[GC_MERGE_NODES - 1].b);
+
+ memmove(&r[1], &r[0],
+ sizeof(struct gc_merge_info) * (GC_MERGE_NODES - 1));
+
+ /* When we've got incremental GC working, we'll want to do
+ * if (should_resched())
+ * return -EAGAIN;
+ */
+ cond_resched();
+#if 0
+ if (need_resched()) {
+ ret = -EAGAIN;
+ break;
+ }
+#endif
+ }
+
+ for (i = 1; i < GC_MERGE_NODES && r[i].b; i++)
+ write(r[i].b);
+
+ /* Might have freed some children, must remove their keys */
+ if (!b->written)
+ bch_btree_sort(b);
+
+ return ret;
+}
+
+static int bch_btree_gc_root(struct btree *b, struct btree_op *op,
+ struct closure *writes, struct gc_stat *gc)
+{
+ struct btree *n = NULL;
+ unsigned keys = 0;
+ int ret = 0, stale = btree_gc_mark_node(b, &keys, gc);
+
+ if (b->level || stale > 10)
+ n = btree_node_alloc_replacement(b, NULL);
+
+ if (!IS_ERR_OR_NULL(n))
+ swap(b, n);
+
+ if (b->level)
+ ret = btree_gc_recurse(b, op, writes, gc);
+
+ if (!b->written || btree_node_dirty(b)) {
+ atomic_inc(&b->c->prio_blocked);
+ b->prio_blocked++;
+ bch_btree_write(b, true, n ? op : NULL);
+ }
+
+ if (!IS_ERR_OR_NULL(n)) {
+ closure_sync(&op->cl);
+ bch_btree_set_root(b);
+ btree_node_free(n, op);
+ rw_unlock(true, b);
+ }
+
+ return ret;
+}
+
+static void btree_gc_start(struct cache_set *c)
+{
+ struct cache *ca;
+ struct bucket *b;
+ struct bcache_device **d;
+ unsigned i;
+
+ if (!c->gc_mark_valid)
+ return;
+
+ mutex_lock(&c->bucket_lock);
+
+ c->gc_mark_valid = 0;
+ c->gc_done = ZERO_KEY;
+
+ for_each_cache(ca, c, i)
+ for_each_bucket(b, ca) {
+ b->gc_gen = b->gen;
+ if (!atomic_read(&b->pin))
+ SET_GC_MARK(b, GC_MARK_RECLAIMABLE);
+ }
+
+ for (d = c->devices;
+ d < c->devices + c->nr_uuids;
+ d++)
+ if (*d)
+ (*d)->sectors_dirty_gc = 0;
+
+ mutex_unlock(&c->bucket_lock);
+}
+
+size_t bch_btree_gc_finish(struct cache_set *c)
+{
+ size_t available = 0;
+ struct bucket *b;
+ struct cache *ca;
+ struct bcache_device **d;
+ unsigned i;
+
+ mutex_lock(&c->bucket_lock);
+
+ set_gc_sectors(c);
+ c->gc_mark_valid = 1;
+ c->need_gc = 0;
+
+ if (c->root)
+ for (i = 0; i < KEY_PTRS(&c->root->key); i++)
+ SET_GC_MARK(PTR_BUCKET(c, &c->root->key, i),
+ GC_MARK_METADATA);
+
+ for (i = 0; i < KEY_PTRS(&c->uuid_bucket); i++)
+ SET_GC_MARK(PTR_BUCKET(c, &c->uuid_bucket, i),
+ GC_MARK_METADATA);
+
+ for_each_cache(ca, c, i) {
+ uint64_t *i;
+
+ ca->invalidate_needs_gc = 0;
+
+ for (i = ca->sb.d; i < ca->sb.d + ca->sb.keys; i++)
+ SET_GC_MARK(ca->buckets + *i, GC_MARK_METADATA);
+
+ for (i = ca->prio_buckets;
+ i < ca->prio_buckets + prio_buckets(ca) * 2; i++)
+ SET_GC_MARK(ca->buckets + *i, GC_MARK_METADATA);
+
+ for_each_bucket(b, ca) {
+ b->last_gc = b->gc_gen;
+ c->need_gc = max(c->need_gc, bucket_gc_gen(b));
+
+ if (!atomic_read(&b->pin) &&
+ GC_MARK(b) == GC_MARK_RECLAIMABLE) {
+ available++;
+ if (!GC_SECTORS_USED(b))
+ bch_bucket_add_unused(ca, b);
+ }
+ }
+ }
+
+ for (d = c->devices;
+ d < c->devices + c->nr_uuids;
+ d++)
+ if (*d) {
+ unsigned long last =
+ atomic_long_read(&((*d)->sectors_dirty));
+ long difference = (*d)->sectors_dirty_gc - last;
+
+ pr_debug("sectors dirty off by %li", difference);
+
+ (*d)->sectors_dirty_last += difference;
+
+ atomic_long_set(&((*d)->sectors_dirty),
+ (*d)->sectors_dirty_gc);
+ }
+
+ mutex_unlock(&c->bucket_lock);
+ return available;
+}
+
+static void bch_btree_gc(struct closure *cl)
+{
+ struct cache_set *c = container_of(cl, struct cache_set, gc.cl);
+ int ret;
+ unsigned long available;
+ struct gc_stat stats;
+ struct closure writes;
+ struct btree_op op;
+
+ uint64_t start_time = local_clock();
+ trace_bcache_gc_start(c->sb.set_uuid);
+ blktrace_msg_all(c, "Starting gc");
+
+ memset(&stats, 0, sizeof(struct gc_stat));
+ closure_init_stack(&writes);
+ bch_btree_op_init_stack(&op);
+ op.lock = SHRT_MAX;
+
+ btree_gc_start(c);
+
+ ret = btree_root(gc_root, c, &op, &writes, &stats);
+ closure_sync(&op.cl);
+ closure_sync(&writes);
+
+ if (ret) {
+ blktrace_msg_all(c, "Stopped gc");
+ pr_warn("gc failed!");
+
+ continue_at(cl, bch_btree_gc, bch_gc_wq);
+ }
+
+ /* Possibly wait for new UUIDs or whatever to hit disk */
+ bch_journal_meta(c, &op.cl);
+ closure_sync(&op.cl);
+
+ available = bch_btree_gc_finish(c);
+
+ bch_time_stats_update(&c->btree_gc_time, start_time);
+
+ stats.key_bytes *= sizeof(uint64_t);
+ stats.dirty <<= 9;
+ stats.data <<= 9;
+ stats.in_use = (c->nbuckets - available) * 100 / c->nbuckets;
+ memcpy(&c->gc_stats, &stats, sizeof(struct gc_stat));
+ blktrace_msg_all(c, "Finished gc");
+
+ trace_bcache_gc_end(c->sb.set_uuid);
+ wake_up(&c->alloc_wait);
+
+ continue_at(cl, bch_moving_gc, bch_gc_wq);
+}
+
+void bch_queue_gc(struct cache_set *c)
+{
+ closure_trylock_call(&c->gc.cl, bch_btree_gc, bch_gc_wq, &c->cl);
+}
+
+/* Initial partial gc */
+
+static int bch_btree_check_recurse(struct btree *b, struct btree_op *op,
+ unsigned long **seen)
+{
+ int ret;
+ unsigned i;
+ struct bkey *k;
+ struct bucket *g;
+ struct btree_iter iter;
+
+ for_each_key_filter(b, k, &iter, bch_ptr_invalid) {
+ for (i = 0; i < KEY_PTRS(k); i++) {
+ if (!ptr_available(b->c, k, i))
+ continue;
+
+ g = PTR_BUCKET(b->c, k, i);
+
+ if (!__test_and_set_bit(PTR_BUCKET_NR(b->c, k, i),
+ seen[PTR_DEV(k, i)]) ||
+ !ptr_stale(b->c, k, i)) {
+ g->gen = PTR_GEN(k, i);
+
+ if (b->level)
+ g->prio = BTREE_PRIO;
+ else if (g->prio == BTREE_PRIO)
+ g->prio = INITIAL_PRIO;
+ }
+ }
+
+ btree_mark_key(b, k);
+ }
+
+ if (b->level) {
+ k = bch_next_recurse_key(b, &ZERO_KEY);
+
+ while (k) {
+ struct bkey *p = bch_next_recurse_key(b, k);
+ if (p)
+ btree_node_prefetch(b->c, p, b->level - 1);
+
+ ret = btree(check_recurse, k, b, op, seen);
+ if (ret)
+ return ret;
+
+ k = p;
+ }
+ }
+
+ return 0;
+}
+
+int bch_btree_check(struct cache_set *c, struct btree_op *op)
+{
+ int ret = -ENOMEM;
+ unsigned i;
+ unsigned long *seen[MAX_CACHES_PER_SET];
+
+ memset(seen, 0, sizeof(seen));
+
+ for (i = 0; c->cache[i]; i++) {
+ size_t n = DIV_ROUND_UP(c->cache[i]->sb.nbuckets, 8);
+ seen[i] = kmalloc(n, GFP_KERNEL);
+ if (!seen[i])
+ goto err;
+
+ /* Disables the seen array until prio_read() uses it too */
+ memset(seen[i], 0xFF, n);
+ }
+
+ ret = btree_root(check_recurse, c, op, seen);
+err:
+ for (i = 0; i < MAX_CACHES_PER_SET; i++)
+ kfree(seen[i]);
+ return ret;
+}
+
+/* Btree insertion */
+
+static void shift_keys(struct btree *b, struct bkey *where, struct bkey *insert)
+{
+ struct bset *i = b->sets[b->nsets].data;
+
+ memmove((uint64_t *) where + bkey_u64s(insert),
+ where,
+ (void *) end(i) - (void *) where);
+
+ i->keys += bkey_u64s(insert);
+ bkey_copy(where, insert);
+ bch_bset_fix_lookup_table(b, where);
+}
+
+static bool fix_overlapping_extents(struct btree *b,
+ struct bkey *insert,
+ struct btree_iter *iter,
+ struct btree_op *op)
+{
+ void subtract_dirty(struct bkey *k, int sectors)
+ {
+ struct bcache_device *d = b->c->devices[KEY_INODE(k)];
+
+ if (KEY_DIRTY(k) && d)
+ atomic_long_sub(sectors, &d->sectors_dirty);
+ }
+
+ unsigned old_size, sectors_found = 0;
+
+ while (1) {
+ struct bkey *k = bch_btree_iter_next(iter);
+ if (!k ||
+ bkey_cmp(&START_KEY(k), insert) >= 0)
+ break;
+
+ if (bkey_cmp(k, &START_KEY(insert)) <= 0)
+ continue;
+
+ old_size = KEY_SIZE(k);
+
+ /*
+ * We might overlap with 0 size extents; we can't skip these
+ * because if they're in the set we're inserting to we have to
+ * adjust them so they don't overlap with the key we're
+ * inserting. But we don't want to check them for BTREE_REPLACE
+ * operations.
+ */
+
+ if (op->type == BTREE_REPLACE &&
+ KEY_SIZE(k)) {
+ /*
+ * k might have been split since we inserted/found the
+ * key we're replacing
+ */
+ unsigned i;
+ uint64_t offset = KEY_START(k) -
+ KEY_START(&op->replace);
+
+ /* But it must be a subset of the replace key */
+ if (KEY_START(k) < KEY_START(&op->replace) ||
+ KEY_OFFSET(k) > KEY_OFFSET(&op->replace))
+ goto check_failed;
+
+ /* We didn't find a key that we were supposed to */
+ if (KEY_START(k) > KEY_START(insert) + sectors_found)
+ goto check_failed;
+
+ if (KEY_PTRS(&op->replace) != KEY_PTRS(k))
+ goto check_failed;
+
+ /* skip past gen */
+ offset <<= 8;
+
+ BUG_ON(!KEY_PTRS(&op->replace));
+
+ for (i = 0; i < KEY_PTRS(&op->replace); i++)
+ if (k->ptr[i] != op->replace.ptr[i] + offset)
+ goto check_failed;
+
+ sectors_found = KEY_OFFSET(k) - KEY_START(insert);
+ }
+
+ if (bkey_cmp(insert, k) < 0 &&
+ bkey_cmp(&START_KEY(insert), &START_KEY(k)) > 0) {
+ /*
+ * We overlapped in the middle of an existing key: that
+ * means we have to split the old key. But we have to do
+ * slightly different things depending on whether the
+ * old key has been written out yet.
+ */
+
+ struct bkey *top;
+
+ subtract_dirty(k, KEY_SIZE(insert));
+
+ if (bkey_written(b, k)) {
+ /*
+ * We insert a new key to cover the top of the
+ * old key, and the old key is modified in place
+ * to represent the bottom split.
+ *
+ * It's completely arbitrary whether the new key
+ * is the top or the bottom, but it has to match
+ * up with what btree_sort_fixup() does - it
+ * doesn't check for this kind of overlap, it
+ * depends on us inserting a new key for the top
+ * here.
+ */
+ top = bch_bset_search(b, &b->sets[b->nsets],
+ insert);
+ shift_keys(b, top, k);
+ } else {
+ BKEY_PADDED(key) temp;
+ bkey_copy(&temp.key, k);
+ shift_keys(b, k, &temp.key);
+ top = bkey_next(k);
+ }
+
+ bch_cut_front(insert, top);
+ bch_cut_back(&START_KEY(insert), k);
+ bch_bset_fix_invalidated_key(b, k);
+ return false;
+ }
+
+ if (bkey_cmp(insert, k) < 0) {
+ bch_cut_front(insert, k);
+ } else {
+ if (bkey_written(b, k) &&
+ bkey_cmp(&START_KEY(insert), &START_KEY(k)) <= 0) {
+ /*
+ * Completely overwrote, so we don't have to
+ * invalidate the binary search tree
+ */
+ bch_cut_front(k, k);
+ } else {
+ __bch_cut_back(&START_KEY(insert), k);
+ bch_bset_fix_invalidated_key(b, k);
+ }
+ }
+
+ subtract_dirty(k, old_size - KEY_SIZE(k));
+ }
+
+check_failed:
+ if (op->type == BTREE_REPLACE) {
+ if (!sectors_found) {
+ op->insert_collision = true;
+ return true;
+ } else if (sectors_found < KEY_SIZE(insert)) {
+ SET_KEY_OFFSET(insert, KEY_OFFSET(insert) -
+ (KEY_SIZE(insert) - sectors_found));
+ SET_KEY_SIZE(insert, sectors_found);
+ }
+ }
+
+ return false;
+}
+
+static bool btree_insert_key(struct btree *b, struct btree_op *op,
+ struct bkey *k)
+{
+ struct bset *i = b->sets[b->nsets].data;
+ struct bkey *m, *prev;
+ const char *status = "insert";
+
+ BUG_ON(bkey_cmp(k, &b->key) > 0);
+ BUG_ON(b->level && !KEY_PTRS(k));
+ BUG_ON(!b->level && !KEY_OFFSET(k));
+
+ if (!b->level) {
+ struct btree_iter iter;
+ struct bkey search = KEY(KEY_INODE(k), KEY_START(k), 0);
+
+ /*
+ * bset_search() returns the first key that is strictly greater
+ * than the search key - but for back merging, we want to find
+ * the first key that is greater than or equal to KEY_START(k) -
+ * unless KEY_START(k) is 0.
+ */
+ if (KEY_OFFSET(&search))
+ SET_KEY_OFFSET(&search, KEY_OFFSET(&search) - 1);
+
+ prev = NULL;
+ m = bch_btree_iter_init(b, &iter, &search);
+
+ if (fix_overlapping_extents(b, k, &iter, op))
+ return false;
+
+ while (m != end(i) &&
+ bkey_cmp(k, &START_KEY(m)) > 0)
+ prev = m, m = bkey_next(m);
+
+ if (key_merging_disabled(b->c))
+ goto insert;
+
+ /* prev is in the tree, if we merge we're done */
+ status = "back merging";
+ if (prev &&
+ bch_bkey_try_merge(b, prev, k))
+ goto merged;
+
+ status = "overwrote front";
+ if (m != end(i) &&
+ KEY_PTRS(m) == KEY_PTRS(k) && !KEY_SIZE(m))
+ goto copy;
+
+ status = "front merge";
+ if (m != end(i) &&
+ bch_bkey_try_merge(b, k, m))
+ goto copy;
+ } else
+ m = bch_bset_search(b, &b->sets[b->nsets], k);
+
+insert: shift_keys(b, m, k);
+copy: bkey_copy(m, k);
+merged:
+ bch_check_keys(b, "%s for %s at %s: %s", status,
+ op_type(op), pbtree(b), pkey(k));
+ bch_check_key_order_msg(b, i, "%s for %s at %s: %s", status,
+ op_type(op), pbtree(b), pkey(k));
+
+ if (b->level && !KEY_OFFSET(k))
+ b->prio_blocked++;
+
+ pr_debug("%s for %s at %s: %s", status,
+ op_type(op), pbtree(b), pkey(k));
+
+ return true;
+}
+
+bool bch_btree_insert_keys(struct btree *b, struct btree_op *op)
+{
+ bool ret = false;
+ struct bkey *k;
+ unsigned oldsize = bch_count_data(b);
+
+ while ((k = bch_keylist_pop(&op->keys))) {
+ bkey_put(b->c, k, b->level);
+ ret |= btree_insert_key(b, op, k);
+ }
+
+ BUG_ON(bch_count_data(b) < oldsize);
+ return ret;
+}
+
+bool bch_btree_insert_check_key(struct btree *b, struct btree_op *op,
+ struct bio *bio)
+{
+ bool ret = false;
+ uint64_t btree_ptr = b->key.ptr[0];
+ unsigned long seq = b->seq;
+ BKEY_PADDED(k) tmp;
+
+ rw_unlock(false, b);
+ rw_lock(true, b, b->level);
+
+ if (b->key.ptr[0] != btree_ptr ||
+ b->seq != seq + 1 ||
+ should_split(b))
+ goto out;
+
+ op->replace = KEY(op->inode, bio_end(bio), bio_sectors(bio));
+
+ SET_KEY_PTRS(&op->replace, 1);
+ get_random_bytes(&op->replace.ptr[0], sizeof(uint64_t));
+
+ SET_PTR_DEV(&op->replace, 0, PTR_CHECK_DEV);
+
+ bkey_copy(&tmp.k, &op->replace);
+
+ BUG_ON(op->type != BTREE_INSERT);
+ BUG_ON(!btree_insert_key(b, op, &tmp.k));
+ bch_btree_write(b, false, NULL);
+ ret = true;
+out:
+ downgrade_write(&b->lock);
+ return ret;
+}
+
+static int btree_split(struct btree *b, struct btree_op *op)
+{
+ bool split, root = b == b->c->root;
+ struct btree *n1, *n2 = NULL, *n3 = NULL;
+ uint64_t start_time = local_clock();
+
+ if (b->level)
+ set_closure_blocking(&op->cl);
+
+ n1 = btree_node_alloc_replacement(b, &op->cl);
+ if (IS_ERR(n1))
+ goto err;
+
+ split = set_blocks(n1->sets[0].data, n1->c) > (btree_blocks(b) * 4) / 5;
+
+ pr_debug("%ssplitting at %s keys %i", split ? "" : "not ",
+ pbtree(b), n1->sets[0].data->keys);
+
+ if (split) {
+ unsigned keys = 0;
+
+ n2 = bch_btree_node_alloc(b->c, b->level, &op->cl);
+ if (IS_ERR(n2))
+ goto err_free1;
+
+ if (root) {
+ n3 = bch_btree_node_alloc(b->c, b->level + 1, &op->cl);
+ if (IS_ERR(n3))
+ goto err_free2;
+ }
+
+ bch_btree_insert_keys(n1, op);
+
+ /* Has to be a linear search because we don't have an auxiliary
+ * search tree yet
+ */
+
+ while (keys < (n1->sets[0].data->keys * 3) / 5)
+ keys += bkey_u64s(node(n1->sets[0].data, keys));
+
+ bkey_copy_key(&n1->key, node(n1->sets[0].data, keys));
+ keys += bkey_u64s(node(n1->sets[0].data, keys));
+
+ n2->sets[0].data->keys = n1->sets[0].data->keys - keys;
+ n1->sets[0].data->keys = keys;
+
+ memcpy(n2->sets[0].data->start,
+ end(n1->sets[0].data),
+ n2->sets[0].data->keys * sizeof(uint64_t));
+
+ bkey_copy_key(&n2->key, &b->key);
+
+ bch_keylist_add(&op->keys, &n2->key);
+ bch_btree_write(n2, true, op);
+ rw_unlock(true, n2);
+ } else
+ bch_btree_insert_keys(n1, op);
+
+ bch_keylist_add(&op->keys, &n1->key);
+ bch_btree_write(n1, true, op);
+
+ if (n3) {
+ bkey_copy_key(&n3->key, &MAX_KEY);
+ bch_btree_insert_keys(n3, op);
+ bch_btree_write(n3, true, op);
+
+ closure_sync(&op->cl);
+ bch_btree_set_root(n3);
+ rw_unlock(true, n3);
+ } else if (root) {
+ op->keys.top = op->keys.bottom;
+ closure_sync(&op->cl);
+ bch_btree_set_root(n1);
+ } else {
+ unsigned i;
+
+ bkey_copy(op->keys.top, &b->key);
+ bkey_copy_key(op->keys.top, &ZERO_KEY);
+
+ for (i = 0; i < KEY_PTRS(&b->key); i++) {
+ uint8_t g = PTR_BUCKET(b->c, &b->key, i)->gen + 1;
+
+ SET_PTR_GEN(op->keys.top, i, g);
+ }
+
+ bch_keylist_push(&op->keys);
+ closure_sync(&op->cl);
+ atomic_inc(&b->c->prio_blocked);
+ }
+
+ rw_unlock(true, n1);
+ btree_node_free(b, op);
+
+ bch_time_stats_update(&b->c->btree_split_time, start_time);
+
+ return 0;
+err_free2:
+ __bkey_put(n2->c, &n2->key);
+ btree_node_free(n2, op);
+ rw_unlock(true, n2);
+err_free1:
+ __bkey_put(n1->c, &n1->key);
+ btree_node_free(n1, op);
+ rw_unlock(true, n1);
+err:
+ if (n3 == ERR_PTR(-EAGAIN) ||
+ n2 == ERR_PTR(-EAGAIN) ||
+ n1 == ERR_PTR(-EAGAIN))
+ return -EAGAIN;
+
+ pr_warn("couldn't split");
+ return -ENOMEM;
+}
+
+static int bch_btree_insert_recurse(struct btree *b, struct btree_op *op,
+ struct keylist *stack_keys)
+{
+ if (b->level) {
+ int ret;
+ struct bkey *insert = op->keys.bottom;
+ struct bkey *k = bch_next_recurse_key(b, &START_KEY(insert));
+
+ if (!k) {
+ btree_bug(b, "no key to recurse on at level %i/%i",
+ b->level, b->c->root->level);
+
+ op->keys.top = op->keys.bottom;
+ return -EIO;
+ }
+
+ if (bkey_cmp(insert, k) > 0) {
+ unsigned i;
+
+ if (op->type == BTREE_REPLACE) {
+ __bkey_put(b->c, insert);
+ op->keys.top = op->keys.bottom;
+ op->insert_collision = true;
+ return 0;
+ }
+
+ for (i = 0; i < KEY_PTRS(insert); i++)
+ atomic_inc(&PTR_BUCKET(b->c, insert, i)->pin);
+
+ bkey_copy(stack_keys->top, insert);
+
+ bch_cut_back(k, insert);
+ bch_cut_front(k, stack_keys->top);
+
+ bch_keylist_push(stack_keys);
+ }
+
+ ret = btree(insert_recurse, k, b, op, stack_keys);
+ if (ret)
+ return ret;
+ }
+
+ if (!bch_keylist_empty(&op->keys)) {
+ if (should_split(b)) {
+ if (op->lock <= b->c->root->level) {
+ BUG_ON(b->level);
+ op->lock = b->c->root->level + 1;
+ return -EINTR;
+ }
+ return btree_split(b, op);
+ }
+
+ BUG_ON(write_block(b) != b->sets[b->nsets].data);
+
+ if (bch_btree_insert_keys(b, op))
+ bch_btree_write(b, false, op);
+ }
+
+ return 0;
+}
+
+int bch_btree_insert(struct btree_op *op, struct cache_set *c)
+{
+ int ret = 0;
+ struct keylist stack_keys;
+
+ /*
+ * Don't want to block with the btree locked unless we have to,
+ * otherwise we get deadlocks with try_harder and between split/gc
+ */
+ clear_closure_blocking(&op->cl);
+
+ BUG_ON(bch_keylist_empty(&op->keys));
+ bch_keylist_copy(&stack_keys, &op->keys);
+ bch_keylist_init(&op->keys);
+
+ while (!bch_keylist_empty(&stack_keys) ||
+ !bch_keylist_empty(&op->keys)) {
+ if (bch_keylist_empty(&op->keys)) {
+ bch_keylist_add(&op->keys,
+ bch_keylist_pop(&stack_keys));
+ op->lock = 0;
+ }
+
+ ret = btree_root(insert_recurse, c, op, &stack_keys);
+
+ if (ret == -EAGAIN) {
+ ret = 0;
+ closure_sync(&op->cl);
+ } else if (ret) {
+ struct bkey *k;
+
+ pr_err("error %i trying to insert key for %s",
+ ret, op_type(op));
+
+ while ((k = bch_keylist_pop(&stack_keys) ?:
+ bch_keylist_pop(&op->keys)))
+ bkey_put(c, k, 0);
+ }
+ }
+
+ bch_keylist_free(&stack_keys);
+
+ if (op->journal)
+ atomic_dec_bug(op->journal);
+ op->journal = NULL;
+ return ret;
+}
+
+void bch_btree_set_root(struct btree *b)
+{
+ unsigned i;
+
+ BUG_ON(!b->written);
+
+ for (i = 0; i < KEY_PTRS(&b->key); i++)
+ BUG_ON(PTR_BUCKET(b->c, &b->key, i)->prio != BTREE_PRIO);
+
+ mutex_lock(&b->c->bucket_lock);
+ list_del_init(&b->list);
+ mutex_unlock(&b->c->bucket_lock);
+
+ b->c->root = b;
+ __bkey_put(b->c, &b->key);
+
+ bch_journal_meta(b->c, NULL);
+ pr_debug("%s for %pf", pbtree(b), __builtin_return_address(0));
+}
+
+/* Cache lookup */
+
+static int submit_partial_cache_miss(struct btree *b, struct btree_op *op,
+ struct bkey *k)
+{
+ struct search *s = container_of(op, struct search, op);
+ struct bio *bio = &s->bio.bio;
+ int ret = 0;
+
+ while (!ret &&
+ !op->lookup_done) {
+ unsigned sectors = INT_MAX;
+
+ if (KEY_INODE(k) == op->inode) {
+ if (KEY_START(k) <= bio->bi_sector)
+ break;
+
+ sectors = min_t(uint64_t, sectors,
+ KEY_START(k) - bio->bi_sector);
+ }
+
+ ret = s->d->cache_miss(b, s, bio, sectors);
+ }
+
+ return ret;
+}
+
+/*
+ * Read from a single key, handling the initial cache miss if the key starts in
+ * the middle of the bio
+ */
+static int submit_partial_cache_hit(struct btree *b, struct btree_op *op,
+ struct bkey *k)
+{
+ struct search *s = container_of(op, struct search, op);
+ struct bio *bio = &s->bio.bio;
+ unsigned ptr;
+ struct bio *n;
+
+ int ret = submit_partial_cache_miss(b, op, k);
+ if (ret || op->lookup_done)
+ return ret;
+
+ /* XXX: figure out best pointer - for multiple cache devices */
+ ptr = 0;
+
+ PTR_BUCKET(b->c, k, ptr)->prio = INITIAL_PRIO;
+
+ while (!op->lookup_done &&
+ KEY_INODE(k) == op->inode &&
+ bio->bi_sector < KEY_OFFSET(k)) {
+ struct bkey *bio_key;
+ sector_t sector = PTR_OFFSET(k, ptr) +
+ (bio->bi_sector - KEY_START(k));
+ unsigned sectors = min_t(uint64_t, INT_MAX,
+ KEY_OFFSET(k) - bio->bi_sector);
+
+ n = bch_bio_split(bio, sectors, GFP_NOIO, s->d->bio_split);
+ if (!n)
+ return -EAGAIN;
+
+ if (n == bio)
+ op->lookup_done = true;
+
+ bio_key = &container_of(n, struct bbio, bio)->key;
+
+ /*
+ * The bucket we're reading from might be reused while our bio
+ * is in flight, and we could then end up reading the wrong
+ * data.
+ *
+ * We guard against this by checking (in cache_read_endio()) if
+ * the pointer is stale again; if so, we treat it as an error
+ * and reread from the backing device (but we don't pass that
+ * error up anywhere).
+ */
+
+ bch_bkey_copy_single_ptr(bio_key, k, ptr);
+ SET_PTR_OFFSET(bio_key, 0, sector);
+
+ n->bi_end_io = bch_cache_read_endio;
+ n->bi_private = &s->cl;
+
+ trace_bcache_cache_hit(n);
+ __bch_submit_bbio(n, b->c);
+ }
+
+ return 0;
+}
+
+int bch_btree_search_recurse(struct btree *b, struct btree_op *op)
+{
+ struct search *s = container_of(op, struct search, op);
+ struct bio *bio = &s->bio.bio;
+
+ int ret = 0;
+ struct bkey *k;
+ struct btree_iter iter;
+ bch_btree_iter_init(b, &iter, &KEY(op->inode, bio->bi_sector, 0));
+
+ pr_debug("at %s searching for %u:%llu", pbtree(b), op->inode,
+ (uint64_t) bio->bi_sector);
+
+ do {
+ k = bch_btree_iter_next_filter(&iter, b, bch_ptr_bad);
+ if (!k) {
+ /*
+ * b->key would be exactly what we want, except that
+ * pointers to btree nodes have nonzero size - we
+ * wouldn't go far enough
+ */
+
+ ret = submit_partial_cache_miss(b, op,
+ &KEY(KEY_INODE(&b->key),
+ KEY_OFFSET(&b->key), 0));
+ break;
+ }
+
+ ret = b->level
+ ? btree(search_recurse, k, b, op)
+ : submit_partial_cache_hit(b, op, k);
+ } while (!ret &&
+ !op->lookup_done);
+
+ return ret;
+}
+
+/* Keybuf code */
+
+static inline int keybuf_cmp(struct keybuf_key *l, struct keybuf_key *r)
+{
+ /* Overlapping keys compare equal */
+ if (bkey_cmp(&l->key, &START_KEY(&r->key)) <= 0)
+ return -1;
+ if (bkey_cmp(&START_KEY(&l->key), &r->key) >= 0)
+ return 1;
+ return 0;
+}
+
+static inline int keybuf_nonoverlapping_cmp(struct keybuf_key *l,
+ struct keybuf_key *r)
+{
+ return clamp_t(int64_t, bkey_cmp(&l->key, &r->key), -1, 1);
+}
+
+static int bch_btree_refill_keybuf(struct btree *b, struct btree_op *op,
+ struct keybuf *buf, struct bkey *end)
+{
+ struct btree_iter iter;
+ bch_btree_iter_init(b, &iter, &buf->last_scanned);
+
+ while (!array_freelist_empty(&buf->freelist)) {
+ struct bkey *k = bch_btree_iter_next_filter(&iter, b,
+ bch_ptr_bad);
+
+ if (!b->level) {
+ if (!k) {
+ buf->last_scanned = b->key;
+ break;
+ }
+
+ buf->last_scanned = *k;
+ if (bkey_cmp(&buf->last_scanned, end) >= 0)
+ break;
+
+ if (buf->key_predicate(buf, k)) {
+ struct keybuf_key *w;
+
+ pr_debug("%s", pkey(k));
+
+ spin_lock(&buf->lock);
+
+ w = array_alloc(&buf->freelist);
+
+ w->private = NULL;
+ bkey_copy(&w->key, k);
+
+ if (RB_INSERT(&buf->keys, w, node, keybuf_cmp))
+ array_free(&buf->freelist, w);
+
+ spin_unlock(&buf->lock);
+ }
+ } else {
+ if (!k)
+ break;
+
+ btree(refill_keybuf, k, b, op, buf, end);
+ /*
+ * Might get an error here, but can't really do anything
+ * and it'll get logged elsewhere. Just read what we
+ * can.
+ */
+
+ if (bkey_cmp(&buf->last_scanned, end) >= 0)
+ break;
+
+ cond_resched();
+ }
+ }
+
+ return 0;
+}
+
+void bch_refill_keybuf(struct cache_set *c, struct keybuf *buf,
+ struct bkey *end)
+{
+ struct bkey start = buf->last_scanned;
+ struct btree_op op;
+ bch_btree_op_init_stack(&op);
+
+ cond_resched();
+
+ btree_root(refill_keybuf, c, &op, buf, end);
+ closure_sync(&op.cl);
+
+ pr_debug("found %s keys from %llu:%llu to %llu:%llu",
+ RB_EMPTY_ROOT(&buf->keys) ? "no" :
+ array_freelist_empty(&buf->freelist) ? "some" : "a few",
+ KEY_INODE(&start), KEY_OFFSET(&start),
+ KEY_INODE(&buf->last_scanned), KEY_OFFSET(&buf->last_scanned));
+
+ spin_lock(&buf->lock);
+
+ if (!RB_EMPTY_ROOT(&buf->keys)) {
+ struct keybuf_key *w;
+ w = RB_FIRST(&buf->keys, struct keybuf_key, node);
+ buf->start = START_KEY(&w->key);
+
+ w = RB_LAST(&buf->keys, struct keybuf_key, node);
+ buf->end = w->key;
+ } else {
+ buf->start = MAX_KEY;
+ buf->end = MAX_KEY;
+ }
+
+ spin_unlock(&buf->lock);
+}
+
+static void __bch_keybuf_del(struct keybuf *buf, struct keybuf_key *w)
+{
+ rb_erase(&w->node, &buf->keys);
+ array_free(&buf->freelist, w);
+}
+
+void bch_keybuf_del(struct keybuf *buf, struct keybuf_key *w)
+{
+ spin_lock(&buf->lock);
+ __bch_keybuf_del(buf, w);
+ spin_unlock(&buf->lock);
+}
+
+bool bch_keybuf_check_overlapping(struct keybuf *buf, struct bkey *start,
+ struct bkey *end)
+{
+ bool ret = false;
+ struct keybuf_key *p, *w, s;
+ s.key = *start;
+
+ if (bkey_cmp(end, &buf->start) <= 0 ||
+ bkey_cmp(start, &buf->end) >= 0)
+ return false;
+
+ spin_lock(&buf->lock);
+ w = RB_GREATER(&buf->keys, s, node, keybuf_nonoverlapping_cmp);
+
+ while (w && bkey_cmp(&START_KEY(&w->key), end) < 0) {
+ p = w;
+ w = RB_NEXT(w, node);
+
+ if (p->private)
+ ret = true;
+ else
+ __bch_keybuf_del(buf, p);
+ }
+
+ spin_unlock(&buf->lock);
+ return ret;
+}
+
+struct keybuf_key *bch_keybuf_next(struct keybuf *buf)
+{
+ struct keybuf_key *w;
+ spin_lock(&buf->lock);
+
+ w = RB_FIRST(&buf->keys, struct keybuf_key, node);
+
+ while (w && w->private)
+ w = RB_NEXT(w, node);
+
+ if (w)
+ w->private = ERR_PTR(-EINTR);
+
+ spin_unlock(&buf->lock);
+ return w;
+}
+
+struct keybuf_key *bch_keybuf_next_rescan(struct cache_set *c,
+ struct keybuf *buf,
+ struct bkey *end)
+{
+ struct keybuf_key *ret;
+
+ while (1) {
+ ret = bch_keybuf_next(buf);
+ if (ret)
+ break;
+
+ if (bkey_cmp(&buf->last_scanned, end) >= 0) {
+ pr_debug("scan finished");
+ break;
+ }
+
+ bch_refill_keybuf(c, buf, end);
+ }
+
+ return ret;
+}
+
+void bch_keybuf_init(struct keybuf *buf, keybuf_pred_fn *fn)
+{
+ buf->key_predicate = fn;
+ buf->last_scanned = MAX_KEY;
+ buf->keys = RB_ROOT;
+
+ spin_lock_init(&buf->lock);
+ array_allocator_init(&buf->freelist);
+}
+
+void bch_btree_exit(void)
+{
+ if (btree_io_wq)
+ destroy_workqueue(btree_io_wq);
+ if (bch_gc_wq)
+ destroy_workqueue(bch_gc_wq);
+}
+
+int __init bch_btree_init(void)
+{
+ if (!(bch_gc_wq = create_singlethread_workqueue("bch_btree_gc")) ||
+ !(btree_io_wq = create_singlethread_workqueue("bch_btree_io")))
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/drivers/md/bcache/btree.h b/drivers/md/bcache/btree.h
new file mode 100644
index 000000000000..af4a7092a28c
--- /dev/null
+++ b/drivers/md/bcache/btree.h
@@ -0,0 +1,405 @@
+#ifndef _BCACHE_BTREE_H
+#define _BCACHE_BTREE_H
+
+/*
+ * THE BTREE:
+ *
+ * At a high level, bcache's btree is relatively standard b+ tree. All keys and
+ * pointers are in the leaves; interior nodes only have pointers to the child
+ * nodes.
+ *
+ * In the interior nodes, a struct bkey always points to a child btree node, and
+ * the key is the highest key in the child node - except that the highest key in
+ * an interior node is always MAX_KEY. The size field refers to the size on disk
+ * of the child node - this would allow us to have variable sized btree nodes
+ * (handy for keeping the depth of the btree 1 by expanding just the root).
+ *
+ * Btree nodes are themselves log structured, but this is hidden fairly
+ * thoroughly. Btree nodes on disk will in practice have extents that overlap
+ * (because they were written at different times), but in memory we never have
+ * overlapping extents - when we read in a btree node from disk, the first thing
+ * we do is resort all the sets of keys with a mergesort, and in the same pass
+ * we check for overlapping extents and adjust them appropriately.
+ *
+ * struct btree_op is a central interface to the btree code. It's used for
+ * specifying read vs. write locking, and the embedded closure is used for
+ * waiting on IO or reserve memory.
+ *
+ * BTREE CACHE:
+ *
+ * Btree nodes are cached in memory; traversing the btree might require reading
+ * in btree nodes which is handled mostly transparently.
+ *
+ * bch_btree_node_get() looks up a btree node in the cache and reads it in from
+ * disk if necessary. This function is almost never called directly though - the
+ * btree() macro is used to get a btree node, call some function on it, and
+ * unlock the node after the function returns.
+ *
+ * The root is special cased - it's taken out of the cache's lru (thus pinning
+ * it in memory), so we can find the root of the btree by just dereferencing a
+ * pointer instead of looking it up in the cache. This makes locking a bit
+ * tricky, since the root pointer is protected by the lock in the btree node it
+ * points to - the btree_root() macro handles this.
+ *
+ * In various places we must be able to allocate memory for multiple btree nodes
+ * in order to make forward progress. To do this we use the btree cache itself
+ * as a reserve; if __get_free_pages() fails, we'll find a node in the btree
+ * cache we can reuse. We can't allow more than one thread to be doing this at a
+ * time, so there's a lock, implemented by a pointer to the btree_op closure -
+ * this allows the btree_root() macro to implicitly release this lock.
+ *
+ * BTREE IO:
+ *
+ * Btree nodes never have to be explicitly read in; bch_btree_node_get() handles
+ * this.
+ *
+ * For writing, we have two btree_write structs embeddded in struct btree - one
+ * write in flight, and one being set up, and we toggle between them.
+ *
+ * Writing is done with a single function - bch_btree_write() really serves two
+ * different purposes and should be broken up into two different functions. When
+ * passing now = false, it merely indicates that the node is now dirty - calling
+ * it ensures that the dirty keys will be written at some point in the future.
+ *
+ * When passing now = true, bch_btree_write() causes a write to happen
+ * "immediately" (if there was already a write in flight, it'll cause the write
+ * to happen as soon as the previous write completes). It returns immediately
+ * though - but it takes a refcount on the closure in struct btree_op you passed
+ * to it, so a closure_sync() later can be used to wait for the write to
+ * complete.
+ *
+ * This is handy because btree_split() and garbage collection can issue writes
+ * in parallel, reducing the amount of time they have to hold write locks.
+ *
+ * LOCKING:
+ *
+ * When traversing the btree, we may need write locks starting at some level -
+ * inserting a key into the btree will typically only require a write lock on
+ * the leaf node.
+ *
+ * This is specified with the lock field in struct btree_op; lock = 0 means we
+ * take write locks at level <= 0, i.e. only leaf nodes. bch_btree_node_get()
+ * checks this field and returns the node with the appropriate lock held.
+ *
+ * If, after traversing the btree, the insertion code discovers it has to split
+ * then it must restart from the root and take new locks - to do this it changes
+ * the lock field and returns -EINTR, which causes the btree_root() macro to
+ * loop.
+ *
+ * Handling cache misses require a different mechanism for upgrading to a write
+ * lock. We do cache lookups with only a read lock held, but if we get a cache
+ * miss and we wish to insert this data into the cache, we have to insert a
+ * placeholder key to detect races - otherwise, we could race with a write and
+ * overwrite the data that was just written to the cache with stale data from
+ * the backing device.
+ *
+ * For this we use a sequence number that write locks and unlocks increment - to
+ * insert the check key it unlocks the btree node and then takes a write lock,
+ * and fails if the sequence number doesn't match.
+ */
+
+#include "bset.h"
+#include "debug.h"
+
+struct btree_write {
+ struct closure *owner;
+ atomic_t *journal;
+
+ /* If btree_split() frees a btree node, it writes a new pointer to that
+ * btree node indicating it was freed; it takes a refcount on
+ * c->prio_blocked because we can't write the gens until the new
+ * pointer is on disk. This allows btree_write_endio() to release the
+ * refcount that btree_split() took.
+ */
+ int prio_blocked;
+};
+
+struct btree {
+ /* Hottest entries first */
+ struct hlist_node hash;
+
+ /* Key/pointer for this btree node */
+ BKEY_PADDED(key);
+
+ /* Single bit - set when accessed, cleared by shrinker */
+ unsigned long accessed;
+ unsigned long seq;
+ struct rw_semaphore lock;
+ struct cache_set *c;
+
+ unsigned long flags;
+ uint16_t written; /* would be nice to kill */
+ uint8_t level;
+ uint8_t nsets;
+ uint8_t page_order;
+
+ /*
+ * Set of sorted keys - the real btree node - plus a binary search tree
+ *
+ * sets[0] is special; set[0]->tree, set[0]->prev and set[0]->data point
+ * to the memory we have allocated for this btree node. Additionally,
+ * set[0]->data points to the entire btree node as it exists on disk.
+ */
+ struct bset_tree sets[MAX_BSETS];
+
+ /* Used to refcount bio splits, also protects b->bio */
+ struct closure_with_waitlist io;
+
+ /* Gets transferred to w->prio_blocked - see the comment there */
+ int prio_blocked;
+
+ struct list_head list;
+ struct delayed_work work;
+
+ uint64_t io_start_time;
+ struct btree_write writes[2];
+ struct bio *bio;
+};
+
+#define BTREE_FLAG(flag) \
+static inline bool btree_node_ ## flag(struct btree *b) \
+{ return test_bit(BTREE_NODE_ ## flag, &b->flags); } \
+ \
+static inline void set_btree_node_ ## flag(struct btree *b) \
+{ set_bit(BTREE_NODE_ ## flag, &b->flags); } \
+
+enum btree_flags {
+ BTREE_NODE_read_done,
+ BTREE_NODE_io_error,
+ BTREE_NODE_dirty,
+ BTREE_NODE_write_idx,
+};
+
+BTREE_FLAG(read_done);
+BTREE_FLAG(io_error);
+BTREE_FLAG(dirty);
+BTREE_FLAG(write_idx);
+
+static inline struct btree_write *btree_current_write(struct btree *b)
+{
+ return b->writes + btree_node_write_idx(b);
+}
+
+static inline struct btree_write *btree_prev_write(struct btree *b)
+{
+ return b->writes + (btree_node_write_idx(b) ^ 1);
+}
+
+static inline unsigned bset_offset(struct btree *b, struct bset *i)
+{
+ return (((size_t) i) - ((size_t) b->sets->data)) >> 9;
+}
+
+static inline struct bset *write_block(struct btree *b)
+{
+ return ((void *) b->sets[0].data) + b->written * block_bytes(b->c);
+}
+
+static inline bool bset_written(struct btree *b, struct bset_tree *t)
+{
+ return t->data < write_block(b);
+}
+
+static inline bool bkey_written(struct btree *b, struct bkey *k)
+{
+ return k < write_block(b)->start;
+}
+
+static inline void set_gc_sectors(struct cache_set *c)
+{
+ atomic_set(&c->sectors_to_gc, c->sb.bucket_size * c->nbuckets / 8);
+}
+
+static inline bool bch_ptr_invalid(struct btree *b, const struct bkey *k)
+{
+ return __bch_ptr_invalid(b->c, b->level, k);
+}
+
+static inline struct bkey *bch_btree_iter_init(struct btree *b,
+ struct btree_iter *iter,
+ struct bkey *search)
+{
+ return __bch_btree_iter_init(b, iter, search, b->sets);
+}
+
+/* Looping macros */
+
+#define for_each_cached_btree(b, c, iter) \
+ for (iter = 0; \
+ iter < ARRAY_SIZE((c)->bucket_hash); \
+ iter++) \
+ hlist_for_each_entry_rcu((b), (c)->bucket_hash + iter, hash)
+
+#define for_each_key_filter(b, k, iter, filter) \
+ for (bch_btree_iter_init((b), (iter), NULL); \
+ ((k) = bch_btree_iter_next_filter((iter), b, filter));)
+
+#define for_each_key(b, k, iter) \
+ for (bch_btree_iter_init((b), (iter), NULL); \
+ ((k) = bch_btree_iter_next(iter));)
+
+/* Recursing down the btree */
+
+struct btree_op {
+ struct closure cl;
+ struct cache_set *c;
+
+ /* Journal entry we have a refcount on */
+ atomic_t *journal;
+
+ /* Bio to be inserted into the cache */
+ struct bio *cache_bio;
+
+ unsigned inode;
+
+ uint16_t write_prio;
+
+ /* Btree level at which we start taking write locks */
+ short lock;
+
+ /* Btree insertion type */
+ enum {
+ BTREE_INSERT,
+ BTREE_REPLACE
+ } type:8;
+
+ unsigned csum:1;
+ unsigned skip:1;
+ unsigned flush_journal:1;
+
+ unsigned insert_data_done:1;
+ unsigned lookup_done:1;
+ unsigned insert_collision:1;
+
+ /* Anything after this point won't get zeroed in do_bio_hook() */
+
+ /* Keys to be inserted */
+ struct keylist keys;
+ BKEY_PADDED(replace);
+};
+
+void bch_btree_op_init_stack(struct btree_op *);
+
+static inline void rw_lock(bool w, struct btree *b, int level)
+{
+ w ? down_write_nested(&b->lock, level + 1)
+ : down_read_nested(&b->lock, level + 1);
+ if (w)
+ b->seq++;
+}
+
+static inline void rw_unlock(bool w, struct btree *b)
+{
+#ifdef CONFIG_BCACHE_EDEBUG
+ unsigned i;
+
+ if (w &&
+ b->key.ptr[0] &&
+ btree_node_read_done(b))
+ for (i = 0; i <= b->nsets; i++)
+ bch_check_key_order(b, b->sets[i].data);
+#endif
+
+ if (w)
+ b->seq++;
+ (w ? up_write : up_read)(&b->lock);
+}
+
+#define insert_lock(s, b) ((b)->level <= (s)->lock)
+
+/*
+ * These macros are for recursing down the btree - they handle the details of
+ * locking and looking up nodes in the cache for you. They're best treated as
+ * mere syntax when reading code that uses them.
+ *
+ * op->lock determines whether we take a read or a write lock at a given depth.
+ * If you've got a read lock and find that you need a write lock (i.e. you're
+ * going to have to split), set op->lock and return -EINTR; btree_root() will
+ * call you again and you'll have the correct lock.
+ */
+
+/**
+ * btree - recurse down the btree on a specified key
+ * @fn: function to call, which will be passed the child node
+ * @key: key to recurse on
+ * @b: parent btree node
+ * @op: pointer to struct btree_op
+ */
+#define btree(fn, key, b, op, ...) \
+({ \
+ int _r, l = (b)->level - 1; \
+ bool _w = l <= (op)->lock; \
+ struct btree *_b = bch_btree_node_get((b)->c, key, l, op); \
+ if (!IS_ERR(_b)) { \
+ _r = bch_btree_ ## fn(_b, op, ##__VA_ARGS__); \
+ rw_unlock(_w, _b); \
+ } else \
+ _r = PTR_ERR(_b); \
+ _r; \
+})
+
+/**
+ * btree_root - call a function on the root of the btree
+ * @fn: function to call, which will be passed the child node
+ * @c: cache set
+ * @op: pointer to struct btree_op
+ */
+#define btree_root(fn, c, op, ...) \
+({ \
+ int _r = -EINTR; \
+ do { \
+ struct btree *_b = (c)->root; \
+ bool _w = insert_lock(op, _b); \
+ rw_lock(_w, _b, _b->level); \
+ if (_b == (c)->root && \
+ _w == insert_lock(op, _b)) \
+ _r = bch_btree_ ## fn(_b, op, ##__VA_ARGS__); \
+ rw_unlock(_w, _b); \
+ bch_cannibalize_unlock(c, &(op)->cl); \
+ } while (_r == -EINTR); \
+ \
+ _r; \
+})
+
+static inline bool should_split(struct btree *b)
+{
+ struct bset *i = write_block(b);
+ return b->written >= btree_blocks(b) ||
+ (i->seq == b->sets[0].data->seq &&
+ b->written + __set_blocks(i, i->keys + 15, b->c)
+ > btree_blocks(b));
+}
+
+void bch_btree_read_done(struct closure *);
+void bch_btree_read(struct btree *);
+void bch_btree_write(struct btree *b, bool now, struct btree_op *op);
+
+void bch_cannibalize_unlock(struct cache_set *, struct closure *);
+void bch_btree_set_root(struct btree *);
+struct btree *bch_btree_node_alloc(struct cache_set *, int, struct closure *);
+struct btree *bch_btree_node_get(struct cache_set *, struct bkey *,
+ int, struct btree_op *);
+
+bool bch_btree_insert_keys(struct btree *, struct btree_op *);
+bool bch_btree_insert_check_key(struct btree *, struct btree_op *,
+ struct bio *);
+int bch_btree_insert(struct btree_op *, struct cache_set *);
+
+int bch_btree_search_recurse(struct btree *, struct btree_op *);
+
+void bch_queue_gc(struct cache_set *);
+size_t bch_btree_gc_finish(struct cache_set *);
+void bch_moving_gc(struct closure *);
+int bch_btree_check(struct cache_set *, struct btree_op *);
+uint8_t __bch_btree_mark_key(struct cache_set *, int, struct bkey *);
+
+void bch_keybuf_init(struct keybuf *, keybuf_pred_fn *);
+void bch_refill_keybuf(struct cache_set *, struct keybuf *, struct bkey *);
+bool bch_keybuf_check_overlapping(struct keybuf *, struct bkey *,
+ struct bkey *);
+void bch_keybuf_del(struct keybuf *, struct keybuf_key *);
+struct keybuf_key *bch_keybuf_next(struct keybuf *);
+struct keybuf_key *bch_keybuf_next_rescan(struct cache_set *,
+ struct keybuf *, struct bkey *);
+
+#endif
diff --git a/drivers/md/bcache/closure.c b/drivers/md/bcache/closure.c
new file mode 100644
index 000000000000..bd05a9a8c7cf
--- /dev/null
+++ b/drivers/md/bcache/closure.c
@@ -0,0 +1,345 @@
+/*
+ * Asynchronous refcounty things
+ *
+ * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
+ * Copyright 2012 Google, Inc.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+
+#include "closure.h"
+
+void closure_queue(struct closure *cl)
+{
+ struct workqueue_struct *wq = cl->wq;
+ if (wq) {
+ INIT_WORK(&cl->work, cl->work.func);
+ BUG_ON(!queue_work(wq, &cl->work));
+ } else
+ cl->fn(cl);
+}
+EXPORT_SYMBOL_GPL(closure_queue);
+
+#define CL_FIELD(type, field) \
+ case TYPE_ ## type: \
+ return &container_of(cl, struct type, cl)->field
+
+static struct closure_waitlist *closure_waitlist(struct closure *cl)
+{
+ switch (cl->type) {
+ CL_FIELD(closure_with_waitlist, wait);
+ CL_FIELD(closure_with_waitlist_and_timer, wait);
+ default:
+ return NULL;
+ }
+}
+
+static struct timer_list *closure_timer(struct closure *cl)
+{
+ switch (cl->type) {
+ CL_FIELD(closure_with_timer, timer);
+ CL_FIELD(closure_with_waitlist_and_timer, timer);
+ default:
+ return NULL;
+ }
+}
+
+static inline void closure_put_after_sub(struct closure *cl, int flags)
+{
+ int r = flags & CLOSURE_REMAINING_MASK;
+
+ BUG_ON(flags & CLOSURE_GUARD_MASK);
+ BUG_ON(!r && (flags & ~(CLOSURE_DESTRUCTOR|CLOSURE_BLOCKING)));
+
+ /* Must deliver precisely one wakeup */
+ if (r == 1 && (flags & CLOSURE_SLEEPING))
+ wake_up_process(cl->task);
+
+ if (!r) {
+ if (cl->fn && !(flags & CLOSURE_DESTRUCTOR)) {
+ /* CLOSURE_BLOCKING might be set - clear it */
+ atomic_set(&cl->remaining,
+ CLOSURE_REMAINING_INITIALIZER);
+ closure_queue(cl);
+ } else {
+ struct closure *parent = cl->parent;
+ struct closure_waitlist *wait = closure_waitlist(cl);
+
+ closure_debug_destroy(cl);
+
+ atomic_set(&cl->remaining, -1);
+
+ if (wait)
+ closure_wake_up(wait);
+
+ if (cl->fn)
+ cl->fn(cl);
+
+ if (parent)
+ closure_put(parent);
+ }
+ }
+}
+
+/* For clearing flags with the same atomic op as a put */
+void closure_sub(struct closure *cl, int v)
+{
+ closure_put_after_sub(cl, atomic_sub_return(v, &cl->remaining));
+}
+EXPORT_SYMBOL_GPL(closure_sub);
+
+void closure_put(struct closure *cl)
+{
+ closure_put_after_sub(cl, atomic_dec_return(&cl->remaining));
+}
+EXPORT_SYMBOL_GPL(closure_put);
+
+static void set_waiting(struct closure *cl, unsigned long f)
+{
+#ifdef CONFIG_BCACHE_CLOSURES_DEBUG
+ cl->waiting_on = f;
+#endif
+}
+
+void __closure_wake_up(struct closure_waitlist *wait_list)
+{
+ struct llist_node *list;
+ struct closure *cl;
+ struct llist_node *reverse = NULL;
+
+ list = llist_del_all(&wait_list->list);
+
+ /* We first reverse the list to preserve FIFO ordering and fairness */
+
+ while (list) {
+ struct llist_node *t = list;
+ list = llist_next(list);
+
+ t->next = reverse;
+ reverse = t;
+ }
+
+ /* Then do the wakeups */
+
+ while (reverse) {
+ cl = container_of(reverse, struct closure, list);
+ reverse = llist_next(reverse);
+
+ set_waiting(cl, 0);
+ closure_sub(cl, CLOSURE_WAITING + 1);
+ }
+}
+EXPORT_SYMBOL_GPL(__closure_wake_up);
+
+bool closure_wait(struct closure_waitlist *list, struct closure *cl)
+{
+ if (atomic_read(&cl->remaining) & CLOSURE_WAITING)
+ return false;
+
+ set_waiting(cl, _RET_IP_);
+ atomic_add(CLOSURE_WAITING + 1, &cl->remaining);
+ llist_add(&cl->list, &list->list);
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(closure_wait);
+
+/**
+ * closure_sync() - sleep until a closure a closure has nothing left to wait on
+ *
+ * Sleeps until the refcount hits 1 - the thread that's running the closure owns
+ * the last refcount.
+ */
+void closure_sync(struct closure *cl)
+{
+ while (1) {
+ __closure_start_sleep(cl);
+ closure_set_ret_ip(cl);
+
+ if ((atomic_read(&cl->remaining) &
+ CLOSURE_REMAINING_MASK) == 1)
+ break;
+
+ schedule();
+ }
+
+ __closure_end_sleep(cl);
+}
+EXPORT_SYMBOL_GPL(closure_sync);
+
+/**
+ * closure_trylock() - try to acquire the closure, without waiting
+ * @cl: closure to lock
+ *
+ * Returns true if the closure was succesfully locked.
+ */
+bool closure_trylock(struct closure *cl, struct closure *parent)
+{
+ if (atomic_cmpxchg(&cl->remaining, -1,
+ CLOSURE_REMAINING_INITIALIZER) != -1)
+ return false;
+
+ closure_set_ret_ip(cl);
+
+ smp_mb();
+ cl->parent = parent;
+ if (parent)
+ closure_get(parent);
+
+ closure_debug_create(cl);
+ return true;
+}
+EXPORT_SYMBOL_GPL(closure_trylock);
+
+void __closure_lock(struct closure *cl, struct closure *parent,
+ struct closure_waitlist *wait_list)
+{
+ struct closure wait;
+ closure_init_stack(&wait);
+
+ while (1) {
+ if (closure_trylock(cl, parent))
+ return;
+
+ closure_wait_event_sync(wait_list, &wait,
+ atomic_read(&cl->remaining) == -1);
+ }
+}
+EXPORT_SYMBOL_GPL(__closure_lock);
+
+static void closure_delay_timer_fn(unsigned long data)
+{
+ struct closure *cl = (struct closure *) data;
+ closure_sub(cl, CLOSURE_TIMER + 1);
+}
+
+void do_closure_timer_init(struct closure *cl)
+{
+ struct timer_list *timer = closure_timer(cl);
+
+ init_timer(timer);
+ timer->data = (unsigned long) cl;
+ timer->function = closure_delay_timer_fn;
+}
+EXPORT_SYMBOL_GPL(do_closure_timer_init);
+
+bool __closure_delay(struct closure *cl, unsigned long delay,
+ struct timer_list *timer)
+{
+ if (atomic_read(&cl->remaining) & CLOSURE_TIMER)
+ return false;
+
+ BUG_ON(timer_pending(timer));
+
+ timer->expires = jiffies + delay;
+
+ atomic_add(CLOSURE_TIMER + 1, &cl->remaining);
+ add_timer(timer);
+ return true;
+}
+EXPORT_SYMBOL_GPL(__closure_delay);
+
+void __closure_flush(struct closure *cl, struct timer_list *timer)
+{
+ if (del_timer(timer))
+ closure_sub(cl, CLOSURE_TIMER + 1);
+}
+EXPORT_SYMBOL_GPL(__closure_flush);
+
+void __closure_flush_sync(struct closure *cl, struct timer_list *timer)
+{
+ if (del_timer_sync(timer))
+ closure_sub(cl, CLOSURE_TIMER + 1);
+}
+EXPORT_SYMBOL_GPL(__closure_flush_sync);
+
+#ifdef CONFIG_BCACHE_CLOSURES_DEBUG
+
+static LIST_HEAD(closure_list);
+static DEFINE_SPINLOCK(closure_list_lock);
+
+void closure_debug_create(struct closure *cl)
+{
+ unsigned long flags;
+
+ BUG_ON(cl->magic == CLOSURE_MAGIC_ALIVE);
+ cl->magic = CLOSURE_MAGIC_ALIVE;
+
+ spin_lock_irqsave(&closure_list_lock, flags);
+ list_add(&cl->all, &closure_list);
+ spin_unlock_irqrestore(&closure_list_lock, flags);
+}
+EXPORT_SYMBOL_GPL(closure_debug_create);
+
+void closure_debug_destroy(struct closure *cl)
+{
+ unsigned long flags;
+
+ BUG_ON(cl->magic != CLOSURE_MAGIC_ALIVE);
+ cl->magic = CLOSURE_MAGIC_DEAD;
+
+ spin_lock_irqsave(&closure_list_lock, flags);
+ list_del(&cl->all);
+ spin_unlock_irqrestore(&closure_list_lock, flags);
+}
+EXPORT_SYMBOL_GPL(closure_debug_destroy);
+
+static struct dentry *debug;
+
+#define work_data_bits(work) ((unsigned long *)(&(work)->data))
+
+static int debug_seq_show(struct seq_file *f, void *data)
+{
+ struct closure *cl;
+ spin_lock_irq(&closure_list_lock);
+
+ list_for_each_entry(cl, &closure_list, all) {
+ int r = atomic_read(&cl->remaining);
+
+ seq_printf(f, "%p: %pF -> %pf p %p r %i ",
+ cl, (void *) cl->ip, cl->fn, cl->parent,
+ r & CLOSURE_REMAINING_MASK);
+
+ seq_printf(f, "%s%s%s%s%s%s\n",
+ test_bit(WORK_STRUCT_PENDING,
+ work_data_bits(&cl->work)) ? "Q" : "",
+ r & CLOSURE_RUNNING ? "R" : "",
+ r & CLOSURE_BLOCKING ? "B" : "",
+ r & CLOSURE_STACK ? "S" : "",
+ r & CLOSURE_SLEEPING ? "Sl" : "",
+ r & CLOSURE_TIMER ? "T" : "");
+
+ if (r & CLOSURE_WAITING)
+ seq_printf(f, " W %pF\n",
+ (void *) cl->waiting_on);
+
+ seq_printf(f, "\n");
+ }
+
+ spin_unlock_irq(&closure_list_lock);
+ return 0;
+}
+
+static int debug_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_seq_show, NULL);
+}
+
+static const struct file_operations debug_ops = {
+ .owner = THIS_MODULE,
+ .open = debug_seq_open,
+ .read = seq_read,
+ .release = single_release
+};
+
+void __init closure_debug_init(void)
+{
+ debug = debugfs_create_file("closures", 0400, NULL, NULL, &debug_ops);
+}
+
+#endif
+
+MODULE_AUTHOR("Kent Overstreet <koverstreet@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/bcache/closure.h b/drivers/md/bcache/closure.h
new file mode 100644
index 000000000000..00039924ea9d
--- /dev/null
+++ b/drivers/md/bcache/closure.h
@@ -0,0 +1,672 @@
+#ifndef _LINUX_CLOSURE_H
+#define _LINUX_CLOSURE_H
+
+#include <linux/llist.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+
+/*
+ * Closure is perhaps the most overused and abused term in computer science, but
+ * since I've been unable to come up with anything better you're stuck with it
+ * again.
+ *
+ * What are closures?
+ *
+ * They embed a refcount. The basic idea is they count "things that are in
+ * progress" - in flight bios, some other thread that's doing something else -
+ * anything you might want to wait on.
+ *
+ * The refcount may be manipulated with closure_get() and closure_put().
+ * closure_put() is where many of the interesting things happen, when it causes
+ * the refcount to go to 0.
+ *
+ * Closures can be used to wait on things both synchronously and asynchronously,
+ * and synchronous and asynchronous use can be mixed without restriction. To
+ * wait synchronously, use closure_sync() - you will sleep until your closure's
+ * refcount hits 1.
+ *
+ * To wait asynchronously, use
+ * continue_at(cl, next_function, workqueue);
+ *
+ * passing it, as you might expect, the function to run when nothing is pending
+ * and the workqueue to run that function out of.
+ *
+ * continue_at() also, critically, is a macro that returns the calling function.
+ * There's good reason for this.
+ *
+ * To use safely closures asynchronously, they must always have a refcount while
+ * they are running owned by the thread that is running them. Otherwise, suppose
+ * you submit some bios and wish to have a function run when they all complete:
+ *
+ * foo_endio(struct bio *bio, int error)
+ * {
+ * closure_put(cl);
+ * }
+ *
+ * closure_init(cl);
+ *
+ * do_stuff();
+ * closure_get(cl);
+ * bio1->bi_endio = foo_endio;
+ * bio_submit(bio1);
+ *
+ * do_more_stuff();
+ * closure_get(cl);
+ * bio2->bi_endio = foo_endio;
+ * bio_submit(bio2);
+ *
+ * continue_at(cl, complete_some_read, system_wq);
+ *
+ * If closure's refcount started at 0, complete_some_read() could run before the
+ * second bio was submitted - which is almost always not what you want! More
+ * importantly, it wouldn't be possible to say whether the original thread or
+ * complete_some_read()'s thread owned the closure - and whatever state it was
+ * associated with!
+ *
+ * So, closure_init() initializes a closure's refcount to 1 - and when a
+ * closure_fn is run, the refcount will be reset to 1 first.
+ *
+ * Then, the rule is - if you got the refcount with closure_get(), release it
+ * with closure_put() (i.e, in a bio->bi_endio function). If you have a refcount
+ * on a closure because you called closure_init() or you were run out of a
+ * closure - _always_ use continue_at(). Doing so consistently will help
+ * eliminate an entire class of particularly pernicious races.
+ *
+ * For a closure to wait on an arbitrary event, we need to introduce waitlists:
+ *
+ * struct closure_waitlist list;
+ * closure_wait_event(list, cl, condition);
+ * closure_wake_up(wait_list);
+ *
+ * These work analagously to wait_event() and wake_up() - except that instead of
+ * operating on the current thread (for wait_event()) and lists of threads, they
+ * operate on an explicit closure and lists of closures.
+ *
+ * Because it's a closure we can now wait either synchronously or
+ * asynchronously. closure_wait_event() returns the current value of the
+ * condition, and if it returned false continue_at() or closure_sync() can be
+ * used to wait for it to become true.
+ *
+ * It's useful for waiting on things when you can't sleep in the context in
+ * which you must check the condition (perhaps a spinlock held, or you might be
+ * beneath generic_make_request() - in which case you can't sleep on IO).
+ *
+ * closure_wait_event() will wait either synchronously or asynchronously,
+ * depending on whether the closure is in blocking mode or not. You can pick a
+ * mode explicitly with closure_wait_event_sync() and
+ * closure_wait_event_async(), which do just what you might expect.
+ *
+ * Lastly, you might have a wait list dedicated to a specific event, and have no
+ * need for specifying the condition - you just want to wait until someone runs
+ * closure_wake_up() on the appropriate wait list. In that case, just use
+ * closure_wait(). It will return either true or false, depending on whether the
+ * closure was already on a wait list or not - a closure can only be on one wait
+ * list at a time.
+ *
+ * Parents:
+ *
+ * closure_init() takes two arguments - it takes the closure to initialize, and
+ * a (possibly null) parent.
+ *
+ * If parent is non null, the new closure will have a refcount for its lifetime;
+ * a closure is considered to be "finished" when its refcount hits 0 and the
+ * function to run is null. Hence
+ *
+ * continue_at(cl, NULL, NULL);
+ *
+ * returns up the (spaghetti) stack of closures, precisely like normal return
+ * returns up the C stack. continue_at() with non null fn is better thought of
+ * as doing a tail call.
+ *
+ * All this implies that a closure should typically be embedded in a particular
+ * struct (which its refcount will normally control the lifetime of), and that
+ * struct can very much be thought of as a stack frame.
+ *
+ * Locking:
+ *
+ * Closures are based on work items but they can be thought of as more like
+ * threads - in that like threads and unlike work items they have a well
+ * defined lifetime; they are created (with closure_init()) and eventually
+ * complete after a continue_at(cl, NULL, NULL).
+ *
+ * Suppose you've got some larger structure with a closure embedded in it that's
+ * used for periodically doing garbage collection. You only want one garbage
+ * collection happening at a time, so the natural thing to do is protect it with
+ * a lock. However, it's difficult to use a lock protecting a closure correctly
+ * because the unlock should come after the last continue_to() (additionally, if
+ * you're using the closure asynchronously a mutex won't work since a mutex has
+ * to be unlocked by the same process that locked it).
+ *
+ * So to make it less error prone and more efficient, we also have the ability
+ * to use closures as locks:
+ *
+ * closure_init_unlocked();
+ * closure_trylock();
+ *
+ * That's all we need for trylock() - the last closure_put() implicitly unlocks
+ * it for you. But for closure_lock(), we also need a wait list:
+ *
+ * struct closure_with_waitlist frobnicator_cl;
+ *
+ * closure_init_unlocked(&frobnicator_cl);
+ * closure_lock(&frobnicator_cl);
+ *
+ * A closure_with_waitlist embeds a closure and a wait list - much like struct
+ * delayed_work embeds a work item and a timer_list. The important thing is, use
+ * it exactly like you would a regular closure and closure_put() will magically
+ * handle everything for you.
+ *
+ * We've got closures that embed timers, too. They're called, appropriately
+ * enough:
+ * struct closure_with_timer;
+ *
+ * This gives you access to closure_delay(). It takes a refcount for a specified
+ * number of jiffies - you could then call closure_sync() (for a slightly
+ * convoluted version of msleep()) or continue_at() - which gives you the same
+ * effect as using a delayed work item, except you can reuse the work_struct
+ * already embedded in struct closure.
+ *
+ * Lastly, there's struct closure_with_waitlist_and_timer. It does what you
+ * probably expect, if you happen to need the features of both. (You don't
+ * really want to know how all this is implemented, but if I've done my job
+ * right you shouldn't have to care).
+ */
+
+struct closure;
+typedef void (closure_fn) (struct closure *);
+
+struct closure_waitlist {
+ struct llist_head list;
+};
+
+enum closure_type {
+ TYPE_closure = 0,
+ TYPE_closure_with_waitlist = 1,
+ TYPE_closure_with_timer = 2,
+ TYPE_closure_with_waitlist_and_timer = 3,
+ MAX_CLOSURE_TYPE = 3,
+};
+
+enum closure_state {
+ /*
+ * CLOSURE_BLOCKING: Causes closure_wait_event() to block, instead of
+ * waiting asynchronously
+ *
+ * CLOSURE_WAITING: Set iff the closure is on a waitlist. Must be set by
+ * the thread that owns the closure, and cleared by the thread that's
+ * waking up the closure.
+ *
+ * CLOSURE_SLEEPING: Must be set before a thread uses a closure to sleep
+ * - indicates that cl->task is valid and closure_put() may wake it up.
+ * Only set or cleared by the thread that owns the closure.
+ *
+ * CLOSURE_TIMER: Analagous to CLOSURE_WAITING, indicates that a closure
+ * has an outstanding timer. Must be set by the thread that owns the
+ * closure, and cleared by the timer function when the timer goes off.
+ *
+ * The rest are for debugging and don't affect behaviour:
+ *
+ * CLOSURE_RUNNING: Set when a closure is running (i.e. by
+ * closure_init() and when closure_put() runs then next function), and
+ * must be cleared before remaining hits 0. Primarily to help guard
+ * against incorrect usage and accidentally transferring references.
+ * continue_at() and closure_return() clear it for you, if you're doing
+ * something unusual you can use closure_set_dead() which also helps
+ * annotate where references are being transferred.
+ *
+ * CLOSURE_STACK: Sanity check - remaining should never hit 0 on a
+ * closure with this flag set
+ */
+
+ CLOSURE_BITS_START = (1 << 19),
+ CLOSURE_DESTRUCTOR = (1 << 19),
+ CLOSURE_BLOCKING = (1 << 21),
+ CLOSURE_WAITING = (1 << 23),
+ CLOSURE_SLEEPING = (1 << 25),
+ CLOSURE_TIMER = (1 << 27),
+ CLOSURE_RUNNING = (1 << 29),
+ CLOSURE_STACK = (1 << 31),
+};
+
+#define CLOSURE_GUARD_MASK \
+ ((CLOSURE_DESTRUCTOR|CLOSURE_BLOCKING|CLOSURE_WAITING| \
+ CLOSURE_SLEEPING|CLOSURE_TIMER|CLOSURE_RUNNING|CLOSURE_STACK) << 1)
+
+#define CLOSURE_REMAINING_MASK (CLOSURE_BITS_START - 1)
+#define CLOSURE_REMAINING_INITIALIZER (1|CLOSURE_RUNNING)
+
+struct closure {
+ union {
+ struct {
+ struct workqueue_struct *wq;
+ struct task_struct *task;
+ struct llist_node list;
+ closure_fn *fn;
+ };
+ struct work_struct work;
+ };
+
+ struct closure *parent;
+
+ atomic_t remaining;
+
+ enum closure_type type;
+
+#ifdef CONFIG_BCACHE_CLOSURES_DEBUG
+#define CLOSURE_MAGIC_DEAD 0xc054dead
+#define CLOSURE_MAGIC_ALIVE 0xc054a11e
+
+ unsigned magic;
+ struct list_head all;
+ unsigned long ip;
+ unsigned long waiting_on;
+#endif
+};
+
+struct closure_with_waitlist {
+ struct closure cl;
+ struct closure_waitlist wait;
+};
+
+struct closure_with_timer {
+ struct closure cl;
+ struct timer_list timer;
+};
+
+struct closure_with_waitlist_and_timer {
+ struct closure cl;
+ struct closure_waitlist wait;
+ struct timer_list timer;
+};
+
+extern unsigned invalid_closure_type(void);
+
+#define __CLOSURE_TYPE(cl, _t) \
+ __builtin_types_compatible_p(typeof(cl), struct _t) \
+ ? TYPE_ ## _t : \
+
+#define __closure_type(cl) \
+( \
+ __CLOSURE_TYPE(cl, closure) \
+ __CLOSURE_TYPE(cl, closure_with_waitlist) \
+ __CLOSURE_TYPE(cl, closure_with_timer) \
+ __CLOSURE_TYPE(cl, closure_with_waitlist_and_timer) \
+ invalid_closure_type() \
+)
+
+void closure_sub(struct closure *cl, int v);
+void closure_put(struct closure *cl);
+void closure_queue(struct closure *cl);
+void __closure_wake_up(struct closure_waitlist *list);
+bool closure_wait(struct closure_waitlist *list, struct closure *cl);
+void closure_sync(struct closure *cl);
+
+bool closure_trylock(struct closure *cl, struct closure *parent);
+void __closure_lock(struct closure *cl, struct closure *parent,
+ struct closure_waitlist *wait_list);
+
+void do_closure_timer_init(struct closure *cl);
+bool __closure_delay(struct closure *cl, unsigned long delay,
+ struct timer_list *timer);
+void __closure_flush(struct closure *cl, struct timer_list *timer);
+void __closure_flush_sync(struct closure *cl, struct timer_list *timer);
+
+#ifdef CONFIG_BCACHE_CLOSURES_DEBUG
+
+void closure_debug_init(void);
+void closure_debug_create(struct closure *cl);
+void closure_debug_destroy(struct closure *cl);
+
+#else
+
+static inline void closure_debug_init(void) {}
+static inline void closure_debug_create(struct closure *cl) {}
+static inline void closure_debug_destroy(struct closure *cl) {}
+
+#endif
+
+static inline void closure_set_ip(struct closure *cl)
+{
+#ifdef CONFIG_BCACHE_CLOSURES_DEBUG
+ cl->ip = _THIS_IP_;
+#endif
+}
+
+static inline void closure_set_ret_ip(struct closure *cl)
+{
+#ifdef CONFIG_BCACHE_CLOSURES_DEBUG
+ cl->ip = _RET_IP_;
+#endif
+}
+
+static inline void closure_get(struct closure *cl)
+{
+#ifdef CONFIG_BCACHE_CLOSURES_DEBUG
+ BUG_ON((atomic_inc_return(&cl->remaining) &
+ CLOSURE_REMAINING_MASK) <= 1);
+#else
+ atomic_inc(&cl->remaining);
+#endif
+}
+
+static inline void closure_set_stopped(struct closure *cl)
+{
+ atomic_sub(CLOSURE_RUNNING, &cl->remaining);
+}
+
+static inline bool closure_is_stopped(struct closure *cl)
+{
+ return !(atomic_read(&cl->remaining) & CLOSURE_RUNNING);
+}
+
+static inline bool closure_is_unlocked(struct closure *cl)
+{
+ return atomic_read(&cl->remaining) == -1;
+}
+
+static inline void do_closure_init(struct closure *cl, struct closure *parent,
+ bool running)
+{
+ switch (cl->type) {
+ case TYPE_closure_with_timer:
+ case TYPE_closure_with_waitlist_and_timer:
+ do_closure_timer_init(cl);
+ default:
+ break;
+ }
+
+ cl->parent = parent;
+ if (parent)
+ closure_get(parent);
+
+ if (running) {
+ closure_debug_create(cl);
+ atomic_set(&cl->remaining, CLOSURE_REMAINING_INITIALIZER);
+ } else
+ atomic_set(&cl->remaining, -1);
+
+ closure_set_ip(cl);
+}
+
+/*
+ * Hack to get at the embedded closure if there is one, by doing an unsafe cast:
+ * the result of __closure_type() is thrown away, it's used merely for type
+ * checking.
+ */
+#define __to_internal_closure(cl) \
+({ \
+ BUILD_BUG_ON(__closure_type(*cl) > MAX_CLOSURE_TYPE); \
+ (struct closure *) cl; \
+})
+
+#define closure_init_type(cl, parent, running) \
+do { \
+ struct closure *_cl = __to_internal_closure(cl); \
+ _cl->type = __closure_type(*(cl)); \
+ do_closure_init(_cl, parent, running); \
+} while (0)
+
+/**
+ * __closure_init() - Initialize a closure, skipping the memset()
+ *
+ * May be used instead of closure_init() when memory has already been zeroed.
+ */
+#define __closure_init(cl, parent) \
+ closure_init_type(cl, parent, true)
+
+/**
+ * closure_init() - Initialize a closure, setting the refcount to 1
+ * @cl: closure to initialize
+ * @parent: parent of the new closure. cl will take a refcount on it for its
+ * lifetime; may be NULL.
+ */
+#define closure_init(cl, parent) \
+do { \
+ memset((cl), 0, sizeof(*(cl))); \
+ __closure_init(cl, parent); \
+} while (0)
+
+static inline void closure_init_stack(struct closure *cl)
+{
+ memset(cl, 0, sizeof(struct closure));
+ atomic_set(&cl->remaining, CLOSURE_REMAINING_INITIALIZER|
+ CLOSURE_BLOCKING|CLOSURE_STACK);
+}
+
+/**
+ * closure_init_unlocked() - Initialize a closure but leave it unlocked.
+ * @cl: closure to initialize
+ *
+ * For when the closure will be used as a lock. The closure may not be used
+ * until after a closure_lock() or closure_trylock().
+ */
+#define closure_init_unlocked(cl) \
+do { \
+ memset((cl), 0, sizeof(*(cl))); \
+ closure_init_type(cl, NULL, false); \
+} while (0)
+
+/**
+ * closure_lock() - lock and initialize a closure.
+ * @cl: the closure to lock
+ * @parent: the new parent for this closure
+ *
+ * The closure must be of one of the types that has a waitlist (otherwise we
+ * wouldn't be able to sleep on contention).
+ *
+ * @parent has exactly the same meaning as in closure_init(); if non null, the
+ * closure will take a reference on @parent which will be released when it is
+ * unlocked.
+ */
+#define closure_lock(cl, parent) \
+ __closure_lock(__to_internal_closure(cl), parent, &(cl)->wait)
+
+/**
+ * closure_delay() - delay some number of jiffies
+ * @cl: the closure that will sleep
+ * @delay: the delay in jiffies
+ *
+ * Takes a refcount on @cl which will be released after @delay jiffies; this may
+ * be used to have a function run after a delay with continue_at(), or
+ * closure_sync() may be used for a convoluted version of msleep().
+ */
+#define closure_delay(cl, delay) \
+ __closure_delay(__to_internal_closure(cl), delay, &(cl)->timer)
+
+#define closure_flush(cl) \
+ __closure_flush(__to_internal_closure(cl), &(cl)->timer)
+
+#define closure_flush_sync(cl) \
+ __closure_flush_sync(__to_internal_closure(cl), &(cl)->timer)
+
+static inline void __closure_end_sleep(struct closure *cl)
+{
+ __set_current_state(TASK_RUNNING);
+
+ if (atomic_read(&cl->remaining) & CLOSURE_SLEEPING)
+ atomic_sub(CLOSURE_SLEEPING, &cl->remaining);
+}
+
+static inline void __closure_start_sleep(struct closure *cl)
+{
+ closure_set_ip(cl);
+ cl->task = current;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ if (!(atomic_read(&cl->remaining) & CLOSURE_SLEEPING))
+ atomic_add(CLOSURE_SLEEPING, &cl->remaining);
+}
+
+/**
+ * closure_blocking() - returns true if the closure is in blocking mode.
+ *
+ * If a closure is in blocking mode, closure_wait_event() will sleep until the
+ * condition is true instead of waiting asynchronously.
+ */
+static inline bool closure_blocking(struct closure *cl)
+{
+ return atomic_read(&cl->remaining) & CLOSURE_BLOCKING;
+}
+
+/**
+ * set_closure_blocking() - put a closure in blocking mode.
+ *
+ * If a closure is in blocking mode, closure_wait_event() will sleep until the
+ * condition is true instead of waiting asynchronously.
+ *
+ * Not thread safe - can only be called by the thread running the closure.
+ */
+static inline void set_closure_blocking(struct closure *cl)
+{
+ if (!closure_blocking(cl))
+ atomic_add(CLOSURE_BLOCKING, &cl->remaining);
+}
+
+/*
+ * Not thread safe - can only be called by the thread running the closure.
+ */
+static inline void clear_closure_blocking(struct closure *cl)
+{
+ if (closure_blocking(cl))
+ atomic_sub(CLOSURE_BLOCKING, &cl->remaining);
+}
+
+/**
+ * closure_wake_up() - wake up all closures on a wait list.
+ */
+static inline void closure_wake_up(struct closure_waitlist *list)
+{
+ smp_mb();
+ __closure_wake_up(list);
+}
+
+/*
+ * Wait on an event, synchronously or asynchronously - analogous to wait_event()
+ * but for closures.
+ *
+ * The loop is oddly structured so as to avoid a race; we must check the
+ * condition again after we've added ourself to the waitlist. We know if we were
+ * already on the waitlist because closure_wait() returns false; thus, we only
+ * schedule or break if closure_wait() returns false. If it returns true, we
+ * just loop again - rechecking the condition.
+ *
+ * The __closure_wake_up() is necessary because we may race with the event
+ * becoming true; i.e. we see event false -> wait -> recheck condition, but the
+ * thread that made the event true may have called closure_wake_up() before we
+ * added ourself to the wait list.
+ *
+ * We have to call closure_sync() at the end instead of just
+ * __closure_end_sleep() because a different thread might've called
+ * closure_wake_up() before us and gotten preempted before they dropped the
+ * refcount on our closure. If this was a stack allocated closure, that would be
+ * bad.
+ */
+#define __closure_wait_event(list, cl, condition, _block) \
+({ \
+ bool block = _block; \
+ typeof(condition) ret; \
+ \
+ while (1) { \
+ ret = (condition); \
+ if (ret) { \
+ __closure_wake_up(list); \
+ if (block) \
+ closure_sync(cl); \
+ \
+ break; \
+ } \
+ \
+ if (block) \
+ __closure_start_sleep(cl); \
+ \
+ if (!closure_wait(list, cl)) { \
+ if (!block) \
+ break; \
+ \
+ schedule(); \
+ } \
+ } \
+ \
+ ret; \
+})
+
+/**
+ * closure_wait_event() - wait on a condition, synchronously or asynchronously.
+ * @list: the wait list to wait on
+ * @cl: the closure that is doing the waiting
+ * @condition: a C expression for the event to wait for
+ *
+ * If the closure is in blocking mode, sleeps until the @condition evaluates to
+ * true - exactly like wait_event().
+ *
+ * If the closure is not in blocking mode, waits asynchronously; if the
+ * condition is currently false the @cl is put onto @list and returns. @list
+ * owns a refcount on @cl; closure_sync() or continue_at() may be used later to
+ * wait for another thread to wake up @list, which drops the refcount on @cl.
+ *
+ * Returns the value of @condition; @cl will be on @list iff @condition was
+ * false.
+ *
+ * closure_wake_up(@list) must be called after changing any variable that could
+ * cause @condition to become true.
+ */
+#define closure_wait_event(list, cl, condition) \
+ __closure_wait_event(list, cl, condition, closure_blocking(cl))
+
+#define closure_wait_event_async(list, cl, condition) \
+ __closure_wait_event(list, cl, condition, false)
+
+#define closure_wait_event_sync(list, cl, condition) \
+ __closure_wait_event(list, cl, condition, true)
+
+static inline void set_closure_fn(struct closure *cl, closure_fn *fn,
+ struct workqueue_struct *wq)
+{
+ BUG_ON(object_is_on_stack(cl));
+ closure_set_ip(cl);
+ cl->fn = fn;
+ cl->wq = wq;
+ /* between atomic_dec() in closure_put() */
+ smp_mb__before_atomic_dec();
+}
+
+#define continue_at(_cl, _fn, _wq) \
+do { \
+ set_closure_fn(_cl, _fn, _wq); \
+ closure_sub(_cl, CLOSURE_RUNNING + 1); \
+ return; \
+} while (0)
+
+#define closure_return(_cl) continue_at((_cl), NULL, NULL)
+
+#define continue_at_nobarrier(_cl, _fn, _wq) \
+do { \
+ set_closure_fn(_cl, _fn, _wq); \
+ closure_queue(cl); \
+ return; \
+} while (0)
+
+#define closure_return_with_destructor(_cl, _destructor) \
+do { \
+ set_closure_fn(_cl, _destructor, NULL); \
+ closure_sub(_cl, CLOSURE_RUNNING - CLOSURE_DESTRUCTOR + 1); \
+ return; \
+} while (0)
+
+static inline void closure_call(struct closure *cl, closure_fn fn,
+ struct workqueue_struct *wq,
+ struct closure *parent)
+{
+ closure_init(cl, parent);
+ continue_at_nobarrier(cl, fn, wq);
+}
+
+static inline void closure_trylock_call(struct closure *cl, closure_fn fn,
+ struct workqueue_struct *wq,
+ struct closure *parent)
+{
+ if (closure_trylock(cl, parent))
+ continue_at_nobarrier(cl, fn, wq);
+}
+
+#endif /* _LINUX_CLOSURE_H */
diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c
new file mode 100644
index 000000000000..89fd5204924e
--- /dev/null
+++ b/drivers/md/bcache/debug.c
@@ -0,0 +1,565 @@
+/*
+ * Assorted bcache debug code
+ *
+ * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "btree.h"
+#include "debug.h"
+#include "request.h"
+
+#include <linux/console.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/seq_file.h>
+
+static struct dentry *debug;
+
+const char *bch_ptr_status(struct cache_set *c, const struct bkey *k)
+{
+ unsigned i;
+
+ for (i = 0; i < KEY_PTRS(k); i++)
+ if (ptr_available(c, k, i)) {
+ struct cache *ca = PTR_CACHE(c, k, i);
+ size_t bucket = PTR_BUCKET_NR(c, k, i);
+ size_t r = bucket_remainder(c, PTR_OFFSET(k, i));
+
+ if (KEY_SIZE(k) + r > c->sb.bucket_size)
+ return "bad, length too big";
+ if (bucket < ca->sb.first_bucket)
+ return "bad, short offset";
+ if (bucket >= ca->sb.nbuckets)
+ return "bad, offset past end of device";
+ if (ptr_stale(c, k, i))
+ return "stale";
+ }
+
+ if (!bkey_cmp(k, &ZERO_KEY))
+ return "bad, null key";
+ if (!KEY_PTRS(k))
+ return "bad, no pointers";
+ if (!KEY_SIZE(k))
+ return "zeroed key";
+ return "";
+}
+
+struct keyprint_hack bch_pkey(const struct bkey *k)
+{
+ unsigned i = 0;
+ struct keyprint_hack r;
+ char *out = r.s, *end = r.s + KEYHACK_SIZE;
+
+#define p(...) (out += scnprintf(out, end - out, __VA_ARGS__))
+
+ p("%llu:%llu len %llu -> [", KEY_INODE(k), KEY_OFFSET(k), KEY_SIZE(k));
+
+ if (KEY_PTRS(k))
+ while (1) {
+ p("%llu:%llu gen %llu",
+ PTR_DEV(k, i), PTR_OFFSET(k, i), PTR_GEN(k, i));
+
+ if (++i == KEY_PTRS(k))
+ break;
+
+ p(", ");
+ }
+
+ p("]");
+
+ if (KEY_DIRTY(k))
+ p(" dirty");
+ if (KEY_CSUM(k))
+ p(" cs%llu %llx", KEY_CSUM(k), k->ptr[1]);
+#undef p
+ return r;
+}
+
+struct keyprint_hack bch_pbtree(const struct btree *b)
+{
+ struct keyprint_hack r;
+
+ snprintf(r.s, 40, "%zu level %i/%i", PTR_BUCKET_NR(b->c, &b->key, 0),
+ b->level, b->c->root ? b->c->root->level : -1);
+ return r;
+}
+
+#if defined(CONFIG_BCACHE_DEBUG) || defined(CONFIG_BCACHE_EDEBUG)
+
+static bool skipped_backwards(struct btree *b, struct bkey *k)
+{
+ return bkey_cmp(k, (!b->level)
+ ? &START_KEY(bkey_next(k))
+ : bkey_next(k)) > 0;
+}
+
+static void dump_bset(struct btree *b, struct bset *i)
+{
+ struct bkey *k;
+ unsigned j;
+
+ for (k = i->start; k < end(i); k = bkey_next(k)) {
+ printk(KERN_ERR "block %zu key %zi/%u: %s", index(i, b),
+ (uint64_t *) k - i->d, i->keys, pkey(k));
+
+ for (j = 0; j < KEY_PTRS(k); j++) {
+ size_t n = PTR_BUCKET_NR(b->c, k, j);
+ printk(" bucket %zu", n);
+
+ if (n >= b->c->sb.first_bucket && n < b->c->sb.nbuckets)
+ printk(" prio %i",
+ PTR_BUCKET(b->c, k, j)->prio);
+ }
+
+ printk(" %s\n", bch_ptr_status(b->c, k));
+
+ if (bkey_next(k) < end(i) &&
+ skipped_backwards(b, k))
+ printk(KERN_ERR "Key skipped backwards\n");
+ }
+}
+
+#endif
+
+#ifdef CONFIG_BCACHE_DEBUG
+
+void bch_btree_verify(struct btree *b, struct bset *new)
+{
+ struct btree *v = b->c->verify_data;
+ struct closure cl;
+ closure_init_stack(&cl);
+
+ if (!b->c->verify)
+ return;
+
+ closure_wait_event(&b->io.wait, &cl,
+ atomic_read(&b->io.cl.remaining) == -1);
+
+ mutex_lock(&b->c->verify_lock);
+
+ bkey_copy(&v->key, &b->key);
+ v->written = 0;
+ v->level = b->level;
+
+ bch_btree_read(v);
+ closure_wait_event(&v->io.wait, &cl,
+ atomic_read(&b->io.cl.remaining) == -1);
+
+ if (new->keys != v->sets[0].data->keys ||
+ memcmp(new->start,
+ v->sets[0].data->start,
+ (void *) end(new) - (void *) new->start)) {
+ unsigned i, j;
+
+ console_lock();
+
+ printk(KERN_ERR "*** original memory node:\n");
+ for (i = 0; i <= b->nsets; i++)
+ dump_bset(b, b->sets[i].data);
+
+ printk(KERN_ERR "*** sorted memory node:\n");
+ dump_bset(b, new);
+
+ printk(KERN_ERR "*** on disk node:\n");
+ dump_bset(v, v->sets[0].data);
+
+ for (j = 0; j < new->keys; j++)
+ if (new->d[j] != v->sets[0].data->d[j])
+ break;
+
+ console_unlock();
+ panic("verify failed at %u\n", j);
+ }
+
+ mutex_unlock(&b->c->verify_lock);
+}
+
+static void data_verify_endio(struct bio *bio, int error)
+{
+ struct closure *cl = bio->bi_private;
+ closure_put(cl);
+}
+
+void bch_data_verify(struct search *s)
+{
+ char name[BDEVNAME_SIZE];
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+ struct closure *cl = &s->cl;
+ struct bio *check;
+ struct bio_vec *bv;
+ int i;
+
+ if (!s->unaligned_bvec)
+ bio_for_each_segment(bv, s->orig_bio, i)
+ bv->bv_offset = 0, bv->bv_len = PAGE_SIZE;
+
+ check = bio_clone(s->orig_bio, GFP_NOIO);
+ if (!check)
+ return;
+
+ if (bch_bio_alloc_pages(check, GFP_NOIO))
+ goto out_put;
+
+ check->bi_rw = READ_SYNC;
+ check->bi_private = cl;
+ check->bi_end_io = data_verify_endio;
+
+ closure_bio_submit(check, cl, &dc->disk);
+ closure_sync(cl);
+
+ bio_for_each_segment(bv, s->orig_bio, i) {
+ void *p1 = kmap(bv->bv_page);
+ void *p2 = kmap(check->bi_io_vec[i].bv_page);
+
+ if (memcmp(p1 + bv->bv_offset,
+ p2 + bv->bv_offset,
+ bv->bv_len))
+ printk(KERN_ERR
+ "bcache (%s): verify failed at sector %llu\n",
+ bdevname(dc->bdev, name),
+ (uint64_t) s->orig_bio->bi_sector);
+
+ kunmap(bv->bv_page);
+ kunmap(check->bi_io_vec[i].bv_page);
+ }
+
+ __bio_for_each_segment(bv, check, i, 0)
+ __free_page(bv->bv_page);
+out_put:
+ bio_put(check);
+}
+
+#endif
+
+#ifdef CONFIG_BCACHE_EDEBUG
+
+unsigned bch_count_data(struct btree *b)
+{
+ unsigned ret = 0;
+ struct btree_iter iter;
+ struct bkey *k;
+
+ if (!b->level)
+ for_each_key(b, k, &iter)
+ ret += KEY_SIZE(k);
+ return ret;
+}
+
+static void vdump_bucket_and_panic(struct btree *b, const char *fmt,
+ va_list args)
+{
+ unsigned i;
+
+ console_lock();
+
+ for (i = 0; i <= b->nsets; i++)
+ dump_bset(b, b->sets[i].data);
+
+ vprintk(fmt, args);
+
+ console_unlock();
+
+ panic("at %s\n", pbtree(b));
+}
+
+void bch_check_key_order_msg(struct btree *b, struct bset *i,
+ const char *fmt, ...)
+{
+ struct bkey *k;
+
+ if (!i->keys)
+ return;
+
+ for (k = i->start; bkey_next(k) < end(i); k = bkey_next(k))
+ if (skipped_backwards(b, k)) {
+ va_list args;
+ va_start(args, fmt);
+
+ vdump_bucket_and_panic(b, fmt, args);
+ va_end(args);
+ }
+}
+
+void bch_check_keys(struct btree *b, const char *fmt, ...)
+{
+ va_list args;
+ struct bkey *k, *p = NULL;
+ struct btree_iter iter;
+
+ if (b->level)
+ return;
+
+ for_each_key(b, k, &iter) {
+ if (p && bkey_cmp(&START_KEY(p), &START_KEY(k)) > 0) {
+ printk(KERN_ERR "Keys out of order:\n");
+ goto bug;
+ }
+
+ if (bch_ptr_invalid(b, k))
+ continue;
+
+ if (p && bkey_cmp(p, &START_KEY(k)) > 0) {
+ printk(KERN_ERR "Overlapping keys:\n");
+ goto bug;
+ }
+ p = k;
+ }
+ return;
+bug:
+ va_start(args, fmt);
+ vdump_bucket_and_panic(b, fmt, args);
+ va_end(args);
+}
+
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+
+/* XXX: cache set refcounting */
+
+struct dump_iterator {
+ char buf[PAGE_SIZE];
+ size_t bytes;
+ struct cache_set *c;
+ struct keybuf keys;
+};
+
+static bool dump_pred(struct keybuf *buf, struct bkey *k)
+{
+ return true;
+}
+
+static ssize_t bch_dump_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct dump_iterator *i = file->private_data;
+ ssize_t ret = 0;
+
+ while (size) {
+ struct keybuf_key *w;
+ unsigned bytes = min(i->bytes, size);
+
+ int err = copy_to_user(buf, i->buf, bytes);
+ if (err)
+ return err;
+
+ ret += bytes;
+ buf += bytes;
+ size -= bytes;
+ i->bytes -= bytes;
+ memmove(i->buf, i->buf + bytes, i->bytes);
+
+ if (i->bytes)
+ break;
+
+ w = bch_keybuf_next_rescan(i->c, &i->keys, &MAX_KEY);
+ if (!w)
+ break;
+
+ i->bytes = snprintf(i->buf, PAGE_SIZE, "%s\n", pkey(&w->key));
+ bch_keybuf_del(&i->keys, w);
+ }
+
+ return ret;
+}
+
+static int bch_dump_open(struct inode *inode, struct file *file)
+{
+ struct cache_set *c = inode->i_private;
+ struct dump_iterator *i;
+
+ i = kzalloc(sizeof(struct dump_iterator), GFP_KERNEL);
+ if (!i)
+ return -ENOMEM;
+
+ file->private_data = i;
+ i->c = c;
+ bch_keybuf_init(&i->keys, dump_pred);
+ i->keys.last_scanned = KEY(0, 0, 0);
+
+ return 0;
+}
+
+static int bch_dump_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static const struct file_operations cache_set_debug_ops = {
+ .owner = THIS_MODULE,
+ .open = bch_dump_open,
+ .read = bch_dump_read,
+ .release = bch_dump_release
+};
+
+void bch_debug_init_cache_set(struct cache_set *c)
+{
+ if (!IS_ERR_OR_NULL(debug)) {
+ char name[50];
+ snprintf(name, 50, "bcache-%pU", c->sb.set_uuid);
+
+ c->debug = debugfs_create_file(name, 0400, debug, c,
+ &cache_set_debug_ops);
+ }
+}
+
+#endif
+
+/* Fuzz tester has rotted: */
+#if 0
+
+static ssize_t btree_fuzz(struct kobject *k, struct kobj_attribute *a,
+ const char *buffer, size_t size)
+{
+ void dump(struct btree *b)
+ {
+ struct bset *i;
+
+ for (i = b->sets[0].data;
+ index(i, b) < btree_blocks(b) &&
+ i->seq == b->sets[0].data->seq;
+ i = ((void *) i) + set_blocks(i, b->c) * block_bytes(b->c))
+ dump_bset(b, i);
+ }
+
+ struct cache_sb *sb;
+ struct cache_set *c;
+ struct btree *all[3], *b, *fill, *orig;
+ int j;
+
+ struct btree_op op;
+ bch_btree_op_init_stack(&op);
+
+ sb = kzalloc(sizeof(struct cache_sb), GFP_KERNEL);
+ if (!sb)
+ return -ENOMEM;
+
+ sb->bucket_size = 128;
+ sb->block_size = 4;
+
+ c = bch_cache_set_alloc(sb);
+ if (!c)
+ return -ENOMEM;
+
+ for (j = 0; j < 3; j++) {
+ BUG_ON(list_empty(&c->btree_cache));
+ all[j] = list_first_entry(&c->btree_cache, struct btree, list);
+ list_del_init(&all[j]->list);
+
+ all[j]->key = KEY(0, 0, c->sb.bucket_size);
+ bkey_copy_key(&all[j]->key, &MAX_KEY);
+ }
+
+ b = all[0];
+ fill = all[1];
+ orig = all[2];
+
+ while (1) {
+ for (j = 0; j < 3; j++)
+ all[j]->written = all[j]->nsets = 0;
+
+ bch_bset_init_next(b);
+
+ while (1) {
+ struct bset *i = write_block(b);
+ struct bkey *k = op.keys.top;
+ unsigned rand;
+
+ bkey_init(k);
+ rand = get_random_int();
+
+ op.type = rand & 1
+ ? BTREE_INSERT
+ : BTREE_REPLACE;
+ rand >>= 1;
+
+ SET_KEY_SIZE(k, bucket_remainder(c, rand));
+ rand >>= c->bucket_bits;
+ rand &= 1024 * 512 - 1;
+ rand += c->sb.bucket_size;
+ SET_KEY_OFFSET(k, rand);
+#if 0
+ SET_KEY_PTRS(k, 1);
+#endif
+ bch_keylist_push(&op.keys);
+ bch_btree_insert_keys(b, &op);
+
+ if (should_split(b) ||
+ set_blocks(i, b->c) !=
+ __set_blocks(i, i->keys + 15, b->c)) {
+ i->csum = csum_set(i);
+
+ memcpy(write_block(fill),
+ i, set_bytes(i));
+
+ b->written += set_blocks(i, b->c);
+ fill->written = b->written;
+ if (b->written == btree_blocks(b))
+ break;
+
+ bch_btree_sort_lazy(b);
+ bch_bset_init_next(b);
+ }
+ }
+
+ memcpy(orig->sets[0].data,
+ fill->sets[0].data,
+ btree_bytes(c));
+
+ bch_btree_sort(b);
+ fill->written = 0;
+ bch_btree_read_done(&fill->io.cl);
+
+ if (b->sets[0].data->keys != fill->sets[0].data->keys ||
+ memcmp(b->sets[0].data->start,
+ fill->sets[0].data->start,
+ b->sets[0].data->keys * sizeof(uint64_t))) {
+ struct bset *i = b->sets[0].data;
+ struct bkey *k, *l;
+
+ for (k = i->start,
+ l = fill->sets[0].data->start;
+ k < end(i);
+ k = bkey_next(k), l = bkey_next(l))
+ if (bkey_cmp(k, l) ||
+ KEY_SIZE(k) != KEY_SIZE(l))
+ pr_err("key %zi differs: %s != %s",
+ (uint64_t *) k - i->d,
+ pkey(k), pkey(l));
+
+ for (j = 0; j < 3; j++) {
+ pr_err("**** Set %i ****", j);
+ dump(all[j]);
+ }
+ panic("\n");
+ }
+
+ pr_info("fuzz complete: %i keys", b->sets[0].data->keys);
+ }
+}
+
+kobj_attribute_write(fuzz, btree_fuzz);
+#endif
+
+void bch_debug_exit(void)
+{
+ if (!IS_ERR_OR_NULL(debug))
+ debugfs_remove_recursive(debug);
+}
+
+int __init bch_debug_init(struct kobject *kobj)
+{
+ int ret = 0;
+#if 0
+ ret = sysfs_create_file(kobj, &ksysfs_fuzz.attr);
+ if (ret)
+ return ret;
+#endif
+
+ debug = debugfs_create_dir("bcache", NULL);
+ return ret;
+}
diff --git a/drivers/md/bcache/debug.h b/drivers/md/bcache/debug.h
new file mode 100644
index 000000000000..f9378a218148
--- /dev/null
+++ b/drivers/md/bcache/debug.h
@@ -0,0 +1,54 @@
+#ifndef _BCACHE_DEBUG_H
+#define _BCACHE_DEBUG_H
+
+/* Btree/bkey debug printing */
+
+#define KEYHACK_SIZE 80
+struct keyprint_hack {
+ char s[KEYHACK_SIZE];
+};
+
+struct keyprint_hack bch_pkey(const struct bkey *k);
+struct keyprint_hack bch_pbtree(const struct btree *b);
+#define pkey(k) (&bch_pkey(k).s[0])
+#define pbtree(b) (&bch_pbtree(b).s[0])
+
+#ifdef CONFIG_BCACHE_EDEBUG
+
+unsigned bch_count_data(struct btree *);
+void bch_check_key_order_msg(struct btree *, struct bset *, const char *, ...);
+void bch_check_keys(struct btree *, const char *, ...);
+
+#define bch_check_key_order(b, i) \
+ bch_check_key_order_msg(b, i, "keys out of order")
+#define EBUG_ON(cond) BUG_ON(cond)
+
+#else /* EDEBUG */
+
+#define bch_count_data(b) 0
+#define bch_check_key_order(b, i) do {} while (0)
+#define bch_check_key_order_msg(b, i, ...) do {} while (0)
+#define bch_check_keys(b, ...) do {} while (0)
+#define EBUG_ON(cond) do {} while (0)
+
+#endif
+
+#ifdef CONFIG_BCACHE_DEBUG
+
+void bch_btree_verify(struct btree *, struct bset *);
+void bch_data_verify(struct search *);
+
+#else /* DEBUG */
+
+static inline void bch_btree_verify(struct btree *b, struct bset *i) {}
+static inline void bch_data_verify(struct search *s) {};
+
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+void bch_debug_init_cache_set(struct cache_set *);
+#else
+static inline void bch_debug_init_cache_set(struct cache_set *c) {}
+#endif
+
+#endif
diff --git a/drivers/md/bcache/io.c b/drivers/md/bcache/io.c
new file mode 100644
index 000000000000..48efd4dea645
--- /dev/null
+++ b/drivers/md/bcache/io.c
@@ -0,0 +1,397 @@
+/*
+ * Some low level IO code, and hacks for various block layer limitations
+ *
+ * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "bset.h"
+#include "debug.h"
+
+static void bch_bi_idx_hack_endio(struct bio *bio, int error)
+{
+ struct bio *p = bio->bi_private;
+
+ bio_endio(p, error);
+ bio_put(bio);
+}
+
+static void bch_generic_make_request_hack(struct bio *bio)
+{
+ if (bio->bi_idx) {
+ struct bio *clone = bio_alloc(GFP_NOIO, bio_segments(bio));
+
+ memcpy(clone->bi_io_vec,
+ bio_iovec(bio),
+ bio_segments(bio) * sizeof(struct bio_vec));
+
+ clone->bi_sector = bio->bi_sector;
+ clone->bi_bdev = bio->bi_bdev;
+ clone->bi_rw = bio->bi_rw;
+ clone->bi_vcnt = bio_segments(bio);
+ clone->bi_size = bio->bi_size;
+
+ clone->bi_private = bio;
+ clone->bi_end_io = bch_bi_idx_hack_endio;
+
+ bio = clone;
+ }
+
+ /*
+ * Hack, since drivers that clone bios clone up to bi_max_vecs, but our
+ * bios might have had more than that (before we split them per device
+ * limitations).
+ *
+ * To be taken out once immutable bvec stuff is in.
+ */
+ bio->bi_max_vecs = bio->bi_vcnt;
+
+ generic_make_request(bio);
+}
+
+/**
+ * bch_bio_split - split a bio
+ * @bio: bio to split
+ * @sectors: number of sectors to split from the front of @bio
+ * @gfp: gfp mask
+ * @bs: bio set to allocate from
+ *
+ * Allocates and returns a new bio which represents @sectors from the start of
+ * @bio, and updates @bio to represent the remaining sectors.
+ *
+ * If bio_sectors(@bio) was less than or equal to @sectors, returns @bio
+ * unchanged.
+ *
+ * The newly allocated bio will point to @bio's bi_io_vec, if the split was on a
+ * bvec boundry; it is the caller's responsibility to ensure that @bio is not
+ * freed before the split.
+ *
+ * If bch_bio_split() is running under generic_make_request(), it's not safe to
+ * allocate more than one bio from the same bio set. Therefore, if it is running
+ * under generic_make_request() it masks out __GFP_WAIT when doing the
+ * allocation. The caller must check for failure if there's any possibility of
+ * it being called from under generic_make_request(); it is then the caller's
+ * responsibility to retry from a safe context (by e.g. punting to workqueue).
+ */
+struct bio *bch_bio_split(struct bio *bio, int sectors,
+ gfp_t gfp, struct bio_set *bs)
+{
+ unsigned idx = bio->bi_idx, vcnt = 0, nbytes = sectors << 9;
+ struct bio_vec *bv;
+ struct bio *ret = NULL;
+
+ BUG_ON(sectors <= 0);
+
+ /*
+ * If we're being called from underneath generic_make_request() and we
+ * already allocated any bios from this bio set, we risk deadlock if we
+ * use the mempool. So instead, we possibly fail and let the caller punt
+ * to workqueue or somesuch and retry in a safe context.
+ */
+ if (current->bio_list)
+ gfp &= ~__GFP_WAIT;
+
+ if (sectors >= bio_sectors(bio))
+ return bio;
+
+ if (bio->bi_rw & REQ_DISCARD) {
+ ret = bio_alloc_bioset(gfp, 1, bs);
+ idx = 0;
+ goto out;
+ }
+
+ bio_for_each_segment(bv, bio, idx) {
+ vcnt = idx - bio->bi_idx;
+
+ if (!nbytes) {
+ ret = bio_alloc_bioset(gfp, vcnt, bs);
+ if (!ret)
+ return NULL;
+
+ memcpy(ret->bi_io_vec, bio_iovec(bio),
+ sizeof(struct bio_vec) * vcnt);
+
+ break;
+ } else if (nbytes < bv->bv_len) {
+ ret = bio_alloc_bioset(gfp, ++vcnt, bs);
+ if (!ret)
+ return NULL;
+
+ memcpy(ret->bi_io_vec, bio_iovec(bio),
+ sizeof(struct bio_vec) * vcnt);
+
+ ret->bi_io_vec[vcnt - 1].bv_len = nbytes;
+ bv->bv_offset += nbytes;
+ bv->bv_len -= nbytes;
+ break;
+ }
+
+ nbytes -= bv->bv_len;
+ }
+out:
+ ret->bi_bdev = bio->bi_bdev;
+ ret->bi_sector = bio->bi_sector;
+ ret->bi_size = sectors << 9;
+ ret->bi_rw = bio->bi_rw;
+ ret->bi_vcnt = vcnt;
+ ret->bi_max_vecs = vcnt;
+
+ bio->bi_sector += sectors;
+ bio->bi_size -= sectors << 9;
+ bio->bi_idx = idx;
+
+ if (bio_integrity(bio)) {
+ if (bio_integrity_clone(ret, bio, gfp)) {
+ bio_put(ret);
+ return NULL;
+ }
+
+ bio_integrity_trim(ret, 0, bio_sectors(ret));
+ bio_integrity_trim(bio, bio_sectors(ret), bio_sectors(bio));
+ }
+
+ return ret;
+}
+
+static unsigned bch_bio_max_sectors(struct bio *bio)
+{
+ unsigned ret = bio_sectors(bio);
+ struct request_queue *q = bdev_get_queue(bio->bi_bdev);
+ unsigned max_segments = min_t(unsigned, BIO_MAX_PAGES,
+ queue_max_segments(q));
+ struct bio_vec *bv, *end = bio_iovec(bio) +
+ min_t(int, bio_segments(bio), max_segments);
+
+ if (bio->bi_rw & REQ_DISCARD)
+ return min(ret, q->limits.max_discard_sectors);
+
+ if (bio_segments(bio) > max_segments ||
+ q->merge_bvec_fn) {
+ ret = 0;
+
+ for (bv = bio_iovec(bio); bv < end; bv++) {
+ struct bvec_merge_data bvm = {
+ .bi_bdev = bio->bi_bdev,
+ .bi_sector = bio->bi_sector,
+ .bi_size = ret << 9,
+ .bi_rw = bio->bi_rw,
+ };
+
+ if (q->merge_bvec_fn &&
+ q->merge_bvec_fn(q, &bvm, bv) < (int) bv->bv_len)
+ break;
+
+ ret += bv->bv_len >> 9;
+ }
+ }
+
+ ret = min(ret, queue_max_sectors(q));
+
+ WARN_ON(!ret);
+ ret = max_t(int, ret, bio_iovec(bio)->bv_len >> 9);
+
+ return ret;
+}
+
+static void bch_bio_submit_split_done(struct closure *cl)
+{
+ struct bio_split_hook *s = container_of(cl, struct bio_split_hook, cl);
+
+ s->bio->bi_end_io = s->bi_end_io;
+ s->bio->bi_private = s->bi_private;
+ bio_endio(s->bio, 0);
+
+ closure_debug_destroy(&s->cl);
+ mempool_free(s, s->p->bio_split_hook);
+}
+
+static void bch_bio_submit_split_endio(struct bio *bio, int error)
+{
+ struct closure *cl = bio->bi_private;
+ struct bio_split_hook *s = container_of(cl, struct bio_split_hook, cl);
+
+ if (error)
+ clear_bit(BIO_UPTODATE, &s->bio->bi_flags);
+
+ bio_put(bio);
+ closure_put(cl);
+}
+
+static void __bch_bio_submit_split(struct closure *cl)
+{
+ struct bio_split_hook *s = container_of(cl, struct bio_split_hook, cl);
+ struct bio *bio = s->bio, *n;
+
+ do {
+ n = bch_bio_split(bio, bch_bio_max_sectors(bio),
+ GFP_NOIO, s->p->bio_split);
+ if (!n)
+ continue_at(cl, __bch_bio_submit_split, system_wq);
+
+ n->bi_end_io = bch_bio_submit_split_endio;
+ n->bi_private = cl;
+
+ closure_get(cl);
+ bch_generic_make_request_hack(n);
+ } while (n != bio);
+
+ continue_at(cl, bch_bio_submit_split_done, NULL);
+}
+
+void bch_generic_make_request(struct bio *bio, struct bio_split_pool *p)
+{
+ struct bio_split_hook *s;
+
+ if (!bio_has_data(bio) && !(bio->bi_rw & REQ_DISCARD))
+ goto submit;
+
+ if (bio_sectors(bio) <= bch_bio_max_sectors(bio))
+ goto submit;
+
+ s = mempool_alloc(p->bio_split_hook, GFP_NOIO);
+
+ s->bio = bio;
+ s->p = p;
+ s->bi_end_io = bio->bi_end_io;
+ s->bi_private = bio->bi_private;
+ bio_get(bio);
+
+ closure_call(&s->cl, __bch_bio_submit_split, NULL, NULL);
+ return;
+submit:
+ bch_generic_make_request_hack(bio);
+}
+
+/* Bios with headers */
+
+void bch_bbio_free(struct bio *bio, struct cache_set *c)
+{
+ struct bbio *b = container_of(bio, struct bbio, bio);
+ mempool_free(b, c->bio_meta);
+}
+
+struct bio *bch_bbio_alloc(struct cache_set *c)
+{
+ struct bbio *b = mempool_alloc(c->bio_meta, GFP_NOIO);
+ struct bio *bio = &b->bio;
+
+ bio_init(bio);
+ bio->bi_flags |= BIO_POOL_NONE << BIO_POOL_OFFSET;
+ bio->bi_max_vecs = bucket_pages(c);
+ bio->bi_io_vec = bio->bi_inline_vecs;
+
+ return bio;
+}
+
+void __bch_submit_bbio(struct bio *bio, struct cache_set *c)
+{
+ struct bbio *b = container_of(bio, struct bbio, bio);
+
+ bio->bi_sector = PTR_OFFSET(&b->key, 0);
+ bio->bi_bdev = PTR_CACHE(c, &b->key, 0)->bdev;
+
+ b->submit_time_us = local_clock_us();
+ closure_bio_submit(bio, bio->bi_private, PTR_CACHE(c, &b->key, 0));
+}
+
+void bch_submit_bbio(struct bio *bio, struct cache_set *c,
+ struct bkey *k, unsigned ptr)
+{
+ struct bbio *b = container_of(bio, struct bbio, bio);
+ bch_bkey_copy_single_ptr(&b->key, k, ptr);
+ __bch_submit_bbio(bio, c);
+}
+
+/* IO errors */
+
+void bch_count_io_errors(struct cache *ca, int error, const char *m)
+{
+ /*
+ * The halflife of an error is:
+ * log2(1/2)/log2(127/128) * refresh ~= 88 * refresh
+ */
+
+ if (ca->set->error_decay) {
+ unsigned count = atomic_inc_return(&ca->io_count);
+
+ while (count > ca->set->error_decay) {
+ unsigned errors;
+ unsigned old = count;
+ unsigned new = count - ca->set->error_decay;
+
+ /*
+ * First we subtract refresh from count; each time we
+ * succesfully do so, we rescale the errors once:
+ */
+
+ count = atomic_cmpxchg(&ca->io_count, old, new);
+
+ if (count == old) {
+ count = new;
+
+ errors = atomic_read(&ca->io_errors);
+ do {
+ old = errors;
+ new = ((uint64_t) errors * 127) / 128;
+ errors = atomic_cmpxchg(&ca->io_errors,
+ old, new);
+ } while (old != errors);
+ }
+ }
+ }
+
+ if (error) {
+ char buf[BDEVNAME_SIZE];
+ unsigned errors = atomic_add_return(1 << IO_ERROR_SHIFT,
+ &ca->io_errors);
+ errors >>= IO_ERROR_SHIFT;
+
+ if (errors < ca->set->error_limit)
+ pr_err("%s: IO error on %s, recovering",
+ bdevname(ca->bdev, buf), m);
+ else
+ bch_cache_set_error(ca->set,
+ "%s: too many IO errors %s",
+ bdevname(ca->bdev, buf), m);
+ }
+}
+
+void bch_bbio_count_io_errors(struct cache_set *c, struct bio *bio,
+ int error, const char *m)
+{
+ struct bbio *b = container_of(bio, struct bbio, bio);
+ struct cache *ca = PTR_CACHE(c, &b->key, 0);
+
+ unsigned threshold = bio->bi_rw & REQ_WRITE
+ ? c->congested_write_threshold_us
+ : c->congested_read_threshold_us;
+
+ if (threshold) {
+ unsigned t = local_clock_us();
+
+ int us = t - b->submit_time_us;
+ int congested = atomic_read(&c->congested);
+
+ if (us > (int) threshold) {
+ int ms = us / 1024;
+ c->congested_last_us = t;
+
+ ms = min(ms, CONGESTED_MAX + congested);
+ atomic_sub(ms, &c->congested);
+ } else if (congested < 0)
+ atomic_inc(&c->congested);
+ }
+
+ bch_count_io_errors(ca, error, m);
+}
+
+void bch_bbio_endio(struct cache_set *c, struct bio *bio,
+ int error, const char *m)
+{
+ struct closure *cl = bio->bi_private;
+
+ bch_bbio_count_io_errors(c, bio, error, m);
+ bio_put(bio);
+ closure_put(cl);
+}
diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
new file mode 100644
index 000000000000..8c8dfdcd9d4c
--- /dev/null
+++ b/drivers/md/bcache/journal.c
@@ -0,0 +1,787 @@
+/*
+ * bcache journalling code, for btree insertions
+ *
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "btree.h"
+#include "debug.h"
+#include "request.h"
+
+/*
+ * Journal replay/recovery:
+ *
+ * This code is all driven from run_cache_set(); we first read the journal
+ * entries, do some other stuff, then we mark all the keys in the journal
+ * entries (same as garbage collection would), then we replay them - reinserting
+ * them into the cache in precisely the same order as they appear in the
+ * journal.
+ *
+ * We only journal keys that go in leaf nodes, which simplifies things quite a
+ * bit.
+ */
+
+static void journal_read_endio(struct bio *bio, int error)
+{
+ struct closure *cl = bio->bi_private;
+ closure_put(cl);
+}
+
+static int journal_read_bucket(struct cache *ca, struct list_head *list,
+ struct btree_op *op, unsigned bucket_index)
+{
+ struct journal_device *ja = &ca->journal;
+ struct bio *bio = &ja->bio;
+
+ struct journal_replay *i;
+ struct jset *j, *data = ca->set->journal.w[0].data;
+ unsigned len, left, offset = 0;
+ int ret = 0;
+ sector_t bucket = bucket_to_sector(ca->set, ca->sb.d[bucket_index]);
+
+ pr_debug("reading %llu", (uint64_t) bucket);
+
+ while (offset < ca->sb.bucket_size) {
+reread: left = ca->sb.bucket_size - offset;
+ len = min_t(unsigned, left, PAGE_SECTORS * 8);
+
+ bio_reset(bio);
+ bio->bi_sector = bucket + offset;
+ bio->bi_bdev = ca->bdev;
+ bio->bi_rw = READ;
+ bio->bi_size = len << 9;
+
+ bio->bi_end_io = journal_read_endio;
+ bio->bi_private = &op->cl;
+ bch_bio_map(bio, data);
+
+ closure_bio_submit(bio, &op->cl, ca);
+ closure_sync(&op->cl);
+
+ /* This function could be simpler now since we no longer write
+ * journal entries that overlap bucket boundaries; this means
+ * the start of a bucket will always have a valid journal entry
+ * if it has any journal entries at all.
+ */
+
+ j = data;
+ while (len) {
+ struct list_head *where;
+ size_t blocks, bytes = set_bytes(j);
+
+ if (j->magic != jset_magic(ca->set))
+ return ret;
+
+ if (bytes > left << 9)
+ return ret;
+
+ if (bytes > len << 9)
+ goto reread;
+
+ if (j->csum != csum_set(j))
+ return ret;
+
+ blocks = set_blocks(j, ca->set);
+
+ while (!list_empty(list)) {
+ i = list_first_entry(list,
+ struct journal_replay, list);
+ if (i->j.seq >= j->last_seq)
+ break;
+ list_del(&i->list);
+ kfree(i);
+ }
+
+ list_for_each_entry_reverse(i, list, list) {
+ if (j->seq == i->j.seq)
+ goto next_set;
+
+ if (j->seq < i->j.last_seq)
+ goto next_set;
+
+ if (j->seq > i->j.seq) {
+ where = &i->list;
+ goto add;
+ }
+ }
+
+ where = list;
+add:
+ i = kmalloc(offsetof(struct journal_replay, j) +
+ bytes, GFP_KERNEL);
+ if (!i)
+ return -ENOMEM;
+ memcpy(&i->j, j, bytes);
+ list_add(&i->list, where);
+ ret = 1;
+
+ ja->seq[bucket_index] = j->seq;
+next_set:
+ offset += blocks * ca->sb.block_size;
+ len -= blocks * ca->sb.block_size;
+ j = ((void *) j) + blocks * block_bytes(ca);
+ }
+ }
+
+ return ret;
+}
+
+int bch_journal_read(struct cache_set *c, struct list_head *list,
+ struct btree_op *op)
+{
+#define read_bucket(b) \
+ ({ \
+ int ret = journal_read_bucket(ca, list, op, b); \
+ __set_bit(b, bitmap); \
+ if (ret < 0) \
+ return ret; \
+ ret; \
+ })
+
+ struct cache *ca;
+ unsigned iter;
+
+ for_each_cache(ca, c, iter) {
+ struct journal_device *ja = &ca->journal;
+ unsigned long bitmap[SB_JOURNAL_BUCKETS / BITS_PER_LONG];
+ unsigned i, l, r, m;
+ uint64_t seq;
+
+ bitmap_zero(bitmap, SB_JOURNAL_BUCKETS);
+ pr_debug("%u journal buckets", ca->sb.njournal_buckets);
+
+ /* Read journal buckets ordered by golden ratio hash to quickly
+ * find a sequence of buckets with valid journal entries
+ */
+ for (i = 0; i < ca->sb.njournal_buckets; i++) {
+ l = (i * 2654435769U) % ca->sb.njournal_buckets;
+
+ if (test_bit(l, bitmap))
+ break;
+
+ if (read_bucket(l))
+ goto bsearch;
+ }
+
+ /* If that fails, check all the buckets we haven't checked
+ * already
+ */
+ pr_debug("falling back to linear search");
+
+ for (l = 0; l < ca->sb.njournal_buckets; l++) {
+ if (test_bit(l, bitmap))
+ continue;
+
+ if (read_bucket(l))
+ goto bsearch;
+ }
+bsearch:
+ /* Binary search */
+ m = r = find_next_bit(bitmap, ca->sb.njournal_buckets, l + 1);
+ pr_debug("starting binary search, l %u r %u", l, r);
+
+ while (l + 1 < r) {
+ m = (l + r) >> 1;
+
+ if (read_bucket(m))
+ l = m;
+ else
+ r = m;
+ }
+
+ /* Read buckets in reverse order until we stop finding more
+ * journal entries
+ */
+ pr_debug("finishing up");
+ l = m;
+
+ while (1) {
+ if (!l--)
+ l = ca->sb.njournal_buckets - 1;
+
+ if (l == m)
+ break;
+
+ if (test_bit(l, bitmap))
+ continue;
+
+ if (!read_bucket(l))
+ break;
+ }
+
+ seq = 0;
+
+ for (i = 0; i < ca->sb.njournal_buckets; i++)
+ if (ja->seq[i] > seq) {
+ seq = ja->seq[i];
+ ja->cur_idx = ja->discard_idx =
+ ja->last_idx = i;
+
+ }
+ }
+
+ c->journal.seq = list_entry(list->prev,
+ struct journal_replay,
+ list)->j.seq;
+
+ return 0;
+#undef read_bucket
+}
+
+void bch_journal_mark(struct cache_set *c, struct list_head *list)
+{
+ atomic_t p = { 0 };
+ struct bkey *k;
+ struct journal_replay *i;
+ struct journal *j = &c->journal;
+ uint64_t last = j->seq;
+
+ /*
+ * journal.pin should never fill up - we never write a journal
+ * entry when it would fill up. But if for some reason it does, we
+ * iterate over the list in reverse order so that we can just skip that
+ * refcount instead of bugging.
+ */
+
+ list_for_each_entry_reverse(i, list, list) {
+ BUG_ON(last < i->j.seq);
+ i->pin = NULL;
+
+ while (last-- != i->j.seq)
+ if (fifo_free(&j->pin) > 1) {
+ fifo_push_front(&j->pin, p);
+ atomic_set(&fifo_front(&j->pin), 0);
+ }
+
+ if (fifo_free(&j->pin) > 1) {
+ fifo_push_front(&j->pin, p);
+ i->pin = &fifo_front(&j->pin);
+ atomic_set(i->pin, 1);
+ }
+
+ for (k = i->j.start;
+ k < end(&i->j);
+ k = bkey_next(k)) {
+ unsigned j;
+
+ for (j = 0; j < KEY_PTRS(k); j++) {
+ struct bucket *g = PTR_BUCKET(c, k, j);
+ atomic_inc(&g->pin);
+
+ if (g->prio == BTREE_PRIO &&
+ !ptr_stale(c, k, j))
+ g->prio = INITIAL_PRIO;
+ }
+
+ __bch_btree_mark_key(c, 0, k);
+ }
+ }
+}
+
+int bch_journal_replay(struct cache_set *s, struct list_head *list,
+ struct btree_op *op)
+{
+ int ret = 0, keys = 0, entries = 0;
+ struct bkey *k;
+ struct journal_replay *i =
+ list_entry(list->prev, struct journal_replay, list);
+
+ uint64_t start = i->j.last_seq, end = i->j.seq, n = start;
+
+ list_for_each_entry(i, list, list) {
+ BUG_ON(i->pin && atomic_read(i->pin) != 1);
+
+ if (n != i->j.seq)
+ pr_err(
+ "journal entries %llu-%llu missing! (replaying %llu-%llu)\n",
+ n, i->j.seq - 1, start, end);
+
+ for (k = i->j.start;
+ k < end(&i->j);
+ k = bkey_next(k)) {
+ pr_debug("%s", pkey(k));
+ bkey_copy(op->keys.top, k);
+ bch_keylist_push(&op->keys);
+
+ op->journal = i->pin;
+ atomic_inc(op->journal);
+
+ ret = bch_btree_insert(op, s);
+ if (ret)
+ goto err;
+
+ BUG_ON(!bch_keylist_empty(&op->keys));
+ keys++;
+
+ cond_resched();
+ }
+
+ if (i->pin)
+ atomic_dec(i->pin);
+ n = i->j.seq + 1;
+ entries++;
+ }
+
+ pr_info("journal replay done, %i keys in %i entries, seq %llu",
+ keys, entries, end);
+
+ while (!list_empty(list)) {
+ i = list_first_entry(list, struct journal_replay, list);
+ list_del(&i->list);
+ kfree(i);
+ }
+err:
+ closure_sync(&op->cl);
+ return ret;
+}
+
+/* Journalling */
+
+static void btree_flush_write(struct cache_set *c)
+{
+ /*
+ * Try to find the btree node with that references the oldest journal
+ * entry, best is our current candidate and is locked if non NULL:
+ */
+ struct btree *b, *best = NULL;
+ unsigned iter;
+
+ for_each_cached_btree(b, c, iter) {
+ if (!down_write_trylock(&b->lock))
+ continue;
+
+ if (!btree_node_dirty(b) ||
+ !btree_current_write(b)->journal) {
+ rw_unlock(true, b);
+ continue;
+ }
+
+ if (!best)
+ best = b;
+ else if (journal_pin_cmp(c,
+ btree_current_write(best),
+ btree_current_write(b))) {
+ rw_unlock(true, best);
+ best = b;
+ } else
+ rw_unlock(true, b);
+ }
+
+ if (best)
+ goto out;
+
+ /* We can't find the best btree node, just pick the first */
+ list_for_each_entry(b, &c->btree_cache, list)
+ if (!b->level && btree_node_dirty(b)) {
+ best = b;
+ rw_lock(true, best, best->level);
+ goto found;
+ }
+
+out:
+ if (!best)
+ return;
+found:
+ if (btree_node_dirty(best))
+ bch_btree_write(best, true, NULL);
+ rw_unlock(true, best);
+}
+
+#define last_seq(j) ((j)->seq - fifo_used(&(j)->pin) + 1)
+
+static void journal_discard_endio(struct bio *bio, int error)
+{
+ struct journal_device *ja =
+ container_of(bio, struct journal_device, discard_bio);
+ struct cache *ca = container_of(ja, struct cache, journal);
+
+ atomic_set(&ja->discard_in_flight, DISCARD_DONE);
+
+ closure_wake_up(&ca->set->journal.wait);
+ closure_put(&ca->set->cl);
+}
+
+static void journal_discard_work(struct work_struct *work)
+{
+ struct journal_device *ja =
+ container_of(work, struct journal_device, discard_work);
+
+ submit_bio(0, &ja->discard_bio);
+}
+
+static void do_journal_discard(struct cache *ca)
+{
+ struct journal_device *ja = &ca->journal;
+ struct bio *bio = &ja->discard_bio;
+
+ if (!ca->discard) {
+ ja->discard_idx = ja->last_idx;
+ return;
+ }
+
+ switch (atomic_read(&ja->discard_in_flight) == DISCARD_IN_FLIGHT) {
+ case DISCARD_IN_FLIGHT:
+ return;
+
+ case DISCARD_DONE:
+ ja->discard_idx = (ja->discard_idx + 1) %
+ ca->sb.njournal_buckets;
+
+ atomic_set(&ja->discard_in_flight, DISCARD_READY);
+ /* fallthrough */
+
+ case DISCARD_READY:
+ if (ja->discard_idx == ja->last_idx)
+ return;
+
+ atomic_set(&ja->discard_in_flight, DISCARD_IN_FLIGHT);
+
+ bio_init(bio);
+ bio->bi_sector = bucket_to_sector(ca->set,
+ ca->sb.d[ja->discard_idx]);
+ bio->bi_bdev = ca->bdev;
+ bio->bi_rw = REQ_WRITE|REQ_DISCARD;
+ bio->bi_max_vecs = 1;
+ bio->bi_io_vec = bio->bi_inline_vecs;
+ bio->bi_size = bucket_bytes(ca);
+ bio->bi_end_io = journal_discard_endio;
+
+ closure_get(&ca->set->cl);
+ INIT_WORK(&ja->discard_work, journal_discard_work);
+ schedule_work(&ja->discard_work);
+ }
+}
+
+static void journal_reclaim(struct cache_set *c)
+{
+ struct bkey *k = &c->journal.key;
+ struct cache *ca;
+ uint64_t last_seq;
+ unsigned iter, n = 0;
+ atomic_t p;
+
+ while (!atomic_read(&fifo_front(&c->journal.pin)))
+ fifo_pop(&c->journal.pin, p);
+
+ last_seq = last_seq(&c->journal);
+
+ /* Update last_idx */
+
+ for_each_cache(ca, c, iter) {
+ struct journal_device *ja = &ca->journal;
+
+ while (ja->last_idx != ja->cur_idx &&
+ ja->seq[ja->last_idx] < last_seq)
+ ja->last_idx = (ja->last_idx + 1) %
+ ca->sb.njournal_buckets;
+ }
+
+ for_each_cache(ca, c, iter)
+ do_journal_discard(ca);
+
+ if (c->journal.blocks_free)
+ return;
+
+ /*
+ * Allocate:
+ * XXX: Sort by free journal space
+ */
+
+ for_each_cache(ca, c, iter) {
+ struct journal_device *ja = &ca->journal;
+ unsigned next = (ja->cur_idx + 1) % ca->sb.njournal_buckets;
+
+ /* No space available on this device */
+ if (next == ja->discard_idx)
+ continue;
+
+ ja->cur_idx = next;
+ k->ptr[n++] = PTR(0,
+ bucket_to_sector(c, ca->sb.d[ja->cur_idx]),
+ ca->sb.nr_this_dev);
+ }
+
+ bkey_init(k);
+ SET_KEY_PTRS(k, n);
+
+ if (n)
+ c->journal.blocks_free = c->sb.bucket_size >> c->block_bits;
+
+ if (!journal_full(&c->journal))
+ __closure_wake_up(&c->journal.wait);
+}
+
+void bch_journal_next(struct journal *j)
+{
+ atomic_t p = { 1 };
+
+ j->cur = (j->cur == j->w)
+ ? &j->w[1]
+ : &j->w[0];
+
+ /*
+ * The fifo_push() needs to happen at the same time as j->seq is
+ * incremented for last_seq() to be calculated correctly
+ */
+ BUG_ON(!fifo_push(&j->pin, p));
+ atomic_set(&fifo_back(&j->pin), 1);
+
+ j->cur->data->seq = ++j->seq;
+ j->cur->need_write = false;
+ j->cur->data->keys = 0;
+
+ if (fifo_full(&j->pin))
+ pr_debug("journal_pin full (%zu)", fifo_used(&j->pin));
+}
+
+static void journal_write_endio(struct bio *bio, int error)
+{
+ struct journal_write *w = bio->bi_private;
+
+ cache_set_err_on(error, w->c, "journal io error");
+ closure_put(&w->c->journal.io.cl);
+}
+
+static void journal_write(struct closure *);
+
+static void journal_write_done(struct closure *cl)
+{
+ struct journal *j = container_of(cl, struct journal, io.cl);
+ struct cache_set *c = container_of(j, struct cache_set, journal);
+
+ struct journal_write *w = (j->cur == j->w)
+ ? &j->w[1]
+ : &j->w[0];
+
+ __closure_wake_up(&w->wait);
+
+ if (c->journal_delay_ms)
+ closure_delay(&j->io, msecs_to_jiffies(c->journal_delay_ms));
+
+ continue_at(cl, journal_write, system_wq);
+}
+
+static void journal_write_unlocked(struct closure *cl)
+ __releases(c->journal.lock)
+{
+ struct cache_set *c = container_of(cl, struct cache_set, journal.io.cl);
+ struct cache *ca;
+ struct journal_write *w = c->journal.cur;
+ struct bkey *k = &c->journal.key;
+ unsigned i, sectors = set_blocks(w->data, c) * c->sb.block_size;
+
+ struct bio *bio;
+ struct bio_list list;
+ bio_list_init(&list);
+
+ if (!w->need_write) {
+ /*
+ * XXX: have to unlock closure before we unlock journal lock,
+ * else we race with bch_journal(). But this way we race
+ * against cache set unregister. Doh.
+ */
+ set_closure_fn(cl, NULL, NULL);
+ closure_sub(cl, CLOSURE_RUNNING + 1);
+ spin_unlock(&c->journal.lock);
+ return;
+ } else if (journal_full(&c->journal)) {
+ journal_reclaim(c);
+ spin_unlock(&c->journal.lock);
+
+ btree_flush_write(c);
+ continue_at(cl, journal_write, system_wq);
+ }
+
+ c->journal.blocks_free -= set_blocks(w->data, c);
+
+ w->data->btree_level = c->root->level;
+
+ bkey_copy(&w->data->btree_root, &c->root->key);
+ bkey_copy(&w->data->uuid_bucket, &c->uuid_bucket);
+
+ for_each_cache(ca, c, i)
+ w->data->prio_bucket[ca->sb.nr_this_dev] = ca->prio_buckets[0];
+
+ w->data->magic = jset_magic(c);
+ w->data->version = BCACHE_JSET_VERSION;
+ w->data->last_seq = last_seq(&c->journal);
+ w->data->csum = csum_set(w->data);
+
+ for (i = 0; i < KEY_PTRS(k); i++) {
+ ca = PTR_CACHE(c, k, i);
+ bio = &ca->journal.bio;
+
+ atomic_long_add(sectors, &ca->meta_sectors_written);
+
+ bio_reset(bio);
+ bio->bi_sector = PTR_OFFSET(k, i);
+ bio->bi_bdev = ca->bdev;
+ bio->bi_rw = REQ_WRITE|REQ_SYNC|REQ_META|REQ_FLUSH;
+ bio->bi_size = sectors << 9;
+
+ bio->bi_end_io = journal_write_endio;
+ bio->bi_private = w;
+ bch_bio_map(bio, w->data);
+
+ trace_bcache_journal_write(bio);
+ bio_list_add(&list, bio);
+
+ SET_PTR_OFFSET(k, i, PTR_OFFSET(k, i) + sectors);
+
+ ca->journal.seq[ca->journal.cur_idx] = w->data->seq;
+ }
+
+ atomic_dec_bug(&fifo_back(&c->journal.pin));
+ bch_journal_next(&c->journal);
+ journal_reclaim(c);
+
+ spin_unlock(&c->journal.lock);
+
+ while ((bio = bio_list_pop(&list)))
+ closure_bio_submit(bio, cl, c->cache[0]);
+
+ continue_at(cl, journal_write_done, NULL);
+}
+
+static void journal_write(struct closure *cl)
+{
+ struct cache_set *c = container_of(cl, struct cache_set, journal.io.cl);
+
+ spin_lock(&c->journal.lock);
+ journal_write_unlocked(cl);
+}
+
+static void __journal_try_write(struct cache_set *c, bool noflush)
+ __releases(c->journal.lock)
+{
+ struct closure *cl = &c->journal.io.cl;
+
+ if (!closure_trylock(cl, &c->cl))
+ spin_unlock(&c->journal.lock);
+ else if (noflush && journal_full(&c->journal)) {
+ spin_unlock(&c->journal.lock);
+ continue_at(cl, journal_write, system_wq);
+ } else
+ journal_write_unlocked(cl);
+}
+
+#define journal_try_write(c) __journal_try_write(c, false)
+
+void bch_journal_meta(struct cache_set *c, struct closure *cl)
+{
+ struct journal_write *w;
+
+ if (CACHE_SYNC(&c->sb)) {
+ spin_lock(&c->journal.lock);
+
+ w = c->journal.cur;
+ w->need_write = true;
+
+ if (cl)
+ BUG_ON(!closure_wait(&w->wait, cl));
+
+ __journal_try_write(c, true);
+ }
+}
+
+/*
+ * Entry point to the journalling code - bio_insert() and btree_invalidate()
+ * pass bch_journal() a list of keys to be journalled, and then
+ * bch_journal() hands those same keys off to btree_insert_async()
+ */
+
+void bch_journal(struct closure *cl)
+{
+ struct btree_op *op = container_of(cl, struct btree_op, cl);
+ struct cache_set *c = op->c;
+ struct journal_write *w;
+ size_t b, n = ((uint64_t *) op->keys.top) - op->keys.list;
+
+ if (op->type != BTREE_INSERT ||
+ !CACHE_SYNC(&c->sb))
+ goto out;
+
+ /*
+ * If we're looping because we errored, might already be waiting on
+ * another journal write:
+ */
+ while (atomic_read(&cl->parent->remaining) & CLOSURE_WAITING)
+ closure_sync(cl->parent);
+
+ spin_lock(&c->journal.lock);
+
+ if (journal_full(&c->journal)) {
+ /* XXX: tracepoint */
+ closure_wait(&c->journal.wait, cl);
+
+ journal_reclaim(c);
+ spin_unlock(&c->journal.lock);
+
+ btree_flush_write(c);
+ continue_at(cl, bch_journal, bcache_wq);
+ }
+
+ w = c->journal.cur;
+ w->need_write = true;
+ b = __set_blocks(w->data, w->data->keys + n, c);
+
+ if (b * c->sb.block_size > PAGE_SECTORS << JSET_BITS ||
+ b > c->journal.blocks_free) {
+ /* XXX: If we were inserting so many keys that they won't fit in
+ * an _empty_ journal write, we'll deadlock. For now, handle
+ * this in bch_keylist_realloc() - but something to think about.
+ */
+ BUG_ON(!w->data->keys);
+
+ /* XXX: tracepoint */
+ BUG_ON(!closure_wait(&w->wait, cl));
+
+ closure_flush(&c->journal.io);
+
+ journal_try_write(c);
+ continue_at(cl, bch_journal, bcache_wq);
+ }
+
+ memcpy(end(w->data), op->keys.list, n * sizeof(uint64_t));
+ w->data->keys += n;
+
+ op->journal = &fifo_back(&c->journal.pin);
+ atomic_inc(op->journal);
+
+ if (op->flush_journal) {
+ closure_flush(&c->journal.io);
+ closure_wait(&w->wait, cl->parent);
+ }
+
+ journal_try_write(c);
+out:
+ bch_btree_insert_async(cl);
+}
+
+void bch_journal_free(struct cache_set *c)
+{
+ free_pages((unsigned long) c->journal.w[1].data, JSET_BITS);
+ free_pages((unsigned long) c->journal.w[0].data, JSET_BITS);
+ free_fifo(&c->journal.pin);
+}
+
+int bch_journal_alloc(struct cache_set *c)
+{
+ struct journal *j = &c->journal;
+
+ closure_init_unlocked(&j->io);
+ spin_lock_init(&j->lock);
+
+ c->journal_delay_ms = 100;
+
+ j->w[0].c = c;
+ j->w[1].c = c;
+
+ if (!(init_fifo(&j->pin, JOURNAL_PIN, GFP_KERNEL)) ||
+ !(j->w[0].data = (void *) __get_free_pages(GFP_KERNEL, JSET_BITS)) ||
+ !(j->w[1].data = (void *) __get_free_pages(GFP_KERNEL, JSET_BITS)))
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/drivers/md/bcache/journal.h b/drivers/md/bcache/journal.h
new file mode 100644
index 000000000000..3d7851274b04
--- /dev/null
+++ b/drivers/md/bcache/journal.h
@@ -0,0 +1,215 @@
+#ifndef _BCACHE_JOURNAL_H
+#define _BCACHE_JOURNAL_H
+
+/*
+ * THE JOURNAL:
+ *
+ * The journal is treated as a circular buffer of buckets - a journal entry
+ * never spans two buckets. This means (not implemented yet) we can resize the
+ * journal at runtime, and will be needed for bcache on raw flash support.
+ *
+ * Journal entries contain a list of keys, ordered by the time they were
+ * inserted; thus journal replay just has to reinsert the keys.
+ *
+ * We also keep some things in the journal header that are logically part of the
+ * superblock - all the things that are frequently updated. This is for future
+ * bcache on raw flash support; the superblock (which will become another
+ * journal) can't be moved or wear leveled, so it contains just enough
+ * information to find the main journal, and the superblock only has to be
+ * rewritten when we want to move/wear level the main journal.
+ *
+ * Currently, we don't journal BTREE_REPLACE operations - this will hopefully be
+ * fixed eventually. This isn't a bug - BTREE_REPLACE is used for insertions
+ * from cache misses, which don't have to be journaled, and for writeback and
+ * moving gc we work around it by flushing the btree to disk before updating the
+ * gc information. But it is a potential issue with incremental garbage
+ * collection, and it's fragile.
+ *
+ * OPEN JOURNAL ENTRIES:
+ *
+ * Each journal entry contains, in the header, the sequence number of the last
+ * journal entry still open - i.e. that has keys that haven't been flushed to
+ * disk in the btree.
+ *
+ * We track this by maintaining a refcount for every open journal entry, in a
+ * fifo; each entry in the fifo corresponds to a particular journal
+ * entry/sequence number. When the refcount at the tail of the fifo goes to
+ * zero, we pop it off - thus, the size of the fifo tells us the number of open
+ * journal entries
+ *
+ * We take a refcount on a journal entry when we add some keys to a journal
+ * entry that we're going to insert (held by struct btree_op), and then when we
+ * insert those keys into the btree the btree write we're setting up takes a
+ * copy of that refcount (held by struct btree_write). That refcount is dropped
+ * when the btree write completes.
+ *
+ * A struct btree_write can only hold a refcount on a single journal entry, but
+ * might contain keys for many journal entries - we handle this by making sure
+ * it always has a refcount on the _oldest_ journal entry of all the journal
+ * entries it has keys for.
+ *
+ * JOURNAL RECLAIM:
+ *
+ * As mentioned previously, our fifo of refcounts tells us the number of open
+ * journal entries; from that and the current journal sequence number we compute
+ * last_seq - the oldest journal entry we still need. We write last_seq in each
+ * journal entry, and we also have to keep track of where it exists on disk so
+ * we don't overwrite it when we loop around the journal.
+ *
+ * To do that we track, for each journal bucket, the sequence number of the
+ * newest journal entry it contains - if we don't need that journal entry we
+ * don't need anything in that bucket anymore. From that we track the last
+ * journal bucket we still need; all this is tracked in struct journal_device
+ * and updated by journal_reclaim().
+ *
+ * JOURNAL FILLING UP:
+ *
+ * There are two ways the journal could fill up; either we could run out of
+ * space to write to, or we could have too many open journal entries and run out
+ * of room in the fifo of refcounts. Since those refcounts are decremented
+ * without any locking we can't safely resize that fifo, so we handle it the
+ * same way.
+ *
+ * If the journal fills up, we start flushing dirty btree nodes until we can
+ * allocate space for a journal write again - preferentially flushing btree
+ * nodes that are pinning the oldest journal entries first.
+ */
+
+#define BCACHE_JSET_VERSION_UUIDv1 1
+/* Always latest UUID format */
+#define BCACHE_JSET_VERSION_UUID 1
+#define BCACHE_JSET_VERSION 1
+
+/*
+ * On disk format for a journal entry:
+ * seq is monotonically increasing; every journal entry has its own unique
+ * sequence number.
+ *
+ * last_seq is the oldest journal entry that still has keys the btree hasn't
+ * flushed to disk yet.
+ *
+ * version is for on disk format changes.
+ */
+struct jset {
+ uint64_t csum;
+ uint64_t magic;
+ uint64_t seq;
+ uint32_t version;
+ uint32_t keys;
+
+ uint64_t last_seq;
+
+ BKEY_PADDED(uuid_bucket);
+ BKEY_PADDED(btree_root);
+ uint16_t btree_level;
+ uint16_t pad[3];
+
+ uint64_t prio_bucket[MAX_CACHES_PER_SET];
+
+ union {
+ struct bkey start[0];
+ uint64_t d[0];
+ };
+};
+
+/*
+ * Only used for holding the journal entries we read in btree_journal_read()
+ * during cache_registration
+ */
+struct journal_replay {
+ struct list_head list;
+ atomic_t *pin;
+ struct jset j;
+};
+
+/*
+ * We put two of these in struct journal; we used them for writes to the
+ * journal that are being staged or in flight.
+ */
+struct journal_write {
+ struct jset *data;
+#define JSET_BITS 3
+
+ struct cache_set *c;
+ struct closure_waitlist wait;
+ bool need_write;
+};
+
+/* Embedded in struct cache_set */
+struct journal {
+ spinlock_t lock;
+ /* used when waiting because the journal was full */
+ struct closure_waitlist wait;
+ struct closure_with_timer io;
+
+ /* Number of blocks free in the bucket(s) we're currently writing to */
+ unsigned blocks_free;
+ uint64_t seq;
+ DECLARE_FIFO(atomic_t, pin);
+
+ BKEY_PADDED(key);
+
+ struct journal_write w[2], *cur;
+};
+
+/*
+ * Embedded in struct cache. First three fields refer to the array of journal
+ * buckets, in cache_sb.
+ */
+struct journal_device {
+ /*
+ * For each journal bucket, contains the max sequence number of the
+ * journal writes it contains - so we know when a bucket can be reused.
+ */
+ uint64_t seq[SB_JOURNAL_BUCKETS];
+
+ /* Journal bucket we're currently writing to */
+ unsigned cur_idx;
+
+ /* Last journal bucket that still contains an open journal entry */
+ unsigned last_idx;
+
+ /* Next journal bucket to be discarded */
+ unsigned discard_idx;
+
+#define DISCARD_READY 0
+#define DISCARD_IN_FLIGHT 1
+#define DISCARD_DONE 2
+ /* 1 - discard in flight, -1 - discard completed */
+ atomic_t discard_in_flight;
+
+ struct work_struct discard_work;
+ struct bio discard_bio;
+ struct bio_vec discard_bv;
+
+ /* Bio for journal reads/writes to this device */
+ struct bio bio;
+ struct bio_vec bv[8];
+};
+
+#define journal_pin_cmp(c, l, r) \
+ (fifo_idx(&(c)->journal.pin, (l)->journal) > \
+ fifo_idx(&(c)->journal.pin, (r)->journal))
+
+#define JOURNAL_PIN 20000
+
+#define journal_full(j) \
+ (!(j)->blocks_free || fifo_free(&(j)->pin) <= 1)
+
+struct closure;
+struct cache_set;
+struct btree_op;
+
+void bch_journal(struct closure *);
+void bch_journal_next(struct journal *);
+void bch_journal_mark(struct cache_set *, struct list_head *);
+void bch_journal_meta(struct cache_set *, struct closure *);
+int bch_journal_read(struct cache_set *, struct list_head *,
+ struct btree_op *);
+int bch_journal_replay(struct cache_set *, struct list_head *,
+ struct btree_op *);
+
+void bch_journal_free(struct cache_set *);
+int bch_journal_alloc(struct cache_set *);
+
+#endif /* _BCACHE_JOURNAL_H */
diff --git a/drivers/md/bcache/movinggc.c b/drivers/md/bcache/movinggc.c
new file mode 100644
index 000000000000..8589512c972e
--- /dev/null
+++ b/drivers/md/bcache/movinggc.c
@@ -0,0 +1,254 @@
+/*
+ * Moving/copying garbage collector
+ *
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "btree.h"
+#include "debug.h"
+#include "request.h"
+
+struct moving_io {
+ struct keybuf_key *w;
+ struct search s;
+ struct bbio bio;
+};
+
+static bool moving_pred(struct keybuf *buf, struct bkey *k)
+{
+ struct cache_set *c = container_of(buf, struct cache_set,
+ moving_gc_keys);
+ unsigned i;
+
+ for (i = 0; i < KEY_PTRS(k); i++) {
+ struct cache *ca = PTR_CACHE(c, k, i);
+ struct bucket *g = PTR_BUCKET(c, k, i);
+
+ if (GC_SECTORS_USED(g) < ca->gc_move_threshold)
+ return true;
+ }
+
+ return false;
+}
+
+/* Moving GC - IO loop */
+
+static void moving_io_destructor(struct closure *cl)
+{
+ struct moving_io *io = container_of(cl, struct moving_io, s.cl);
+ kfree(io);
+}
+
+static void write_moving_finish(struct closure *cl)
+{
+ struct moving_io *io = container_of(cl, struct moving_io, s.cl);
+ struct bio *bio = &io->bio.bio;
+ struct bio_vec *bv = bio_iovec_idx(bio, bio->bi_vcnt);
+
+ while (bv-- != bio->bi_io_vec)
+ __free_page(bv->bv_page);
+
+ pr_debug("%s %s", io->s.op.insert_collision
+ ? "collision moving" : "moved",
+ pkey(&io->w->key));
+
+ bch_keybuf_del(&io->s.op.c->moving_gc_keys, io->w);
+
+ atomic_dec_bug(&io->s.op.c->in_flight);
+ closure_wake_up(&io->s.op.c->moving_gc_wait);
+
+ closure_return_with_destructor(cl, moving_io_destructor);
+}
+
+static void read_moving_endio(struct bio *bio, int error)
+{
+ struct moving_io *io = container_of(bio->bi_private,
+ struct moving_io, s.cl);
+
+ if (error)
+ io->s.error = error;
+
+ bch_bbio_endio(io->s.op.c, bio, error, "reading data to move");
+}
+
+static void moving_init(struct moving_io *io)
+{
+ struct bio *bio = &io->bio.bio;
+
+ bio_init(bio);
+ bio_get(bio);
+ bio_set_prio(bio, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
+
+ bio->bi_size = KEY_SIZE(&io->w->key) << 9;
+ bio->bi_max_vecs = DIV_ROUND_UP(KEY_SIZE(&io->w->key),
+ PAGE_SECTORS);
+ bio->bi_private = &io->s.cl;
+ bio->bi_io_vec = bio->bi_inline_vecs;
+ bch_bio_map(bio, NULL);
+}
+
+static void write_moving(struct closure *cl)
+{
+ struct search *s = container_of(cl, struct search, cl);
+ struct moving_io *io = container_of(s, struct moving_io, s);
+
+ if (!s->error) {
+ trace_bcache_write_moving(&io->bio.bio);
+
+ moving_init(io);
+
+ io->bio.bio.bi_sector = KEY_START(&io->w->key);
+ s->op.lock = -1;
+ s->op.write_prio = 1;
+ s->op.cache_bio = &io->bio.bio;
+
+ s->writeback = KEY_DIRTY(&io->w->key);
+ s->op.csum = KEY_CSUM(&io->w->key);
+
+ s->op.type = BTREE_REPLACE;
+ bkey_copy(&s->op.replace, &io->w->key);
+
+ closure_init(&s->op.cl, cl);
+ bch_insert_data(&s->op.cl);
+ }
+
+ continue_at(cl, write_moving_finish, NULL);
+}
+
+static void read_moving_submit(struct closure *cl)
+{
+ struct search *s = container_of(cl, struct search, cl);
+ struct moving_io *io = container_of(s, struct moving_io, s);
+ struct bio *bio = &io->bio.bio;
+
+ trace_bcache_read_moving(bio);
+ bch_submit_bbio(bio, s->op.c, &io->w->key, 0);
+
+ continue_at(cl, write_moving, bch_gc_wq);
+}
+
+static void read_moving(struct closure *cl)
+{
+ struct cache_set *c = container_of(cl, struct cache_set, moving_gc);
+ struct keybuf_key *w;
+ struct moving_io *io;
+ struct bio *bio;
+
+ /* XXX: if we error, background writeback could stall indefinitely */
+
+ while (!test_bit(CACHE_SET_STOPPING, &c->flags)) {
+ w = bch_keybuf_next_rescan(c, &c->moving_gc_keys, &MAX_KEY);
+ if (!w)
+ break;
+
+ io = kzalloc(sizeof(struct moving_io) + sizeof(struct bio_vec)
+ * DIV_ROUND_UP(KEY_SIZE(&w->key), PAGE_SECTORS),
+ GFP_KERNEL);
+ if (!io)
+ goto err;
+
+ w->private = io;
+ io->w = w;
+ io->s.op.inode = KEY_INODE(&w->key);
+ io->s.op.c = c;
+
+ moving_init(io);
+ bio = &io->bio.bio;
+
+ bio->bi_rw = READ;
+ bio->bi_end_io = read_moving_endio;
+
+ if (bch_bio_alloc_pages(bio, GFP_KERNEL))
+ goto err;
+
+ pr_debug("%s", pkey(&w->key));
+
+ closure_call(&io->s.cl, read_moving_submit, NULL, &c->gc.cl);
+
+ if (atomic_inc_return(&c->in_flight) >= 64) {
+ closure_wait_event(&c->moving_gc_wait, cl,
+ atomic_read(&c->in_flight) < 64);
+ continue_at(cl, read_moving, bch_gc_wq);
+ }
+ }
+
+ if (0) {
+err: if (!IS_ERR_OR_NULL(w->private))
+ kfree(w->private);
+
+ bch_keybuf_del(&c->moving_gc_keys, w);
+ }
+
+ closure_return(cl);
+}
+
+static bool bucket_cmp(struct bucket *l, struct bucket *r)
+{
+ return GC_SECTORS_USED(l) < GC_SECTORS_USED(r);
+}
+
+static unsigned bucket_heap_top(struct cache *ca)
+{
+ return GC_SECTORS_USED(heap_peek(&ca->heap));
+}
+
+void bch_moving_gc(struct closure *cl)
+{
+ struct cache_set *c = container_of(cl, struct cache_set, gc.cl);
+ struct cache *ca;
+ struct bucket *b;
+ unsigned i;
+
+ if (!c->copy_gc_enabled)
+ closure_return(cl);
+
+ mutex_lock(&c->bucket_lock);
+
+ for_each_cache(ca, c, i) {
+ unsigned sectors_to_move = 0;
+ unsigned reserve_sectors = ca->sb.bucket_size *
+ min(fifo_used(&ca->free), ca->free.size / 2);
+
+ ca->heap.used = 0;
+
+ for_each_bucket(b, ca) {
+ if (!GC_SECTORS_USED(b))
+ continue;
+
+ if (!heap_full(&ca->heap)) {
+ sectors_to_move += GC_SECTORS_USED(b);
+ heap_add(&ca->heap, b, bucket_cmp);
+ } else if (bucket_cmp(b, heap_peek(&ca->heap))) {
+ sectors_to_move -= bucket_heap_top(ca);
+ sectors_to_move += GC_SECTORS_USED(b);
+
+ ca->heap.data[0] = b;
+ heap_sift(&ca->heap, 0, bucket_cmp);
+ }
+ }
+
+ while (sectors_to_move > reserve_sectors) {
+ heap_pop(&ca->heap, b, bucket_cmp);
+ sectors_to_move -= GC_SECTORS_USED(b);
+ }
+
+ ca->gc_move_threshold = bucket_heap_top(ca);
+
+ pr_debug("threshold %u", ca->gc_move_threshold);
+ }
+
+ mutex_unlock(&c->bucket_lock);
+
+ c->moving_gc_keys.last_scanned = ZERO_KEY;
+
+ closure_init(&c->moving_gc, cl);
+ read_moving(&c->moving_gc);
+
+ closure_return(cl);
+}
+
+void bch_moving_init_cache_set(struct cache_set *c)
+{
+ bch_keybuf_init(&c->moving_gc_keys, moving_pred);
+}
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
new file mode 100644
index 000000000000..e5ff12e52d5b
--- /dev/null
+++ b/drivers/md/bcache/request.c
@@ -0,0 +1,1411 @@
+/*
+ * Main bcache entry point - handle a read or a write request and decide what to
+ * do with it; the make_request functions are called by the block layer.
+ *
+ * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "btree.h"
+#include "debug.h"
+#include "request.h"
+
+#include <linux/cgroup.h>
+#include <linux/module.h>
+#include <linux/hash.h>
+#include <linux/random.h>
+#include "blk-cgroup.h"
+
+#include <trace/events/bcache.h>
+
+#define CUTOFF_CACHE_ADD 95
+#define CUTOFF_CACHE_READA 90
+#define CUTOFF_WRITEBACK 50
+#define CUTOFF_WRITEBACK_SYNC 75
+
+struct kmem_cache *bch_search_cache;
+
+static void check_should_skip(struct cached_dev *, struct search *);
+
+/* Cgroup interface */
+
+#ifdef CONFIG_CGROUP_BCACHE
+static struct bch_cgroup bcache_default_cgroup = { .cache_mode = -1 };
+
+static struct bch_cgroup *cgroup_to_bcache(struct cgroup *cgroup)
+{
+ struct cgroup_subsys_state *css;
+ return cgroup &&
+ (css = cgroup_subsys_state(cgroup, bcache_subsys_id))
+ ? container_of(css, struct bch_cgroup, css)
+ : &bcache_default_cgroup;
+}
+
+struct bch_cgroup *bch_bio_to_cgroup(struct bio *bio)
+{
+ struct cgroup_subsys_state *css = bio->bi_css
+ ? cgroup_subsys_state(bio->bi_css->cgroup, bcache_subsys_id)
+ : task_subsys_state(current, bcache_subsys_id);
+
+ return css
+ ? container_of(css, struct bch_cgroup, css)
+ : &bcache_default_cgroup;
+}
+
+static ssize_t cache_mode_read(struct cgroup *cgrp, struct cftype *cft,
+ struct file *file,
+ char __user *buf, size_t nbytes, loff_t *ppos)
+{
+ char tmp[1024];
+ int len = bch_snprint_string_list(tmp, PAGE_SIZE, bch_cache_modes,
+ cgroup_to_bcache(cgrp)->cache_mode + 1);
+
+ if (len < 0)
+ return len;
+
+ return simple_read_from_buffer(buf, nbytes, ppos, tmp, len);
+}
+
+static int cache_mode_write(struct cgroup *cgrp, struct cftype *cft,
+ const char *buf)
+{
+ int v = bch_read_string_list(buf, bch_cache_modes);
+ if (v < 0)
+ return v;
+
+ cgroup_to_bcache(cgrp)->cache_mode = v - 1;
+ return 0;
+}
+
+static u64 bch_verify_read(struct cgroup *cgrp, struct cftype *cft)
+{
+ return cgroup_to_bcache(cgrp)->verify;
+}
+
+static int bch_verify_write(struct cgroup *cgrp, struct cftype *cft, u64 val)
+{
+ cgroup_to_bcache(cgrp)->verify = val;
+ return 0;
+}
+
+static u64 bch_cache_hits_read(struct cgroup *cgrp, struct cftype *cft)
+{
+ struct bch_cgroup *bcachecg = cgroup_to_bcache(cgrp);
+ return atomic_read(&bcachecg->stats.cache_hits);
+}
+
+static u64 bch_cache_misses_read(struct cgroup *cgrp, struct cftype *cft)
+{
+ struct bch_cgroup *bcachecg = cgroup_to_bcache(cgrp);
+ return atomic_read(&bcachecg->stats.cache_misses);
+}
+
+static u64 bch_cache_bypass_hits_read(struct cgroup *cgrp,
+ struct cftype *cft)
+{
+ struct bch_cgroup *bcachecg = cgroup_to_bcache(cgrp);
+ return atomic_read(&bcachecg->stats.cache_bypass_hits);
+}
+
+static u64 bch_cache_bypass_misses_read(struct cgroup *cgrp,
+ struct cftype *cft)
+{
+ struct bch_cgroup *bcachecg = cgroup_to_bcache(cgrp);
+ return atomic_read(&bcachecg->stats.cache_bypass_misses);
+}
+
+static struct cftype bch_files[] = {
+ {
+ .name = "cache_mode",
+ .read = cache_mode_read,
+ .write_string = cache_mode_write,
+ },
+ {
+ .name = "verify",
+ .read_u64 = bch_verify_read,
+ .write_u64 = bch_verify_write,
+ },
+ {
+ .name = "cache_hits",
+ .read_u64 = bch_cache_hits_read,
+ },
+ {
+ .name = "cache_misses",
+ .read_u64 = bch_cache_misses_read,
+ },
+ {
+ .name = "cache_bypass_hits",
+ .read_u64 = bch_cache_bypass_hits_read,
+ },
+ {
+ .name = "cache_bypass_misses",
+ .read_u64 = bch_cache_bypass_misses_read,
+ },
+ { } /* terminate */
+};
+
+static void init_bch_cgroup(struct bch_cgroup *cg)
+{
+ cg->cache_mode = -1;
+}
+
+static struct cgroup_subsys_state *bcachecg_create(struct cgroup *cgroup)
+{
+ struct bch_cgroup *cg;
+
+ cg = kzalloc(sizeof(*cg), GFP_KERNEL);
+ if (!cg)
+ return ERR_PTR(-ENOMEM);
+ init_bch_cgroup(cg);
+ return &cg->css;
+}
+
+static void bcachecg_destroy(struct cgroup *cgroup)
+{
+ struct bch_cgroup *cg = cgroup_to_bcache(cgroup);
+ free_css_id(&bcache_subsys, &cg->css);
+ kfree(cg);
+}
+
+struct cgroup_subsys bcache_subsys = {
+ .create = bcachecg_create,
+ .destroy = bcachecg_destroy,
+ .subsys_id = bcache_subsys_id,
+ .name = "bcache",
+ .module = THIS_MODULE,
+};
+EXPORT_SYMBOL_GPL(bcache_subsys);
+#endif
+
+static unsigned cache_mode(struct cached_dev *dc, struct bio *bio)
+{
+#ifdef CONFIG_CGROUP_BCACHE
+ int r = bch_bio_to_cgroup(bio)->cache_mode;
+ if (r >= 0)
+ return r;
+#endif
+ return BDEV_CACHE_MODE(&dc->sb);
+}
+
+static bool verify(struct cached_dev *dc, struct bio *bio)
+{
+#ifdef CONFIG_CGROUP_BCACHE
+ if (bch_bio_to_cgroup(bio)->verify)
+ return true;
+#endif
+ return dc->verify;
+}
+
+static void bio_csum(struct bio *bio, struct bkey *k)
+{
+ struct bio_vec *bv;
+ uint64_t csum = 0;
+ int i;
+
+ bio_for_each_segment(bv, bio, i) {
+ void *d = kmap(bv->bv_page) + bv->bv_offset;
+ csum = bch_crc64_update(csum, d, bv->bv_len);
+ kunmap(bv->bv_page);
+ }
+
+ k->ptr[KEY_PTRS(k)] = csum & (~0ULL >> 1);
+}
+
+/* Insert data into cache */
+
+static void bio_invalidate(struct closure *cl)
+{
+ struct btree_op *op = container_of(cl, struct btree_op, cl);
+ struct bio *bio = op->cache_bio;
+
+ pr_debug("invalidating %i sectors from %llu",
+ bio_sectors(bio), (uint64_t) bio->bi_sector);
+
+ while (bio_sectors(bio)) {
+ unsigned len = min(bio_sectors(bio), 1U << 14);
+
+ if (bch_keylist_realloc(&op->keys, 0, op->c))
+ goto out;
+
+ bio->bi_sector += len;
+ bio->bi_size -= len << 9;
+
+ bch_keylist_add(&op->keys,
+ &KEY(op->inode, bio->bi_sector, len));
+ }
+
+ op->insert_data_done = true;
+ bio_put(bio);
+out:
+ continue_at(cl, bch_journal, bcache_wq);
+}
+
+struct open_bucket {
+ struct list_head list;
+ struct task_struct *last;
+ unsigned sectors_free;
+ BKEY_PADDED(key);
+};
+
+void bch_open_buckets_free(struct cache_set *c)
+{
+ struct open_bucket *b;
+
+ while (!list_empty(&c->data_buckets)) {
+ b = list_first_entry(&c->data_buckets,
+ struct open_bucket, list);
+ list_del(&b->list);
+ kfree(b);
+ }
+}
+
+int bch_open_buckets_alloc(struct cache_set *c)
+{
+ int i;
+
+ spin_lock_init(&c->data_bucket_lock);
+
+ for (i = 0; i < 6; i++) {
+ struct open_bucket *b = kzalloc(sizeof(*b), GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ list_add(&b->list, &c->data_buckets);
+ }
+
+ return 0;
+}
+
+/*
+ * We keep multiple buckets open for writes, and try to segregate different
+ * write streams for better cache utilization: first we look for a bucket where
+ * the last write to it was sequential with the current write, and failing that
+ * we look for a bucket that was last used by the same task.
+ *
+ * The ideas is if you've got multiple tasks pulling data into the cache at the
+ * same time, you'll get better cache utilization if you try to segregate their
+ * data and preserve locality.
+ *
+ * For example, say you've starting Firefox at the same time you're copying a
+ * bunch of files. Firefox will likely end up being fairly hot and stay in the
+ * cache awhile, but the data you copied might not be; if you wrote all that
+ * data to the same buckets it'd get invalidated at the same time.
+ *
+ * Both of those tasks will be doing fairly random IO so we can't rely on
+ * detecting sequential IO to segregate their data, but going off of the task
+ * should be a sane heuristic.
+ */
+static struct open_bucket *pick_data_bucket(struct cache_set *c,
+ const struct bkey *search,
+ struct task_struct *task,
+ struct bkey *alloc)
+{
+ struct open_bucket *ret, *ret_task = NULL;
+
+ list_for_each_entry_reverse(ret, &c->data_buckets, list)
+ if (!bkey_cmp(&ret->key, search))
+ goto found;
+ else if (ret->last == task)
+ ret_task = ret;
+
+ ret = ret_task ?: list_first_entry(&c->data_buckets,
+ struct open_bucket, list);
+found:
+ if (!ret->sectors_free && KEY_PTRS(alloc)) {
+ ret->sectors_free = c->sb.bucket_size;
+ bkey_copy(&ret->key, alloc);
+ bkey_init(alloc);
+ }
+
+ if (!ret->sectors_free)
+ ret = NULL;
+
+ return ret;
+}
+
+/*
+ * Allocates some space in the cache to write to, and k to point to the newly
+ * allocated space, and updates KEY_SIZE(k) and KEY_OFFSET(k) (to point to the
+ * end of the newly allocated space).
+ *
+ * May allocate fewer sectors than @sectors, KEY_SIZE(k) indicates how many
+ * sectors were actually allocated.
+ *
+ * If s->writeback is true, will not fail.
+ */
+static bool bch_alloc_sectors(struct bkey *k, unsigned sectors,
+ struct search *s)
+{
+ struct cache_set *c = s->op.c;
+ struct open_bucket *b;
+ BKEY_PADDED(key) alloc;
+ struct closure cl, *w = NULL;
+ unsigned i;
+
+ if (s->writeback) {
+ closure_init_stack(&cl);
+ w = &cl;
+ }
+
+ /*
+ * We might have to allocate a new bucket, which we can't do with a
+ * spinlock held. So if we have to allocate, we drop the lock, allocate
+ * and then retry. KEY_PTRS() indicates whether alloc points to
+ * allocated bucket(s).
+ */
+
+ bkey_init(&alloc.key);
+ spin_lock(&c->data_bucket_lock);
+
+ while (!(b = pick_data_bucket(c, k, s->task, &alloc.key))) {
+ unsigned watermark = s->op.write_prio
+ ? WATERMARK_MOVINGGC
+ : WATERMARK_NONE;
+
+ spin_unlock(&c->data_bucket_lock);
+
+ if (bch_bucket_alloc_set(c, watermark, &alloc.key, 1, w))
+ return false;
+
+ spin_lock(&c->data_bucket_lock);
+ }
+
+ /*
+ * If we had to allocate, we might race and not need to allocate the
+ * second time we call find_data_bucket(). If we allocated a bucket but
+ * didn't use it, drop the refcount bch_bucket_alloc_set() took:
+ */
+ if (KEY_PTRS(&alloc.key))
+ __bkey_put(c, &alloc.key);
+
+ for (i = 0; i < KEY_PTRS(&b->key); i++)
+ EBUG_ON(ptr_stale(c, &b->key, i));
+
+ /* Set up the pointer to the space we're allocating: */
+
+ for (i = 0; i < KEY_PTRS(&b->key); i++)
+ k->ptr[i] = b->key.ptr[i];
+
+ sectors = min(sectors, b->sectors_free);
+
+ SET_KEY_OFFSET(k, KEY_OFFSET(k) + sectors);
+ SET_KEY_SIZE(k, sectors);
+ SET_KEY_PTRS(k, KEY_PTRS(&b->key));
+
+ /*
+ * Move b to the end of the lru, and keep track of what this bucket was
+ * last used for:
+ */
+ list_move_tail(&b->list, &c->data_buckets);
+ bkey_copy_key(&b->key, k);
+ b->last = s->task;
+
+ b->sectors_free -= sectors;
+
+ for (i = 0; i < KEY_PTRS(&b->key); i++) {
+ SET_PTR_OFFSET(&b->key, i, PTR_OFFSET(&b->key, i) + sectors);
+
+ atomic_long_add(sectors,
+ &PTR_CACHE(c, &b->key, i)->sectors_written);
+ }
+
+ if (b->sectors_free < c->sb.block_size)
+ b->sectors_free = 0;
+
+ /*
+ * k takes refcounts on the buckets it points to until it's inserted
+ * into the btree, but if we're done with this bucket we just transfer
+ * get_data_bucket()'s refcount.
+ */
+ if (b->sectors_free)
+ for (i = 0; i < KEY_PTRS(&b->key); i++)
+ atomic_inc(&PTR_BUCKET(c, &b->key, i)->pin);
+
+ spin_unlock(&c->data_bucket_lock);
+ return true;
+}
+
+static void bch_insert_data_error(struct closure *cl)
+{
+ struct btree_op *op = container_of(cl, struct btree_op, cl);
+
+ /*
+ * Our data write just errored, which means we've got a bunch of keys to
+ * insert that point to data that wasn't succesfully written.
+ *
+ * We don't have to insert those keys but we still have to invalidate
+ * that region of the cache - so, if we just strip off all the pointers
+ * from the keys we'll accomplish just that.
+ */
+
+ struct bkey *src = op->keys.bottom, *dst = op->keys.bottom;
+
+ while (src != op->keys.top) {
+ struct bkey *n = bkey_next(src);
+
+ SET_KEY_PTRS(src, 0);
+ bkey_copy(dst, src);
+
+ dst = bkey_next(dst);
+ src = n;
+ }
+
+ op->keys.top = dst;
+
+ bch_journal(cl);
+}
+
+static void bch_insert_data_endio(struct bio *bio, int error)
+{
+ struct closure *cl = bio->bi_private;
+ struct btree_op *op = container_of(cl, struct btree_op, cl);
+ struct search *s = container_of(op, struct search, op);
+
+ if (error) {
+ /* TODO: We could try to recover from this. */
+ if (s->writeback)
+ s->error = error;
+ else if (s->write)
+ set_closure_fn(cl, bch_insert_data_error, bcache_wq);
+ else
+ set_closure_fn(cl, NULL, NULL);
+ }
+
+ bch_bbio_endio(op->c, bio, error, "writing data to cache");
+}
+
+static void bch_insert_data_loop(struct closure *cl)
+{
+ struct btree_op *op = container_of(cl, struct btree_op, cl);
+ struct search *s = container_of(op, struct search, op);
+ struct bio *bio = op->cache_bio, *n;
+
+ if (op->skip)
+ return bio_invalidate(cl);
+
+ if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) {
+ set_gc_sectors(op->c);
+ bch_queue_gc(op->c);
+ }
+
+ do {
+ unsigned i;
+ struct bkey *k;
+ struct bio_set *split = s->d
+ ? s->d->bio_split : op->c->bio_split;
+
+ /* 1 for the device pointer and 1 for the chksum */
+ if (bch_keylist_realloc(&op->keys,
+ 1 + (op->csum ? 1 : 0),
+ op->c))
+ continue_at(cl, bch_journal, bcache_wq);
+
+ k = op->keys.top;
+ bkey_init(k);
+ SET_KEY_INODE(k, op->inode);
+ SET_KEY_OFFSET(k, bio->bi_sector);
+
+ if (!bch_alloc_sectors(k, bio_sectors(bio), s))
+ goto err;
+
+ n = bch_bio_split(bio, KEY_SIZE(k), GFP_NOIO, split);
+ if (!n) {
+ __bkey_put(op->c, k);
+ continue_at(cl, bch_insert_data_loop, bcache_wq);
+ }
+
+ n->bi_end_io = bch_insert_data_endio;
+ n->bi_private = cl;
+
+ if (s->writeback) {
+ SET_KEY_DIRTY(k, true);
+
+ for (i = 0; i < KEY_PTRS(k); i++)
+ SET_GC_MARK(PTR_BUCKET(op->c, k, i),
+ GC_MARK_DIRTY);
+ }
+
+ SET_KEY_CSUM(k, op->csum);
+ if (KEY_CSUM(k))
+ bio_csum(n, k);
+
+ pr_debug("%s", pkey(k));
+ bch_keylist_push(&op->keys);
+
+ trace_bcache_cache_insert(n, n->bi_sector, n->bi_bdev);
+ n->bi_rw |= REQ_WRITE;
+ bch_submit_bbio(n, op->c, k, 0);
+ } while (n != bio);
+
+ op->insert_data_done = true;
+ continue_at(cl, bch_journal, bcache_wq);
+err:
+ /* bch_alloc_sectors() blocks if s->writeback = true */
+ BUG_ON(s->writeback);
+
+ /*
+ * But if it's not a writeback write we'd rather just bail out if
+ * there aren't any buckets ready to write to - it might take awhile and
+ * we might be starving btree writes for gc or something.
+ */
+
+ if (s->write) {
+ /*
+ * Writethrough write: We can't complete the write until we've
+ * updated the index. But we don't want to delay the write while
+ * we wait for buckets to be freed up, so just invalidate the
+ * rest of the write.
+ */
+ op->skip = true;
+ return bio_invalidate(cl);
+ } else {
+ /*
+ * From a cache miss, we can just insert the keys for the data
+ * we have written or bail out if we didn't do anything.
+ */
+ op->insert_data_done = true;
+ bio_put(bio);
+
+ if (!bch_keylist_empty(&op->keys))
+ continue_at(cl, bch_journal, bcache_wq);
+ else
+ closure_return(cl);
+ }
+}
+
+/**
+ * bch_insert_data - stick some data in the cache
+ *
+ * This is the starting point for any data to end up in a cache device; it could
+ * be from a normal write, or a writeback write, or a write to a flash only
+ * volume - it's also used by the moving garbage collector to compact data in
+ * mostly empty buckets.
+ *
+ * It first writes the data to the cache, creating a list of keys to be inserted
+ * (if the data had to be fragmented there will be multiple keys); after the
+ * data is written it calls bch_journal, and after the keys have been added to
+ * the next journal write they're inserted into the btree.
+ *
+ * It inserts the data in op->cache_bio; bi_sector is used for the key offset,
+ * and op->inode is used for the key inode.
+ *
+ * If op->skip is true, instead of inserting the data it invalidates the region
+ * of the cache represented by op->cache_bio and op->inode.
+ */
+void bch_insert_data(struct closure *cl)
+{
+ struct btree_op *op = container_of(cl, struct btree_op, cl);
+
+ bch_keylist_init(&op->keys);
+ bio_get(op->cache_bio);
+ bch_insert_data_loop(cl);
+}
+
+void bch_btree_insert_async(struct closure *cl)
+{
+ struct btree_op *op = container_of(cl, struct btree_op, cl);
+ struct search *s = container_of(op, struct search, op);
+
+ if (bch_btree_insert(op, op->c)) {
+ s->error = -ENOMEM;
+ op->insert_data_done = true;
+ }
+
+ if (op->insert_data_done) {
+ bch_keylist_free(&op->keys);
+ closure_return(cl);
+ } else
+ continue_at(cl, bch_insert_data_loop, bcache_wq);
+}
+
+/* Common code for the make_request functions */
+
+static void request_endio(struct bio *bio, int error)
+{
+ struct closure *cl = bio->bi_private;
+
+ if (error) {
+ struct search *s = container_of(cl, struct search, cl);
+ s->error = error;
+ /* Only cache read errors are recoverable */
+ s->recoverable = false;
+ }
+
+ bio_put(bio);
+ closure_put(cl);
+}
+
+void bch_cache_read_endio(struct bio *bio, int error)
+{
+ struct bbio *b = container_of(bio, struct bbio, bio);
+ struct closure *cl = bio->bi_private;
+ struct search *s = container_of(cl, struct search, cl);
+
+ /*
+ * If the bucket was reused while our bio was in flight, we might have
+ * read the wrong data. Set s->error but not error so it doesn't get
+ * counted against the cache device, but we'll still reread the data
+ * from the backing device.
+ */
+
+ if (error)
+ s->error = error;
+ else if (ptr_stale(s->op.c, &b->key, 0)) {
+ atomic_long_inc(&s->op.c->cache_read_races);
+ s->error = -EINTR;
+ }
+
+ bch_bbio_endio(s->op.c, bio, error, "reading from cache");
+}
+
+static void bio_complete(struct search *s)
+{
+ if (s->orig_bio) {
+ int cpu, rw = bio_data_dir(s->orig_bio);
+ unsigned long duration = jiffies - s->start_time;
+
+ cpu = part_stat_lock();
+ part_round_stats(cpu, &s->d->disk->part0);
+ part_stat_add(cpu, &s->d->disk->part0, ticks[rw], duration);
+ part_stat_unlock();
+
+ trace_bcache_request_end(s, s->orig_bio);
+ bio_endio(s->orig_bio, s->error);
+ s->orig_bio = NULL;
+ }
+}
+
+static void do_bio_hook(struct search *s)
+{
+ struct bio *bio = &s->bio.bio;
+ memcpy(bio, s->orig_bio, sizeof(struct bio));
+
+ bio->bi_end_io = request_endio;
+ bio->bi_private = &s->cl;
+ atomic_set(&bio->bi_cnt, 3);
+}
+
+static void search_free(struct closure *cl)
+{
+ struct search *s = container_of(cl, struct search, cl);
+ bio_complete(s);
+
+ if (s->op.cache_bio)
+ bio_put(s->op.cache_bio);
+
+ if (s->unaligned_bvec)
+ mempool_free(s->bio.bio.bi_io_vec, s->d->unaligned_bvec);
+
+ closure_debug_destroy(cl);
+ mempool_free(s, s->d->c->search);
+}
+
+static struct search *search_alloc(struct bio *bio, struct bcache_device *d)
+{
+ struct bio_vec *bv;
+ struct search *s = mempool_alloc(d->c->search, GFP_NOIO);
+ memset(s, 0, offsetof(struct search, op.keys));
+
+ __closure_init(&s->cl, NULL);
+
+ s->op.inode = d->id;
+ s->op.c = d->c;
+ s->d = d;
+ s->op.lock = -1;
+ s->task = current;
+ s->orig_bio = bio;
+ s->write = (bio->bi_rw & REQ_WRITE) != 0;
+ s->op.flush_journal = (bio->bi_rw & REQ_FLUSH) != 0;
+ s->op.skip = (bio->bi_rw & REQ_DISCARD) != 0;
+ s->recoverable = 1;
+ s->start_time = jiffies;
+ do_bio_hook(s);
+
+ if (bio->bi_size != bio_segments(bio) * PAGE_SIZE) {
+ bv = mempool_alloc(d->unaligned_bvec, GFP_NOIO);
+ memcpy(bv, bio_iovec(bio),
+ sizeof(struct bio_vec) * bio_segments(bio));
+
+ s->bio.bio.bi_io_vec = bv;
+ s->unaligned_bvec = 1;
+ }
+
+ return s;
+}
+
+static void btree_read_async(struct closure *cl)
+{
+ struct btree_op *op = container_of(cl, struct btree_op, cl);
+
+ int ret = btree_root(search_recurse, op->c, op);
+
+ if (ret == -EAGAIN)
+ continue_at(cl, btree_read_async, bcache_wq);
+
+ closure_return(cl);
+}
+
+/* Cached devices */
+
+static void cached_dev_bio_complete(struct closure *cl)
+{
+ struct search *s = container_of(cl, struct search, cl);
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+
+ search_free(cl);
+ cached_dev_put(dc);
+}
+
+/* Process reads */
+
+static void cached_dev_read_complete(struct closure *cl)
+{
+ struct search *s = container_of(cl, struct search, cl);
+
+ if (s->op.insert_collision)
+ bch_mark_cache_miss_collision(s);
+
+ if (s->op.cache_bio) {
+ int i;
+ struct bio_vec *bv;
+
+ __bio_for_each_segment(bv, s->op.cache_bio, i, 0)
+ __free_page(bv->bv_page);
+ }
+
+ cached_dev_bio_complete(cl);
+}
+
+static void request_read_error(struct closure *cl)
+{
+ struct search *s = container_of(cl, struct search, cl);
+ struct bio_vec *bv;
+ int i;
+
+ if (s->recoverable) {
+ /* The cache read failed, but we can retry from the backing
+ * device.
+ */
+ pr_debug("recovering at sector %llu",
+ (uint64_t) s->orig_bio->bi_sector);
+
+ s->error = 0;
+ bv = s->bio.bio.bi_io_vec;
+ do_bio_hook(s);
+ s->bio.bio.bi_io_vec = bv;
+
+ if (!s->unaligned_bvec)
+ bio_for_each_segment(bv, s->orig_bio, i)
+ bv->bv_offset = 0, bv->bv_len = PAGE_SIZE;
+ else
+ memcpy(s->bio.bio.bi_io_vec,
+ bio_iovec(s->orig_bio),
+ sizeof(struct bio_vec) *
+ bio_segments(s->orig_bio));
+
+ /* XXX: invalidate cache */
+
+ trace_bcache_read_retry(&s->bio.bio);
+ closure_bio_submit(&s->bio.bio, &s->cl, s->d);
+ }
+
+ continue_at(cl, cached_dev_read_complete, NULL);
+}
+
+static void request_read_done(struct closure *cl)
+{
+ struct search *s = container_of(cl, struct search, cl);
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+
+ /*
+ * s->cache_bio != NULL implies that we had a cache miss; cache_bio now
+ * contains data ready to be inserted into the cache.
+ *
+ * First, we copy the data we just read from cache_bio's bounce buffers
+ * to the buffers the original bio pointed to:
+ */
+
+ if (s->op.cache_bio) {
+ struct bio_vec *src, *dst;
+ unsigned src_offset, dst_offset, bytes;
+ void *dst_ptr;
+
+ bio_reset(s->op.cache_bio);
+ s->op.cache_bio->bi_sector = s->cache_miss->bi_sector;
+ s->op.cache_bio->bi_bdev = s->cache_miss->bi_bdev;
+ s->op.cache_bio->bi_size = s->cache_bio_sectors << 9;
+ bch_bio_map(s->op.cache_bio, NULL);
+
+ src = bio_iovec(s->op.cache_bio);
+ dst = bio_iovec(s->cache_miss);
+ src_offset = src->bv_offset;
+ dst_offset = dst->bv_offset;
+ dst_ptr = kmap(dst->bv_page);
+
+ while (1) {
+ if (dst_offset == dst->bv_offset + dst->bv_len) {
+ kunmap(dst->bv_page);
+ dst++;
+ if (dst == bio_iovec_idx(s->cache_miss,
+ s->cache_miss->bi_vcnt))
+ break;
+
+ dst_offset = dst->bv_offset;
+ dst_ptr = kmap(dst->bv_page);
+ }
+
+ if (src_offset == src->bv_offset + src->bv_len) {
+ src++;
+ if (src == bio_iovec_idx(s->op.cache_bio,
+ s->op.cache_bio->bi_vcnt))
+ BUG();
+
+ src_offset = src->bv_offset;
+ }
+
+ bytes = min(dst->bv_offset + dst->bv_len - dst_offset,
+ src->bv_offset + src->bv_len - src_offset);
+
+ memcpy(dst_ptr + dst_offset,
+ page_address(src->bv_page) + src_offset,
+ bytes);
+
+ src_offset += bytes;
+ dst_offset += bytes;
+ }
+
+ bio_put(s->cache_miss);
+ s->cache_miss = NULL;
+ }
+
+ if (verify(dc, &s->bio.bio) && s->recoverable)
+ bch_data_verify(s);
+
+ bio_complete(s);
+
+ if (s->op.cache_bio &&
+ !test_bit(CACHE_SET_STOPPING, &s->op.c->flags)) {
+ s->op.type = BTREE_REPLACE;
+ closure_call(&s->op.cl, bch_insert_data, NULL, cl);
+ }
+
+ continue_at(cl, cached_dev_read_complete, NULL);
+}
+
+static void request_read_done_bh(struct closure *cl)
+{
+ struct search *s = container_of(cl, struct search, cl);
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+
+ bch_mark_cache_accounting(s, !s->cache_miss, s->op.skip);
+
+ if (s->error)
+ continue_at_nobarrier(cl, request_read_error, bcache_wq);
+ else if (s->op.cache_bio || verify(dc, &s->bio.bio))
+ continue_at_nobarrier(cl, request_read_done, bcache_wq);
+ else
+ continue_at_nobarrier(cl, cached_dev_read_complete, NULL);
+}
+
+static int cached_dev_cache_miss(struct btree *b, struct search *s,
+ struct bio *bio, unsigned sectors)
+{
+ int ret = 0;
+ unsigned reada;
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+ struct bio *miss;
+
+ miss = bch_bio_split(bio, sectors, GFP_NOIO, s->d->bio_split);
+ if (!miss)
+ return -EAGAIN;
+
+ if (miss == bio)
+ s->op.lookup_done = true;
+
+ miss->bi_end_io = request_endio;
+ miss->bi_private = &s->cl;
+
+ if (s->cache_miss || s->op.skip)
+ goto out_submit;
+
+ if (miss != bio ||
+ (bio->bi_rw & REQ_RAHEAD) ||
+ (bio->bi_rw & REQ_META) ||
+ s->op.c->gc_stats.in_use >= CUTOFF_CACHE_READA)
+ reada = 0;
+ else {
+ reada = min(dc->readahead >> 9,
+ sectors - bio_sectors(miss));
+
+ if (bio_end(miss) + reada > bdev_sectors(miss->bi_bdev))
+ reada = bdev_sectors(miss->bi_bdev) - bio_end(miss);
+ }
+
+ s->cache_bio_sectors = bio_sectors(miss) + reada;
+ s->op.cache_bio = bio_alloc_bioset(GFP_NOWAIT,
+ DIV_ROUND_UP(s->cache_bio_sectors, PAGE_SECTORS),
+ dc->disk.bio_split);
+
+ if (!s->op.cache_bio)
+ goto out_submit;
+
+ s->op.cache_bio->bi_sector = miss->bi_sector;
+ s->op.cache_bio->bi_bdev = miss->bi_bdev;
+ s->op.cache_bio->bi_size = s->cache_bio_sectors << 9;
+
+ s->op.cache_bio->bi_end_io = request_endio;
+ s->op.cache_bio->bi_private = &s->cl;
+
+ /* btree_search_recurse()'s btree iterator is no good anymore */
+ ret = -EINTR;
+ if (!bch_btree_insert_check_key(b, &s->op, s->op.cache_bio))
+ goto out_put;
+
+ bch_bio_map(s->op.cache_bio, NULL);
+ if (bch_bio_alloc_pages(s->op.cache_bio, __GFP_NOWARN|GFP_NOIO))
+ goto out_put;
+
+ s->cache_miss = miss;
+ bio_get(s->op.cache_bio);
+
+ trace_bcache_cache_miss(s->orig_bio);
+ closure_bio_submit(s->op.cache_bio, &s->cl, s->d);
+
+ return ret;
+out_put:
+ bio_put(s->op.cache_bio);
+ s->op.cache_bio = NULL;
+out_submit:
+ closure_bio_submit(miss, &s->cl, s->d);
+ return ret;
+}
+
+static void request_read(struct cached_dev *dc, struct search *s)
+{
+ struct closure *cl = &s->cl;
+
+ check_should_skip(dc, s);
+ closure_call(&s->op.cl, btree_read_async, NULL, cl);
+
+ continue_at(cl, request_read_done_bh, NULL);
+}
+
+/* Process writes */
+
+static void cached_dev_write_complete(struct closure *cl)
+{
+ struct search *s = container_of(cl, struct search, cl);
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+
+ up_read_non_owner(&dc->writeback_lock);
+ cached_dev_bio_complete(cl);
+}
+
+static bool should_writeback(struct cached_dev *dc, struct bio *bio)
+{
+ unsigned threshold = (bio->bi_rw & REQ_SYNC)
+ ? CUTOFF_WRITEBACK_SYNC
+ : CUTOFF_WRITEBACK;
+
+ return !atomic_read(&dc->disk.detaching) &&
+ cache_mode(dc, bio) == CACHE_MODE_WRITEBACK &&
+ dc->disk.c->gc_stats.in_use < threshold;
+}
+
+static void request_write(struct cached_dev *dc, struct search *s)
+{
+ struct closure *cl = &s->cl;
+ struct bio *bio = &s->bio.bio;
+ struct bkey start, end;
+ start = KEY(dc->disk.id, bio->bi_sector, 0);
+ end = KEY(dc->disk.id, bio_end(bio), 0);
+
+ bch_keybuf_check_overlapping(&s->op.c->moving_gc_keys, &start, &end);
+
+ check_should_skip(dc, s);
+ down_read_non_owner(&dc->writeback_lock);
+
+ if (bch_keybuf_check_overlapping(&dc->writeback_keys, &start, &end)) {
+ s->op.skip = false;
+ s->writeback = true;
+ }
+
+ if (bio->bi_rw & REQ_DISCARD)
+ goto skip;
+
+ if (s->op.skip)
+ goto skip;
+
+ if (should_writeback(dc, s->orig_bio))
+ s->writeback = true;
+
+ if (!s->writeback) {
+ s->op.cache_bio = bio_clone_bioset(bio, GFP_NOIO,
+ dc->disk.bio_split);
+
+ trace_bcache_writethrough(s->orig_bio);
+ closure_bio_submit(bio, cl, s->d);
+ } else {
+ s->op.cache_bio = bio;
+ trace_bcache_writeback(s->orig_bio);
+ bch_writeback_add(dc, bio_sectors(bio));
+ }
+out:
+ closure_call(&s->op.cl, bch_insert_data, NULL, cl);
+ continue_at(cl, cached_dev_write_complete, NULL);
+skip:
+ s->op.skip = true;
+ s->op.cache_bio = s->orig_bio;
+ bio_get(s->op.cache_bio);
+ trace_bcache_write_skip(s->orig_bio);
+
+ if ((bio->bi_rw & REQ_DISCARD) &&
+ !blk_queue_discard(bdev_get_queue(dc->bdev)))
+ goto out;
+
+ closure_bio_submit(bio, cl, s->d);
+ goto out;
+}
+
+static void request_nodata(struct cached_dev *dc, struct search *s)
+{
+ struct closure *cl = &s->cl;
+ struct bio *bio = &s->bio.bio;
+
+ if (bio->bi_rw & REQ_DISCARD) {
+ request_write(dc, s);
+ return;
+ }
+
+ if (s->op.flush_journal)
+ bch_journal_meta(s->op.c, cl);
+
+ closure_bio_submit(bio, cl, s->d);
+
+ continue_at(cl, cached_dev_bio_complete, NULL);
+}
+
+/* Cached devices - read & write stuff */
+
+int bch_get_congested(struct cache_set *c)
+{
+ int i;
+
+ if (!c->congested_read_threshold_us &&
+ !c->congested_write_threshold_us)
+ return 0;
+
+ i = (local_clock_us() - c->congested_last_us) / 1024;
+ if (i < 0)
+ return 0;
+
+ i += atomic_read(&c->congested);
+ if (i >= 0)
+ return 0;
+
+ i += CONGESTED_MAX;
+
+ return i <= 0 ? 1 : fract_exp_two(i, 6);
+}
+
+static void add_sequential(struct task_struct *t)
+{
+ ewma_add(t->sequential_io_avg,
+ t->sequential_io, 8, 0);
+
+ t->sequential_io = 0;
+}
+
+static struct hlist_head *iohash(struct cached_dev *dc, uint64_t k)
+{
+ return &dc->io_hash[hash_64(k, RECENT_IO_BITS)];
+}
+
+static void check_should_skip(struct cached_dev *dc, struct search *s)
+{
+ struct cache_set *c = s->op.c;
+ struct bio *bio = &s->bio.bio;
+
+ long rand;
+ int cutoff = bch_get_congested(c);
+ unsigned mode = cache_mode(dc, bio);
+
+ if (atomic_read(&dc->disk.detaching) ||
+ c->gc_stats.in_use > CUTOFF_CACHE_ADD ||
+ (bio->bi_rw & REQ_DISCARD))
+ goto skip;
+
+ if (mode == CACHE_MODE_NONE ||
+ (mode == CACHE_MODE_WRITEAROUND &&
+ (bio->bi_rw & REQ_WRITE)))
+ goto skip;
+
+ if (bio->bi_sector & (c->sb.block_size - 1) ||
+ bio_sectors(bio) & (c->sb.block_size - 1)) {
+ pr_debug("skipping unaligned io");
+ goto skip;
+ }
+
+ if (!cutoff) {
+ cutoff = dc->sequential_cutoff >> 9;
+
+ if (!cutoff)
+ goto rescale;
+
+ if (mode == CACHE_MODE_WRITEBACK &&
+ (bio->bi_rw & REQ_WRITE) &&
+ (bio->bi_rw & REQ_SYNC))
+ goto rescale;
+ }
+
+ if (dc->sequential_merge) {
+ struct io *i;
+
+ spin_lock(&dc->io_lock);
+
+ hlist_for_each_entry(i, iohash(dc, bio->bi_sector), hash)
+ if (i->last == bio->bi_sector &&
+ time_before(jiffies, i->jiffies))
+ goto found;
+
+ i = list_first_entry(&dc->io_lru, struct io, lru);
+
+ add_sequential(s->task);
+ i->sequential = 0;
+found:
+ if (i->sequential + bio->bi_size > i->sequential)
+ i->sequential += bio->bi_size;
+
+ i->last = bio_end(bio);
+ i->jiffies = jiffies + msecs_to_jiffies(5000);
+ s->task->sequential_io = i->sequential;
+
+ hlist_del(&i->hash);
+ hlist_add_head(&i->hash, iohash(dc, i->last));
+ list_move_tail(&i->lru, &dc->io_lru);
+
+ spin_unlock(&dc->io_lock);
+ } else {
+ s->task->sequential_io = bio->bi_size;
+
+ add_sequential(s->task);
+ }
+
+ rand = get_random_int();
+ cutoff -= bitmap_weight(&rand, BITS_PER_LONG);
+
+ if (cutoff <= (int) (max(s->task->sequential_io,
+ s->task->sequential_io_avg) >> 9))
+ goto skip;
+
+rescale:
+ bch_rescale_priorities(c, bio_sectors(bio));
+ return;
+skip:
+ bch_mark_sectors_bypassed(s, bio_sectors(bio));
+ s->op.skip = true;
+}
+
+static void cached_dev_make_request(struct request_queue *q, struct bio *bio)
+{
+ struct search *s;
+ struct bcache_device *d = bio->bi_bdev->bd_disk->private_data;
+ struct cached_dev *dc = container_of(d, struct cached_dev, disk);
+ int cpu, rw = bio_data_dir(bio);
+
+ cpu = part_stat_lock();
+ part_stat_inc(cpu, &d->disk->part0, ios[rw]);
+ part_stat_add(cpu, &d->disk->part0, sectors[rw], bio_sectors(bio));
+ part_stat_unlock();
+
+ bio->bi_bdev = dc->bdev;
+ bio->bi_sector += dc->sb.data_offset;
+
+ if (cached_dev_get(dc)) {
+ s = search_alloc(bio, d);
+ trace_bcache_request_start(s, bio);
+
+ if (!bio_has_data(bio))
+ request_nodata(dc, s);
+ else if (rw)
+ request_write(dc, s);
+ else
+ request_read(dc, s);
+ } else {
+ if ((bio->bi_rw & REQ_DISCARD) &&
+ !blk_queue_discard(bdev_get_queue(dc->bdev)))
+ bio_endio(bio, 0);
+ else
+ bch_generic_make_request(bio, &d->bio_split_hook);
+ }
+}
+
+static int cached_dev_ioctl(struct bcache_device *d, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ struct cached_dev *dc = container_of(d, struct cached_dev, disk);
+ return __blkdev_driver_ioctl(dc->bdev, mode, cmd, arg);
+}
+
+static int cached_dev_congested(void *data, int bits)
+{
+ struct bcache_device *d = data;
+ struct cached_dev *dc = container_of(d, struct cached_dev, disk);
+ struct request_queue *q = bdev_get_queue(dc->bdev);
+ int ret = 0;
+
+ if (bdi_congested(&q->backing_dev_info, bits))
+ return 1;
+
+ if (cached_dev_get(dc)) {
+ unsigned i;
+ struct cache *ca;
+
+ for_each_cache(ca, d->c, i) {
+ q = bdev_get_queue(ca->bdev);
+ ret |= bdi_congested(&q->backing_dev_info, bits);
+ }
+
+ cached_dev_put(dc);
+ }
+
+ return ret;
+}
+
+void bch_cached_dev_request_init(struct cached_dev *dc)
+{
+ struct gendisk *g = dc->disk.disk;
+
+ g->queue->make_request_fn = cached_dev_make_request;
+ g->queue->backing_dev_info.congested_fn = cached_dev_congested;
+ dc->disk.cache_miss = cached_dev_cache_miss;
+ dc->disk.ioctl = cached_dev_ioctl;
+}
+
+/* Flash backed devices */
+
+static int flash_dev_cache_miss(struct btree *b, struct search *s,
+ struct bio *bio, unsigned sectors)
+{
+ /* Zero fill bio */
+
+ while (bio->bi_idx != bio->bi_vcnt) {
+ struct bio_vec *bv = bio_iovec(bio);
+ unsigned j = min(bv->bv_len >> 9, sectors);
+
+ void *p = kmap(bv->bv_page);
+ memset(p + bv->bv_offset, 0, j << 9);
+ kunmap(bv->bv_page);
+
+ bv->bv_len -= j << 9;
+ bv->bv_offset += j << 9;
+
+ if (bv->bv_len)
+ return 0;
+
+ bio->bi_sector += j;
+ bio->bi_size -= j << 9;
+
+ bio->bi_idx++;
+ sectors -= j;
+ }
+
+ s->op.lookup_done = true;
+
+ return 0;
+}
+
+static void flash_dev_make_request(struct request_queue *q, struct bio *bio)
+{
+ struct search *s;
+ struct closure *cl;
+ struct bcache_device *d = bio->bi_bdev->bd_disk->private_data;
+ int cpu, rw = bio_data_dir(bio);
+
+ cpu = part_stat_lock();
+ part_stat_inc(cpu, &d->disk->part0, ios[rw]);
+ part_stat_add(cpu, &d->disk->part0, sectors[rw], bio_sectors(bio));
+ part_stat_unlock();
+
+ s = search_alloc(bio, d);
+ cl = &s->cl;
+ bio = &s->bio.bio;
+
+ trace_bcache_request_start(s, bio);
+
+ if (bio_has_data(bio) && !rw) {
+ closure_call(&s->op.cl, btree_read_async, NULL, cl);
+ } else if (bio_has_data(bio) || s->op.skip) {
+ bch_keybuf_check_overlapping(&s->op.c->moving_gc_keys,
+ &KEY(d->id, bio->bi_sector, 0),
+ &KEY(d->id, bio_end(bio), 0));
+
+ s->writeback = true;
+ s->op.cache_bio = bio;
+
+ closure_call(&s->op.cl, bch_insert_data, NULL, cl);
+ } else {
+ /* No data - probably a cache flush */
+ if (s->op.flush_journal)
+ bch_journal_meta(s->op.c, cl);
+ }
+
+ continue_at(cl, search_free, NULL);
+}
+
+static int flash_dev_ioctl(struct bcache_device *d, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ return -ENOTTY;
+}
+
+static int flash_dev_congested(void *data, int bits)
+{
+ struct bcache_device *d = data;
+ struct request_queue *q;
+ struct cache *ca;
+ unsigned i;
+ int ret = 0;
+
+ for_each_cache(ca, d->c, i) {
+ q = bdev_get_queue(ca->bdev);
+ ret |= bdi_congested(&q->backing_dev_info, bits);
+ }
+
+ return ret;
+}
+
+void bch_flash_dev_request_init(struct bcache_device *d)
+{
+ struct gendisk *g = d->disk;
+
+ g->queue->make_request_fn = flash_dev_make_request;
+ g->queue->backing_dev_info.congested_fn = flash_dev_congested;
+ d->cache_miss = flash_dev_cache_miss;
+ d->ioctl = flash_dev_ioctl;
+}
+
+void bch_request_exit(void)
+{
+#ifdef CONFIG_CGROUP_BCACHE
+ cgroup_unload_subsys(&bcache_subsys);
+#endif
+ if (bch_search_cache)
+ kmem_cache_destroy(bch_search_cache);
+}
+
+int __init bch_request_init(void)
+{
+ bch_search_cache = KMEM_CACHE(search, 0);
+ if (!bch_search_cache)
+ return -ENOMEM;
+
+#ifdef CONFIG_CGROUP_BCACHE
+ cgroup_load_subsys(&bcache_subsys);
+ init_bch_cgroup(&bcache_default_cgroup);
+
+ cgroup_add_cftypes(&bcache_subsys, bch_files);
+#endif
+ return 0;
+}
diff --git a/drivers/md/bcache/request.h b/drivers/md/bcache/request.h
new file mode 100644
index 000000000000..254d9ab5707c
--- /dev/null
+++ b/drivers/md/bcache/request.h
@@ -0,0 +1,62 @@
+#ifndef _BCACHE_REQUEST_H_
+#define _BCACHE_REQUEST_H_
+
+#include <linux/cgroup.h>
+
+struct search {
+ /* Stack frame for bio_complete */
+ struct closure cl;
+
+ struct bcache_device *d;
+ struct task_struct *task;
+
+ struct bbio bio;
+ struct bio *orig_bio;
+ struct bio *cache_miss;
+ unsigned cache_bio_sectors;
+
+ unsigned recoverable:1;
+ unsigned unaligned_bvec:1;
+
+ unsigned write:1;
+ unsigned writeback:1;
+
+ /* IO error returned to s->bio */
+ short error;
+ unsigned long start_time;
+
+ /* Anything past op->keys won't get zeroed in do_bio_hook */
+ struct btree_op op;
+};
+
+void bch_cache_read_endio(struct bio *, int);
+int bch_get_congested(struct cache_set *);
+void bch_insert_data(struct closure *cl);
+void bch_btree_insert_async(struct closure *);
+void bch_cache_read_endio(struct bio *, int);
+
+void bch_open_buckets_free(struct cache_set *);
+int bch_open_buckets_alloc(struct cache_set *);
+
+void bch_cached_dev_request_init(struct cached_dev *dc);
+void bch_flash_dev_request_init(struct bcache_device *d);
+
+extern struct kmem_cache *bch_search_cache, *bch_passthrough_cache;
+
+struct bch_cgroup {
+#ifdef CONFIG_CGROUP_BCACHE
+ struct cgroup_subsys_state css;
+#endif
+ /*
+ * We subtract one from the index into bch_cache_modes[], so that
+ * default == -1; this makes it so the rest match up with d->cache_mode,
+ * and we use d->cache_mode if cgrp->cache_mode < 0
+ */
+ short cache_mode;
+ bool verify;
+ struct cache_stat_collector stats;
+};
+
+struct bch_cgroup *bch_bio_to_cgroup(struct bio *bio);
+
+#endif /* _BCACHE_REQUEST_H_ */
diff --git a/drivers/md/bcache/stats.c b/drivers/md/bcache/stats.c
new file mode 100644
index 000000000000..64e679449c2a
--- /dev/null
+++ b/drivers/md/bcache/stats.c
@@ -0,0 +1,246 @@
+/*
+ * bcache stats code
+ *
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "stats.h"
+#include "btree.h"
+#include "request.h"
+#include "sysfs.h"
+
+/*
+ * We keep absolute totals of various statistics, and addionally a set of three
+ * rolling averages.
+ *
+ * Every so often, a timer goes off and rescales the rolling averages.
+ * accounting_rescale[] is how many times the timer has to go off before we
+ * rescale each set of numbers; that gets us half lives of 5 minutes, one hour,
+ * and one day.
+ *
+ * accounting_delay is how often the timer goes off - 22 times in 5 minutes,
+ * and accounting_weight is what we use to rescale:
+ *
+ * pow(31 / 32, 22) ~= 1/2
+ *
+ * So that we don't have to increment each set of numbers every time we (say)
+ * get a cache hit, we increment a single atomic_t in acc->collector, and when
+ * the rescale function runs it resets the atomic counter to 0 and adds its
+ * old value to each of the exported numbers.
+ *
+ * To reduce rounding error, the numbers in struct cache_stats are all
+ * stored left shifted by 16, and scaled back in the sysfs show() function.
+ */
+
+static const unsigned DAY_RESCALE = 288;
+static const unsigned HOUR_RESCALE = 12;
+static const unsigned FIVE_MINUTE_RESCALE = 1;
+static const unsigned accounting_delay = (HZ * 300) / 22;
+static const unsigned accounting_weight = 32;
+
+/* sysfs reading/writing */
+
+read_attribute(cache_hits);
+read_attribute(cache_misses);
+read_attribute(cache_bypass_hits);
+read_attribute(cache_bypass_misses);
+read_attribute(cache_hit_ratio);
+read_attribute(cache_readaheads);
+read_attribute(cache_miss_collisions);
+read_attribute(bypassed);
+
+SHOW(bch_stats)
+{
+ struct cache_stats *s =
+ container_of(kobj, struct cache_stats, kobj);
+#define var(stat) (s->stat >> 16)
+ var_print(cache_hits);
+ var_print(cache_misses);
+ var_print(cache_bypass_hits);
+ var_print(cache_bypass_misses);
+
+ sysfs_print(cache_hit_ratio,
+ DIV_SAFE(var(cache_hits) * 100,
+ var(cache_hits) + var(cache_misses)));
+
+ var_print(cache_readaheads);
+ var_print(cache_miss_collisions);
+ sysfs_hprint(bypassed, var(sectors_bypassed) << 9);
+#undef var
+ return 0;
+}
+
+STORE(bch_stats)
+{
+ return size;
+}
+
+static void bch_stats_release(struct kobject *k)
+{
+}
+
+static struct attribute *bch_stats_files[] = {
+ &sysfs_cache_hits,
+ &sysfs_cache_misses,
+ &sysfs_cache_bypass_hits,
+ &sysfs_cache_bypass_misses,
+ &sysfs_cache_hit_ratio,
+ &sysfs_cache_readaheads,
+ &sysfs_cache_miss_collisions,
+ &sysfs_bypassed,
+ NULL
+};
+static KTYPE(bch_stats);
+
+static void scale_accounting(unsigned long data);
+
+void bch_cache_accounting_init(struct cache_accounting *acc,
+ struct closure *parent)
+{
+ kobject_init(&acc->total.kobj, &bch_stats_ktype);
+ kobject_init(&acc->five_minute.kobj, &bch_stats_ktype);
+ kobject_init(&acc->hour.kobj, &bch_stats_ktype);
+ kobject_init(&acc->day.kobj, &bch_stats_ktype);
+
+ closure_init(&acc->cl, parent);
+ init_timer(&acc->timer);
+ acc->timer.expires = jiffies + accounting_delay;
+ acc->timer.data = (unsigned long) acc;
+ acc->timer.function = scale_accounting;
+ add_timer(&acc->timer);
+}
+
+int bch_cache_accounting_add_kobjs(struct cache_accounting *acc,
+ struct kobject *parent)
+{
+ int ret = kobject_add(&acc->total.kobj, parent,
+ "stats_total");
+ ret = ret ?: kobject_add(&acc->five_minute.kobj, parent,
+ "stats_five_minute");
+ ret = ret ?: kobject_add(&acc->hour.kobj, parent,
+ "stats_hour");
+ ret = ret ?: kobject_add(&acc->day.kobj, parent,
+ "stats_day");
+ return ret;
+}
+
+void bch_cache_accounting_clear(struct cache_accounting *acc)
+{
+ memset(&acc->total.cache_hits,
+ 0,
+ sizeof(unsigned long) * 7);
+}
+
+void bch_cache_accounting_destroy(struct cache_accounting *acc)
+{
+ kobject_put(&acc->total.kobj);
+ kobject_put(&acc->five_minute.kobj);
+ kobject_put(&acc->hour.kobj);
+ kobject_put(&acc->day.kobj);
+
+ atomic_set(&acc->closing, 1);
+ if (del_timer_sync(&acc->timer))
+ closure_return(&acc->cl);
+}
+
+/* EWMA scaling */
+
+static void scale_stat(unsigned long *stat)
+{
+ *stat = ewma_add(*stat, 0, accounting_weight, 0);
+}
+
+static void scale_stats(struct cache_stats *stats, unsigned long rescale_at)
+{
+ if (++stats->rescale == rescale_at) {
+ stats->rescale = 0;
+ scale_stat(&stats->cache_hits);
+ scale_stat(&stats->cache_misses);
+ scale_stat(&stats->cache_bypass_hits);
+ scale_stat(&stats->cache_bypass_misses);
+ scale_stat(&stats->cache_readaheads);
+ scale_stat(&stats->cache_miss_collisions);
+ scale_stat(&stats->sectors_bypassed);
+ }
+}
+
+static void scale_accounting(unsigned long data)
+{
+ struct cache_accounting *acc = (struct cache_accounting *) data;
+
+#define move_stat(name) do { \
+ unsigned t = atomic_xchg(&acc->collector.name, 0); \
+ t <<= 16; \
+ acc->five_minute.name += t; \
+ acc->hour.name += t; \
+ acc->day.name += t; \
+ acc->total.name += t; \
+} while (0)
+
+ move_stat(cache_hits);
+ move_stat(cache_misses);
+ move_stat(cache_bypass_hits);
+ move_stat(cache_bypass_misses);
+ move_stat(cache_readaheads);
+ move_stat(cache_miss_collisions);
+ move_stat(sectors_bypassed);
+
+ scale_stats(&acc->total, 0);
+ scale_stats(&acc->day, DAY_RESCALE);
+ scale_stats(&acc->hour, HOUR_RESCALE);
+ scale_stats(&acc->five_minute, FIVE_MINUTE_RESCALE);
+
+ acc->timer.expires += accounting_delay;
+
+ if (!atomic_read(&acc->closing))
+ add_timer(&acc->timer);
+ else
+ closure_return(&acc->cl);
+}
+
+static void mark_cache_stats(struct cache_stat_collector *stats,
+ bool hit, bool bypass)
+{
+ if (!bypass)
+ if (hit)
+ atomic_inc(&stats->cache_hits);
+ else
+ atomic_inc(&stats->cache_misses);
+ else
+ if (hit)
+ atomic_inc(&stats->cache_bypass_hits);
+ else
+ atomic_inc(&stats->cache_bypass_misses);
+}
+
+void bch_mark_cache_accounting(struct search *s, bool hit, bool bypass)
+{
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+ mark_cache_stats(&dc->accounting.collector, hit, bypass);
+ mark_cache_stats(&s->op.c->accounting.collector, hit, bypass);
+#ifdef CONFIG_CGROUP_BCACHE
+ mark_cache_stats(&(bch_bio_to_cgroup(s->orig_bio)->stats), hit, bypass);
+#endif
+}
+
+void bch_mark_cache_readahead(struct search *s)
+{
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+ atomic_inc(&dc->accounting.collector.cache_readaheads);
+ atomic_inc(&s->op.c->accounting.collector.cache_readaheads);
+}
+
+void bch_mark_cache_miss_collision(struct search *s)
+{
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+ atomic_inc(&dc->accounting.collector.cache_miss_collisions);
+ atomic_inc(&s->op.c->accounting.collector.cache_miss_collisions);
+}
+
+void bch_mark_sectors_bypassed(struct search *s, int sectors)
+{
+ struct cached_dev *dc = container_of(s->d, struct cached_dev, disk);
+ atomic_add(sectors, &dc->accounting.collector.sectors_bypassed);
+ atomic_add(sectors, &s->op.c->accounting.collector.sectors_bypassed);
+}
diff --git a/drivers/md/bcache/stats.h b/drivers/md/bcache/stats.h
new file mode 100644
index 000000000000..c7c7a8fd29fe
--- /dev/null
+++ b/drivers/md/bcache/stats.h
@@ -0,0 +1,58 @@
+#ifndef _BCACHE_STATS_H_
+#define _BCACHE_STATS_H_
+
+struct cache_stat_collector {
+ atomic_t cache_hits;
+ atomic_t cache_misses;
+ atomic_t cache_bypass_hits;
+ atomic_t cache_bypass_misses;
+ atomic_t cache_readaheads;
+ atomic_t cache_miss_collisions;
+ atomic_t sectors_bypassed;
+};
+
+struct cache_stats {
+ struct kobject kobj;
+
+ unsigned long cache_hits;
+ unsigned long cache_misses;
+ unsigned long cache_bypass_hits;
+ unsigned long cache_bypass_misses;
+ unsigned long cache_readaheads;
+ unsigned long cache_miss_collisions;
+ unsigned long sectors_bypassed;
+
+ unsigned rescale;
+};
+
+struct cache_accounting {
+ struct closure cl;
+ struct timer_list timer;
+ atomic_t closing;
+
+ struct cache_stat_collector collector;
+
+ struct cache_stats total;
+ struct cache_stats five_minute;
+ struct cache_stats hour;
+ struct cache_stats day;
+};
+
+struct search;
+
+void bch_cache_accounting_init(struct cache_accounting *acc,
+ struct closure *parent);
+
+int bch_cache_accounting_add_kobjs(struct cache_accounting *acc,
+ struct kobject *parent);
+
+void bch_cache_accounting_clear(struct cache_accounting *acc);
+
+void bch_cache_accounting_destroy(struct cache_accounting *acc);
+
+void bch_mark_cache_accounting(struct search *s, bool hit, bool bypass);
+void bch_mark_cache_readahead(struct search *s);
+void bch_mark_cache_miss_collision(struct search *s);
+void bch_mark_sectors_bypassed(struct search *s, int sectors);
+
+#endif /* _BCACHE_STATS_H_ */
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
new file mode 100644
index 000000000000..c8046bc4aa57
--- /dev/null
+++ b/drivers/md/bcache/super.c
@@ -0,0 +1,1987 @@
+/*
+ * bcache setup/teardown code, and some metadata io - read a superblock and
+ * figure out what to do with it.
+ *
+ * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "btree.h"
+#include "debug.h"
+#include "request.h"
+
+#include <linux/buffer_head.h>
+#include <linux/debugfs.h>
+#include <linux/genhd.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/reboot.h>
+#include <linux/sysfs.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kent Overstreet <kent.overstreet@gmail.com>");
+
+static const char bcache_magic[] = {
+ 0xc6, 0x85, 0x73, 0xf6, 0x4e, 0x1a, 0x45, 0xca,
+ 0x82, 0x65, 0xf5, 0x7f, 0x48, 0xba, 0x6d, 0x81
+};
+
+static const char invalid_uuid[] = {
+ 0xa0, 0x3e, 0xf8, 0xed, 0x3e, 0xe1, 0xb8, 0x78,
+ 0xc8, 0x50, 0xfc, 0x5e, 0xcb, 0x16, 0xcd, 0x99
+};
+
+/* Default is -1; we skip past it for struct cached_dev's cache mode */
+const char * const bch_cache_modes[] = {
+ "default",
+ "writethrough",
+ "writeback",
+ "writearound",
+ "none",
+ NULL
+};
+
+struct uuid_entry_v0 {
+ uint8_t uuid[16];
+ uint8_t label[32];
+ uint32_t first_reg;
+ uint32_t last_reg;
+ uint32_t invalidated;
+ uint32_t pad;
+};
+
+static struct kobject *bcache_kobj;
+struct mutex bch_register_lock;
+LIST_HEAD(bch_cache_sets);
+static LIST_HEAD(uncached_devices);
+
+static int bcache_major, bcache_minor;
+static wait_queue_head_t unregister_wait;
+struct workqueue_struct *bcache_wq;
+
+#define BTREE_MAX_PAGES (256 * 1024 / PAGE_SIZE)
+
+static void bio_split_pool_free(struct bio_split_pool *p)
+{
+ if (p->bio_split_hook)
+ mempool_destroy(p->bio_split_hook);
+
+ if (p->bio_split)
+ bioset_free(p->bio_split);
+}
+
+static int bio_split_pool_init(struct bio_split_pool *p)
+{
+ p->bio_split = bioset_create(4, 0);
+ if (!p->bio_split)
+ return -ENOMEM;
+
+ p->bio_split_hook = mempool_create_kmalloc_pool(4,
+ sizeof(struct bio_split_hook));
+ if (!p->bio_split_hook)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* Superblock */
+
+static const char *read_super(struct cache_sb *sb, struct block_device *bdev,
+ struct page **res)
+{
+ const char *err;
+ struct cache_sb *s;
+ struct buffer_head *bh = __bread(bdev, 1, SB_SIZE);
+ unsigned i;
+
+ if (!bh)
+ return "IO error";
+
+ s = (struct cache_sb *) bh->b_data;
+
+ sb->offset = le64_to_cpu(s->offset);
+ sb->version = le64_to_cpu(s->version);
+
+ memcpy(sb->magic, s->magic, 16);
+ memcpy(sb->uuid, s->uuid, 16);
+ memcpy(sb->set_uuid, s->set_uuid, 16);
+ memcpy(sb->label, s->label, SB_LABEL_SIZE);
+
+ sb->flags = le64_to_cpu(s->flags);
+ sb->seq = le64_to_cpu(s->seq);
+ sb->last_mount = le32_to_cpu(s->last_mount);
+ sb->first_bucket = le16_to_cpu(s->first_bucket);
+ sb->keys = le16_to_cpu(s->keys);
+
+ for (i = 0; i < SB_JOURNAL_BUCKETS; i++)
+ sb->d[i] = le64_to_cpu(s->d[i]);
+
+ pr_debug("read sb version %llu, flags %llu, seq %llu, journal size %u",
+ sb->version, sb->flags, sb->seq, sb->keys);
+
+ err = "Not a bcache superblock";
+ if (sb->offset != SB_SECTOR)
+ goto err;
+
+ if (memcmp(sb->magic, bcache_magic, 16))
+ goto err;
+
+ err = "Too many journal buckets";
+ if (sb->keys > SB_JOURNAL_BUCKETS)
+ goto err;
+
+ err = "Bad checksum";
+ if (s->csum != csum_set(s))
+ goto err;
+
+ err = "Bad UUID";
+ if (bch_is_zero(sb->uuid, 16))
+ goto err;
+
+ sb->block_size = le16_to_cpu(s->block_size);
+
+ err = "Superblock block size smaller than device block size";
+ if (sb->block_size << 9 < bdev_logical_block_size(bdev))
+ goto err;
+
+ switch (sb->version) {
+ case BCACHE_SB_VERSION_BDEV:
+ sb->data_offset = BDEV_DATA_START_DEFAULT;
+ break;
+ case BCACHE_SB_VERSION_BDEV_WITH_OFFSET:
+ sb->data_offset = le64_to_cpu(s->data_offset);
+
+ err = "Bad data offset";
+ if (sb->data_offset < BDEV_DATA_START_DEFAULT)
+ goto err;
+
+ break;
+ case BCACHE_SB_VERSION_CDEV:
+ case BCACHE_SB_VERSION_CDEV_WITH_UUID:
+ sb->nbuckets = le64_to_cpu(s->nbuckets);
+ sb->block_size = le16_to_cpu(s->block_size);
+ sb->bucket_size = le16_to_cpu(s->bucket_size);
+
+ sb->nr_in_set = le16_to_cpu(s->nr_in_set);
+ sb->nr_this_dev = le16_to_cpu(s->nr_this_dev);
+
+ err = "Too many buckets";
+ if (sb->nbuckets > LONG_MAX)
+ goto err;
+
+ err = "Not enough buckets";
+ if (sb->nbuckets < 1 << 7)
+ goto err;
+
+ err = "Bad block/bucket size";
+ if (!is_power_of_2(sb->block_size) ||
+ sb->block_size > PAGE_SECTORS ||
+ !is_power_of_2(sb->bucket_size) ||
+ sb->bucket_size < PAGE_SECTORS)
+ goto err;
+
+ err = "Invalid superblock: device too small";
+ if (get_capacity(bdev->bd_disk) < sb->bucket_size * sb->nbuckets)
+ goto err;
+
+ err = "Bad UUID";
+ if (bch_is_zero(sb->set_uuid, 16))
+ goto err;
+
+ err = "Bad cache device number in set";
+ if (!sb->nr_in_set ||
+ sb->nr_in_set <= sb->nr_this_dev ||
+ sb->nr_in_set > MAX_CACHES_PER_SET)
+ goto err;
+
+ err = "Journal buckets not sequential";
+ for (i = 0; i < sb->keys; i++)
+ if (sb->d[i] != sb->first_bucket + i)
+ goto err;
+
+ err = "Too many journal buckets";
+ if (sb->first_bucket + sb->keys > sb->nbuckets)
+ goto err;
+
+ err = "Invalid superblock: first bucket comes before end of super";
+ if (sb->first_bucket * sb->bucket_size < 16)
+ goto err;
+
+ break;
+ default:
+ err = "Unsupported superblock version";
+ goto err;
+ }
+
+ sb->last_mount = get_seconds();
+ err = NULL;
+
+ get_page(bh->b_page);
+ *res = bh->b_page;
+err:
+ put_bh(bh);
+ return err;
+}
+
+static void write_bdev_super_endio(struct bio *bio, int error)
+{
+ struct cached_dev *dc = bio->bi_private;
+ /* XXX: error checking */
+
+ closure_put(&dc->sb_write.cl);
+}
+
+static void __write_super(struct cache_sb *sb, struct bio *bio)
+{
+ struct cache_sb *out = page_address(bio->bi_io_vec[0].bv_page);
+ unsigned i;
+
+ bio->bi_sector = SB_SECTOR;
+ bio->bi_rw = REQ_SYNC|REQ_META;
+ bio->bi_size = SB_SIZE;
+ bch_bio_map(bio, NULL);
+
+ out->offset = cpu_to_le64(sb->offset);
+ out->version = cpu_to_le64(sb->version);
+
+ memcpy(out->uuid, sb->uuid, 16);
+ memcpy(out->set_uuid, sb->set_uuid, 16);
+ memcpy(out->label, sb->label, SB_LABEL_SIZE);
+
+ out->flags = cpu_to_le64(sb->flags);
+ out->seq = cpu_to_le64(sb->seq);
+
+ out->last_mount = cpu_to_le32(sb->last_mount);
+ out->first_bucket = cpu_to_le16(sb->first_bucket);
+ out->keys = cpu_to_le16(sb->keys);
+
+ for (i = 0; i < sb->keys; i++)
+ out->d[i] = cpu_to_le64(sb->d[i]);
+
+ out->csum = csum_set(out);
+
+ pr_debug("ver %llu, flags %llu, seq %llu",
+ sb->version, sb->flags, sb->seq);
+
+ submit_bio(REQ_WRITE, bio);
+}
+
+void bch_write_bdev_super(struct cached_dev *dc, struct closure *parent)
+{
+ struct closure *cl = &dc->sb_write.cl;
+ struct bio *bio = &dc->sb_bio;
+
+ closure_lock(&dc->sb_write, parent);
+
+ bio_reset(bio);
+ bio->bi_bdev = dc->bdev;
+ bio->bi_end_io = write_bdev_super_endio;
+ bio->bi_private = dc;
+
+ closure_get(cl);
+ __write_super(&dc->sb, bio);
+
+ closure_return(cl);
+}
+
+static void write_super_endio(struct bio *bio, int error)
+{
+ struct cache *ca = bio->bi_private;
+
+ bch_count_io_errors(ca, error, "writing superblock");
+ closure_put(&ca->set->sb_write.cl);
+}
+
+void bcache_write_super(struct cache_set *c)
+{
+ struct closure *cl = &c->sb_write.cl;
+ struct cache *ca;
+ unsigned i;
+
+ closure_lock(&c->sb_write, &c->cl);
+
+ c->sb.seq++;
+
+ for_each_cache(ca, c, i) {
+ struct bio *bio = &ca->sb_bio;
+
+ ca->sb.version = BCACHE_SB_VERSION_CDEV_WITH_UUID;
+ ca->sb.seq = c->sb.seq;
+ ca->sb.last_mount = c->sb.last_mount;
+
+ SET_CACHE_SYNC(&ca->sb, CACHE_SYNC(&c->sb));
+
+ bio_reset(bio);
+ bio->bi_bdev = ca->bdev;
+ bio->bi_end_io = write_super_endio;
+ bio->bi_private = ca;
+
+ closure_get(cl);
+ __write_super(&ca->sb, bio);
+ }
+
+ closure_return(cl);
+}
+
+/* UUID io */
+
+static void uuid_endio(struct bio *bio, int error)
+{
+ struct closure *cl = bio->bi_private;
+ struct cache_set *c = container_of(cl, struct cache_set, uuid_write.cl);
+
+ cache_set_err_on(error, c, "accessing uuids");
+ bch_bbio_free(bio, c);
+ closure_put(cl);
+}
+
+static void uuid_io(struct cache_set *c, unsigned long rw,
+ struct bkey *k, struct closure *parent)
+{
+ struct closure *cl = &c->uuid_write.cl;
+ struct uuid_entry *u;
+ unsigned i;
+
+ BUG_ON(!parent);
+ closure_lock(&c->uuid_write, parent);
+
+ for (i = 0; i < KEY_PTRS(k); i++) {
+ struct bio *bio = bch_bbio_alloc(c);
+
+ bio->bi_rw = REQ_SYNC|REQ_META|rw;
+ bio->bi_size = KEY_SIZE(k) << 9;
+
+ bio->bi_end_io = uuid_endio;
+ bio->bi_private = cl;
+ bch_bio_map(bio, c->uuids);
+
+ bch_submit_bbio(bio, c, k, i);
+
+ if (!(rw & WRITE))
+ break;
+ }
+
+ pr_debug("%s UUIDs at %s", rw & REQ_WRITE ? "wrote" : "read",
+ pkey(&c->uuid_bucket));
+
+ for (u = c->uuids; u < c->uuids + c->nr_uuids; u++)
+ if (!bch_is_zero(u->uuid, 16))
+ pr_debug("Slot %zi: %pU: %s: 1st: %u last: %u inv: %u",
+ u - c->uuids, u->uuid, u->label,
+ u->first_reg, u->last_reg, u->invalidated);
+
+ closure_return(cl);
+}
+
+static char *uuid_read(struct cache_set *c, struct jset *j, struct closure *cl)
+{
+ struct bkey *k = &j->uuid_bucket;
+
+ if (__bch_ptr_invalid(c, 1, k))
+ return "bad uuid pointer";
+
+ bkey_copy(&c->uuid_bucket, k);
+ uuid_io(c, READ_SYNC, k, cl);
+
+ if (j->version < BCACHE_JSET_VERSION_UUIDv1) {
+ struct uuid_entry_v0 *u0 = (void *) c->uuids;
+ struct uuid_entry *u1 = (void *) c->uuids;
+ int i;
+
+ closure_sync(cl);
+
+ /*
+ * Since the new uuid entry is bigger than the old, we have to
+ * convert starting at the highest memory address and work down
+ * in order to do it in place
+ */
+
+ for (i = c->nr_uuids - 1;
+ i >= 0;
+ --i) {
+ memcpy(u1[i].uuid, u0[i].uuid, 16);
+ memcpy(u1[i].label, u0[i].label, 32);
+
+ u1[i].first_reg = u0[i].first_reg;
+ u1[i].last_reg = u0[i].last_reg;
+ u1[i].invalidated = u0[i].invalidated;
+
+ u1[i].flags = 0;
+ u1[i].sectors = 0;
+ }
+ }
+
+ return NULL;
+}
+
+static int __uuid_write(struct cache_set *c)
+{
+ BKEY_PADDED(key) k;
+ struct closure cl;
+ closure_init_stack(&cl);
+
+ lockdep_assert_held(&bch_register_lock);
+
+ if (bch_bucket_alloc_set(c, WATERMARK_METADATA, &k.key, 1, &cl))
+ return 1;
+
+ SET_KEY_SIZE(&k.key, c->sb.bucket_size);
+ uuid_io(c, REQ_WRITE, &k.key, &cl);
+ closure_sync(&cl);
+
+ bkey_copy(&c->uuid_bucket, &k.key);
+ __bkey_put(c, &k.key);
+ return 0;
+}
+
+int bch_uuid_write(struct cache_set *c)
+{
+ int ret = __uuid_write(c);
+
+ if (!ret)
+ bch_journal_meta(c, NULL);
+
+ return ret;
+}
+
+static struct uuid_entry *uuid_find(struct cache_set *c, const char *uuid)
+{
+ struct uuid_entry *u;
+
+ for (u = c->uuids;
+ u < c->uuids + c->nr_uuids; u++)
+ if (!memcmp(u->uuid, uuid, 16))
+ return u;
+
+ return NULL;
+}
+
+static struct uuid_entry *uuid_find_empty(struct cache_set *c)
+{
+ static const char zero_uuid[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+ return uuid_find(c, zero_uuid);
+}
+
+/*
+ * Bucket priorities/gens:
+ *
+ * For each bucket, we store on disk its
+ * 8 bit gen
+ * 16 bit priority
+ *
+ * See alloc.c for an explanation of the gen. The priority is used to implement
+ * lru (and in the future other) cache replacement policies; for most purposes
+ * it's just an opaque integer.
+ *
+ * The gens and the priorities don't have a whole lot to do with each other, and
+ * it's actually the gens that must be written out at specific times - it's no
+ * big deal if the priorities don't get written, if we lose them we just reuse
+ * buckets in suboptimal order.
+ *
+ * On disk they're stored in a packed array, and in as many buckets are required
+ * to fit them all. The buckets we use to store them form a list; the journal
+ * header points to the first bucket, the first bucket points to the second
+ * bucket, et cetera.
+ *
+ * This code is used by the allocation code; periodically (whenever it runs out
+ * of buckets to allocate from) the allocation code will invalidate some
+ * buckets, but it can't use those buckets until their new gens are safely on
+ * disk.
+ */
+
+static void prio_endio(struct bio *bio, int error)
+{
+ struct cache *ca = bio->bi_private;
+
+ cache_set_err_on(error, ca->set, "accessing priorities");
+ bch_bbio_free(bio, ca->set);
+ closure_put(&ca->prio);
+}
+
+static void prio_io(struct cache *ca, uint64_t bucket, unsigned long rw)
+{
+ struct closure *cl = &ca->prio;
+ struct bio *bio = bch_bbio_alloc(ca->set);
+
+ closure_init_stack(cl);
+
+ bio->bi_sector = bucket * ca->sb.bucket_size;
+ bio->bi_bdev = ca->bdev;
+ bio->bi_rw = REQ_SYNC|REQ_META|rw;
+ bio->bi_size = bucket_bytes(ca);
+
+ bio->bi_end_io = prio_endio;
+ bio->bi_private = ca;
+ bch_bio_map(bio, ca->disk_buckets);
+
+ closure_bio_submit(bio, &ca->prio, ca);
+ closure_sync(cl);
+}
+
+#define buckets_free(c) "free %zu, free_inc %zu, unused %zu", \
+ fifo_used(&c->free), fifo_used(&c->free_inc), fifo_used(&c->unused)
+
+void bch_prio_write(struct cache *ca)
+{
+ int i;
+ struct bucket *b;
+ struct closure cl;
+
+ closure_init_stack(&cl);
+
+ lockdep_assert_held(&ca->set->bucket_lock);
+
+ for (b = ca->buckets;
+ b < ca->buckets + ca->sb.nbuckets; b++)
+ b->disk_gen = b->gen;
+
+ ca->disk_buckets->seq++;
+
+ atomic_long_add(ca->sb.bucket_size * prio_buckets(ca),
+ &ca->meta_sectors_written);
+
+ pr_debug("free %zu, free_inc %zu, unused %zu", fifo_used(&ca->free),
+ fifo_used(&ca->free_inc), fifo_used(&ca->unused));
+ blktrace_msg(ca, "Starting priorities: " buckets_free(ca));
+
+ for (i = prio_buckets(ca) - 1; i >= 0; --i) {
+ long bucket;
+ struct prio_set *p = ca->disk_buckets;
+ struct bucket_disk *d = p->data;
+ struct bucket_disk *end = d + prios_per_bucket(ca);
+
+ for (b = ca->buckets + i * prios_per_bucket(ca);
+ b < ca->buckets + ca->sb.nbuckets && d < end;
+ b++, d++) {
+ d->prio = cpu_to_le16(b->prio);
+ d->gen = b->gen;
+ }
+
+ p->next_bucket = ca->prio_buckets[i + 1];
+ p->magic = pset_magic(ca);
+ p->csum = bch_crc64(&p->magic, bucket_bytes(ca) - 8);
+
+ bucket = bch_bucket_alloc(ca, WATERMARK_PRIO, &cl);
+ BUG_ON(bucket == -1);
+
+ mutex_unlock(&ca->set->bucket_lock);
+ prio_io(ca, bucket, REQ_WRITE);
+ mutex_lock(&ca->set->bucket_lock);
+
+ ca->prio_buckets[i] = bucket;
+ atomic_dec_bug(&ca->buckets[bucket].pin);
+ }
+
+ mutex_unlock(&ca->set->bucket_lock);
+
+ bch_journal_meta(ca->set, &cl);
+ closure_sync(&cl);
+
+ mutex_lock(&ca->set->bucket_lock);
+
+ ca->need_save_prio = 0;
+
+ /*
+ * Don't want the old priorities to get garbage collected until after we
+ * finish writing the new ones, and they're journalled
+ */
+ for (i = 0; i < prio_buckets(ca); i++)
+ ca->prio_last_buckets[i] = ca->prio_buckets[i];
+}
+
+static void prio_read(struct cache *ca, uint64_t bucket)
+{
+ struct prio_set *p = ca->disk_buckets;
+ struct bucket_disk *d = p->data + prios_per_bucket(ca), *end = d;
+ struct bucket *b;
+ unsigned bucket_nr = 0;
+
+ for (b = ca->buckets;
+ b < ca->buckets + ca->sb.nbuckets;
+ b++, d++) {
+ if (d == end) {
+ ca->prio_buckets[bucket_nr] = bucket;
+ ca->prio_last_buckets[bucket_nr] = bucket;
+ bucket_nr++;
+
+ prio_io(ca, bucket, READ_SYNC);
+
+ if (p->csum != bch_crc64(&p->magic, bucket_bytes(ca) - 8))
+ pr_warn("bad csum reading priorities");
+
+ if (p->magic != pset_magic(ca))
+ pr_warn("bad magic reading priorities");
+
+ bucket = p->next_bucket;
+ d = p->data;
+ }
+
+ b->prio = le16_to_cpu(d->prio);
+ b->gen = b->disk_gen = b->last_gc = b->gc_gen = d->gen;
+ }
+}
+
+/* Bcache device */
+
+static int open_dev(struct block_device *b, fmode_t mode)
+{
+ struct bcache_device *d = b->bd_disk->private_data;
+ if (atomic_read(&d->closing))
+ return -ENXIO;
+
+ closure_get(&d->cl);
+ return 0;
+}
+
+static int release_dev(struct gendisk *b, fmode_t mode)
+{
+ struct bcache_device *d = b->private_data;
+ closure_put(&d->cl);
+ return 0;
+}
+
+static int ioctl_dev(struct block_device *b, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ struct bcache_device *d = b->bd_disk->private_data;
+ return d->ioctl(d, mode, cmd, arg);
+}
+
+static const struct block_device_operations bcache_ops = {
+ .open = open_dev,
+ .release = release_dev,
+ .ioctl = ioctl_dev,
+ .owner = THIS_MODULE,
+};
+
+void bcache_device_stop(struct bcache_device *d)
+{
+ if (!atomic_xchg(&d->closing, 1))
+ closure_queue(&d->cl);
+}
+
+static void bcache_device_unlink(struct bcache_device *d)
+{
+ unsigned i;
+ struct cache *ca;
+
+ sysfs_remove_link(&d->c->kobj, d->name);
+ sysfs_remove_link(&d->kobj, "cache");
+
+ for_each_cache(ca, d->c, i)
+ bd_unlink_disk_holder(ca->bdev, d->disk);
+}
+
+static void bcache_device_link(struct bcache_device *d, struct cache_set *c,
+ const char *name)
+{
+ unsigned i;
+ struct cache *ca;
+
+ for_each_cache(ca, d->c, i)
+ bd_link_disk_holder(ca->bdev, d->disk);
+
+ snprintf(d->name, BCACHEDEVNAME_SIZE,
+ "%s%u", name, d->id);
+
+ WARN(sysfs_create_link(&d->kobj, &c->kobj, "cache") ||
+ sysfs_create_link(&c->kobj, &d->kobj, d->name),
+ "Couldn't create device <-> cache set symlinks");
+}
+
+static void bcache_device_detach(struct bcache_device *d)
+{
+ lockdep_assert_held(&bch_register_lock);
+
+ if (atomic_read(&d->detaching)) {
+ struct uuid_entry *u = d->c->uuids + d->id;
+
+ SET_UUID_FLASH_ONLY(u, 0);
+ memcpy(u->uuid, invalid_uuid, 16);
+ u->invalidated = cpu_to_le32(get_seconds());
+ bch_uuid_write(d->c);
+
+ atomic_set(&d->detaching, 0);
+ }
+
+ bcache_device_unlink(d);
+
+ d->c->devices[d->id] = NULL;
+ closure_put(&d->c->caching);
+ d->c = NULL;
+}
+
+static void bcache_device_attach(struct bcache_device *d, struct cache_set *c,
+ unsigned id)
+{
+ BUG_ON(test_bit(CACHE_SET_STOPPING, &c->flags));
+
+ d->id = id;
+ d->c = c;
+ c->devices[id] = d;
+
+ closure_get(&c->caching);
+}
+
+static void bcache_device_free(struct bcache_device *d)
+{
+ lockdep_assert_held(&bch_register_lock);
+
+ pr_info("%s stopped", d->disk->disk_name);
+
+ if (d->c)
+ bcache_device_detach(d);
+
+ if (d->disk)
+ del_gendisk(d->disk);
+ if (d->disk && d->disk->queue)
+ blk_cleanup_queue(d->disk->queue);
+ if (d->disk)
+ put_disk(d->disk);
+
+ bio_split_pool_free(&d->bio_split_hook);
+ if (d->unaligned_bvec)
+ mempool_destroy(d->unaligned_bvec);
+ if (d->bio_split)
+ bioset_free(d->bio_split);
+
+ closure_debug_destroy(&d->cl);
+}
+
+static int bcache_device_init(struct bcache_device *d, unsigned block_size)
+{
+ struct request_queue *q;
+
+ if (!(d->bio_split = bioset_create(4, offsetof(struct bbio, bio))) ||
+ !(d->unaligned_bvec = mempool_create_kmalloc_pool(1,
+ sizeof(struct bio_vec) * BIO_MAX_PAGES)) ||
+ bio_split_pool_init(&d->bio_split_hook))
+
+ return -ENOMEM;
+
+ d->disk = alloc_disk(1);
+ if (!d->disk)
+ return -ENOMEM;
+
+ snprintf(d->disk->disk_name, DISK_NAME_LEN, "bcache%i", bcache_minor);
+
+ d->disk->major = bcache_major;
+ d->disk->first_minor = bcache_minor++;
+ d->disk->fops = &bcache_ops;
+ d->disk->private_data = d;
+
+ q = blk_alloc_queue(GFP_KERNEL);
+ if (!q)
+ return -ENOMEM;
+
+ blk_queue_make_request(q, NULL);
+ d->disk->queue = q;
+ q->queuedata = d;
+ q->backing_dev_info.congested_data = d;
+ q->limits.max_hw_sectors = UINT_MAX;
+ q->limits.max_sectors = UINT_MAX;
+ q->limits.max_segment_size = UINT_MAX;
+ q->limits.max_segments = BIO_MAX_PAGES;
+ q->limits.max_discard_sectors = UINT_MAX;
+ q->limits.io_min = block_size;
+ q->limits.logical_block_size = block_size;
+ q->limits.physical_block_size = block_size;
+ set_bit(QUEUE_FLAG_NONROT, &d->disk->queue->queue_flags);
+ set_bit(QUEUE_FLAG_DISCARD, &d->disk->queue->queue_flags);
+
+ return 0;
+}
+
+/* Cached device */
+
+static void calc_cached_dev_sectors(struct cache_set *c)
+{
+ uint64_t sectors = 0;
+ struct cached_dev *dc;
+
+ list_for_each_entry(dc, &c->cached_devs, list)
+ sectors += bdev_sectors(dc->bdev);
+
+ c->cached_dev_sectors = sectors;
+}
+
+void bch_cached_dev_run(struct cached_dev *dc)
+{
+ struct bcache_device *d = &dc->disk;
+
+ if (atomic_xchg(&dc->running, 1))
+ return;
+
+ if (!d->c &&
+ BDEV_STATE(&dc->sb) != BDEV_STATE_NONE) {
+ struct closure cl;
+ closure_init_stack(&cl);
+
+ SET_BDEV_STATE(&dc->sb, BDEV_STATE_STALE);
+ bch_write_bdev_super(dc, &cl);
+ closure_sync(&cl);
+ }
+
+ add_disk(d->disk);
+ bd_link_disk_holder(dc->bdev, dc->disk.disk);
+#if 0
+ char *env[] = { "SYMLINK=label" , NULL };
+ kobject_uevent_env(&disk_to_dev(d->disk)->kobj, KOBJ_CHANGE, env);
+#endif
+ if (sysfs_create_link(&d->kobj, &disk_to_dev(d->disk)->kobj, "dev") ||
+ sysfs_create_link(&disk_to_dev(d->disk)->kobj, &d->kobj, "bcache"))
+ pr_debug("error creating sysfs link");
+}
+
+static void cached_dev_detach_finish(struct work_struct *w)
+{
+ struct cached_dev *dc = container_of(w, struct cached_dev, detach);
+ char buf[BDEVNAME_SIZE];
+ struct closure cl;
+ closure_init_stack(&cl);
+
+ BUG_ON(!atomic_read(&dc->disk.detaching));
+ BUG_ON(atomic_read(&dc->count));
+
+ mutex_lock(&bch_register_lock);
+
+ memset(&dc->sb.set_uuid, 0, 16);
+ SET_BDEV_STATE(&dc->sb, BDEV_STATE_NONE);
+
+ bch_write_bdev_super(dc, &cl);
+ closure_sync(&cl);
+
+ bcache_device_detach(&dc->disk);
+ list_move(&dc->list, &uncached_devices);
+
+ mutex_unlock(&bch_register_lock);
+
+ pr_info("Caching disabled for %s", bdevname(dc->bdev, buf));
+
+ /* Drop ref we took in cached_dev_detach() */
+ closure_put(&dc->disk.cl);
+}
+
+void bch_cached_dev_detach(struct cached_dev *dc)
+{
+ lockdep_assert_held(&bch_register_lock);
+
+ if (atomic_read(&dc->disk.closing))
+ return;
+
+ if (atomic_xchg(&dc->disk.detaching, 1))
+ return;
+
+ /*
+ * Block the device from being closed and freed until we're finished
+ * detaching
+ */
+ closure_get(&dc->disk.cl);
+
+ bch_writeback_queue(dc);
+ cached_dev_put(dc);
+}
+
+int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c)
+{
+ uint32_t rtime = cpu_to_le32(get_seconds());
+ struct uuid_entry *u;
+ char buf[BDEVNAME_SIZE];
+
+ bdevname(dc->bdev, buf);
+
+ if (memcmp(dc->sb.set_uuid, c->sb.set_uuid, 16))
+ return -ENOENT;
+
+ if (dc->disk.c) {
+ pr_err("Can't attach %s: already attached", buf);
+ return -EINVAL;
+ }
+
+ if (test_bit(CACHE_SET_STOPPING, &c->flags)) {
+ pr_err("Can't attach %s: shutting down", buf);
+ return -EINVAL;
+ }
+
+ if (dc->sb.block_size < c->sb.block_size) {
+ /* Will die */
+ pr_err("Couldn't attach %s: block size less than set's block size",
+ buf);
+ return -EINVAL;
+ }
+
+ u = uuid_find(c, dc->sb.uuid);
+
+ if (u &&
+ (BDEV_STATE(&dc->sb) == BDEV_STATE_STALE ||
+ BDEV_STATE(&dc->sb) == BDEV_STATE_NONE)) {
+ memcpy(u->uuid, invalid_uuid, 16);
+ u->invalidated = cpu_to_le32(get_seconds());
+ u = NULL;
+ }
+
+ if (!u) {
+ if (BDEV_STATE(&dc->sb) == BDEV_STATE_DIRTY) {
+ pr_err("Couldn't find uuid for %s in set", buf);
+ return -ENOENT;
+ }
+
+ u = uuid_find_empty(c);
+ if (!u) {
+ pr_err("Not caching %s, no room for UUID", buf);
+ return -EINVAL;
+ }
+ }
+
+ /* Deadlocks since we're called via sysfs...
+ sysfs_remove_file(&dc->kobj, &sysfs_attach);
+ */
+
+ if (bch_is_zero(u->uuid, 16)) {
+ struct closure cl;
+ closure_init_stack(&cl);
+
+ memcpy(u->uuid, dc->sb.uuid, 16);
+ memcpy(u->label, dc->sb.label, SB_LABEL_SIZE);
+ u->first_reg = u->last_reg = rtime;
+ bch_uuid_write(c);
+
+ memcpy(dc->sb.set_uuid, c->sb.set_uuid, 16);
+ SET_BDEV_STATE(&dc->sb, BDEV_STATE_CLEAN);
+
+ bch_write_bdev_super(dc, &cl);
+ closure_sync(&cl);
+ } else {
+ u->last_reg = rtime;
+ bch_uuid_write(c);
+ }
+
+ bcache_device_attach(&dc->disk, c, u - c->uuids);
+ list_move(&dc->list, &c->cached_devs);
+ calc_cached_dev_sectors(c);
+
+ smp_wmb();
+ /*
+ * dc->c must be set before dc->count != 0 - paired with the mb in
+ * cached_dev_get()
+ */
+ atomic_set(&dc->count, 1);
+
+ if (BDEV_STATE(&dc->sb) == BDEV_STATE_DIRTY) {
+ atomic_set(&dc->has_dirty, 1);
+ atomic_inc(&dc->count);
+ bch_writeback_queue(dc);
+ }
+
+ bch_cached_dev_run(dc);
+ bcache_device_link(&dc->disk, c, "bdev");
+
+ pr_info("Caching %s as %s on set %pU",
+ bdevname(dc->bdev, buf), dc->disk.disk->disk_name,
+ dc->disk.c->sb.set_uuid);
+ return 0;
+}
+
+void bch_cached_dev_release(struct kobject *kobj)
+{
+ struct cached_dev *dc = container_of(kobj, struct cached_dev,
+ disk.kobj);
+ kfree(dc);
+ module_put(THIS_MODULE);
+}
+
+static void cached_dev_free(struct closure *cl)
+{
+ struct cached_dev *dc = container_of(cl, struct cached_dev, disk.cl);
+
+ cancel_delayed_work_sync(&dc->writeback_rate_update);
+
+ mutex_lock(&bch_register_lock);
+
+ bd_unlink_disk_holder(dc->bdev, dc->disk.disk);
+ bcache_device_free(&dc->disk);
+ list_del(&dc->list);
+
+ mutex_unlock(&bch_register_lock);
+
+ if (!IS_ERR_OR_NULL(dc->bdev)) {
+ blk_sync_queue(bdev_get_queue(dc->bdev));
+ blkdev_put(dc->bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
+ }
+
+ wake_up(&unregister_wait);
+
+ kobject_put(&dc->disk.kobj);
+}
+
+static void cached_dev_flush(struct closure *cl)
+{
+ struct cached_dev *dc = container_of(cl, struct cached_dev, disk.cl);
+ struct bcache_device *d = &dc->disk;
+
+ bch_cache_accounting_destroy(&dc->accounting);
+ kobject_del(&d->kobj);
+
+ continue_at(cl, cached_dev_free, system_wq);
+}
+
+static int cached_dev_init(struct cached_dev *dc, unsigned block_size)
+{
+ int err;
+ struct io *io;
+
+ closure_init(&dc->disk.cl, NULL);
+ set_closure_fn(&dc->disk.cl, cached_dev_flush, system_wq);
+
+ __module_get(THIS_MODULE);
+ INIT_LIST_HEAD(&dc->list);
+ kobject_init(&dc->disk.kobj, &bch_cached_dev_ktype);
+
+ bch_cache_accounting_init(&dc->accounting, &dc->disk.cl);
+
+ err = bcache_device_init(&dc->disk, block_size);
+ if (err)
+ goto err;
+
+ spin_lock_init(&dc->io_lock);
+ closure_init_unlocked(&dc->sb_write);
+ INIT_WORK(&dc->detach, cached_dev_detach_finish);
+
+ dc->sequential_merge = true;
+ dc->sequential_cutoff = 4 << 20;
+
+ INIT_LIST_HEAD(&dc->io_lru);
+ dc->sb_bio.bi_max_vecs = 1;
+ dc->sb_bio.bi_io_vec = dc->sb_bio.bi_inline_vecs;
+
+ for (io = dc->io; io < dc->io + RECENT_IO; io++) {
+ list_add(&io->lru, &dc->io_lru);
+ hlist_add_head(&io->hash, dc->io_hash + RECENT_IO);
+ }
+
+ bch_writeback_init_cached_dev(dc);
+ return 0;
+err:
+ bcache_device_stop(&dc->disk);
+ return err;
+}
+
+/* Cached device - bcache superblock */
+
+static const char *register_bdev(struct cache_sb *sb, struct page *sb_page,
+ struct block_device *bdev,
+ struct cached_dev *dc)
+{
+ char name[BDEVNAME_SIZE];
+ const char *err = "cannot allocate memory";
+ struct gendisk *g;
+ struct cache_set *c;
+
+ if (!dc || cached_dev_init(dc, sb->block_size << 9) != 0)
+ return err;
+
+ memcpy(&dc->sb, sb, sizeof(struct cache_sb));
+ dc->sb_bio.bi_io_vec[0].bv_page = sb_page;
+ dc->bdev = bdev;
+ dc->bdev->bd_holder = dc;
+
+ g = dc->disk.disk;
+
+ set_capacity(g, dc->bdev->bd_part->nr_sects - dc->sb.data_offset);
+
+ g->queue->backing_dev_info.ra_pages =
+ max(g->queue->backing_dev_info.ra_pages,
+ bdev->bd_queue->backing_dev_info.ra_pages);
+
+ bch_cached_dev_request_init(dc);
+
+ err = "error creating kobject";
+ if (kobject_add(&dc->disk.kobj, &part_to_dev(bdev->bd_part)->kobj,
+ "bcache"))
+ goto err;
+ if (bch_cache_accounting_add_kobjs(&dc->accounting, &dc->disk.kobj))
+ goto err;
+
+ list_add(&dc->list, &uncached_devices);
+ list_for_each_entry(c, &bch_cache_sets, list)
+ bch_cached_dev_attach(dc, c);
+
+ if (BDEV_STATE(&dc->sb) == BDEV_STATE_NONE ||
+ BDEV_STATE(&dc->sb) == BDEV_STATE_STALE)
+ bch_cached_dev_run(dc);
+
+ return NULL;
+err:
+ kobject_put(&dc->disk.kobj);
+ pr_notice("error opening %s: %s", bdevname(bdev, name), err);
+ /*
+ * Return NULL instead of an error because kobject_put() cleans
+ * everything up
+ */
+ return NULL;
+}
+
+/* Flash only volumes */
+
+void bch_flash_dev_release(struct kobject *kobj)
+{
+ struct bcache_device *d = container_of(kobj, struct bcache_device,
+ kobj);
+ kfree(d);
+}
+
+static void flash_dev_free(struct closure *cl)
+{
+ struct bcache_device *d = container_of(cl, struct bcache_device, cl);
+ bcache_device_free(d);
+ kobject_put(&d->kobj);
+}
+
+static void flash_dev_flush(struct closure *cl)
+{
+ struct bcache_device *d = container_of(cl, struct bcache_device, cl);
+
+ bcache_device_unlink(d);
+ kobject_del(&d->kobj);
+ continue_at(cl, flash_dev_free, system_wq);
+}
+
+static int flash_dev_run(struct cache_set *c, struct uuid_entry *u)
+{
+ struct bcache_device *d = kzalloc(sizeof(struct bcache_device),
+ GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ closure_init(&d->cl, NULL);
+ set_closure_fn(&d->cl, flash_dev_flush, system_wq);
+
+ kobject_init(&d->kobj, &bch_flash_dev_ktype);
+
+ if (bcache_device_init(d, block_bytes(c)))
+ goto err;
+
+ bcache_device_attach(d, c, u - c->uuids);
+ set_capacity(d->disk, u->sectors);
+ bch_flash_dev_request_init(d);
+ add_disk(d->disk);
+
+ if (kobject_add(&d->kobj, &disk_to_dev(d->disk)->kobj, "bcache"))
+ goto err;
+
+ bcache_device_link(d, c, "volume");
+
+ return 0;
+err:
+ kobject_put(&d->kobj);
+ return -ENOMEM;
+}
+
+static int flash_devs_run(struct cache_set *c)
+{
+ int ret = 0;
+ struct uuid_entry *u;
+
+ for (u = c->uuids;
+ u < c->uuids + c->nr_uuids && !ret;
+ u++)
+ if (UUID_FLASH_ONLY(u))
+ ret = flash_dev_run(c, u);
+
+ return ret;
+}
+
+int bch_flash_dev_create(struct cache_set *c, uint64_t size)
+{
+ struct uuid_entry *u;
+
+ if (test_bit(CACHE_SET_STOPPING, &c->flags))
+ return -EINTR;
+
+ u = uuid_find_empty(c);
+ if (!u) {
+ pr_err("Can't create volume, no room for UUID");
+ return -EINVAL;
+ }
+
+ get_random_bytes(u->uuid, 16);
+ memset(u->label, 0, 32);
+ u->first_reg = u->last_reg = cpu_to_le32(get_seconds());
+
+ SET_UUID_FLASH_ONLY(u, 1);
+ u->sectors = size >> 9;
+
+ bch_uuid_write(c);
+
+ return flash_dev_run(c, u);
+}
+
+/* Cache set */
+
+__printf(2, 3)
+bool bch_cache_set_error(struct cache_set *c, const char *fmt, ...)
+{
+ va_list args;
+
+ if (test_bit(CACHE_SET_STOPPING, &c->flags))
+ return false;
+
+ /* XXX: we can be called from atomic context
+ acquire_console_sem();
+ */
+
+ printk(KERN_ERR "bcache: error on %pU: ", c->sb.set_uuid);
+
+ va_start(args, fmt);
+ vprintk(fmt, args);
+ va_end(args);
+
+ printk(", disabling caching\n");
+
+ bch_cache_set_unregister(c);
+ return true;
+}
+
+void bch_cache_set_release(struct kobject *kobj)
+{
+ struct cache_set *c = container_of(kobj, struct cache_set, kobj);
+ kfree(c);
+ module_put(THIS_MODULE);
+}
+
+static void cache_set_free(struct closure *cl)
+{
+ struct cache_set *c = container_of(cl, struct cache_set, cl);
+ struct cache *ca;
+ unsigned i;
+
+ if (!IS_ERR_OR_NULL(c->debug))
+ debugfs_remove(c->debug);
+
+ bch_open_buckets_free(c);
+ bch_btree_cache_free(c);
+ bch_journal_free(c);
+
+ for_each_cache(ca, c, i)
+ if (ca)
+ kobject_put(&ca->kobj);
+
+ free_pages((unsigned long) c->uuids, ilog2(bucket_pages(c)));
+ free_pages((unsigned long) c->sort, ilog2(bucket_pages(c)));
+
+ kfree(c->fill_iter);
+ if (c->bio_split)
+ bioset_free(c->bio_split);
+ if (c->bio_meta)
+ mempool_destroy(c->bio_meta);
+ if (c->search)
+ mempool_destroy(c->search);
+ kfree(c->devices);
+
+ mutex_lock(&bch_register_lock);
+ list_del(&c->list);
+ mutex_unlock(&bch_register_lock);
+
+ pr_info("Cache set %pU unregistered", c->sb.set_uuid);
+ wake_up(&unregister_wait);
+
+ closure_debug_destroy(&c->cl);
+ kobject_put(&c->kobj);
+}
+
+static void cache_set_flush(struct closure *cl)
+{
+ struct cache_set *c = container_of(cl, struct cache_set, caching);
+ struct btree *b;
+
+ /* Shut down allocator threads */
+ set_bit(CACHE_SET_STOPPING_2, &c->flags);
+ wake_up(&c->alloc_wait);
+
+ bch_cache_accounting_destroy(&c->accounting);
+
+ kobject_put(&c->internal);
+ kobject_del(&c->kobj);
+
+ if (!IS_ERR_OR_NULL(c->root))
+ list_add(&c->root->list, &c->btree_cache);
+
+ /* Should skip this if we're unregistering because of an error */
+ list_for_each_entry(b, &c->btree_cache, list)
+ if (btree_node_dirty(b))
+ bch_btree_write(b, true, NULL);
+
+ closure_return(cl);
+}
+
+static void __cache_set_unregister(struct closure *cl)
+{
+ struct cache_set *c = container_of(cl, struct cache_set, caching);
+ struct cached_dev *dc, *t;
+ size_t i;
+
+ mutex_lock(&bch_register_lock);
+
+ if (test_bit(CACHE_SET_UNREGISTERING, &c->flags))
+ list_for_each_entry_safe(dc, t, &c->cached_devs, list)
+ bch_cached_dev_detach(dc);
+
+ for (i = 0; i < c->nr_uuids; i++)
+ if (c->devices[i] && UUID_FLASH_ONLY(&c->uuids[i]))
+ bcache_device_stop(c->devices[i]);
+
+ mutex_unlock(&bch_register_lock);
+
+ continue_at(cl, cache_set_flush, system_wq);
+}
+
+void bch_cache_set_stop(struct cache_set *c)
+{
+ if (!test_and_set_bit(CACHE_SET_STOPPING, &c->flags))
+ closure_queue(&c->caching);
+}
+
+void bch_cache_set_unregister(struct cache_set *c)
+{
+ set_bit(CACHE_SET_UNREGISTERING, &c->flags);
+ bch_cache_set_stop(c);
+}
+
+#define alloc_bucket_pages(gfp, c) \
+ ((void *) __get_free_pages(__GFP_ZERO|gfp, ilog2(bucket_pages(c))))
+
+struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
+{
+ int iter_size;
+ struct cache_set *c = kzalloc(sizeof(struct cache_set), GFP_KERNEL);
+ if (!c)
+ return NULL;
+
+ __module_get(THIS_MODULE);
+ closure_init(&c->cl, NULL);
+ set_closure_fn(&c->cl, cache_set_free, system_wq);
+
+ closure_init(&c->caching, &c->cl);
+ set_closure_fn(&c->caching, __cache_set_unregister, system_wq);
+
+ /* Maybe create continue_at_noreturn() and use it here? */
+ closure_set_stopped(&c->cl);
+ closure_put(&c->cl);
+
+ kobject_init(&c->kobj, &bch_cache_set_ktype);
+ kobject_init(&c->internal, &bch_cache_set_internal_ktype);
+
+ bch_cache_accounting_init(&c->accounting, &c->cl);
+
+ memcpy(c->sb.set_uuid, sb->set_uuid, 16);
+ c->sb.block_size = sb->block_size;
+ c->sb.bucket_size = sb->bucket_size;
+ c->sb.nr_in_set = sb->nr_in_set;
+ c->sb.last_mount = sb->last_mount;
+ c->bucket_bits = ilog2(sb->bucket_size);
+ c->block_bits = ilog2(sb->block_size);
+ c->nr_uuids = bucket_bytes(c) / sizeof(struct uuid_entry);
+
+ c->btree_pages = c->sb.bucket_size / PAGE_SECTORS;
+ if (c->btree_pages > BTREE_MAX_PAGES)
+ c->btree_pages = max_t(int, c->btree_pages / 4,
+ BTREE_MAX_PAGES);
+
+ init_waitqueue_head(&c->alloc_wait);
+ mutex_init(&c->bucket_lock);
+ mutex_init(&c->fill_lock);
+ mutex_init(&c->sort_lock);
+ spin_lock_init(&c->sort_time_lock);
+ closure_init_unlocked(&c->sb_write);
+ closure_init_unlocked(&c->uuid_write);
+ spin_lock_init(&c->btree_read_time_lock);
+ bch_moving_init_cache_set(c);
+
+ INIT_LIST_HEAD(&c->list);
+ INIT_LIST_HEAD(&c->cached_devs);
+ INIT_LIST_HEAD(&c->btree_cache);
+ INIT_LIST_HEAD(&c->btree_cache_freeable);
+ INIT_LIST_HEAD(&c->btree_cache_freed);
+ INIT_LIST_HEAD(&c->data_buckets);
+
+ c->search = mempool_create_slab_pool(32, bch_search_cache);
+ if (!c->search)
+ goto err;
+
+ iter_size = (sb->bucket_size / sb->block_size + 1) *
+ sizeof(struct btree_iter_set);
+
+ if (!(c->devices = kzalloc(c->nr_uuids * sizeof(void *), GFP_KERNEL)) ||
+ !(c->bio_meta = mempool_create_kmalloc_pool(2,
+ sizeof(struct bbio) + sizeof(struct bio_vec) *
+ bucket_pages(c))) ||
+ !(c->bio_split = bioset_create(4, offsetof(struct bbio, bio))) ||
+ !(c->fill_iter = kmalloc(iter_size, GFP_KERNEL)) ||
+ !(c->sort = alloc_bucket_pages(GFP_KERNEL, c)) ||
+ !(c->uuids = alloc_bucket_pages(GFP_KERNEL, c)) ||
+ bch_journal_alloc(c) ||
+ bch_btree_cache_alloc(c) ||
+ bch_open_buckets_alloc(c))
+ goto err;
+
+ c->fill_iter->size = sb->bucket_size / sb->block_size;
+
+ c->congested_read_threshold_us = 2000;
+ c->congested_write_threshold_us = 20000;
+ c->error_limit = 8 << IO_ERROR_SHIFT;
+
+ return c;
+err:
+ bch_cache_set_unregister(c);
+ return NULL;
+}
+
+static void run_cache_set(struct cache_set *c)
+{
+ const char *err = "cannot allocate memory";
+ struct cached_dev *dc, *t;
+ struct cache *ca;
+ unsigned i;
+
+ struct btree_op op;
+ bch_btree_op_init_stack(&op);
+ op.lock = SHRT_MAX;
+
+ for_each_cache(ca, c, i)
+ c->nbuckets += ca->sb.nbuckets;
+
+ if (CACHE_SYNC(&c->sb)) {
+ LIST_HEAD(journal);
+ struct bkey *k;
+ struct jset *j;
+
+ err = "cannot allocate memory for journal";
+ if (bch_journal_read(c, &journal, &op))
+ goto err;
+
+ pr_debug("btree_journal_read() done");
+
+ err = "no journal entries found";
+ if (list_empty(&journal))
+ goto err;
+
+ j = &list_entry(journal.prev, struct journal_replay, list)->j;
+
+ err = "IO error reading priorities";
+ for_each_cache(ca, c, i)
+ prio_read(ca, j->prio_bucket[ca->sb.nr_this_dev]);
+
+ /*
+ * If prio_read() fails it'll call cache_set_error and we'll
+ * tear everything down right away, but if we perhaps checked
+ * sooner we could avoid journal replay.
+ */
+
+ k = &j->btree_root;
+
+ err = "bad btree root";
+ if (__bch_ptr_invalid(c, j->btree_level + 1, k))
+ goto err;
+
+ err = "error reading btree root";
+ c->root = bch_btree_node_get(c, k, j->btree_level, &op);
+ if (IS_ERR_OR_NULL(c->root))
+ goto err;
+
+ list_del_init(&c->root->list);
+ rw_unlock(true, c->root);
+
+ err = uuid_read(c, j, &op.cl);
+ if (err)
+ goto err;
+
+ err = "error in recovery";
+ if (bch_btree_check(c, &op))
+ goto err;
+
+ bch_journal_mark(c, &journal);
+ bch_btree_gc_finish(c);
+ pr_debug("btree_check() done");
+
+ /*
+ * bcache_journal_next() can't happen sooner, or
+ * btree_gc_finish() will give spurious errors about last_gc >
+ * gc_gen - this is a hack but oh well.
+ */
+ bch_journal_next(&c->journal);
+
+ for_each_cache(ca, c, i)
+ closure_call(&ca->alloc, bch_allocator_thread,
+ system_wq, &c->cl);
+
+ /*
+ * First place it's safe to allocate: btree_check() and
+ * btree_gc_finish() have to run before we have buckets to
+ * allocate, and bch_bucket_alloc_set() might cause a journal
+ * entry to be written so bcache_journal_next() has to be called
+ * first.
+ *
+ * If the uuids were in the old format we have to rewrite them
+ * before the next journal entry is written:
+ */
+ if (j->version < BCACHE_JSET_VERSION_UUID)
+ __uuid_write(c);
+
+ bch_journal_replay(c, &journal, &op);
+ } else {
+ pr_notice("invalidating existing data");
+ /* Don't want invalidate_buckets() to queue a gc yet */
+ closure_lock(&c->gc, NULL);
+
+ for_each_cache(ca, c, i) {
+ unsigned j;
+
+ ca->sb.keys = clamp_t(int, ca->sb.nbuckets >> 7,
+ 2, SB_JOURNAL_BUCKETS);
+
+ for (j = 0; j < ca->sb.keys; j++)
+ ca->sb.d[j] = ca->sb.first_bucket + j;
+ }
+
+ bch_btree_gc_finish(c);
+
+ for_each_cache(ca, c, i)
+ closure_call(&ca->alloc, bch_allocator_thread,
+ ca->alloc_workqueue, &c->cl);
+
+ mutex_lock(&c->bucket_lock);
+ for_each_cache(ca, c, i)
+ bch_prio_write(ca);
+ mutex_unlock(&c->bucket_lock);
+
+ wake_up(&c->alloc_wait);
+
+ err = "cannot allocate new UUID bucket";
+ if (__uuid_write(c))
+ goto err_unlock_gc;
+
+ err = "cannot allocate new btree root";
+ c->root = bch_btree_node_alloc(c, 0, &op.cl);
+ if (IS_ERR_OR_NULL(c->root))
+ goto err_unlock_gc;
+
+ bkey_copy_key(&c->root->key, &MAX_KEY);
+ bch_btree_write(c->root, true, &op);
+
+ bch_btree_set_root(c->root);
+ rw_unlock(true, c->root);
+
+ /*
+ * We don't want to write the first journal entry until
+ * everything is set up - fortunately journal entries won't be
+ * written until the SET_CACHE_SYNC() here:
+ */
+ SET_CACHE_SYNC(&c->sb, true);
+
+ bch_journal_next(&c->journal);
+ bch_journal_meta(c, &op.cl);
+
+ /* Unlock */
+ closure_set_stopped(&c->gc.cl);
+ closure_put(&c->gc.cl);
+ }
+
+ closure_sync(&op.cl);
+ c->sb.last_mount = get_seconds();
+ bcache_write_super(c);
+
+ list_for_each_entry_safe(dc, t, &uncached_devices, list)
+ bch_cached_dev_attach(dc, c);
+
+ flash_devs_run(c);
+
+ return;
+err_unlock_gc:
+ closure_set_stopped(&c->gc.cl);
+ closure_put(&c->gc.cl);
+err:
+ closure_sync(&op.cl);
+ /* XXX: test this, it's broken */
+ bch_cache_set_error(c, err);
+}
+
+static bool can_attach_cache(struct cache *ca, struct cache_set *c)
+{
+ return ca->sb.block_size == c->sb.block_size &&
+ ca->sb.bucket_size == c->sb.block_size &&
+ ca->sb.nr_in_set == c->sb.nr_in_set;
+}
+
+static const char *register_cache_set(struct cache *ca)
+{
+ char buf[12];
+ const char *err = "cannot allocate memory";
+ struct cache_set *c;
+
+ list_for_each_entry(c, &bch_cache_sets, list)
+ if (!memcmp(c->sb.set_uuid, ca->sb.set_uuid, 16)) {
+ if (c->cache[ca->sb.nr_this_dev])
+ return "duplicate cache set member";
+
+ if (!can_attach_cache(ca, c))
+ return "cache sb does not match set";
+
+ if (!CACHE_SYNC(&ca->sb))
+ SET_CACHE_SYNC(&c->sb, false);
+
+ goto found;
+ }
+
+ c = bch_cache_set_alloc(&ca->sb);
+ if (!c)
+ return err;
+
+ err = "error creating kobject";
+ if (kobject_add(&c->kobj, bcache_kobj, "%pU", c->sb.set_uuid) ||
+ kobject_add(&c->internal, &c->kobj, "internal"))
+ goto err;
+
+ if (bch_cache_accounting_add_kobjs(&c->accounting, &c->kobj))
+ goto err;
+
+ bch_debug_init_cache_set(c);
+
+ list_add(&c->list, &bch_cache_sets);
+found:
+ sprintf(buf, "cache%i", ca->sb.nr_this_dev);
+ if (sysfs_create_link(&ca->kobj, &c->kobj, "set") ||
+ sysfs_create_link(&c->kobj, &ca->kobj, buf))
+ goto err;
+
+ if (ca->sb.seq > c->sb.seq) {
+ c->sb.version = ca->sb.version;
+ memcpy(c->sb.set_uuid, ca->sb.set_uuid, 16);
+ c->sb.flags = ca->sb.flags;
+ c->sb.seq = ca->sb.seq;
+ pr_debug("set version = %llu", c->sb.version);
+ }
+
+ ca->set = c;
+ ca->set->cache[ca->sb.nr_this_dev] = ca;
+ c->cache_by_alloc[c->caches_loaded++] = ca;
+
+ if (c->caches_loaded == c->sb.nr_in_set)
+ run_cache_set(c);
+
+ return NULL;
+err:
+ bch_cache_set_unregister(c);
+ return err;
+}
+
+/* Cache device */
+
+void bch_cache_release(struct kobject *kobj)
+{
+ struct cache *ca = container_of(kobj, struct cache, kobj);
+
+ if (ca->set)
+ ca->set->cache[ca->sb.nr_this_dev] = NULL;
+
+ bch_cache_allocator_exit(ca);
+
+ bio_split_pool_free(&ca->bio_split_hook);
+
+ if (ca->alloc_workqueue)
+ destroy_workqueue(ca->alloc_workqueue);
+
+ free_pages((unsigned long) ca->disk_buckets, ilog2(bucket_pages(ca)));
+ kfree(ca->prio_buckets);
+ vfree(ca->buckets);
+
+ free_heap(&ca->heap);
+ free_fifo(&ca->unused);
+ free_fifo(&ca->free_inc);
+ free_fifo(&ca->free);
+
+ if (ca->sb_bio.bi_inline_vecs[0].bv_page)
+ put_page(ca->sb_bio.bi_io_vec[0].bv_page);
+
+ if (!IS_ERR_OR_NULL(ca->bdev)) {
+ blk_sync_queue(bdev_get_queue(ca->bdev));
+ blkdev_put(ca->bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
+ }
+
+ kfree(ca);
+ module_put(THIS_MODULE);
+}
+
+static int cache_alloc(struct cache_sb *sb, struct cache *ca)
+{
+ size_t free;
+ struct bucket *b;
+
+ if (!ca)
+ return -ENOMEM;
+
+ __module_get(THIS_MODULE);
+ kobject_init(&ca->kobj, &bch_cache_ktype);
+
+ memcpy(&ca->sb, sb, sizeof(struct cache_sb));
+
+ INIT_LIST_HEAD(&ca->discards);
+
+ bio_init(&ca->sb_bio);
+ ca->sb_bio.bi_max_vecs = 1;
+ ca->sb_bio.bi_io_vec = ca->sb_bio.bi_inline_vecs;
+
+ bio_init(&ca->journal.bio);
+ ca->journal.bio.bi_max_vecs = 8;
+ ca->journal.bio.bi_io_vec = ca->journal.bio.bi_inline_vecs;
+
+ free = roundup_pow_of_two(ca->sb.nbuckets) >> 9;
+ free = max_t(size_t, free, (prio_buckets(ca) + 8) * 2);
+
+ if (!init_fifo(&ca->free, free, GFP_KERNEL) ||
+ !init_fifo(&ca->free_inc, free << 2, GFP_KERNEL) ||
+ !init_fifo(&ca->unused, free << 2, GFP_KERNEL) ||
+ !init_heap(&ca->heap, free << 3, GFP_KERNEL) ||
+ !(ca->buckets = vmalloc(sizeof(struct bucket) *
+ ca->sb.nbuckets)) ||
+ !(ca->prio_buckets = kzalloc(sizeof(uint64_t) * prio_buckets(ca) *
+ 2, GFP_KERNEL)) ||
+ !(ca->disk_buckets = alloc_bucket_pages(GFP_KERNEL, ca)) ||
+ !(ca->alloc_workqueue = alloc_workqueue("bch_allocator", 0, 1)) ||
+ bio_split_pool_init(&ca->bio_split_hook))
+ goto err;
+
+ ca->prio_last_buckets = ca->prio_buckets + prio_buckets(ca);
+
+ memset(ca->buckets, 0, ca->sb.nbuckets * sizeof(struct bucket));
+ for_each_bucket(b, ca)
+ atomic_set(&b->pin, 0);
+
+ if (bch_cache_allocator_init(ca))
+ goto err;
+
+ return 0;
+err:
+ kobject_put(&ca->kobj);
+ return -ENOMEM;
+}
+
+static const char *register_cache(struct cache_sb *sb, struct page *sb_page,
+ struct block_device *bdev, struct cache *ca)
+{
+ char name[BDEVNAME_SIZE];
+ const char *err = "cannot allocate memory";
+
+ if (cache_alloc(sb, ca) != 0)
+ return err;
+
+ ca->sb_bio.bi_io_vec[0].bv_page = sb_page;
+ ca->bdev = bdev;
+ ca->bdev->bd_holder = ca;
+
+ if (blk_queue_discard(bdev_get_queue(ca->bdev)))
+ ca->discard = CACHE_DISCARD(&ca->sb);
+
+ err = "error creating kobject";
+ if (kobject_add(&ca->kobj, &part_to_dev(bdev->bd_part)->kobj, "bcache"))
+ goto err;
+
+ err = register_cache_set(ca);
+ if (err)
+ goto err;
+
+ pr_info("registered cache device %s", bdevname(bdev, name));
+
+ return NULL;
+err:
+ kobject_put(&ca->kobj);
+ pr_info("error opening %s: %s", bdevname(bdev, name), err);
+ /* Return NULL instead of an error because kobject_put() cleans
+ * everything up
+ */
+ return NULL;
+}
+
+/* Global interfaces/init */
+
+static ssize_t register_bcache(struct kobject *, struct kobj_attribute *,
+ const char *, size_t);
+
+kobj_attribute_write(register, register_bcache);
+kobj_attribute_write(register_quiet, register_bcache);
+
+static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr,
+ const char *buffer, size_t size)
+{
+ ssize_t ret = size;
+ const char *err = "cannot allocate memory";
+ char *path = NULL;
+ struct cache_sb *sb = NULL;
+ struct block_device *bdev = NULL;
+ struct page *sb_page = NULL;
+
+ if (!try_module_get(THIS_MODULE))
+ return -EBUSY;
+
+ mutex_lock(&bch_register_lock);
+
+ if (!(path = kstrndup(buffer, size, GFP_KERNEL)) ||
+ !(sb = kmalloc(sizeof(struct cache_sb), GFP_KERNEL)))
+ goto err;
+
+ err = "failed to open device";
+ bdev = blkdev_get_by_path(strim(path),
+ FMODE_READ|FMODE_WRITE|FMODE_EXCL,
+ sb);
+ if (bdev == ERR_PTR(-EBUSY))
+ err = "device busy";
+
+ if (IS_ERR(bdev) ||
+ set_blocksize(bdev, 4096))
+ goto err;
+
+ err = read_super(sb, bdev, &sb_page);
+ if (err)
+ goto err_close;
+
+ if (SB_IS_BDEV(sb)) {
+ struct cached_dev *dc = kzalloc(sizeof(*dc), GFP_KERNEL);
+
+ err = register_bdev(sb, sb_page, bdev, dc);
+ } else {
+ struct cache *ca = kzalloc(sizeof(*ca), GFP_KERNEL);
+
+ err = register_cache(sb, sb_page, bdev, ca);
+ }
+
+ if (err) {
+ /* register_(bdev|cache) will only return an error if they
+ * didn't get far enough to create the kobject - if they did,
+ * the kobject destructor will do this cleanup.
+ */
+ put_page(sb_page);
+err_close:
+ blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
+err:
+ if (attr != &ksysfs_register_quiet)
+ pr_info("error opening %s: %s", path, err);
+ ret = -EINVAL;
+ }
+
+ kfree(sb);
+ kfree(path);
+ mutex_unlock(&bch_register_lock);
+ module_put(THIS_MODULE);
+ return ret;
+}
+
+static int bcache_reboot(struct notifier_block *n, unsigned long code, void *x)
+{
+ if (code == SYS_DOWN ||
+ code == SYS_HALT ||
+ code == SYS_POWER_OFF) {
+ DEFINE_WAIT(wait);
+ unsigned long start = jiffies;
+ bool stopped = false;
+
+ struct cache_set *c, *tc;
+ struct cached_dev *dc, *tdc;
+
+ mutex_lock(&bch_register_lock);
+
+ if (list_empty(&bch_cache_sets) &&
+ list_empty(&uncached_devices))
+ goto out;
+
+ pr_info("Stopping all devices:");
+
+ list_for_each_entry_safe(c, tc, &bch_cache_sets, list)
+ bch_cache_set_stop(c);
+
+ list_for_each_entry_safe(dc, tdc, &uncached_devices, list)
+ bcache_device_stop(&dc->disk);
+
+ /* What's a condition variable? */
+ while (1) {
+ long timeout = start + 2 * HZ - jiffies;
+
+ stopped = list_empty(&bch_cache_sets) &&
+ list_empty(&uncached_devices);
+
+ if (timeout < 0 || stopped)
+ break;
+
+ prepare_to_wait(&unregister_wait, &wait,
+ TASK_UNINTERRUPTIBLE);
+
+ mutex_unlock(&bch_register_lock);
+ schedule_timeout(timeout);
+ mutex_lock(&bch_register_lock);
+ }
+
+ finish_wait(&unregister_wait, &wait);
+
+ if (stopped)
+ pr_info("All devices stopped");
+ else
+ pr_notice("Timeout waiting for devices to be closed");
+out:
+ mutex_unlock(&bch_register_lock);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block reboot = {
+ .notifier_call = bcache_reboot,
+ .priority = INT_MAX, /* before any real devices */
+};
+
+static void bcache_exit(void)
+{
+ bch_debug_exit();
+ bch_writeback_exit();
+ bch_request_exit();
+ bch_btree_exit();
+ if (bcache_kobj)
+ kobject_put(bcache_kobj);
+ if (bcache_wq)
+ destroy_workqueue(bcache_wq);
+ unregister_blkdev(bcache_major, "bcache");
+ unregister_reboot_notifier(&reboot);
+}
+
+static int __init bcache_init(void)
+{
+ static const struct attribute *files[] = {
+ &ksysfs_register.attr,
+ &ksysfs_register_quiet.attr,
+ NULL
+ };
+
+ mutex_init(&bch_register_lock);
+ init_waitqueue_head(&unregister_wait);
+ register_reboot_notifier(&reboot);
+ closure_debug_init();
+
+ bcache_major = register_blkdev(0, "bcache");
+ if (bcache_major < 0)
+ return bcache_major;
+
+ if (!(bcache_wq = create_workqueue("bcache")) ||
+ !(bcache_kobj = kobject_create_and_add("bcache", fs_kobj)) ||
+ sysfs_create_files(bcache_kobj, files) ||
+ bch_btree_init() ||
+ bch_request_init() ||
+ bch_writeback_init() ||
+ bch_debug_init(bcache_kobj))
+ goto err;
+
+ return 0;
+err:
+ bcache_exit();
+ return -ENOMEM;
+}
+
+module_exit(bcache_exit);
+module_init(bcache_init);
diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
new file mode 100644
index 000000000000..4d9cca47e4c6
--- /dev/null
+++ b/drivers/md/bcache/sysfs.c
@@ -0,0 +1,817 @@
+/*
+ * bcache sysfs interfaces
+ *
+ * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "sysfs.h"
+#include "btree.h"
+#include "request.h"
+
+#include <linux/sort.h>
+
+static const char * const cache_replacement_policies[] = {
+ "lru",
+ "fifo",
+ "random",
+ NULL
+};
+
+write_attribute(attach);
+write_attribute(detach);
+write_attribute(unregister);
+write_attribute(stop);
+write_attribute(clear_stats);
+write_attribute(trigger_gc);
+write_attribute(prune_cache);
+write_attribute(flash_vol_create);
+
+read_attribute(bucket_size);
+read_attribute(block_size);
+read_attribute(nbuckets);
+read_attribute(tree_depth);
+read_attribute(root_usage_percent);
+read_attribute(priority_stats);
+read_attribute(btree_cache_size);
+read_attribute(btree_cache_max_chain);
+read_attribute(cache_available_percent);
+read_attribute(written);
+read_attribute(btree_written);
+read_attribute(metadata_written);
+read_attribute(active_journal_entries);
+
+sysfs_time_stats_attribute(btree_gc, sec, ms);
+sysfs_time_stats_attribute(btree_split, sec, us);
+sysfs_time_stats_attribute(btree_sort, ms, us);
+sysfs_time_stats_attribute(btree_read, ms, us);
+sysfs_time_stats_attribute(try_harder, ms, us);
+
+read_attribute(btree_nodes);
+read_attribute(btree_used_percent);
+read_attribute(average_key_size);
+read_attribute(dirty_data);
+read_attribute(bset_tree_stats);
+
+read_attribute(state);
+read_attribute(cache_read_races);
+read_attribute(writeback_keys_done);
+read_attribute(writeback_keys_failed);
+read_attribute(io_errors);
+read_attribute(congested);
+rw_attribute(congested_read_threshold_us);
+rw_attribute(congested_write_threshold_us);
+
+rw_attribute(sequential_cutoff);
+rw_attribute(sequential_merge);
+rw_attribute(data_csum);
+rw_attribute(cache_mode);
+rw_attribute(writeback_metadata);
+rw_attribute(writeback_running);
+rw_attribute(writeback_percent);
+rw_attribute(writeback_delay);
+rw_attribute(writeback_rate);
+
+rw_attribute(writeback_rate_update_seconds);
+rw_attribute(writeback_rate_d_term);
+rw_attribute(writeback_rate_p_term_inverse);
+rw_attribute(writeback_rate_d_smooth);
+read_attribute(writeback_rate_debug);
+
+rw_attribute(synchronous);
+rw_attribute(journal_delay_ms);
+rw_attribute(discard);
+rw_attribute(running);
+rw_attribute(label);
+rw_attribute(readahead);
+rw_attribute(io_error_limit);
+rw_attribute(io_error_halflife);
+rw_attribute(verify);
+rw_attribute(key_merging_disabled);
+rw_attribute(gc_always_rewrite);
+rw_attribute(freelist_percent);
+rw_attribute(cache_replacement_policy);
+rw_attribute(btree_shrinker_disabled);
+rw_attribute(copy_gc_enabled);
+rw_attribute(size);
+
+SHOW(__bch_cached_dev)
+{
+ struct cached_dev *dc = container_of(kobj, struct cached_dev,
+ disk.kobj);
+ const char *states[] = { "no cache", "clean", "dirty", "inconsistent" };
+
+#define var(stat) (dc->stat)
+
+ if (attr == &sysfs_cache_mode)
+ return bch_snprint_string_list(buf, PAGE_SIZE,
+ bch_cache_modes + 1,
+ BDEV_CACHE_MODE(&dc->sb));
+
+ sysfs_printf(data_csum, "%i", dc->disk.data_csum);
+ var_printf(verify, "%i");
+ var_printf(writeback_metadata, "%i");
+ var_printf(writeback_running, "%i");
+ var_print(writeback_delay);
+ var_print(writeback_percent);
+ sysfs_print(writeback_rate, dc->writeback_rate.rate);
+
+ var_print(writeback_rate_update_seconds);
+ var_print(writeback_rate_d_term);
+ var_print(writeback_rate_p_term_inverse);
+ var_print(writeback_rate_d_smooth);
+
+ if (attr == &sysfs_writeback_rate_debug) {
+ char dirty[20];
+ char derivative[20];
+ char target[20];
+ bch_hprint(dirty,
+ atomic_long_read(&dc->disk.sectors_dirty) << 9);
+ bch_hprint(derivative, dc->writeback_rate_derivative << 9);
+ bch_hprint(target, dc->writeback_rate_target << 9);
+
+ return sprintf(buf,
+ "rate:\t\t%u\n"
+ "change:\t\t%i\n"
+ "dirty:\t\t%s\n"
+ "derivative:\t%s\n"
+ "target:\t\t%s\n",
+ dc->writeback_rate.rate,
+ dc->writeback_rate_change,
+ dirty, derivative, target);
+ }
+
+ sysfs_hprint(dirty_data,
+ atomic_long_read(&dc->disk.sectors_dirty) << 9);
+
+ var_printf(sequential_merge, "%i");
+ var_hprint(sequential_cutoff);
+ var_hprint(readahead);
+
+ sysfs_print(running, atomic_read(&dc->running));
+ sysfs_print(state, states[BDEV_STATE(&dc->sb)]);
+
+ if (attr == &sysfs_label) {
+ memcpy(buf, dc->sb.label, SB_LABEL_SIZE);
+ buf[SB_LABEL_SIZE + 1] = '\0';
+ strcat(buf, "\n");
+ return strlen(buf);
+ }
+
+#undef var
+ return 0;
+}
+SHOW_LOCKED(bch_cached_dev)
+
+STORE(__cached_dev)
+{
+ struct cached_dev *dc = container_of(kobj, struct cached_dev,
+ disk.kobj);
+ unsigned v = size;
+ struct cache_set *c;
+
+#define d_strtoul(var) sysfs_strtoul(var, dc->var)
+#define d_strtoi_h(var) sysfs_hatoi(var, dc->var)
+
+ sysfs_strtoul(data_csum, dc->disk.data_csum);
+ d_strtoul(verify);
+ d_strtoul(writeback_metadata);
+ d_strtoul(writeback_running);
+ d_strtoul(writeback_delay);
+ sysfs_strtoul_clamp(writeback_rate,
+ dc->writeback_rate.rate, 1, 1000000);
+ sysfs_strtoul_clamp(writeback_percent, dc->writeback_percent, 0, 40);
+
+ d_strtoul(writeback_rate_update_seconds);
+ d_strtoul(writeback_rate_d_term);
+ d_strtoul(writeback_rate_p_term_inverse);
+ sysfs_strtoul_clamp(writeback_rate_p_term_inverse,
+ dc->writeback_rate_p_term_inverse, 1, INT_MAX);
+ d_strtoul(writeback_rate_d_smooth);
+
+ d_strtoul(sequential_merge);
+ d_strtoi_h(sequential_cutoff);
+ d_strtoi_h(readahead);
+
+ if (attr == &sysfs_clear_stats)
+ bch_cache_accounting_clear(&dc->accounting);
+
+ if (attr == &sysfs_running &&
+ strtoul_or_return(buf))
+ bch_cached_dev_run(dc);
+
+ if (attr == &sysfs_cache_mode) {
+ ssize_t v = bch_read_string_list(buf, bch_cache_modes + 1);
+
+ if (v < 0)
+ return v;
+
+ if ((unsigned) v != BDEV_CACHE_MODE(&dc->sb)) {
+ SET_BDEV_CACHE_MODE(&dc->sb, v);
+ bch_write_bdev_super(dc, NULL);
+ }
+ }
+
+ if (attr == &sysfs_label) {
+ memcpy(dc->sb.label, buf, SB_LABEL_SIZE);
+ bch_write_bdev_super(dc, NULL);
+ if (dc->disk.c) {
+ memcpy(dc->disk.c->uuids[dc->disk.id].label,
+ buf, SB_LABEL_SIZE);
+ bch_uuid_write(dc->disk.c);
+ }
+ }
+
+ if (attr == &sysfs_attach) {
+ if (bch_parse_uuid(buf, dc->sb.set_uuid) < 16)
+ return -EINVAL;
+
+ list_for_each_entry(c, &bch_cache_sets, list) {
+ v = bch_cached_dev_attach(dc, c);
+ if (!v)
+ return size;
+ }
+
+ pr_err("Can't attach %s: cache set not found", buf);
+ size = v;
+ }
+
+ if (attr == &sysfs_detach && dc->disk.c)
+ bch_cached_dev_detach(dc);
+
+ if (attr == &sysfs_stop)
+ bcache_device_stop(&dc->disk);
+
+ return size;
+}
+
+STORE(bch_cached_dev)
+{
+ struct cached_dev *dc = container_of(kobj, struct cached_dev,
+ disk.kobj);
+
+ mutex_lock(&bch_register_lock);
+ size = __cached_dev_store(kobj, attr, buf, size);
+
+ if (attr == &sysfs_writeback_running)
+ bch_writeback_queue(dc);
+
+ if (attr == &sysfs_writeback_percent)
+ schedule_delayed_work(&dc->writeback_rate_update,
+ dc->writeback_rate_update_seconds * HZ);
+
+ mutex_unlock(&bch_register_lock);
+ return size;
+}
+
+static struct attribute *bch_cached_dev_files[] = {
+ &sysfs_attach,
+ &sysfs_detach,
+ &sysfs_stop,
+#if 0
+ &sysfs_data_csum,
+#endif
+ &sysfs_cache_mode,
+ &sysfs_writeback_metadata,
+ &sysfs_writeback_running,
+ &sysfs_writeback_delay,
+ &sysfs_writeback_percent,
+ &sysfs_writeback_rate,
+ &sysfs_writeback_rate_update_seconds,
+ &sysfs_writeback_rate_d_term,
+ &sysfs_writeback_rate_p_term_inverse,
+ &sysfs_writeback_rate_d_smooth,
+ &sysfs_writeback_rate_debug,
+ &sysfs_dirty_data,
+ &sysfs_sequential_cutoff,
+ &sysfs_sequential_merge,
+ &sysfs_clear_stats,
+ &sysfs_running,
+ &sysfs_state,
+ &sysfs_label,
+ &sysfs_readahead,
+#ifdef CONFIG_BCACHE_DEBUG
+ &sysfs_verify,
+#endif
+ NULL
+};
+KTYPE(bch_cached_dev);
+
+SHOW(bch_flash_dev)
+{
+ struct bcache_device *d = container_of(kobj, struct bcache_device,
+ kobj);
+ struct uuid_entry *u = &d->c->uuids[d->id];
+
+ sysfs_printf(data_csum, "%i", d->data_csum);
+ sysfs_hprint(size, u->sectors << 9);
+
+ if (attr == &sysfs_label) {
+ memcpy(buf, u->label, SB_LABEL_SIZE);
+ buf[SB_LABEL_SIZE + 1] = '\0';
+ strcat(buf, "\n");
+ return strlen(buf);
+ }
+
+ return 0;
+}
+
+STORE(__bch_flash_dev)
+{
+ struct bcache_device *d = container_of(kobj, struct bcache_device,
+ kobj);
+ struct uuid_entry *u = &d->c->uuids[d->id];
+
+ sysfs_strtoul(data_csum, d->data_csum);
+
+ if (attr == &sysfs_size) {
+ uint64_t v;
+ strtoi_h_or_return(buf, v);
+
+ u->sectors = v >> 9;
+ bch_uuid_write(d->c);
+ set_capacity(d->disk, u->sectors);
+ }
+
+ if (attr == &sysfs_label) {
+ memcpy(u->label, buf, SB_LABEL_SIZE);
+ bch_uuid_write(d->c);
+ }
+
+ if (attr == &sysfs_unregister) {
+ atomic_set(&d->detaching, 1);
+ bcache_device_stop(d);
+ }
+
+ return size;
+}
+STORE_LOCKED(bch_flash_dev)
+
+static struct attribute *bch_flash_dev_files[] = {
+ &sysfs_unregister,
+#if 0
+ &sysfs_data_csum,
+#endif
+ &sysfs_label,
+ &sysfs_size,
+ NULL
+};
+KTYPE(bch_flash_dev);
+
+SHOW(__bch_cache_set)
+{
+ unsigned root_usage(struct cache_set *c)
+ {
+ unsigned bytes = 0;
+ struct bkey *k;
+ struct btree *b;
+ struct btree_iter iter;
+
+ goto lock_root;
+
+ do {
+ rw_unlock(false, b);
+lock_root:
+ b = c->root;
+ rw_lock(false, b, b->level);
+ } while (b != c->root);
+
+ for_each_key_filter(b, k, &iter, bch_ptr_bad)
+ bytes += bkey_bytes(k);
+
+ rw_unlock(false, b);
+
+ return (bytes * 100) / btree_bytes(c);
+ }
+
+ size_t cache_size(struct cache_set *c)
+ {
+ size_t ret = 0;
+ struct btree *b;
+
+ mutex_lock(&c->bucket_lock);
+ list_for_each_entry(b, &c->btree_cache, list)
+ ret += 1 << (b->page_order + PAGE_SHIFT);
+
+ mutex_unlock(&c->bucket_lock);
+ return ret;
+ }
+
+ unsigned cache_max_chain(struct cache_set *c)
+ {
+ unsigned ret = 0;
+ struct hlist_head *h;
+
+ mutex_lock(&c->bucket_lock);
+
+ for (h = c->bucket_hash;
+ h < c->bucket_hash + (1 << BUCKET_HASH_BITS);
+ h++) {
+ unsigned i = 0;
+ struct hlist_node *p;
+
+ hlist_for_each(p, h)
+ i++;
+
+ ret = max(ret, i);
+ }
+
+ mutex_unlock(&c->bucket_lock);
+ return ret;
+ }
+
+ unsigned btree_used(struct cache_set *c)
+ {
+ return div64_u64(c->gc_stats.key_bytes * 100,
+ (c->gc_stats.nodes ?: 1) * btree_bytes(c));
+ }
+
+ unsigned average_key_size(struct cache_set *c)
+ {
+ return c->gc_stats.nkeys
+ ? div64_u64(c->gc_stats.data, c->gc_stats.nkeys)
+ : 0;
+ }
+
+ struct cache_set *c = container_of(kobj, struct cache_set, kobj);
+
+ sysfs_print(synchronous, CACHE_SYNC(&c->sb));
+ sysfs_print(journal_delay_ms, c->journal_delay_ms);
+ sysfs_hprint(bucket_size, bucket_bytes(c));
+ sysfs_hprint(block_size, block_bytes(c));
+ sysfs_print(tree_depth, c->root->level);
+ sysfs_print(root_usage_percent, root_usage(c));
+
+ sysfs_hprint(btree_cache_size, cache_size(c));
+ sysfs_print(btree_cache_max_chain, cache_max_chain(c));
+ sysfs_print(cache_available_percent, 100 - c->gc_stats.in_use);
+
+ sysfs_print_time_stats(&c->btree_gc_time, btree_gc, sec, ms);
+ sysfs_print_time_stats(&c->btree_split_time, btree_split, sec, us);
+ sysfs_print_time_stats(&c->sort_time, btree_sort, ms, us);
+ sysfs_print_time_stats(&c->btree_read_time, btree_read, ms, us);
+ sysfs_print_time_stats(&c->try_harder_time, try_harder, ms, us);
+
+ sysfs_print(btree_used_percent, btree_used(c));
+ sysfs_print(btree_nodes, c->gc_stats.nodes);
+ sysfs_hprint(dirty_data, c->gc_stats.dirty);
+ sysfs_hprint(average_key_size, average_key_size(c));
+
+ sysfs_print(cache_read_races,
+ atomic_long_read(&c->cache_read_races));
+
+ sysfs_print(writeback_keys_done,
+ atomic_long_read(&c->writeback_keys_done));
+ sysfs_print(writeback_keys_failed,
+ atomic_long_read(&c->writeback_keys_failed));
+
+ /* See count_io_errors for why 88 */
+ sysfs_print(io_error_halflife, c->error_decay * 88);
+ sysfs_print(io_error_limit, c->error_limit >> IO_ERROR_SHIFT);
+
+ sysfs_hprint(congested,
+ ((uint64_t) bch_get_congested(c)) << 9);
+ sysfs_print(congested_read_threshold_us,
+ c->congested_read_threshold_us);
+ sysfs_print(congested_write_threshold_us,
+ c->congested_write_threshold_us);
+
+ sysfs_print(active_journal_entries, fifo_used(&c->journal.pin));
+ sysfs_printf(verify, "%i", c->verify);
+ sysfs_printf(key_merging_disabled, "%i", c->key_merging_disabled);
+ sysfs_printf(gc_always_rewrite, "%i", c->gc_always_rewrite);
+ sysfs_printf(btree_shrinker_disabled, "%i", c->shrinker_disabled);
+ sysfs_printf(copy_gc_enabled, "%i", c->copy_gc_enabled);
+
+ if (attr == &sysfs_bset_tree_stats)
+ return bch_bset_print_stats(c, buf);
+
+ return 0;
+}
+SHOW_LOCKED(bch_cache_set)
+
+STORE(__bch_cache_set)
+{
+ struct cache_set *c = container_of(kobj, struct cache_set, kobj);
+
+ if (attr == &sysfs_unregister)
+ bch_cache_set_unregister(c);
+
+ if (attr == &sysfs_stop)
+ bch_cache_set_stop(c);
+
+ if (attr == &sysfs_synchronous) {
+ bool sync = strtoul_or_return(buf);
+
+ if (sync != CACHE_SYNC(&c->sb)) {
+ SET_CACHE_SYNC(&c->sb, sync);
+ bcache_write_super(c);
+ }
+ }
+
+ if (attr == &sysfs_flash_vol_create) {
+ int r;
+ uint64_t v;
+ strtoi_h_or_return(buf, v);
+
+ r = bch_flash_dev_create(c, v);
+ if (r)
+ return r;
+ }
+
+ if (attr == &sysfs_clear_stats) {
+ atomic_long_set(&c->writeback_keys_done, 0);
+ atomic_long_set(&c->writeback_keys_failed, 0);
+
+ memset(&c->gc_stats, 0, sizeof(struct gc_stat));
+ bch_cache_accounting_clear(&c->accounting);
+ }
+
+ if (attr == &sysfs_trigger_gc)
+ bch_queue_gc(c);
+
+ if (attr == &sysfs_prune_cache) {
+ struct shrink_control sc;
+ sc.gfp_mask = GFP_KERNEL;
+ sc.nr_to_scan = strtoul_or_return(buf);
+ c->shrink.shrink(&c->shrink, &sc);
+ }
+
+ sysfs_strtoul(congested_read_threshold_us,
+ c->congested_read_threshold_us);
+ sysfs_strtoul(congested_write_threshold_us,
+ c->congested_write_threshold_us);
+
+ if (attr == &sysfs_io_error_limit)
+ c->error_limit = strtoul_or_return(buf) << IO_ERROR_SHIFT;
+
+ /* See count_io_errors() for why 88 */
+ if (attr == &sysfs_io_error_halflife)
+ c->error_decay = strtoul_or_return(buf) / 88;
+
+ sysfs_strtoul(journal_delay_ms, c->journal_delay_ms);
+ sysfs_strtoul(verify, c->verify);
+ sysfs_strtoul(key_merging_disabled, c->key_merging_disabled);
+ sysfs_strtoul(gc_always_rewrite, c->gc_always_rewrite);
+ sysfs_strtoul(btree_shrinker_disabled, c->shrinker_disabled);
+ sysfs_strtoul(copy_gc_enabled, c->copy_gc_enabled);
+
+ return size;
+}
+STORE_LOCKED(bch_cache_set)
+
+SHOW(bch_cache_set_internal)
+{
+ struct cache_set *c = container_of(kobj, struct cache_set, internal);
+ return bch_cache_set_show(&c->kobj, attr, buf);
+}
+
+STORE(bch_cache_set_internal)
+{
+ struct cache_set *c = container_of(kobj, struct cache_set, internal);
+ return bch_cache_set_store(&c->kobj, attr, buf, size);
+}
+
+static void bch_cache_set_internal_release(struct kobject *k)
+{
+}
+
+static struct attribute *bch_cache_set_files[] = {
+ &sysfs_unregister,
+ &sysfs_stop,
+ &sysfs_synchronous,
+ &sysfs_journal_delay_ms,
+ &sysfs_flash_vol_create,
+
+ &sysfs_bucket_size,
+ &sysfs_block_size,
+ &sysfs_tree_depth,
+ &sysfs_root_usage_percent,
+ &sysfs_btree_cache_size,
+ &sysfs_cache_available_percent,
+
+ &sysfs_average_key_size,
+ &sysfs_dirty_data,
+
+ &sysfs_io_error_limit,
+ &sysfs_io_error_halflife,
+ &sysfs_congested,
+ &sysfs_congested_read_threshold_us,
+ &sysfs_congested_write_threshold_us,
+ &sysfs_clear_stats,
+ NULL
+};
+KTYPE(bch_cache_set);
+
+static struct attribute *bch_cache_set_internal_files[] = {
+ &sysfs_active_journal_entries,
+
+ sysfs_time_stats_attribute_list(btree_gc, sec, ms)
+ sysfs_time_stats_attribute_list(btree_split, sec, us)
+ sysfs_time_stats_attribute_list(btree_sort, ms, us)
+ sysfs_time_stats_attribute_list(btree_read, ms, us)
+ sysfs_time_stats_attribute_list(try_harder, ms, us)
+
+ &sysfs_btree_nodes,
+ &sysfs_btree_used_percent,
+ &sysfs_btree_cache_max_chain,
+
+ &sysfs_bset_tree_stats,
+ &sysfs_cache_read_races,
+ &sysfs_writeback_keys_done,
+ &sysfs_writeback_keys_failed,
+
+ &sysfs_trigger_gc,
+ &sysfs_prune_cache,
+#ifdef CONFIG_BCACHE_DEBUG
+ &sysfs_verify,
+ &sysfs_key_merging_disabled,
+#endif
+ &sysfs_gc_always_rewrite,
+ &sysfs_btree_shrinker_disabled,
+ &sysfs_copy_gc_enabled,
+ NULL
+};
+KTYPE(bch_cache_set_internal);
+
+SHOW(__bch_cache)
+{
+ struct cache *ca = container_of(kobj, struct cache, kobj);
+
+ sysfs_hprint(bucket_size, bucket_bytes(ca));
+ sysfs_hprint(block_size, block_bytes(ca));
+ sysfs_print(nbuckets, ca->sb.nbuckets);
+ sysfs_print(discard, ca->discard);
+ sysfs_hprint(written, atomic_long_read(&ca->sectors_written) << 9);
+ sysfs_hprint(btree_written,
+ atomic_long_read(&ca->btree_sectors_written) << 9);
+ sysfs_hprint(metadata_written,
+ (atomic_long_read(&ca->meta_sectors_written) +
+ atomic_long_read(&ca->btree_sectors_written)) << 9);
+
+ sysfs_print(io_errors,
+ atomic_read(&ca->io_errors) >> IO_ERROR_SHIFT);
+
+ sysfs_print(freelist_percent, ca->free.size * 100 /
+ ((size_t) ca->sb.nbuckets));
+
+ if (attr == &sysfs_cache_replacement_policy)
+ return bch_snprint_string_list(buf, PAGE_SIZE,
+ cache_replacement_policies,
+ CACHE_REPLACEMENT(&ca->sb));
+
+ if (attr == &sysfs_priority_stats) {
+ int cmp(const void *l, const void *r)
+ { return *((uint16_t *) r) - *((uint16_t *) l); }
+
+ /* Number of quantiles we compute */
+ const unsigned nq = 31;
+
+ size_t n = ca->sb.nbuckets, i, unused, btree;
+ uint64_t sum = 0;
+ uint16_t q[nq], *p, *cached;
+ ssize_t ret;
+
+ cached = p = vmalloc(ca->sb.nbuckets * sizeof(uint16_t));
+ if (!p)
+ return -ENOMEM;
+
+ mutex_lock(&ca->set->bucket_lock);
+ for (i = ca->sb.first_bucket; i < n; i++)
+ p[i] = ca->buckets[i].prio;
+ mutex_unlock(&ca->set->bucket_lock);
+
+ sort(p, n, sizeof(uint16_t), cmp, NULL);
+
+ while (n &&
+ !cached[n - 1])
+ --n;
+
+ unused = ca->sb.nbuckets - n;
+
+ while (cached < p + n &&
+ *cached == BTREE_PRIO)
+ cached++;
+
+ btree = cached - p;
+ n -= btree;
+
+ for (i = 0; i < n; i++)
+ sum += INITIAL_PRIO - cached[i];
+
+ if (n)
+ do_div(sum, n);
+
+ for (i = 0; i < nq; i++)
+ q[i] = INITIAL_PRIO - cached[n * (i + 1) / (nq + 1)];
+
+ vfree(p);
+
+ ret = snprintf(buf, PAGE_SIZE,
+ "Unused: %zu%%\n"
+ "Metadata: %zu%%\n"
+ "Average: %llu\n"
+ "Sectors per Q: %zu\n"
+ "Quantiles: [",
+ unused * 100 / (size_t) ca->sb.nbuckets,
+ btree * 100 / (size_t) ca->sb.nbuckets, sum,
+ n * ca->sb.bucket_size / (nq + 1));
+
+ for (i = 0; i < nq && ret < (ssize_t) PAGE_SIZE; i++)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ i < nq - 1 ? "%u " : "%u]\n", q[i]);
+
+ buf[PAGE_SIZE - 1] = '\0';
+ return ret;
+ }
+
+ return 0;
+}
+SHOW_LOCKED(bch_cache)
+
+STORE(__bch_cache)
+{
+ struct cache *ca = container_of(kobj, struct cache, kobj);
+
+ if (attr == &sysfs_discard) {
+ bool v = strtoul_or_return(buf);
+
+ if (blk_queue_discard(bdev_get_queue(ca->bdev)))
+ ca->discard = v;
+
+ if (v != CACHE_DISCARD(&ca->sb)) {
+ SET_CACHE_DISCARD(&ca->sb, v);
+ bcache_write_super(ca->set);
+ }
+ }
+
+ if (attr == &sysfs_cache_replacement_policy) {
+ ssize_t v = bch_read_string_list(buf, cache_replacement_policies);
+
+ if (v < 0)
+ return v;
+
+ if ((unsigned) v != CACHE_REPLACEMENT(&ca->sb)) {
+ mutex_lock(&ca->set->bucket_lock);
+ SET_CACHE_REPLACEMENT(&ca->sb, v);
+ mutex_unlock(&ca->set->bucket_lock);
+
+ bcache_write_super(ca->set);
+ }
+ }
+
+ if (attr == &sysfs_freelist_percent) {
+ DECLARE_FIFO(long, free);
+ long i;
+ size_t p = strtoul_or_return(buf);
+
+ p = clamp_t(size_t,
+ ((size_t) ca->sb.nbuckets * p) / 100,
+ roundup_pow_of_two(ca->sb.nbuckets) >> 9,
+ ca->sb.nbuckets / 2);
+
+ if (!init_fifo_exact(&free, p, GFP_KERNEL))
+ return -ENOMEM;
+
+ mutex_lock(&ca->set->bucket_lock);
+
+ fifo_move(&free, &ca->free);
+ fifo_swap(&free, &ca->free);
+
+ mutex_unlock(&ca->set->bucket_lock);
+
+ while (fifo_pop(&free, i))
+ atomic_dec(&ca->buckets[i].pin);
+
+ free_fifo(&free);
+ }
+
+ if (attr == &sysfs_clear_stats) {
+ atomic_long_set(&ca->sectors_written, 0);
+ atomic_long_set(&ca->btree_sectors_written, 0);
+ atomic_long_set(&ca->meta_sectors_written, 0);
+ atomic_set(&ca->io_count, 0);
+ atomic_set(&ca->io_errors, 0);
+ }
+
+ return size;
+}
+STORE_LOCKED(bch_cache)
+
+static struct attribute *bch_cache_files[] = {
+ &sysfs_bucket_size,
+ &sysfs_block_size,
+ &sysfs_nbuckets,
+ &sysfs_priority_stats,
+ &sysfs_discard,
+ &sysfs_written,
+ &sysfs_btree_written,
+ &sysfs_metadata_written,
+ &sysfs_io_errors,
+ &sysfs_clear_stats,
+ &sysfs_freelist_percent,
+ &sysfs_cache_replacement_policy,
+ NULL
+};
+KTYPE(bch_cache);
diff --git a/drivers/md/bcache/sysfs.h b/drivers/md/bcache/sysfs.h
new file mode 100644
index 000000000000..0526fe92a683
--- /dev/null
+++ b/drivers/md/bcache/sysfs.h
@@ -0,0 +1,110 @@
+#ifndef _BCACHE_SYSFS_H_
+#define _BCACHE_SYSFS_H_
+
+#define KTYPE(type) \
+struct kobj_type type ## _ktype = { \
+ .release = type ## _release, \
+ .sysfs_ops = &((const struct sysfs_ops) { \
+ .show = type ## _show, \
+ .store = type ## _store \
+ }), \
+ .default_attrs = type ## _files \
+}
+
+#define SHOW(fn) \
+static ssize_t fn ## _show(struct kobject *kobj, struct attribute *attr,\
+ char *buf) \
+
+#define STORE(fn) \
+static ssize_t fn ## _store(struct kobject *kobj, struct attribute *attr,\
+ const char *buf, size_t size) \
+
+#define SHOW_LOCKED(fn) \
+SHOW(fn) \
+{ \
+ ssize_t ret; \
+ mutex_lock(&bch_register_lock); \
+ ret = __ ## fn ## _show(kobj, attr, buf); \
+ mutex_unlock(&bch_register_lock); \
+ return ret; \
+}
+
+#define STORE_LOCKED(fn) \
+STORE(fn) \
+{ \
+ ssize_t ret; \
+ mutex_lock(&bch_register_lock); \
+ ret = __ ## fn ## _store(kobj, attr, buf, size); \
+ mutex_unlock(&bch_register_lock); \
+ return ret; \
+}
+
+#define __sysfs_attribute(_name, _mode) \
+ static struct attribute sysfs_##_name = \
+ { .name = #_name, .mode = _mode }
+
+#define write_attribute(n) __sysfs_attribute(n, S_IWUSR)
+#define read_attribute(n) __sysfs_attribute(n, S_IRUGO)
+#define rw_attribute(n) __sysfs_attribute(n, S_IRUGO|S_IWUSR)
+
+#define sysfs_printf(file, fmt, ...) \
+do { \
+ if (attr == &sysfs_ ## file) \
+ return snprintf(buf, PAGE_SIZE, fmt "\n", __VA_ARGS__); \
+} while (0)
+
+#define sysfs_print(file, var) \
+do { \
+ if (attr == &sysfs_ ## file) \
+ return snprint(buf, PAGE_SIZE, var); \
+} while (0)
+
+#define sysfs_hprint(file, val) \
+do { \
+ if (attr == &sysfs_ ## file) { \
+ ssize_t ret = bch_hprint(buf, val); \
+ strcat(buf, "\n"); \
+ return ret + 1; \
+ } \
+} while (0)
+
+#define var_printf(_var, fmt) sysfs_printf(_var, fmt, var(_var))
+#define var_print(_var) sysfs_print(_var, var(_var))
+#define var_hprint(_var) sysfs_hprint(_var, var(_var))
+
+#define sysfs_strtoul(file, var) \
+do { \
+ if (attr == &sysfs_ ## file) \
+ return strtoul_safe(buf, var) ?: (ssize_t) size; \
+} while (0)
+
+#define sysfs_strtoul_clamp(file, var, min, max) \
+do { \
+ if (attr == &sysfs_ ## file) \
+ return strtoul_safe_clamp(buf, var, min, max) \
+ ?: (ssize_t) size; \
+} while (0)
+
+#define strtoul_or_return(cp) \
+({ \
+ unsigned long _v; \
+ int _r = kstrtoul(cp, 10, &_v); \
+ if (_r) \
+ return _r; \
+ _v; \
+})
+
+#define strtoi_h_or_return(cp, v) \
+do { \
+ int _r = strtoi_h(cp, &v); \
+ if (_r) \
+ return _r; \
+} while (0)
+
+#define sysfs_hatoi(file, var) \
+do { \
+ if (attr == &sysfs_ ## file) \
+ return strtoi_h(buf, &var) ?: (ssize_t) size; \
+} while (0)
+
+#endif /* _BCACHE_SYSFS_H_ */
diff --git a/drivers/md/bcache/trace.c b/drivers/md/bcache/trace.c
new file mode 100644
index 000000000000..983f9bb411bc
--- /dev/null
+++ b/drivers/md/bcache/trace.c
@@ -0,0 +1,26 @@
+#include "bcache.h"
+#include "btree.h"
+#include "request.h"
+
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/bcache.h>
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_request_start);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_request_end);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_passthrough);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_cache_hit);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_cache_miss);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_read_retry);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_writethrough);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_writeback);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_write_skip);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_btree_read);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_btree_write);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_write_dirty);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_read_dirty);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_journal_write);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_cache_insert);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_gc_start);
+EXPORT_TRACEPOINT_SYMBOL_GPL(bcache_gc_end);
diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
new file mode 100644
index 000000000000..da3a99e85b1e
--- /dev/null
+++ b/drivers/md/bcache/util.c
@@ -0,0 +1,377 @@
+/*
+ * random utiility code, for bcache but in theory not specific to bcache
+ *
+ * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
+ * Copyright 2012 Google, Inc.
+ */
+
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/types.h>
+
+#include "util.h"
+
+#define simple_strtoint(c, end, base) simple_strtol(c, end, base)
+#define simple_strtouint(c, end, base) simple_strtoul(c, end, base)
+
+#define STRTO_H(name, type) \
+int bch_ ## name ## _h(const char *cp, type *res) \
+{ \
+ int u = 0; \
+ char *e; \
+ type i = simple_ ## name(cp, &e, 10); \
+ \
+ switch (tolower(*e)) { \
+ default: \
+ return -EINVAL; \
+ case 'y': \
+ case 'z': \
+ u++; \
+ case 'e': \
+ u++; \
+ case 'p': \
+ u++; \
+ case 't': \
+ u++; \
+ case 'g': \
+ u++; \
+ case 'm': \
+ u++; \
+ case 'k': \
+ u++; \
+ if (e++ == cp) \
+ return -EINVAL; \
+ case '\n': \
+ case '\0': \
+ if (*e == '\n') \
+ e++; \
+ } \
+ \
+ if (*e) \
+ return -EINVAL; \
+ \
+ while (u--) { \
+ if ((type) ~0 > 0 && \
+ (type) ~0 / 1024 <= i) \
+ return -EINVAL; \
+ if ((i > 0 && ANYSINT_MAX(type) / 1024 < i) || \
+ (i < 0 && -ANYSINT_MAX(type) / 1024 > i)) \
+ return -EINVAL; \
+ i *= 1024; \
+ } \
+ \
+ *res = i; \
+ return 0; \
+} \
+
+STRTO_H(strtoint, int)
+STRTO_H(strtouint, unsigned int)
+STRTO_H(strtoll, long long)
+STRTO_H(strtoull, unsigned long long)
+
+ssize_t bch_hprint(char *buf, int64_t v)
+{
+ static const char units[] = "?kMGTPEZY";
+ char dec[4] = "";
+ int u, t = 0;
+
+ for (u = 0; v >= 1024 || v <= -1024; u++) {
+ t = v & ~(~0 << 10);
+ v >>= 10;
+ }
+
+ if (!u)
+ return sprintf(buf, "%llu", v);
+
+ if (v < 100 && v > -100)
+ snprintf(dec, sizeof(dec), ".%i", t / 100);
+
+ return sprintf(buf, "%lli%s%c", v, dec, units[u]);
+}
+
+ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[],
+ size_t selected)
+{
+ char *out = buf;
+ size_t i;
+
+ for (i = 0; list[i]; i++)
+ out += snprintf(out, buf + size - out,
+ i == selected ? "[%s] " : "%s ", list[i]);
+
+ out[-1] = '\n';
+ return out - buf;
+}
+
+ssize_t bch_read_string_list(const char *buf, const char * const list[])
+{
+ size_t i;
+ char *s, *d = kstrndup(buf, PAGE_SIZE - 1, GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ s = strim(d);
+
+ for (i = 0; list[i]; i++)
+ if (!strcmp(list[i], s))
+ break;
+
+ kfree(d);
+
+ if (!list[i])
+ return -EINVAL;
+
+ return i;
+}
+
+bool bch_is_zero(const char *p, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ if (p[i])
+ return false;
+ return true;
+}
+
+int bch_parse_uuid(const char *s, char *uuid)
+{
+ size_t i, j, x;
+ memset(uuid, 0, 16);
+
+ for (i = 0, j = 0;
+ i < strspn(s, "-0123456789:ABCDEFabcdef") && j < 32;
+ i++) {
+ x = s[i] | 32;
+
+ switch (x) {
+ case '0'...'9':
+ x -= '0';
+ break;
+ case 'a'...'f':
+ x -= 'a' - 10;
+ break;
+ default:
+ continue;
+ }
+
+ if (!(j & 1))
+ x <<= 4;
+ uuid[j++ >> 1] |= x;
+ }
+ return i;
+}
+
+void bch_time_stats_update(struct time_stats *stats, uint64_t start_time)
+{
+ uint64_t now = local_clock();
+ uint64_t duration = time_after64(now, start_time)
+ ? now - start_time : 0;
+ uint64_t last = time_after64(now, stats->last)
+ ? now - stats->last : 0;
+
+ stats->max_duration = max(stats->max_duration, duration);
+
+ if (stats->last) {
+ ewma_add(stats->average_duration, duration, 8, 8);
+
+ if (stats->average_frequency)
+ ewma_add(stats->average_frequency, last, 8, 8);
+ else
+ stats->average_frequency = last << 8;
+ } else {
+ stats->average_duration = duration << 8;
+ }
+
+ stats->last = now ?: 1;
+}
+
+unsigned bch_next_delay(struct ratelimit *d, uint64_t done)
+{
+ uint64_t now = local_clock();
+
+ d->next += div_u64(done, d->rate);
+
+ return time_after64(d->next, now)
+ ? div_u64(d->next - now, NSEC_PER_SEC / HZ)
+ : 0;
+}
+
+void bch_bio_map(struct bio *bio, void *base)
+{
+ size_t size = bio->bi_size;
+ struct bio_vec *bv = bio->bi_io_vec;
+
+ BUG_ON(!bio->bi_size);
+ BUG_ON(bio->bi_vcnt);
+
+ bv->bv_offset = base ? ((unsigned long) base) % PAGE_SIZE : 0;
+ goto start;
+
+ for (; size; bio->bi_vcnt++, bv++) {
+ bv->bv_offset = 0;
+start: bv->bv_len = min_t(size_t, PAGE_SIZE - bv->bv_offset,
+ size);
+ if (base) {
+ bv->bv_page = is_vmalloc_addr(base)
+ ? vmalloc_to_page(base)
+ : virt_to_page(base);
+
+ base += bv->bv_len;
+ }
+
+ size -= bv->bv_len;
+ }
+}
+
+int bch_bio_alloc_pages(struct bio *bio, gfp_t gfp)
+{
+ int i;
+ struct bio_vec *bv;
+
+ bio_for_each_segment(bv, bio, i) {
+ bv->bv_page = alloc_page(gfp);
+ if (!bv->bv_page) {
+ while (bv-- != bio->bi_io_vec + bio->bi_idx)
+ __free_page(bv->bv_page);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group (Any
+ * use permitted, subject to terms of PostgreSQL license; see.)
+
+ * If we have a 64-bit integer type, then a 64-bit CRC looks just like the
+ * usual sort of implementation. (See Ross Williams' excellent introduction
+ * A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS, available from
+ * ftp://ftp.rocksoft.com/papers/crc_v3.txt or several other net sites.)
+ * If we have no working 64-bit type, then fake it with two 32-bit registers.
+ *
+ * The present implementation is a normal (not "reflected", in Williams'
+ * terms) 64-bit CRC, using initial all-ones register contents and a final
+ * bit inversion. The chosen polynomial is borrowed from the DLT1 spec
+ * (ECMA-182, available from http://www.ecma.ch/ecma1/STAND/ECMA-182.HTM):
+ *
+ * x^64 + x^62 + x^57 + x^55 + x^54 + x^53 + x^52 + x^47 + x^46 + x^45 +
+ * x^40 + x^39 + x^38 + x^37 + x^35 + x^33 + x^32 + x^31 + x^29 + x^27 +
+ * x^24 + x^23 + x^22 + x^21 + x^19 + x^17 + x^13 + x^12 + x^10 + x^9 +
+ * x^7 + x^4 + x + 1
+*/
+
+static const uint64_t crc_table[256] = {
+ 0x0000000000000000ULL, 0x42F0E1EBA9EA3693ULL, 0x85E1C3D753D46D26ULL,
+ 0xC711223CFA3E5BB5ULL, 0x493366450E42ECDFULL, 0x0BC387AEA7A8DA4CULL,
+ 0xCCD2A5925D9681F9ULL, 0x8E224479F47CB76AULL, 0x9266CC8A1C85D9BEULL,
+ 0xD0962D61B56FEF2DULL, 0x17870F5D4F51B498ULL, 0x5577EEB6E6BB820BULL,
+ 0xDB55AACF12C73561ULL, 0x99A54B24BB2D03F2ULL, 0x5EB4691841135847ULL,
+ 0x1C4488F3E8F96ED4ULL, 0x663D78FF90E185EFULL, 0x24CD9914390BB37CULL,
+ 0xE3DCBB28C335E8C9ULL, 0xA12C5AC36ADFDE5AULL, 0x2F0E1EBA9EA36930ULL,
+ 0x6DFEFF5137495FA3ULL, 0xAAEFDD6DCD770416ULL, 0xE81F3C86649D3285ULL,
+ 0xF45BB4758C645C51ULL, 0xB6AB559E258E6AC2ULL, 0x71BA77A2DFB03177ULL,
+ 0x334A9649765A07E4ULL, 0xBD68D2308226B08EULL, 0xFF9833DB2BCC861DULL,
+ 0x388911E7D1F2DDA8ULL, 0x7A79F00C7818EB3BULL, 0xCC7AF1FF21C30BDEULL,
+ 0x8E8A101488293D4DULL, 0x499B3228721766F8ULL, 0x0B6BD3C3DBFD506BULL,
+ 0x854997BA2F81E701ULL, 0xC7B97651866BD192ULL, 0x00A8546D7C558A27ULL,
+ 0x4258B586D5BFBCB4ULL, 0x5E1C3D753D46D260ULL, 0x1CECDC9E94ACE4F3ULL,
+ 0xDBFDFEA26E92BF46ULL, 0x990D1F49C77889D5ULL, 0x172F5B3033043EBFULL,
+ 0x55DFBADB9AEE082CULL, 0x92CE98E760D05399ULL, 0xD03E790CC93A650AULL,
+ 0xAA478900B1228E31ULL, 0xE8B768EB18C8B8A2ULL, 0x2FA64AD7E2F6E317ULL,
+ 0x6D56AB3C4B1CD584ULL, 0xE374EF45BF6062EEULL, 0xA1840EAE168A547DULL,
+ 0x66952C92ECB40FC8ULL, 0x2465CD79455E395BULL, 0x3821458AADA7578FULL,
+ 0x7AD1A461044D611CULL, 0xBDC0865DFE733AA9ULL, 0xFF3067B657990C3AULL,
+ 0x711223CFA3E5BB50ULL, 0x33E2C2240A0F8DC3ULL, 0xF4F3E018F031D676ULL,
+ 0xB60301F359DBE0E5ULL, 0xDA050215EA6C212FULL, 0x98F5E3FE438617BCULL,
+ 0x5FE4C1C2B9B84C09ULL, 0x1D14202910527A9AULL, 0x93366450E42ECDF0ULL,
+ 0xD1C685BB4DC4FB63ULL, 0x16D7A787B7FAA0D6ULL, 0x5427466C1E109645ULL,
+ 0x4863CE9FF6E9F891ULL, 0x0A932F745F03CE02ULL, 0xCD820D48A53D95B7ULL,
+ 0x8F72ECA30CD7A324ULL, 0x0150A8DAF8AB144EULL, 0x43A04931514122DDULL,
+ 0x84B16B0DAB7F7968ULL, 0xC6418AE602954FFBULL, 0xBC387AEA7A8DA4C0ULL,
+ 0xFEC89B01D3679253ULL, 0x39D9B93D2959C9E6ULL, 0x7B2958D680B3FF75ULL,
+ 0xF50B1CAF74CF481FULL, 0xB7FBFD44DD257E8CULL, 0x70EADF78271B2539ULL,
+ 0x321A3E938EF113AAULL, 0x2E5EB66066087D7EULL, 0x6CAE578BCFE24BEDULL,
+ 0xABBF75B735DC1058ULL, 0xE94F945C9C3626CBULL, 0x676DD025684A91A1ULL,
+ 0x259D31CEC1A0A732ULL, 0xE28C13F23B9EFC87ULL, 0xA07CF2199274CA14ULL,
+ 0x167FF3EACBAF2AF1ULL, 0x548F120162451C62ULL, 0x939E303D987B47D7ULL,
+ 0xD16ED1D631917144ULL, 0x5F4C95AFC5EDC62EULL, 0x1DBC74446C07F0BDULL,
+ 0xDAAD56789639AB08ULL, 0x985DB7933FD39D9BULL, 0x84193F60D72AF34FULL,
+ 0xC6E9DE8B7EC0C5DCULL, 0x01F8FCB784FE9E69ULL, 0x43081D5C2D14A8FAULL,
+ 0xCD2A5925D9681F90ULL, 0x8FDAB8CE70822903ULL, 0x48CB9AF28ABC72B6ULL,
+ 0x0A3B7B1923564425ULL, 0x70428B155B4EAF1EULL, 0x32B26AFEF2A4998DULL,
+ 0xF5A348C2089AC238ULL, 0xB753A929A170F4ABULL, 0x3971ED50550C43C1ULL,
+ 0x7B810CBBFCE67552ULL, 0xBC902E8706D82EE7ULL, 0xFE60CF6CAF321874ULL,
+ 0xE224479F47CB76A0ULL, 0xA0D4A674EE214033ULL, 0x67C58448141F1B86ULL,
+ 0x253565A3BDF52D15ULL, 0xAB1721DA49899A7FULL, 0xE9E7C031E063ACECULL,
+ 0x2EF6E20D1A5DF759ULL, 0x6C0603E6B3B7C1CAULL, 0xF6FAE5C07D3274CDULL,
+ 0xB40A042BD4D8425EULL, 0x731B26172EE619EBULL, 0x31EBC7FC870C2F78ULL,
+ 0xBFC9838573709812ULL, 0xFD39626EDA9AAE81ULL, 0x3A28405220A4F534ULL,
+ 0x78D8A1B9894EC3A7ULL, 0x649C294A61B7AD73ULL, 0x266CC8A1C85D9BE0ULL,
+ 0xE17DEA9D3263C055ULL, 0xA38D0B769B89F6C6ULL, 0x2DAF4F0F6FF541ACULL,
+ 0x6F5FAEE4C61F773FULL, 0xA84E8CD83C212C8AULL, 0xEABE6D3395CB1A19ULL,
+ 0x90C79D3FEDD3F122ULL, 0xD2377CD44439C7B1ULL, 0x15265EE8BE079C04ULL,
+ 0x57D6BF0317EDAA97ULL, 0xD9F4FB7AE3911DFDULL, 0x9B041A914A7B2B6EULL,
+ 0x5C1538ADB04570DBULL, 0x1EE5D94619AF4648ULL, 0x02A151B5F156289CULL,
+ 0x4051B05E58BC1E0FULL, 0x87409262A28245BAULL, 0xC5B073890B687329ULL,
+ 0x4B9237F0FF14C443ULL, 0x0962D61B56FEF2D0ULL, 0xCE73F427ACC0A965ULL,
+ 0x8C8315CC052A9FF6ULL, 0x3A80143F5CF17F13ULL, 0x7870F5D4F51B4980ULL,
+ 0xBF61D7E80F251235ULL, 0xFD913603A6CF24A6ULL, 0x73B3727A52B393CCULL,
+ 0x31439391FB59A55FULL, 0xF652B1AD0167FEEAULL, 0xB4A25046A88DC879ULL,
+ 0xA8E6D8B54074A6ADULL, 0xEA16395EE99E903EULL, 0x2D071B6213A0CB8BULL,
+ 0x6FF7FA89BA4AFD18ULL, 0xE1D5BEF04E364A72ULL, 0xA3255F1BE7DC7CE1ULL,
+ 0x64347D271DE22754ULL, 0x26C49CCCB40811C7ULL, 0x5CBD6CC0CC10FAFCULL,
+ 0x1E4D8D2B65FACC6FULL, 0xD95CAF179FC497DAULL, 0x9BAC4EFC362EA149ULL,
+ 0x158E0A85C2521623ULL, 0x577EEB6E6BB820B0ULL, 0x906FC95291867B05ULL,
+ 0xD29F28B9386C4D96ULL, 0xCEDBA04AD0952342ULL, 0x8C2B41A1797F15D1ULL,
+ 0x4B3A639D83414E64ULL, 0x09CA82762AAB78F7ULL, 0x87E8C60FDED7CF9DULL,
+ 0xC51827E4773DF90EULL, 0x020905D88D03A2BBULL, 0x40F9E43324E99428ULL,
+ 0x2CFFE7D5975E55E2ULL, 0x6E0F063E3EB46371ULL, 0xA91E2402C48A38C4ULL,
+ 0xEBEEC5E96D600E57ULL, 0x65CC8190991CB93DULL, 0x273C607B30F68FAEULL,
+ 0xE02D4247CAC8D41BULL, 0xA2DDA3AC6322E288ULL, 0xBE992B5F8BDB8C5CULL,
+ 0xFC69CAB42231BACFULL, 0x3B78E888D80FE17AULL, 0x7988096371E5D7E9ULL,
+ 0xF7AA4D1A85996083ULL, 0xB55AACF12C735610ULL, 0x724B8ECDD64D0DA5ULL,
+ 0x30BB6F267FA73B36ULL, 0x4AC29F2A07BFD00DULL, 0x08327EC1AE55E69EULL,
+ 0xCF235CFD546BBD2BULL, 0x8DD3BD16FD818BB8ULL, 0x03F1F96F09FD3CD2ULL,
+ 0x41011884A0170A41ULL, 0x86103AB85A2951F4ULL, 0xC4E0DB53F3C36767ULL,
+ 0xD8A453A01B3A09B3ULL, 0x9A54B24BB2D03F20ULL, 0x5D45907748EE6495ULL,
+ 0x1FB5719CE1045206ULL, 0x919735E51578E56CULL, 0xD367D40EBC92D3FFULL,
+ 0x1476F63246AC884AULL, 0x568617D9EF46BED9ULL, 0xE085162AB69D5E3CULL,
+ 0xA275F7C11F7768AFULL, 0x6564D5FDE549331AULL, 0x279434164CA30589ULL,
+ 0xA9B6706FB8DFB2E3ULL, 0xEB46918411358470ULL, 0x2C57B3B8EB0BDFC5ULL,
+ 0x6EA7525342E1E956ULL, 0x72E3DAA0AA188782ULL, 0x30133B4B03F2B111ULL,
+ 0xF7021977F9CCEAA4ULL, 0xB5F2F89C5026DC37ULL, 0x3BD0BCE5A45A6B5DULL,
+ 0x79205D0E0DB05DCEULL, 0xBE317F32F78E067BULL, 0xFCC19ED95E6430E8ULL,
+ 0x86B86ED5267CDBD3ULL, 0xC4488F3E8F96ED40ULL, 0x0359AD0275A8B6F5ULL,
+ 0x41A94CE9DC428066ULL, 0xCF8B0890283E370CULL, 0x8D7BE97B81D4019FULL,
+ 0x4A6ACB477BEA5A2AULL, 0x089A2AACD2006CB9ULL, 0x14DEA25F3AF9026DULL,
+ 0x562E43B4931334FEULL, 0x913F6188692D6F4BULL, 0xD3CF8063C0C759D8ULL,
+ 0x5DEDC41A34BBEEB2ULL, 0x1F1D25F19D51D821ULL, 0xD80C07CD676F8394ULL,
+ 0x9AFCE626CE85B507ULL,
+};
+
+uint64_t bch_crc64_update(uint64_t crc, const void *_data, size_t len)
+{
+ const unsigned char *data = _data;
+
+ while (len--) {
+ int i = ((int) (crc >> 56) ^ *data++) & 0xFF;
+ crc = crc_table[i] ^ (crc << 8);
+ }
+
+ return crc;
+}
+
+uint64_t bch_crc64(const void *data, size_t len)
+{
+ uint64_t crc = 0xffffffffffffffffULL;
+
+ crc = bch_crc64_update(crc, data, len);
+
+ return crc ^ 0xffffffffffffffffULL;
+}
diff --git a/drivers/md/bcache/util.h b/drivers/md/bcache/util.h
new file mode 100644
index 000000000000..577393e38c3a
--- /dev/null
+++ b/drivers/md/bcache/util.h
@@ -0,0 +1,589 @@
+
+#ifndef _BCACHE_UTIL_H
+#define _BCACHE_UTIL_H
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/llist.h>
+#include <linux/ratelimit.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+
+#include "closure.h"
+
+#define PAGE_SECTORS (PAGE_SIZE / 512)
+
+struct closure;
+
+#include <trace/events/bcache.h>
+
+#ifdef CONFIG_BCACHE_EDEBUG
+
+#define atomic_dec_bug(v) BUG_ON(atomic_dec_return(v) < 0)
+#define atomic_inc_bug(v, i) BUG_ON(atomic_inc_return(v) <= i)
+
+#else /* EDEBUG */
+
+#define atomic_dec_bug(v) atomic_dec(v)
+#define atomic_inc_bug(v, i) atomic_inc(v)
+
+#endif
+
+#define BITMASK(name, type, field, offset, size) \
+static inline uint64_t name(const type *k) \
+{ return (k->field >> offset) & ~(((uint64_t) ~0) << size); } \
+ \
+static inline void SET_##name(type *k, uint64_t v) \
+{ \
+ k->field &= ~(~((uint64_t) ~0 << size) << offset); \
+ k->field |= v << offset; \
+}
+
+#define DECLARE_HEAP(type, name) \
+ struct { \
+ size_t size, used; \
+ type *data; \
+ } name
+
+#define init_heap(heap, _size, gfp) \
+({ \
+ size_t _bytes; \
+ (heap)->used = 0; \
+ (heap)->size = (_size); \
+ _bytes = (heap)->size * sizeof(*(heap)->data); \
+ (heap)->data = NULL; \
+ if (_bytes < KMALLOC_MAX_SIZE) \
+ (heap)->data = kmalloc(_bytes, (gfp)); \
+ if ((!(heap)->data) && ((gfp) & GFP_KERNEL)) \
+ (heap)->data = vmalloc(_bytes); \
+ (heap)->data; \
+})
+
+#define free_heap(heap) \
+do { \
+ if (is_vmalloc_addr((heap)->data)) \
+ vfree((heap)->data); \
+ else \
+ kfree((heap)->data); \
+ (heap)->data = NULL; \
+} while (0)
+
+#define heap_swap(h, i, j) swap((h)->data[i], (h)->data[j])
+
+#define heap_sift(h, i, cmp) \
+do { \
+ size_t _r, _j = i; \
+ \
+ for (; _j * 2 + 1 < (h)->used; _j = _r) { \
+ _r = _j * 2 + 1; \
+ if (_r + 1 < (h)->used && \
+ cmp((h)->data[_r], (h)->data[_r + 1])) \
+ _r++; \
+ \
+ if (cmp((h)->data[_r], (h)->data[_j])) \
+ break; \
+ heap_swap(h, _r, _j); \
+ } \
+} while (0)
+
+#define heap_sift_down(h, i, cmp) \
+do { \
+ while (i) { \
+ size_t p = (i - 1) / 2; \
+ if (cmp((h)->data[i], (h)->data[p])) \
+ break; \
+ heap_swap(h, i, p); \
+ i = p; \
+ } \
+} while (0)
+
+#define heap_add(h, d, cmp) \
+({ \
+ bool _r = !heap_full(h); \
+ if (_r) { \
+ size_t _i = (h)->used++; \
+ (h)->data[_i] = d; \
+ \
+ heap_sift_down(h, _i, cmp); \
+ heap_sift(h, _i, cmp); \
+ } \
+ _r; \
+})
+
+#define heap_pop(h, d, cmp) \
+({ \
+ bool _r = (h)->used; \
+ if (_r) { \
+ (d) = (h)->data[0]; \
+ (h)->used--; \
+ heap_swap(h, 0, (h)->used); \
+ heap_sift(h, 0, cmp); \
+ } \
+ _r; \
+})
+
+#define heap_peek(h) ((h)->size ? (h)->data[0] : NULL)
+
+#define heap_full(h) ((h)->used == (h)->size)
+
+#define DECLARE_FIFO(type, name) \
+ struct { \
+ size_t front, back, size, mask; \
+ type *data; \
+ } name
+
+#define fifo_for_each(c, fifo, iter) \
+ for (iter = (fifo)->front; \
+ c = (fifo)->data[iter], iter != (fifo)->back; \
+ iter = (iter + 1) & (fifo)->mask)
+
+#define __init_fifo(fifo, gfp) \
+({ \
+ size_t _allocated_size, _bytes; \
+ BUG_ON(!(fifo)->size); \
+ \
+ _allocated_size = roundup_pow_of_two((fifo)->size + 1); \
+ _bytes = _allocated_size * sizeof(*(fifo)->data); \
+ \
+ (fifo)->mask = _allocated_size - 1; \
+ (fifo)->front = (fifo)->back = 0; \
+ (fifo)->data = NULL; \
+ \
+ if (_bytes < KMALLOC_MAX_SIZE) \
+ (fifo)->data = kmalloc(_bytes, (gfp)); \
+ if ((!(fifo)->data) && ((gfp) & GFP_KERNEL)) \
+ (fifo)->data = vmalloc(_bytes); \
+ (fifo)->data; \
+})
+
+#define init_fifo_exact(fifo, _size, gfp) \
+({ \
+ (fifo)->size = (_size); \
+ __init_fifo(fifo, gfp); \
+})
+
+#define init_fifo(fifo, _size, gfp) \
+({ \
+ (fifo)->size = (_size); \
+ if ((fifo)->size > 4) \
+ (fifo)->size = roundup_pow_of_two((fifo)->size) - 1; \
+ __init_fifo(fifo, gfp); \
+})
+
+#define free_fifo(fifo) \
+do { \
+ if (is_vmalloc_addr((fifo)->data)) \
+ vfree((fifo)->data); \
+ else \
+ kfree((fifo)->data); \
+ (fifo)->data = NULL; \
+} while (0)
+
+#define fifo_used(fifo) (((fifo)->back - (fifo)->front) & (fifo)->mask)
+#define fifo_free(fifo) ((fifo)->size - fifo_used(fifo))
+
+#define fifo_empty(fifo) (!fifo_used(fifo))
+#define fifo_full(fifo) (!fifo_free(fifo))
+
+#define fifo_front(fifo) ((fifo)->data[(fifo)->front])
+#define fifo_back(fifo) \
+ ((fifo)->data[((fifo)->back - 1) & (fifo)->mask])
+
+#define fifo_idx(fifo, p) (((p) - &fifo_front(fifo)) & (fifo)->mask)
+
+#define fifo_push_back(fifo, i) \
+({ \
+ bool _r = !fifo_full((fifo)); \
+ if (_r) { \
+ (fifo)->data[(fifo)->back++] = (i); \
+ (fifo)->back &= (fifo)->mask; \
+ } \
+ _r; \
+})
+
+#define fifo_pop_front(fifo, i) \
+({ \
+ bool _r = !fifo_empty((fifo)); \
+ if (_r) { \
+ (i) = (fifo)->data[(fifo)->front++]; \
+ (fifo)->front &= (fifo)->mask; \
+ } \
+ _r; \
+})
+
+#define fifo_push_front(fifo, i) \
+({ \
+ bool _r = !fifo_full((fifo)); \
+ if (_r) { \
+ --(fifo)->front; \
+ (fifo)->front &= (fifo)->mask; \
+ (fifo)->data[(fifo)->front] = (i); \
+ } \
+ _r; \
+})
+
+#define fifo_pop_back(fifo, i) \
+({ \
+ bool _r = !fifo_empty((fifo)); \
+ if (_r) { \
+ --(fifo)->back; \
+ (fifo)->back &= (fifo)->mask; \
+ (i) = (fifo)->data[(fifo)->back] \
+ } \
+ _r; \
+})
+
+#define fifo_push(fifo, i) fifo_push_back(fifo, (i))
+#define fifo_pop(fifo, i) fifo_pop_front(fifo, (i))
+
+#define fifo_swap(l, r) \
+do { \
+ swap((l)->front, (r)->front); \
+ swap((l)->back, (r)->back); \
+ swap((l)->size, (r)->size); \
+ swap((l)->mask, (r)->mask); \
+ swap((l)->data, (r)->data); \
+} while (0)
+
+#define fifo_move(dest, src) \
+do { \
+ typeof(*((dest)->data)) _t; \
+ while (!fifo_full(dest) && \
+ fifo_pop(src, _t)) \
+ fifo_push(dest, _t); \
+} while (0)
+
+/*
+ * Simple array based allocator - preallocates a number of elements and you can
+ * never allocate more than that, also has no locking.
+ *
+ * Handy because if you know you only need a fixed number of elements you don't
+ * have to worry about memory allocation failure, and sometimes a mempool isn't
+ * what you want.
+ *
+ * We treat the free elements as entries in a singly linked list, and the
+ * freelist as a stack - allocating and freeing push and pop off the freelist.
+ */
+
+#define DECLARE_ARRAY_ALLOCATOR(type, name, size) \
+ struct { \
+ type *freelist; \
+ type data[size]; \
+ } name
+
+#define array_alloc(array) \
+({ \
+ typeof((array)->freelist) _ret = (array)->freelist; \
+ \
+ if (_ret) \
+ (array)->freelist = *((typeof((array)->freelist) *) _ret);\
+ \
+ _ret; \
+})
+
+#define array_free(array, ptr) \
+do { \
+ typeof((array)->freelist) _ptr = ptr; \
+ \
+ *((typeof((array)->freelist) *) _ptr) = (array)->freelist; \
+ (array)->freelist = _ptr; \
+} while (0)
+
+#define array_allocator_init(array) \
+do { \
+ typeof((array)->freelist) _i; \
+ \
+ BUILD_BUG_ON(sizeof((array)->data[0]) < sizeof(void *)); \
+ (array)->freelist = NULL; \
+ \
+ for (_i = (array)->data; \
+ _i < (array)->data + ARRAY_SIZE((array)->data); \
+ _i++) \
+ array_free(array, _i); \
+} while (0)
+
+#define array_freelist_empty(array) ((array)->freelist == NULL)
+
+#define ANYSINT_MAX(t) \
+ ((((t) 1 << (sizeof(t) * 8 - 2)) - (t) 1) * (t) 2 + (t) 1)
+
+int bch_strtoint_h(const char *, int *);
+int bch_strtouint_h(const char *, unsigned int *);
+int bch_strtoll_h(const char *, long long *);
+int bch_strtoull_h(const char *, unsigned long long *);
+
+static inline int bch_strtol_h(const char *cp, long *res)
+{
+#if BITS_PER_LONG == 32
+ return bch_strtoint_h(cp, (int *) res);
+#else
+ return bch_strtoll_h(cp, (long long *) res);
+#endif
+}
+
+static inline int bch_strtoul_h(const char *cp, long *res)
+{
+#if BITS_PER_LONG == 32
+ return bch_strtouint_h(cp, (unsigned int *) res);
+#else
+ return bch_strtoull_h(cp, (unsigned long long *) res);
+#endif
+}
+
+#define strtoi_h(cp, res) \
+ (__builtin_types_compatible_p(typeof(*res), int) \
+ ? bch_strtoint_h(cp, (void *) res) \
+ : __builtin_types_compatible_p(typeof(*res), long) \
+ ? bch_strtol_h(cp, (void *) res) \
+ : __builtin_types_compatible_p(typeof(*res), long long) \
+ ? bch_strtoll_h(cp, (void *) res) \
+ : __builtin_types_compatible_p(typeof(*res), unsigned int) \
+ ? bch_strtouint_h(cp, (void *) res) \
+ : __builtin_types_compatible_p(typeof(*res), unsigned long) \
+ ? bch_strtoul_h(cp, (void *) res) \
+ : __builtin_types_compatible_p(typeof(*res), unsigned long long)\
+ ? bch_strtoull_h(cp, (void *) res) : -EINVAL)
+
+#define strtoul_safe(cp, var) \
+({ \
+ unsigned long _v; \
+ int _r = kstrtoul(cp, 10, &_v); \
+ if (!_r) \
+ var = _v; \
+ _r; \
+})
+
+#define strtoul_safe_clamp(cp, var, min, max) \
+({ \
+ unsigned long _v; \
+ int _r = kstrtoul(cp, 10, &_v); \
+ if (!_r) \
+ var = clamp_t(typeof(var), _v, min, max); \
+ _r; \
+})
+
+#define snprint(buf, size, var) \
+ snprintf(buf, size, \
+ __builtin_types_compatible_p(typeof(var), int) \
+ ? "%i\n" : \
+ __builtin_types_compatible_p(typeof(var), unsigned) \
+ ? "%u\n" : \
+ __builtin_types_compatible_p(typeof(var), long) \
+ ? "%li\n" : \
+ __builtin_types_compatible_p(typeof(var), unsigned long)\
+ ? "%lu\n" : \
+ __builtin_types_compatible_p(typeof(var), int64_t) \
+ ? "%lli\n" : \
+ __builtin_types_compatible_p(typeof(var), uint64_t) \
+ ? "%llu\n" : \
+ __builtin_types_compatible_p(typeof(var), const char *) \
+ ? "%s\n" : "%i\n", var)
+
+ssize_t bch_hprint(char *buf, int64_t v);
+
+bool bch_is_zero(const char *p, size_t n);
+int bch_parse_uuid(const char *s, char *uuid);
+
+ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[],
+ size_t selected);
+
+ssize_t bch_read_string_list(const char *buf, const char * const list[]);
+
+struct time_stats {
+ /*
+ * all fields are in nanoseconds, averages are ewmas stored left shifted
+ * by 8
+ */
+ uint64_t max_duration;
+ uint64_t average_duration;
+ uint64_t average_frequency;
+ uint64_t last;
+};
+
+void bch_time_stats_update(struct time_stats *stats, uint64_t time);
+
+#define NSEC_PER_ns 1L
+#define NSEC_PER_us NSEC_PER_USEC
+#define NSEC_PER_ms NSEC_PER_MSEC
+#define NSEC_PER_sec NSEC_PER_SEC
+
+#define __print_time_stat(stats, name, stat, units) \
+ sysfs_print(name ## _ ## stat ## _ ## units, \
+ div_u64((stats)->stat >> 8, NSEC_PER_ ## units))
+
+#define sysfs_print_time_stats(stats, name, \
+ frequency_units, \
+ duration_units) \
+do { \
+ __print_time_stat(stats, name, \
+ average_frequency, frequency_units); \
+ __print_time_stat(stats, name, \
+ average_duration, duration_units); \
+ __print_time_stat(stats, name, \
+ max_duration, duration_units); \
+ \
+ sysfs_print(name ## _last_ ## frequency_units, (stats)->last \
+ ? div_s64(local_clock() - (stats)->last, \
+ NSEC_PER_ ## frequency_units) \
+ : -1LL); \
+} while (0)
+
+#define sysfs_time_stats_attribute(name, \
+ frequency_units, \
+ duration_units) \
+read_attribute(name ## _average_frequency_ ## frequency_units); \
+read_attribute(name ## _average_duration_ ## duration_units); \
+read_attribute(name ## _max_duration_ ## duration_units); \
+read_attribute(name ## _last_ ## frequency_units)
+
+#define sysfs_time_stats_attribute_list(name, \
+ frequency_units, \
+ duration_units) \
+&sysfs_ ## name ## _average_frequency_ ## frequency_units, \
+&sysfs_ ## name ## _average_duration_ ## duration_units, \
+&sysfs_ ## name ## _max_duration_ ## duration_units, \
+&sysfs_ ## name ## _last_ ## frequency_units,
+
+#define ewma_add(ewma, val, weight, factor) \
+({ \
+ (ewma) *= (weight) - 1; \
+ (ewma) += (val) << factor; \
+ (ewma) /= (weight); \
+ (ewma) >> factor; \
+})
+
+struct ratelimit {
+ uint64_t next;
+ unsigned rate;
+};
+
+static inline void ratelimit_reset(struct ratelimit *d)
+{
+ d->next = local_clock();
+}
+
+unsigned bch_next_delay(struct ratelimit *d, uint64_t done);
+
+#define __DIV_SAFE(n, d, zero) \
+({ \
+ typeof(n) _n = (n); \
+ typeof(d) _d = (d); \
+ _d ? _n / _d : zero; \
+})
+
+#define DIV_SAFE(n, d) __DIV_SAFE(n, d, 0)
+
+#define container_of_or_null(ptr, type, member) \
+({ \
+ typeof(ptr) _ptr = ptr; \
+ _ptr ? container_of(_ptr, type, member) : NULL; \
+})
+
+#define RB_INSERT(root, new, member, cmp) \
+({ \
+ __label__ dup; \
+ struct rb_node **n = &(root)->rb_node, *parent = NULL; \
+ typeof(new) this; \
+ int res, ret = -1; \
+ \
+ while (*n) { \
+ parent = *n; \
+ this = container_of(*n, typeof(*(new)), member); \
+ res = cmp(new, this); \
+ if (!res) \
+ goto dup; \
+ n = res < 0 \
+ ? &(*n)->rb_left \
+ : &(*n)->rb_right; \
+ } \
+ \
+ rb_link_node(&(new)->member, parent, n); \
+ rb_insert_color(&(new)->member, root); \
+ ret = 0; \
+dup: \
+ ret; \
+})
+
+#define RB_SEARCH(root, search, member, cmp) \
+({ \
+ struct rb_node *n = (root)->rb_node; \
+ typeof(&(search)) this, ret = NULL; \
+ int res; \
+ \
+ while (n) { \
+ this = container_of(n, typeof(search), member); \
+ res = cmp(&(search), this); \
+ if (!res) { \
+ ret = this; \
+ break; \
+ } \
+ n = res < 0 \
+ ? n->rb_left \
+ : n->rb_right; \
+ } \
+ ret; \
+})
+
+#define RB_GREATER(root, search, member, cmp) \
+({ \
+ struct rb_node *n = (root)->rb_node; \
+ typeof(&(search)) this, ret = NULL; \
+ int res; \
+ \
+ while (n) { \
+ this = container_of(n, typeof(search), member); \
+ res = cmp(&(search), this); \
+ if (res < 0) { \
+ ret = this; \
+ n = n->rb_left; \
+ } else \
+ n = n->rb_right; \
+ } \
+ ret; \
+})
+
+#define RB_FIRST(root, type, member) \
+ container_of_or_null(rb_first(root), type, member)
+
+#define RB_LAST(root, type, member) \
+ container_of_or_null(rb_last(root), type, member)
+
+#define RB_NEXT(ptr, member) \
+ container_of_or_null(rb_next(&(ptr)->member), typeof(*ptr), member)
+
+#define RB_PREV(ptr, member) \
+ container_of_or_null(rb_prev(&(ptr)->member), typeof(*ptr), member)
+
+/* Does linear interpolation between powers of two */
+static inline unsigned fract_exp_two(unsigned x, unsigned fract_bits)
+{
+ unsigned fract = x & ~(~0 << fract_bits);
+
+ x >>= fract_bits;
+ x = 1 << x;
+ x += (x * fract) >> fract_bits;
+
+ return x;
+}
+
+#define bio_end(bio) ((bio)->bi_sector + bio_sectors(bio))
+
+void bch_bio_map(struct bio *bio, void *base);
+
+int bch_bio_alloc_pages(struct bio *bio, gfp_t gfp);
+
+static inline sector_t bdev_sectors(struct block_device *bdev)
+{
+ return bdev->bd_inode->i_size >> 9;
+}
+
+#define closure_bio_submit(bio, cl, dev) \
+do { \
+ closure_get(cl); \
+ bch_generic_make_request(bio, &(dev)->bio_split_hook); \
+} while (0)
+
+uint64_t bch_crc64_update(uint64_t, const void *, size_t);
+uint64_t bch_crc64(const void *, size_t);
+
+#endif /* _BCACHE_UTIL_H */
diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
new file mode 100644
index 000000000000..93e7e31a4bd3
--- /dev/null
+++ b/drivers/md/bcache/writeback.c
@@ -0,0 +1,414 @@
+/*
+ * background writeback - scan btree for dirty data and write it to the backing
+ * device
+ *
+ * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
+ * Copyright 2012 Google, Inc.
+ */
+
+#include "bcache.h"
+#include "btree.h"
+#include "debug.h"
+
+static struct workqueue_struct *dirty_wq;
+
+static void read_dirty(struct closure *);
+
+struct dirty_io {
+ struct closure cl;
+ struct cached_dev *dc;
+ struct bio bio;
+};
+
+/* Rate limiting */
+
+static void __update_writeback_rate(struct cached_dev *dc)
+{
+ struct cache_set *c = dc->disk.c;
+ uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size;
+ uint64_t cache_dirty_target =
+ div_u64(cache_sectors * dc->writeback_percent, 100);
+
+ int64_t target = div64_u64(cache_dirty_target * bdev_sectors(dc->bdev),
+ c->cached_dev_sectors);
+
+ /* PD controller */
+
+ int change = 0;
+ int64_t error;
+ int64_t dirty = atomic_long_read(&dc->disk.sectors_dirty);
+ int64_t derivative = dirty - dc->disk.sectors_dirty_last;
+
+ dc->disk.sectors_dirty_last = dirty;
+
+ derivative *= dc->writeback_rate_d_term;
+ derivative = clamp(derivative, -dirty, dirty);
+
+ derivative = ewma_add(dc->disk.sectors_dirty_derivative, derivative,
+ dc->writeback_rate_d_smooth, 0);
+
+ /* Avoid divide by zero */
+ if (!target)
+ goto out;
+
+ error = div64_s64((dirty + derivative - target) << 8, target);
+
+ change = div_s64((dc->writeback_rate.rate * error) >> 8,
+ dc->writeback_rate_p_term_inverse);
+
+ /* Don't increase writeback rate if the device isn't keeping up */
+ if (change > 0 &&
+ time_after64(local_clock(),
+ dc->writeback_rate.next + 10 * NSEC_PER_MSEC))
+ change = 0;
+
+ dc->writeback_rate.rate =
+ clamp_t(int64_t, dc->writeback_rate.rate + change,
+ 1, NSEC_PER_MSEC);
+out:
+ dc->writeback_rate_derivative = derivative;
+ dc->writeback_rate_change = change;
+ dc->writeback_rate_target = target;
+
+ schedule_delayed_work(&dc->writeback_rate_update,
+ dc->writeback_rate_update_seconds * HZ);
+}
+
+static void update_writeback_rate(struct work_struct *work)
+{
+ struct cached_dev *dc = container_of(to_delayed_work(work),
+ struct cached_dev,
+ writeback_rate_update);
+
+ down_read(&dc->writeback_lock);
+
+ if (atomic_read(&dc->has_dirty) &&
+ dc->writeback_percent)
+ __update_writeback_rate(dc);
+
+ up_read(&dc->writeback_lock);
+}
+
+static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors)
+{
+ if (atomic_read(&dc->disk.detaching) ||
+ !dc->writeback_percent)
+ return 0;
+
+ return bch_next_delay(&dc->writeback_rate, sectors * 10000000ULL);
+}
+
+/* Background writeback */
+
+static bool dirty_pred(struct keybuf *buf, struct bkey *k)
+{
+ return KEY_DIRTY(k);
+}
+
+static void dirty_init(struct keybuf_key *w)
+{
+ struct dirty_io *io = w->private;
+ struct bio *bio = &io->bio;
+
+ bio_init(bio);
+ if (!io->dc->writeback_percent)
+ bio_set_prio(bio, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
+
+ bio->bi_size = KEY_SIZE(&w->key) << 9;
+ bio->bi_max_vecs = DIV_ROUND_UP(KEY_SIZE(&w->key), PAGE_SECTORS);
+ bio->bi_private = w;
+ bio->bi_io_vec = bio->bi_inline_vecs;
+ bch_bio_map(bio, NULL);
+}
+
+static void refill_dirty(struct closure *cl)
+{
+ struct cached_dev *dc = container_of(cl, struct cached_dev,
+ writeback.cl);
+ struct keybuf *buf = &dc->writeback_keys;
+ bool searched_from_start = false;
+ struct bkey end = MAX_KEY;
+ SET_KEY_INODE(&end, dc->disk.id);
+
+ if (!atomic_read(&dc->disk.detaching) &&
+ !dc->writeback_running)
+ closure_return(cl);
+
+ down_write(&dc->writeback_lock);
+
+ if (!atomic_read(&dc->has_dirty)) {
+ SET_BDEV_STATE(&dc->sb, BDEV_STATE_CLEAN);
+ bch_write_bdev_super(dc, NULL);
+
+ up_write(&dc->writeback_lock);
+ closure_return(cl);
+ }
+
+ if (bkey_cmp(&buf->last_scanned, &end) >= 0) {
+ buf->last_scanned = KEY(dc->disk.id, 0, 0);
+ searched_from_start = true;
+ }
+
+ bch_refill_keybuf(dc->disk.c, buf, &end);
+
+ if (bkey_cmp(&buf->last_scanned, &end) >= 0 && searched_from_start) {
+ /* Searched the entire btree - delay awhile */
+
+ if (RB_EMPTY_ROOT(&buf->keys)) {
+ atomic_set(&dc->has_dirty, 0);
+ cached_dev_put(dc);
+ }
+
+ if (!atomic_read(&dc->disk.detaching))
+ closure_delay(&dc->writeback, dc->writeback_delay * HZ);
+ }
+
+ up_write(&dc->writeback_lock);
+
+ ratelimit_reset(&dc->writeback_rate);
+
+ /* Punt to workqueue only so we don't recurse and blow the stack */
+ continue_at(cl, read_dirty, dirty_wq);
+}
+
+void bch_writeback_queue(struct cached_dev *dc)
+{
+ if (closure_trylock(&dc->writeback.cl, &dc->disk.cl)) {
+ if (!atomic_read(&dc->disk.detaching))
+ closure_delay(&dc->writeback, dc->writeback_delay * HZ);
+
+ continue_at(&dc->writeback.cl, refill_dirty, dirty_wq);
+ }
+}
+
+void bch_writeback_add(struct cached_dev *dc, unsigned sectors)
+{
+ atomic_long_add(sectors, &dc->disk.sectors_dirty);
+
+ if (!atomic_read(&dc->has_dirty) &&
+ !atomic_xchg(&dc->has_dirty, 1)) {
+ atomic_inc(&dc->count);
+
+ if (BDEV_STATE(&dc->sb) != BDEV_STATE_DIRTY) {
+ SET_BDEV_STATE(&dc->sb, BDEV_STATE_DIRTY);
+ /* XXX: should do this synchronously */
+ bch_write_bdev_super(dc, NULL);
+ }
+
+ bch_writeback_queue(dc);
+
+ if (dc->writeback_percent)
+ schedule_delayed_work(&dc->writeback_rate_update,
+ dc->writeback_rate_update_seconds * HZ);
+ }
+}
+
+/* Background writeback - IO loop */
+
+static void dirty_io_destructor(struct closure *cl)
+{
+ struct dirty_io *io = container_of(cl, struct dirty_io, cl);
+ kfree(io);
+}
+
+static void write_dirty_finish(struct closure *cl)
+{
+ struct dirty_io *io = container_of(cl, struct dirty_io, cl);
+ struct keybuf_key *w = io->bio.bi_private;
+ struct cached_dev *dc = io->dc;
+ struct bio_vec *bv = bio_iovec_idx(&io->bio, io->bio.bi_vcnt);
+
+ while (bv-- != io->bio.bi_io_vec)
+ __free_page(bv->bv_page);
+
+ /* This is kind of a dumb way of signalling errors. */
+ if (KEY_DIRTY(&w->key)) {
+ unsigned i;
+ struct btree_op op;
+ bch_btree_op_init_stack(&op);
+
+ op.type = BTREE_REPLACE;
+ bkey_copy(&op.replace, &w->key);
+
+ SET_KEY_DIRTY(&w->key, false);
+ bch_keylist_add(&op.keys, &w->key);
+
+ for (i = 0; i < KEY_PTRS(&w->key); i++)
+ atomic_inc(&PTR_BUCKET(dc->disk.c, &w->key, i)->pin);
+
+ pr_debug("clearing %s", pkey(&w->key));
+ bch_btree_insert(&op, dc->disk.c);
+ closure_sync(&op.cl);
+
+ atomic_long_inc(op.insert_collision
+ ? &dc->disk.c->writeback_keys_failed
+ : &dc->disk.c->writeback_keys_done);
+ }
+
+ bch_keybuf_del(&dc->writeback_keys, w);
+ atomic_dec_bug(&dc->in_flight);
+
+ closure_wake_up(&dc->writeback_wait);
+
+ closure_return_with_destructor(cl, dirty_io_destructor);
+}
+
+static void dirty_endio(struct bio *bio, int error)
+{
+ struct keybuf_key *w = bio->bi_private;
+ struct dirty_io *io = w->private;
+
+ if (error)
+ SET_KEY_DIRTY(&w->key, false);
+
+ closure_put(&io->cl);
+}
+
+static void write_dirty(struct closure *cl)
+{
+ struct dirty_io *io = container_of(cl, struct dirty_io, cl);
+ struct keybuf_key *w = io->bio.bi_private;
+
+ dirty_init(w);
+ io->bio.bi_rw = WRITE;
+ io->bio.bi_sector = KEY_START(&w->key);
+ io->bio.bi_bdev = io->dc->bdev;
+ io->bio.bi_end_io = dirty_endio;
+
+ trace_bcache_write_dirty(&io->bio);
+ closure_bio_submit(&io->bio, cl, &io->dc->disk);
+
+ continue_at(cl, write_dirty_finish, dirty_wq);
+}
+
+static void read_dirty_endio(struct bio *bio, int error)
+{
+ struct keybuf_key *w = bio->bi_private;
+ struct dirty_io *io = w->private;
+
+ bch_count_io_errors(PTR_CACHE(io->dc->disk.c, &w->key, 0),
+ error, "reading dirty data from cache");
+
+ dirty_endio(bio, error);
+}
+
+static void read_dirty_submit(struct closure *cl)
+{
+ struct dirty_io *io = container_of(cl, struct dirty_io, cl);
+
+ trace_bcache_read_dirty(&io->bio);
+ closure_bio_submit(&io->bio, cl, &io->dc->disk);
+
+ continue_at(cl, write_dirty, dirty_wq);
+}
+
+static void read_dirty(struct closure *cl)
+{
+ struct cached_dev *dc = container_of(cl, struct cached_dev,
+ writeback.cl);
+ unsigned delay = writeback_delay(dc, 0);
+ struct keybuf_key *w;
+ struct dirty_io *io;
+
+ /*
+ * XXX: if we error, background writeback just spins. Should use some
+ * mempools.
+ */
+
+ while (1) {
+ w = bch_keybuf_next(&dc->writeback_keys);
+ if (!w)
+ break;
+
+ BUG_ON(ptr_stale(dc->disk.c, &w->key, 0));
+
+ if (delay > 0 &&
+ (KEY_START(&w->key) != dc->last_read ||
+ jiffies_to_msecs(delay) > 50)) {
+ w->private = NULL;
+
+ closure_delay(&dc->writeback, delay);
+ continue_at(cl, read_dirty, dirty_wq);
+ }
+
+ dc->last_read = KEY_OFFSET(&w->key);
+
+ io = kzalloc(sizeof(struct dirty_io) + sizeof(struct bio_vec)
+ * DIV_ROUND_UP(KEY_SIZE(&w->key), PAGE_SECTORS),
+ GFP_KERNEL);
+ if (!io)
+ goto err;
+
+ w->private = io;
+ io->dc = dc;
+
+ dirty_init(w);
+ io->bio.bi_sector = PTR_OFFSET(&w->key, 0);
+ io->bio.bi_bdev = PTR_CACHE(dc->disk.c,
+ &w->key, 0)->bdev;
+ io->bio.bi_rw = READ;
+ io->bio.bi_end_io = read_dirty_endio;
+
+ if (bch_bio_alloc_pages(&io->bio, GFP_KERNEL))
+ goto err_free;
+
+ pr_debug("%s", pkey(&w->key));
+
+ closure_call(&io->cl, read_dirty_submit, NULL, &dc->disk.cl);
+
+ delay = writeback_delay(dc, KEY_SIZE(&w->key));
+
+ atomic_inc(&dc->in_flight);
+
+ if (!closure_wait_event(&dc->writeback_wait, cl,
+ atomic_read(&dc->in_flight) < 64))
+ continue_at(cl, read_dirty, dirty_wq);
+ }
+
+ if (0) {
+err_free:
+ kfree(w->private);
+err:
+ bch_keybuf_del(&dc->writeback_keys, w);
+ }
+
+ refill_dirty(cl);
+}
+
+void bch_writeback_init_cached_dev(struct cached_dev *dc)
+{
+ closure_init_unlocked(&dc->writeback);
+ init_rwsem(&dc->writeback_lock);
+
+ bch_keybuf_init(&dc->writeback_keys, dirty_pred);
+
+ dc->writeback_metadata = true;
+ dc->writeback_running = true;
+ dc->writeback_percent = 10;
+ dc->writeback_delay = 30;
+ dc->writeback_rate.rate = 1024;
+
+ dc->writeback_rate_update_seconds = 30;
+ dc->writeback_rate_d_term = 16;
+ dc->writeback_rate_p_term_inverse = 64;
+ dc->writeback_rate_d_smooth = 8;
+
+ INIT_DELAYED_WORK(&dc->writeback_rate_update, update_writeback_rate);
+ schedule_delayed_work(&dc->writeback_rate_update,
+ dc->writeback_rate_update_seconds * HZ);
+}
+
+void bch_writeback_exit(void)
+{
+ if (dirty_wq)
+ destroy_workqueue(dirty_wq);
+}
+
+int __init bch_writeback_init(void)
+{
+ dirty_wq = create_singlethread_workqueue("bcache_writeback");
+ if (!dirty_wq)
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 13c15480d940..6d2d41ae9e32 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -858,8 +858,7 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone)
unsigned int i;
struct bio_vec *bv;
- for (i = 0; i < clone->bi_vcnt; i++) {
- bv = bio_iovec_idx(clone, i);
+ bio_for_each_segment_all(bv, clone, i) {
BUG_ON(!bv->bv_page);
mempool_free(bv->bv_page, cc->page_pool);
bv->bv_page = NULL;
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index d053098c6a91..699b5be68d31 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -458,7 +458,7 @@ static void map_region(struct dm_io_region *io, struct mirror *m,
{
io->bdev = m->dev->bdev;
io->sector = map_sector(m, bio);
- io->count = bio->bi_size >> 9;
+ io->count = bio_sectors(bio);
}
static void hold_bio(struct mirror_set *ms, struct bio *bio)
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index d8837d313f54..ea5e878a30b9 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -258,7 +258,7 @@ static int stripe_map_range(struct stripe_c *sc, struct bio *bio,
sector_t begin, end;
stripe_map_range_sector(sc, bio->bi_sector, target_stripe, &begin);
- stripe_map_range_sector(sc, bio->bi_sector + bio_sectors(bio),
+ stripe_map_range_sector(sc, bio_end_sector(bio),
target_stripe, &end);
if (begin < end) {
bio->bi_bdev = sc->stripe[target_stripe].dev->bdev;
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index a746f1d21c66..b948fd864d45 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -501,7 +501,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
return -EIO;
}
- if ((bio->bi_sector + bio_sectors(bio)) >>
+ if (bio_end_sector(bio) >>
(v->data_dev_block_bits - SECTOR_SHIFT) > v->data_blocks) {
DMERR_LIMIT("io out of range");
return -EIO;
@@ -519,7 +519,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
bio->bi_end_io = verity_end_io;
bio->bi_private = io;
- io->io_vec_size = bio->bi_vcnt - bio->bi_idx;
+ io->io_vec_size = bio_segments(bio);
if (io->io_vec_size < DM_VERITY_IO_VEC_INLINE)
io->io_vec = io->io_vec_inline;
else
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 9a0bdad9ad8f..d5370a94b2c1 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -339,7 +339,7 @@ out:
return md ? 0 : -ENXIO;
}
-static int dm_blk_close(struct gendisk *disk, fmode_t mode)
+static void dm_blk_close(struct gendisk *disk, fmode_t mode)
{
struct mapped_device *md = disk->private_data;
@@ -349,8 +349,6 @@ static int dm_blk_close(struct gendisk *disk, fmode_t mode)
dm_put(md);
spin_unlock(&_minor_lock);
-
- return 0;
}
int dm_open_count(struct mapped_device *md)
diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c
index 5e7dc772f5de..3193aefe982b 100644
--- a/drivers/md/faulty.c
+++ b/drivers/md/faulty.c
@@ -185,8 +185,7 @@ static void make_request(struct mddev *mddev, struct bio *bio)
return;
}
- if (check_sector(conf, bio->bi_sector, bio->bi_sector+(bio->bi_size>>9),
- WRITE))
+ if (check_sector(conf, bio->bi_sector, bio_end_sector(bio), WRITE))
failit = 1;
if (check_mode(conf, WritePersistent)) {
add_sector(conf, bio->bi_sector, WritePersistent);
@@ -196,8 +195,7 @@ static void make_request(struct mddev *mddev, struct bio *bio)
failit = 1;
} else {
/* read request */
- if (check_sector(conf, bio->bi_sector, bio->bi_sector + (bio->bi_size>>9),
- READ))
+ if (check_sector(conf, bio->bi_sector, bio_end_sector(bio), READ))
failit = 1;
if (check_mode(conf, ReadTransient))
failit = 1;
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 21014836bdbf..f03fabd2b37b 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -317,8 +317,7 @@ static void linear_make_request(struct mddev *mddev, struct bio *bio)
bio_io_error(bio);
return;
}
- if (unlikely(bio->bi_sector + (bio->bi_size >> 9) >
- tmp_dev->end_sector)) {
+ if (unlikely(bio_end_sector(bio) > tmp_dev->end_sector)) {
/* This bio crosses a device boundary, so we have to
* split it.
*/
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 4c74424c78b0..681d1099a2d5 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -197,21 +197,12 @@ void md_trim_bio(struct bio *bio, int offset, int size)
if (offset == 0 && size == bio->bi_size)
return;
- bio->bi_sector += offset;
- bio->bi_size = size;
- offset <<= 9;
clear_bit(BIO_SEG_VALID, &bio->bi_flags);
- while (bio->bi_idx < bio->bi_vcnt &&
- bio->bi_io_vec[bio->bi_idx].bv_len <= offset) {
- /* remove this whole bio_vec */
- offset -= bio->bi_io_vec[bio->bi_idx].bv_len;
- bio->bi_idx++;
- }
- if (bio->bi_idx < bio->bi_vcnt) {
- bio->bi_io_vec[bio->bi_idx].bv_offset += offset;
- bio->bi_io_vec[bio->bi_idx].bv_len -= offset;
- }
+ bio_advance(bio, offset << 9);
+
+ bio->bi_size = size;
+
/* avoid any complications with bi_idx being non-zero*/
if (bio->bi_idx) {
memmove(bio->bi_io_vec, bio->bi_io_vec+bio->bi_idx,
@@ -6674,15 +6665,13 @@ static int md_open(struct block_device *bdev, fmode_t mode)
return err;
}
-static int md_release(struct gendisk *disk, fmode_t mode)
+static void md_release(struct gendisk *disk, fmode_t mode)
{
struct mddev *mddev = disk->private_data;
BUG_ON(!mddev);
atomic_dec(&mddev->openers);
mddev_put(mddev);
-
- return 0;
}
static int md_media_changed(struct gendisk *disk)
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 0505452de8d6..fcf65e512cf5 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -502,11 +502,11 @@ static inline int is_io_in_chunk_boundary(struct mddev *mddev,
{
if (likely(is_power_of_2(chunk_sects))) {
return chunk_sects >= ((bio->bi_sector & (chunk_sects-1))
- + (bio->bi_size >> 9));
+ + bio_sectors(bio));
} else{
sector_t sector = bio->bi_sector;
return chunk_sects >= (sector_div(sector, chunk_sects)
- + (bio->bi_size >> 9));
+ + bio_sectors(bio));
}
}
@@ -527,8 +527,7 @@ static void raid0_make_request(struct mddev *mddev, struct bio *bio)
sector_t sector = bio->bi_sector;
struct bio_pair *bp;
/* Sanity check -- queue functions should prevent this happening */
- if ((bio->bi_vcnt != 1 && bio->bi_vcnt != 0) ||
- bio->bi_idx != 0)
+ if (bio_segments(bio) > 1)
goto bad_map;
/* This is a one page bio that upper layers
* refuse to split for us, so we need to split it.
@@ -567,7 +566,7 @@ bad_map:
printk("md/raid0:%s: make_request bug: can't convert block across chunks"
" or bigger than %dk %llu %d\n",
mdname(mddev), chunk_sects / 2,
- (unsigned long long)bio->bi_sector, bio->bi_size >> 10);
+ (unsigned long long)bio->bi_sector, bio_sectors(bio) / 2);
bio_io_error(bio);
return;
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 851023e2ba5d..55951182af73 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -92,7 +92,6 @@ static void r1bio_pool_free(void *r1_bio, void *data)
static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
{
struct pool_info *pi = data;
- struct page *page;
struct r1bio *r1_bio;
struct bio *bio;
int i, j;
@@ -122,14 +121,10 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
j = 1;
while(j--) {
bio = r1_bio->bios[j];
- for (i = 0; i < RESYNC_PAGES; i++) {
- page = alloc_page(gfp_flags);
- if (unlikely(!page))
- goto out_free_pages;
+ bio->bi_vcnt = RESYNC_PAGES;
- bio->bi_io_vec[i].bv_page = page;
- bio->bi_vcnt = i+1;
- }
+ if (bio_alloc_pages(bio, gfp_flags))
+ goto out_free_bio;
}
/* If not user-requests, copy the page pointers to all bios */
if (!test_bit(MD_RECOVERY_REQUESTED, &pi->mddev->recovery)) {
@@ -143,11 +138,6 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
return r1_bio;
-out_free_pages:
- for (j=0 ; j < pi->raid_disks; j++)
- for (i=0; i < r1_bio->bios[j]->bi_vcnt ; i++)
- put_page(r1_bio->bios[j]->bi_io_vec[i].bv_page);
- j = -1;
out_free_bio:
while (++j < pi->raid_disks)
bio_put(r1_bio->bios[j]);
@@ -267,7 +257,7 @@ static void raid_end_bio_io(struct r1bio *r1_bio)
(bio_data_dir(bio) == WRITE) ? "write" : "read",
(unsigned long long) bio->bi_sector,
(unsigned long long) bio->bi_sector +
- (bio->bi_size >> 9) - 1);
+ bio_sectors(bio) - 1);
call_bio_endio(r1_bio);
}
@@ -458,7 +448,7 @@ static void raid1_end_write_request(struct bio *bio, int error)
" %llu-%llu\n",
(unsigned long long) mbio->bi_sector,
(unsigned long long) mbio->bi_sector +
- (mbio->bi_size >> 9) - 1);
+ bio_sectors(mbio) - 1);
call_bio_endio(r1_bio);
}
}
@@ -925,7 +915,7 @@ static void alloc_behind_pages(struct bio *bio, struct r1bio *r1_bio)
if (unlikely(!bvecs))
return;
- bio_for_each_segment(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i) {
bvecs[i] = *bvec;
bvecs[i].bv_page = alloc_page(GFP_NOIO);
if (unlikely(!bvecs[i].bv_page))
@@ -1023,7 +1013,7 @@ static void make_request(struct mddev *mddev, struct bio * bio)
md_write_start(mddev, bio); /* wait on superblock update early */
if (bio_data_dir(bio) == WRITE &&
- bio->bi_sector + bio->bi_size/512 > mddev->suspend_lo &&
+ bio_end_sector(bio) > mddev->suspend_lo &&
bio->bi_sector < mddev->suspend_hi) {
/* As the suspend_* range is controlled by
* userspace, we want an interruptible
@@ -1034,7 +1024,7 @@ static void make_request(struct mddev *mddev, struct bio * bio)
flush_signals(current);
prepare_to_wait(&conf->wait_barrier,
&w, TASK_INTERRUPTIBLE);
- if (bio->bi_sector + bio->bi_size/512 <= mddev->suspend_lo ||
+ if (bio_end_sector(bio) <= mddev->suspend_lo ||
bio->bi_sector >= mddev->suspend_hi)
break;
schedule();
@@ -1054,7 +1044,7 @@ static void make_request(struct mddev *mddev, struct bio * bio)
r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
r1_bio->master_bio = bio;
- r1_bio->sectors = bio->bi_size >> 9;
+ r1_bio->sectors = bio_sectors(bio);
r1_bio->state = 0;
r1_bio->mddev = mddev;
r1_bio->sector = bio->bi_sector;
@@ -1132,7 +1122,7 @@ read_again:
r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
r1_bio->master_bio = bio;
- r1_bio->sectors = (bio->bi_size >> 9) - sectors_handled;
+ r1_bio->sectors = bio_sectors(bio) - sectors_handled;
r1_bio->state = 0;
r1_bio->mddev = mddev;
r1_bio->sector = bio->bi_sector + sectors_handled;
@@ -1289,14 +1279,10 @@ read_again:
struct bio_vec *bvec;
int j;
- /* Yes, I really want the '__' version so that
- * we clear any unused pointer in the io_vec, rather
- * than leave them unchanged. This is important
- * because when we come to free the pages, we won't
- * know the original bi_idx, so we just free
- * them all
+ /*
+ * We trimmed the bio, so _all is legit
*/
- __bio_for_each_segment(bvec, mbio, j, 0)
+ bio_for_each_segment_all(bvec, mbio, j)
bvec->bv_page = r1_bio->behind_bvecs[j].bv_page;
if (test_bit(WriteMostly, &conf->mirrors[i].rdev->flags))
atomic_inc(&r1_bio->behind_remaining);
@@ -1334,14 +1320,14 @@ read_again:
/* Mustn't call r1_bio_write_done before this next test,
* as it could result in the bio being freed.
*/
- if (sectors_handled < (bio->bi_size >> 9)) {
+ if (sectors_handled < bio_sectors(bio)) {
r1_bio_write_done(r1_bio);
/* We need another r1_bio. It has already been counted
* in bio->bi_phys_segments
*/
r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
r1_bio->master_bio = bio;
- r1_bio->sectors = (bio->bi_size >> 9) - sectors_handled;
+ r1_bio->sectors = bio_sectors(bio) - sectors_handled;
r1_bio->state = 0;
r1_bio->mddev = mddev;
r1_bio->sector = bio->bi_sector + sectors_handled;
@@ -1867,7 +1853,7 @@ static int process_checks(struct r1bio *r1_bio)
struct bio *sbio = r1_bio->bios[i];
int size;
- if (r1_bio->bios[i]->bi_end_io != end_sync_read)
+ if (sbio->bi_end_io != end_sync_read)
continue;
if (test_bit(BIO_UPTODATE, &sbio->bi_flags)) {
@@ -1892,16 +1878,15 @@ static int process_checks(struct r1bio *r1_bio)
continue;
}
/* fixup the bio for reuse */
+ bio_reset(sbio);
sbio->bi_vcnt = vcnt;
sbio->bi_size = r1_bio->sectors << 9;
- sbio->bi_idx = 0;
- sbio->bi_phys_segments = 0;
- sbio->bi_flags &= ~(BIO_POOL_MASK - 1);
- sbio->bi_flags |= 1 << BIO_UPTODATE;
- sbio->bi_next = NULL;
sbio->bi_sector = r1_bio->sector +
conf->mirrors[i].rdev->data_offset;
sbio->bi_bdev = conf->mirrors[i].rdev->bdev;
+ sbio->bi_end_io = end_sync_read;
+ sbio->bi_private = r1_bio;
+
size = sbio->bi_size;
for (j = 0; j < vcnt ; j++) {
struct bio_vec *bi;
@@ -1912,10 +1897,9 @@ static int process_checks(struct r1bio *r1_bio)
else
bi->bv_len = size;
size -= PAGE_SIZE;
- memcpy(page_address(bi->bv_page),
- page_address(pbio->bi_io_vec[j].bv_page),
- PAGE_SIZE);
}
+
+ bio_copy_data(sbio, pbio);
}
return 0;
}
@@ -1952,7 +1936,7 @@ static void sync_request_write(struct mddev *mddev, struct r1bio *r1_bio)
wbio->bi_rw = WRITE;
wbio->bi_end_io = end_sync_write;
atomic_inc(&r1_bio->remaining);
- md_sync_acct(conf->mirrors[i].rdev->bdev, wbio->bi_size >> 9);
+ md_sync_acct(conf->mirrors[i].rdev->bdev, bio_sectors(wbio));
generic_make_request(wbio);
}
@@ -2064,32 +2048,11 @@ static void fix_read_error(struct r1conf *conf, int read_disk,
}
}
-static void bi_complete(struct bio *bio, int error)
-{
- complete((struct completion *)bio->bi_private);
-}
-
-static int submit_bio_wait(int rw, struct bio *bio)
-{
- struct completion event;
- rw |= REQ_SYNC;
-
- init_completion(&event);
- bio->bi_private = &event;
- bio->bi_end_io = bi_complete;
- submit_bio(rw, bio);
- wait_for_completion(&event);
-
- return test_bit(BIO_UPTODATE, &bio->bi_flags);
-}
-
static int narrow_write_error(struct r1bio *r1_bio, int i)
{
struct mddev *mddev = r1_bio->mddev;
struct r1conf *conf = mddev->private;
struct md_rdev *rdev = conf->mirrors[i].rdev;
- int vcnt, idx;
- struct bio_vec *vec;
/* bio has the data to be written to device 'i' where
* we just recently had a write error.
@@ -2117,30 +2080,32 @@ static int narrow_write_error(struct r1bio *r1_bio, int i)
& ~(sector_t)(block_sectors - 1))
- sector;
- if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
- vcnt = r1_bio->behind_page_count;
- vec = r1_bio->behind_bvecs;
- idx = 0;
- while (vec[idx].bv_page == NULL)
- idx++;
- } else {
- vcnt = r1_bio->master_bio->bi_vcnt;
- vec = r1_bio->master_bio->bi_io_vec;
- idx = r1_bio->master_bio->bi_idx;
- }
while (sect_to_write) {
struct bio *wbio;
if (sectors > sect_to_write)
sectors = sect_to_write;
/* Write at 'sector' for 'sectors'*/
- wbio = bio_alloc_mddev(GFP_NOIO, vcnt, mddev);
- memcpy(wbio->bi_io_vec, vec, vcnt * sizeof(struct bio_vec));
- wbio->bi_sector = r1_bio->sector;
+ if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
+ unsigned vcnt = r1_bio->behind_page_count;
+ struct bio_vec *vec = r1_bio->behind_bvecs;
+
+ while (!vec->bv_page) {
+ vec++;
+ vcnt--;
+ }
+
+ wbio = bio_alloc_mddev(GFP_NOIO, vcnt, mddev);
+ memcpy(wbio->bi_io_vec, vec, vcnt * sizeof(struct bio_vec));
+
+ wbio->bi_vcnt = vcnt;
+ } else {
+ wbio = bio_clone_mddev(r1_bio->master_bio, GFP_NOIO, mddev);
+ }
+
wbio->bi_rw = WRITE;
- wbio->bi_vcnt = vcnt;
+ wbio->bi_sector = r1_bio->sector;
wbio->bi_size = r1_bio->sectors << 9;
- wbio->bi_idx = idx;
md_trim_bio(wbio, sector - r1_bio->sector, sectors);
wbio->bi_sector += rdev->data_offset;
@@ -2289,8 +2254,7 @@ read_more:
r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO);
r1_bio->master_bio = mbio;
- r1_bio->sectors = (mbio->bi_size >> 9)
- - sectors_handled;
+ r1_bio->sectors = bio_sectors(mbio) - sectors_handled;
r1_bio->state = 0;
set_bit(R1BIO_ReadError, &r1_bio->state);
r1_bio->mddev = mddev;
@@ -2464,18 +2428,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp
for (i = 0; i < conf->raid_disks * 2; i++) {
struct md_rdev *rdev;
bio = r1_bio->bios[i];
-
- /* take from bio_init */
- bio->bi_next = NULL;
- bio->bi_flags &= ~(BIO_POOL_MASK-1);
- bio->bi_flags |= 1 << BIO_UPTODATE;
- bio->bi_rw = READ;
- bio->bi_vcnt = 0;
- bio->bi_idx = 0;
- bio->bi_phys_segments = 0;
- bio->bi_size = 0;
- bio->bi_end_io = NULL;
- bio->bi_private = NULL;
+ bio_reset(bio);
rdev = rcu_dereference(conf->mirrors[i].rdev);
if (rdev == NULL ||
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 018741ba9310..59d4daa5f4c7 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1174,14 +1174,13 @@ static void make_request(struct mddev *mddev, struct bio * bio)
/* If this request crosses a chunk boundary, we need to
* split it. This will only happen for 1 PAGE (or less) requests.
*/
- if (unlikely((bio->bi_sector & chunk_mask) + (bio->bi_size >> 9)
+ if (unlikely((bio->bi_sector & chunk_mask) + bio_sectors(bio)
> chunk_sects
&& (conf->geo.near_copies < conf->geo.raid_disks
|| conf->prev.near_copies < conf->prev.raid_disks))) {
struct bio_pair *bp;
/* Sanity check -- queue functions should prevent this happening */
- if ((bio->bi_vcnt != 1 && bio->bi_vcnt != 0) ||
- bio->bi_idx != 0)
+ if (bio_segments(bio) > 1)
goto bad_map;
/* This is a one page bio that upper layers
* refuse to split for us, so we need to split it.
@@ -1214,7 +1213,7 @@ static void make_request(struct mddev *mddev, struct bio * bio)
bad_map:
printk("md/raid10:%s: make_request bug: can't convert block across chunks"
" or bigger than %dk %llu %d\n", mdname(mddev), chunk_sects/2,
- (unsigned long long)bio->bi_sector, bio->bi_size >> 10);
+ (unsigned long long)bio->bi_sector, bio_sectors(bio) / 2);
bio_io_error(bio);
return;
@@ -1229,7 +1228,7 @@ static void make_request(struct mddev *mddev, struct bio * bio)
*/
wait_barrier(conf);
- sectors = bio->bi_size >> 9;
+ sectors = bio_sectors(bio);
while (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
bio->bi_sector < conf->reshape_progress &&
bio->bi_sector + sectors > conf->reshape_progress) {
@@ -1331,8 +1330,7 @@ read_again:
r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
r10_bio->master_bio = bio;
- r10_bio->sectors = ((bio->bi_size >> 9)
- - sectors_handled);
+ r10_bio->sectors = bio_sectors(bio) - sectors_handled;
r10_bio->state = 0;
r10_bio->mddev = mddev;
r10_bio->sector = bio->bi_sector + sectors_handled;
@@ -1574,7 +1572,7 @@ retry_write:
* after checking if we need to go around again.
*/
- if (sectors_handled < (bio->bi_size >> 9)) {
+ if (sectors_handled < bio_sectors(bio)) {
one_write_done(r10_bio);
/* We need another r10_bio. It has already been counted
* in bio->bi_phys_segments.
@@ -1582,7 +1580,7 @@ retry_write:
r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
r10_bio->master_bio = bio;
- r10_bio->sectors = (bio->bi_size >> 9) - sectors_handled;
+ r10_bio->sectors = bio_sectors(bio) - sectors_handled;
r10_bio->mddev = mddev;
r10_bio->sector = bio->bi_sector + sectors_handled;
@@ -2084,13 +2082,10 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
* First we need to fixup bv_offset, bv_len and
* bi_vecs, as the read request might have corrupted these
*/
+ bio_reset(tbio);
+
tbio->bi_vcnt = vcnt;
tbio->bi_size = r10_bio->sectors << 9;
- tbio->bi_idx = 0;
- tbio->bi_phys_segments = 0;
- tbio->bi_flags &= ~(BIO_POOL_MASK - 1);
- tbio->bi_flags |= 1 << BIO_UPTODATE;
- tbio->bi_next = NULL;
tbio->bi_rw = WRITE;
tbio->bi_private = r10_bio;
tbio->bi_sector = r10_bio->devs[i].addr;
@@ -2108,7 +2103,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
d = r10_bio->devs[i].devnum;
atomic_inc(&conf->mirrors[d].rdev->nr_pending);
atomic_inc(&r10_bio->remaining);
- md_sync_acct(conf->mirrors[d].rdev->bdev, tbio->bi_size >> 9);
+ md_sync_acct(conf->mirrors[d].rdev->bdev, bio_sectors(tbio));
tbio->bi_sector += conf->mirrors[d].rdev->data_offset;
tbio->bi_bdev = conf->mirrors[d].rdev->bdev;
@@ -2133,7 +2128,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
d = r10_bio->devs[i].devnum;
atomic_inc(&r10_bio->remaining);
md_sync_acct(conf->mirrors[d].replacement->bdev,
- tbio->bi_size >> 9);
+ bio_sectors(tbio));
generic_make_request(tbio);
}
@@ -2259,13 +2254,13 @@ static void recovery_request_write(struct mddev *mddev, struct r10bio *r10_bio)
wbio2 = r10_bio->devs[1].repl_bio;
if (wbio->bi_end_io) {
atomic_inc(&conf->mirrors[d].rdev->nr_pending);
- md_sync_acct(conf->mirrors[d].rdev->bdev, wbio->bi_size >> 9);
+ md_sync_acct(conf->mirrors[d].rdev->bdev, bio_sectors(wbio));
generic_make_request(wbio);
}
if (wbio2 && wbio2->bi_end_io) {
atomic_inc(&conf->mirrors[d].replacement->nr_pending);
md_sync_acct(conf->mirrors[d].replacement->bdev,
- wbio2->bi_size >> 9);
+ bio_sectors(wbio2));
generic_make_request(wbio2);
}
}
@@ -2536,25 +2531,6 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10
}
}
-static void bi_complete(struct bio *bio, int error)
-{
- complete((struct completion *)bio->bi_private);
-}
-
-static int submit_bio_wait(int rw, struct bio *bio)
-{
- struct completion event;
- rw |= REQ_SYNC;
-
- init_completion(&event);
- bio->bi_private = &event;
- bio->bi_end_io = bi_complete;
- submit_bio(rw, bio);
- wait_for_completion(&event);
-
- return test_bit(BIO_UPTODATE, &bio->bi_flags);
-}
-
static int narrow_write_error(struct r10bio *r10_bio, int i)
{
struct bio *bio = r10_bio->master_bio;
@@ -2695,8 +2671,7 @@ read_more:
r10_bio = mempool_alloc(conf->r10bio_pool,
GFP_NOIO);
r10_bio->master_bio = mbio;
- r10_bio->sectors = (mbio->bi_size >> 9)
- - sectors_handled;
+ r10_bio->sectors = bio_sectors(mbio) - sectors_handled;
r10_bio->state = 0;
set_bit(R10BIO_ReadError,
&r10_bio->state);
@@ -3133,6 +3108,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
}
}
bio = r10_bio->devs[0].bio;
+ bio_reset(bio);
bio->bi_next = biolist;
biolist = bio;
bio->bi_private = r10_bio;
@@ -3157,6 +3133,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
rdev = mirror->rdev;
if (!test_bit(In_sync, &rdev->flags)) {
bio = r10_bio->devs[1].bio;
+ bio_reset(bio);
bio->bi_next = biolist;
biolist = bio;
bio->bi_private = r10_bio;
@@ -3185,6 +3162,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
if (rdev == NULL || bio == NULL ||
test_bit(Faulty, &rdev->flags))
break;
+ bio_reset(bio);
bio->bi_next = biolist;
biolist = bio;
bio->bi_private = r10_bio;
@@ -3283,7 +3261,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
r10_bio->devs[i].repl_bio->bi_end_io = NULL;
bio = r10_bio->devs[i].bio;
- bio->bi_end_io = NULL;
+ bio_reset(bio);
clear_bit(BIO_UPTODATE, &bio->bi_flags);
if (conf->mirrors[d].rdev == NULL ||
test_bit(Faulty, &conf->mirrors[d].rdev->flags))
@@ -3320,6 +3298,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
/* Need to set up for writing to the replacement */
bio = r10_bio->devs[i].repl_bio;
+ bio_reset(bio);
clear_bit(BIO_UPTODATE, &bio->bi_flags);
sector = r10_bio->devs[i].addr;
@@ -3353,17 +3332,6 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
}
}
- for (bio = biolist; bio ; bio=bio->bi_next) {
-
- bio->bi_flags &= ~(BIO_POOL_MASK - 1);
- if (bio->bi_end_io)
- bio->bi_flags |= 1 << BIO_UPTODATE;
- bio->bi_vcnt = 0;
- bio->bi_idx = 0;
- bio->bi_phys_segments = 0;
- bio->bi_size = 0;
- }
-
nr_sectors = 0;
if (sector_nr + max_sync < max_sector)
max_sector = sector_nr + max_sync;
@@ -4411,7 +4379,6 @@ read_more:
read_bio->bi_flags &= ~(BIO_POOL_MASK - 1);
read_bio->bi_flags |= 1 << BIO_UPTODATE;
read_bio->bi_vcnt = 0;
- read_bio->bi_idx = 0;
read_bio->bi_size = 0;
r10_bio->master_bio = read_bio;
r10_bio->read_slot = r10_bio->devs[r10_bio->read_slot].devnum;
@@ -4435,17 +4402,14 @@ read_more:
}
if (!rdev2 || test_bit(Faulty, &rdev2->flags))
continue;
+
+ bio_reset(b);
b->bi_bdev = rdev2->bdev;
b->bi_sector = r10_bio->devs[s/2].addr + rdev2->new_data_offset;
b->bi_private = r10_bio;
b->bi_end_io = end_reshape_write;
b->bi_rw = WRITE;
- b->bi_flags &= ~(BIO_POOL_MASK - 1);
- b->bi_flags |= 1 << BIO_UPTODATE;
b->bi_next = blist;
- b->bi_vcnt = 0;
- b->bi_idx = 0;
- b->bi_size = 0;
blist = b;
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 4a7be455d6d8..9359828ffe26 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -90,7 +90,7 @@ static inline struct hlist_head *stripe_hash(struct r5conf *conf, sector_t sect)
*/
static inline struct bio *r5_next_bio(struct bio *bio, sector_t sector)
{
- int sectors = bio->bi_size >> 9;
+ int sectors = bio_sectors(bio);
if (bio->bi_sector + sectors < sector + STRIPE_SECTORS)
return bio->bi_next;
else
@@ -569,14 +569,6 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
bi = &sh->dev[i].req;
rbi = &sh->dev[i].rreq; /* For writing to replacement */
- bi->bi_rw = rw;
- rbi->bi_rw = rw;
- if (rw & WRITE) {
- bi->bi_end_io = raid5_end_write_request;
- rbi->bi_end_io = raid5_end_write_request;
- } else
- bi->bi_end_io = raid5_end_read_request;
-
rcu_read_lock();
rrdev = rcu_dereference(conf->disks[i].replacement);
smp_mb(); /* Ensure that if rrdev is NULL, rdev won't be */
@@ -651,7 +643,14 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
set_bit(STRIPE_IO_STARTED, &sh->state);
+ bio_reset(bi);
bi->bi_bdev = rdev->bdev;
+ bi->bi_rw = rw;
+ bi->bi_end_io = (rw & WRITE)
+ ? raid5_end_write_request
+ : raid5_end_read_request;
+ bi->bi_private = sh;
+
pr_debug("%s: for %llu schedule op %ld on disc %d\n",
__func__, (unsigned long long)sh->sector,
bi->bi_rw, i);
@@ -665,12 +664,9 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
bi->bi_rw |= REQ_FLUSH;
- bi->bi_flags = 1 << BIO_UPTODATE;
- bi->bi_idx = 0;
bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
bi->bi_io_vec[0].bv_offset = 0;
bi->bi_size = STRIPE_SIZE;
- bi->bi_next = NULL;
if (rrdev)
set_bit(R5_DOUBLE_LOCKED, &sh->dev[i].flags);
@@ -687,7 +683,13 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
set_bit(STRIPE_IO_STARTED, &sh->state);
+ bio_reset(rbi);
rbi->bi_bdev = rrdev->bdev;
+ rbi->bi_rw = rw;
+ BUG_ON(!(rw & WRITE));
+ rbi->bi_end_io = raid5_end_write_request;
+ rbi->bi_private = sh;
+
pr_debug("%s: for %llu schedule op %ld on "
"replacement disc %d\n",
__func__, (unsigned long long)sh->sector,
@@ -699,12 +701,9 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
else
rbi->bi_sector = (sh->sector
+ rrdev->data_offset);
- rbi->bi_flags = 1 << BIO_UPTODATE;
- rbi->bi_idx = 0;
rbi->bi_io_vec[0].bv_len = STRIPE_SIZE;
rbi->bi_io_vec[0].bv_offset = 0;
rbi->bi_size = STRIPE_SIZE;
- rbi->bi_next = NULL;
if (conf->mddev->gendisk)
trace_block_bio_remap(bdev_get_queue(rbi->bi_bdev),
rbi, disk_devt(conf->mddev->gendisk),
@@ -2402,11 +2401,11 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
} else
bip = &sh->dev[dd_idx].toread;
while (*bip && (*bip)->bi_sector < bi->bi_sector) {
- if ((*bip)->bi_sector + ((*bip)->bi_size >> 9) > bi->bi_sector)
+ if (bio_end_sector(*bip) > bi->bi_sector)
goto overlap;
bip = & (*bip)->bi_next;
}
- if (*bip && (*bip)->bi_sector < bi->bi_sector + ((bi->bi_size)>>9))
+ if (*bip && (*bip)->bi_sector < bio_end_sector(bi))
goto overlap;
BUG_ON(*bip && bi->bi_next && (*bip) != bi->bi_next);
@@ -2422,8 +2421,8 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
sector < sh->dev[dd_idx].sector + STRIPE_SECTORS &&
bi && bi->bi_sector <= sector;
bi = r5_next_bio(bi, sh->dev[dd_idx].sector)) {
- if (bi->bi_sector + (bi->bi_size>>9) >= sector)
- sector = bi->bi_sector + (bi->bi_size>>9);
+ if (bio_end_sector(bi) >= sector)
+ sector = bio_end_sector(bi);
}
if (sector >= sh->dev[dd_idx].sector + STRIPE_SECTORS)
set_bit(R5_OVERWRITE, &sh->dev[dd_idx].flags);
@@ -3849,7 +3848,7 @@ static int in_chunk_boundary(struct mddev *mddev, struct bio *bio)
{
sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);
unsigned int chunk_sectors = mddev->chunk_sectors;
- unsigned int bio_sectors = bio->bi_size >> 9;
+ unsigned int bio_sectors = bio_sectors(bio);
if (mddev->new_chunk_sectors < mddev->chunk_sectors)
chunk_sectors = mddev->new_chunk_sectors;
@@ -3941,7 +3940,7 @@ static int bio_fits_rdev(struct bio *bi)
{
struct request_queue *q = bdev_get_queue(bi->bi_bdev);
- if ((bi->bi_size>>9) > queue_max_sectors(q))
+ if (bio_sectors(bi) > queue_max_sectors(q))
return 0;
blk_recount_segments(q, bi);
if (bi->bi_phys_segments > queue_max_segments(q))
@@ -3988,7 +3987,7 @@ static int chunk_aligned_read(struct mddev *mddev, struct bio * raid_bio)
0,
&dd_idx, NULL);
- end_sector = align_bi->bi_sector + (align_bi->bi_size >> 9);
+ end_sector = bio_end_sector(align_bi);
rcu_read_lock();
rdev = rcu_dereference(conf->disks[dd_idx].replacement);
if (!rdev || test_bit(Faulty, &rdev->flags) ||
@@ -4011,7 +4010,7 @@ static int chunk_aligned_read(struct mddev *mddev, struct bio * raid_bio)
align_bi->bi_flags &= ~(1 << BIO_SEG_VALID);
if (!bio_fits_rdev(align_bi) ||
- is_badblock(rdev, align_bi->bi_sector, align_bi->bi_size>>9,
+ is_badblock(rdev, align_bi->bi_sector, bio_sectors(align_bi),
&first_bad, &bad_sectors)) {
/* too big in some way, or has a known bad block */
bio_put(align_bi);
@@ -4273,7 +4272,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
}
logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1);
- last_sector = bi->bi_sector + (bi->bi_size>>9);
+ last_sector = bio_end_sector(bi);
bi->bi_next = NULL;
bi->bi_phys_segments = 1; /* over-loaded to count active stripes */
@@ -4739,7 +4738,7 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
logical_sector = raid_bio->bi_sector & ~((sector_t)STRIPE_SECTORS-1);
sector = raid5_compute_sector(conf, logical_sector,
0, &dd_idx, NULL);
- last_sector = raid_bio->bi_sector + (raid_bio->bi_size>>9);
+ last_sector = bio_end_sector(raid_bio);
for (; logical_sector < last_sector;
logical_sector += STRIPE_SECTORS,
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index f12b78dbce04..f4176ca3a794 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -204,7 +204,7 @@ static int mspro_block_bd_open(struct block_device *bdev, fmode_t mode)
}
-static int mspro_block_disk_release(struct gendisk *disk)
+static void mspro_block_disk_release(struct gendisk *disk)
{
struct mspro_block_data *msb = disk->private_data;
int disk_id = MINOR(disk_devt(disk)) >> MSPRO_BLOCK_PART_SHIFT;
@@ -224,13 +224,11 @@ static int mspro_block_disk_release(struct gendisk *disk)
}
mutex_unlock(&mspro_block_disk_lock);
-
- return 0;
}
-static int mspro_block_bd_release(struct gendisk *disk, fmode_t mode)
+static void mspro_block_bd_release(struct gendisk *disk, fmode_t mode)
{
- return mspro_block_disk_release(disk);
+ mspro_block_disk_release(disk);
}
static int mspro_block_bd_getgeo(struct block_device *bdev,
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index ffee6f781e30..dd239bdbfcb4 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -2235,10 +2235,10 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
}
/* do we need to support multiple segments? */
- if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
+ if (bio_segments(req->bio) > 1 || bio_segments(rsp->bio) > 1) {
printk(MYIOC_s_ERR_FMT "%s: multiple segments req %u %u, rsp %u %u\n",
- ioc->name, __func__, req->bio->bi_vcnt, blk_rq_bytes(req),
- rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
+ ioc->name, __func__, bio_segments(req->bio), blk_rq_bytes(req),
+ bio_segments(rsp->bio), blk_rq_bytes(rsp));
return -EINVAL;
}
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index 49e86aed2bc4..6fc3866965df 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -600,10 +600,8 @@ static int i2o_block_open(struct block_device *bdev, fmode_t mode)
*
* Unlock and unmount the media, and power down the device. Gets called if
* the block device is closed.
- *
- * Returns 0 on success or negative error code on failure.
*/
-static int i2o_block_release(struct gendisk *disk, fmode_t mode)
+static void i2o_block_release(struct gendisk *disk, fmode_t mode)
{
struct i2o_block_device *dev = disk->private_data;
u8 operation;
@@ -617,7 +615,7 @@ static int i2o_block_release(struct gendisk *disk, fmode_t mode)
* the TID no longer exists.
*/
if (!dev->i2o_dev)
- return 0;
+ return;
mutex_lock(&i2o_block_mutex);
i2o_block_device_flush(dev->i2o_dev);
@@ -631,8 +629,6 @@ static int i2o_block_release(struct gendisk *disk, fmode_t mode)
i2o_block_device_power(dev, operation);
mutex_unlock(&i2o_block_mutex);
-
- return 0;
}
static int i2o_block_getgeo(struct block_device *bdev, struct hd_geometry *geo)
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index b9d4984a5cbf..1b5c22a3b34d 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -300,14 +300,13 @@ static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
return ret;
}
-static int mmc_blk_release(struct gendisk *disk, fmode_t mode)
+static void mmc_blk_release(struct gendisk *disk, fmode_t mode)
{
struct mmc_blk_data *md = disk->private_data;
mutex_lock(&block_mutex);
mmc_blk_put(md);
mutex_unlock(&block_mutex);
- return 0;
}
static int
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 146a53bfab71..4278a1787d08 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -552,22 +552,6 @@ static const struct mmc_host_ops mxs_mmc_ops = {
.enable_sdio_irq = mxs_mmc_enable_sdio_irq,
};
-static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param)
-{
- struct mxs_mmc_host *host = param;
- struct mxs_ssp *ssp = &host->ssp;
-
- if (!mxs_dma_is_apbh(chan))
- return false;
-
- if (chan->chan_id != ssp->dma_channel)
- return false;
-
- chan->private = &ssp->dma_data;
-
- return true;
-}
-
static struct platform_device_id mxs_ssp_ids[] = {
{
.name = "imx23-mmc",
@@ -595,20 +579,17 @@ static int mxs_mmc_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct mxs_mmc_host *host;
struct mmc_host *mmc;
- struct resource *iores, *dmares;
+ struct resource *iores;
struct pinctrl *pinctrl;
- int ret = 0, irq_err, irq_dma;
- dma_cap_mask_t mask;
+ int ret = 0, irq_err;
struct regulator *reg_vmmc;
enum of_gpio_flags flags;
struct mxs_ssp *ssp;
u32 bus_width = 0;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
irq_err = platform_get_irq(pdev, 0);
- irq_dma = platform_get_irq(pdev, 1);
- if (!iores || irq_err < 0 || irq_dma < 0)
+ if (!iores || irq_err < 0)
return -EINVAL;
mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev);
@@ -624,23 +605,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
goto out_mmc_free;
}
- if (np) {
- ssp->devid = (enum mxs_ssp_id) of_id->data;
- /*
- * TODO: This is a temporary solution and should be changed
- * to use generic DMA binding later when the helpers get in.
- */
- ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
- &ssp->dma_channel);
- if (ret) {
- dev_err(mmc_dev(host->mmc),
- "failed to get dma channel\n");
- goto out_mmc_free;
- }
- } else {
- ssp->devid = pdev->id_entry->driver_data;
- ssp->dma_channel = dmares->start;
- }
+ ssp->devid = (enum mxs_ssp_id) of_id->data;
host->mmc = mmc;
host->sdio_irq_en = 0;
@@ -670,10 +635,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
mxs_mmc_reset(host);
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- ssp->dma_data.chan_irq = irq_dma;
- ssp->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host);
+ ssp->dmach = dma_request_slave_channel(&pdev->dev, "rx-tx");
if (!ssp->dmach) {
dev_err(mmc_dev(host->mmc),
"%s: failed to request dma\n", __func__);
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 5ad39bb5ab4c..5073cbc796d8 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -237,13 +237,12 @@ error_put:
return ret;
}
-static int blktrans_release(struct gendisk *disk, fmode_t mode)
+static void blktrans_release(struct gendisk *disk, fmode_t mode)
{
struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
- int ret = 0;
if (!dev)
- return ret;
+ return;
mutex_lock(&dev->lock);
@@ -254,13 +253,13 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode)
module_put(dev->tr->owner);
if (dev->mtd) {
- ret = dev->tr->release ? dev->tr->release(dev) : 0;
+ if (dev->tr->release)
+ dev->tr->release(dev);
__put_mtd_device(dev->mtd);
}
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
- return ret;
}
static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index 6c6d80736fad..2aef5dda522b 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -308,7 +308,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
return 0;
}
-static int mtdblock_release(struct mtd_blktrans_dev *mbd)
+static void mtdblock_release(struct mtd_blktrans_dev *mbd)
{
struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd);
@@ -333,8 +333,6 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
mutex_unlock(&mtdblks_lock);
pr_debug("ok\n");
-
- return 0;
}
static int mtdblock_flush(struct mtd_blktrans_dev *dev)
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index 717881a3d1b8..25ecfa1822a8 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -36,7 +36,6 @@
#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand"
#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch"
#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch"
-#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "gpmi-dma"
/* add our owner bbt descriptor */
static uint8_t scan_ff_pattern[] = { 0xff };
@@ -420,28 +419,6 @@ static void release_bch_irq(struct gpmi_nand_data *this)
free_irq(i, this);
}
-static bool gpmi_dma_filter(struct dma_chan *chan, void *param)
-{
- struct gpmi_nand_data *this = param;
- int dma_channel = (int)this->private;
-
- if (!mxs_dma_is_apbh(chan))
- return false;
- /*
- * only catch the GPMI dma channels :
- * for mx23 : MX23_DMA_GPMI0 ~ MX23_DMA_GPMI3
- * (These four channels share the same IRQ!)
- *
- * for mx28 : MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7
- * (These eight channels share the same IRQ!)
- */
- if (dma_channel == chan->chan_id) {
- chan->private = &this->dma_data;
- return true;
- }
- return false;
-}
-
static void release_dma_channels(struct gpmi_nand_data *this)
{
unsigned int i;
@@ -455,36 +432,10 @@ static void release_dma_channels(struct gpmi_nand_data *this)
static int acquire_dma_channels(struct gpmi_nand_data *this)
{
struct platform_device *pdev = this->pdev;
- struct resource *r_dma;
- struct device_node *dn;
- u32 dma_channel;
- int ret;
struct dma_chan *dma_chan;
- dma_cap_mask_t mask;
-
- /* dma channel, we only use the first one. */
- dn = pdev->dev.of_node;
- ret = of_property_read_u32(dn, "fsl,gpmi-dma-channel", &dma_channel);
- if (ret) {
- pr_err("unable to get DMA channel from dt.\n");
- goto acquire_err;
- }
- this->private = (void *)dma_channel;
-
- /* gpmi dma interrupt */
- r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- GPMI_NAND_DMA_INTERRUPT_RES_NAME);
- if (!r_dma) {
- pr_err("Can't get resource for DMA\n");
- goto acquire_err;
- }
- this->dma_data.chan_irq = r_dma->start;
/* request dma channel */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- dma_chan = dma_request_channel(mask, gpmi_dma_filter, this);
+ dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx");
if (!dma_chan) {
pr_err("Failed to request DMA channel.\n");
goto acquire_err;
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
index 072947731277..a7685e3a8748 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -20,7 +20,7 @@
#include <linux/mtd/nand.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
-#include <linux/fsl/mxs-dma.h>
+#include <linux/dmaengine.h>
#define GPMI_CLK_MAX 5 /* MX6Q needs five clocks */
struct resources {
@@ -180,7 +180,6 @@ struct gpmi_nand_data {
/* DMA channels */
#define DMA_CHANS 8
struct dma_chan *dma_chans[DMA_CHANS];
- struct mxs_dma_data dma_data;
enum dma_ops_type last_dma_type;
enum dma_ops_type dma_type;
struct completion dma_done;
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index 8dd6ba52404a..f9d5615c5727 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -1107,7 +1107,7 @@ static int sm_flush(struct mtd_blktrans_dev *dev)
}
/* outside interface: device is released */
-static int sm_release(struct mtd_blktrans_dev *dev)
+static void sm_release(struct mtd_blktrans_dev *dev)
{
struct sm_ftl *ftl = dev->priv;
@@ -1116,7 +1116,6 @@ static int sm_release(struct mtd_blktrans_dev *dev)
cancel_work_sync(&ftl->flush_work);
sm_cache_flush(ftl);
mutex_unlock(&ftl->mutex);
- return 0;
}
/* outside interface: get geometry */
diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c
index 1928e2001587..de570a8f8967 100644
--- a/drivers/net/ethernet/3com/3c59x.c
+++ b/drivers/net/ethernet/3com/3c59x.c
@@ -951,7 +951,7 @@ static int vortex_eisa_remove(struct device *device)
unregister_netdev(dev);
iowrite16(TotalReset|0x14, ioaddr + EL3_CMD);
- release_region(dev->base_addr, VORTEX_TOTAL_SIZE);
+ release_region(edev->base_addr, VORTEX_TOTAL_SIZE);
free_netdev(dev);
return 0;
diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c
index 40649a8bf390..6b0dc131b20e 100644
--- a/drivers/net/ethernet/broadcom/cnic.c
+++ b/drivers/net/ethernet/broadcom/cnic.c
@@ -4085,7 +4085,7 @@ static int cnic_cm_alloc_mem(struct cnic_dev *dev)
if (!cp->csk_tbl)
return -ENOMEM;
- port_id = random32();
+ port_id = prandom_u32();
port_id %= CNIC_LOCAL_PORT_RANGE;
if (cnic_init_id_tbl(&cp->csk_port_tbl, CNIC_LOCAL_PORT_RANGE,
CNIC_LOCAL_PORT_MIN, port_id)) {
@@ -4145,7 +4145,7 @@ static int cnic_cm_init_bnx2_hw(struct cnic_dev *dev)
{
u32 seed;
- seed = random32();
+ seed = prandom_u32();
cnic_ctx_wr(dev, 45, 0, seed);
return 0;
}
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index d44f65bac1d4..ceb4d43c132d 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -214,6 +214,7 @@ struct fec_enet_private {
struct clk *clk_ipg;
struct clk *clk_ahb;
+ struct clk *clk_enet_out;
struct clk *clk_ptp;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index b9748f14ea78..e25bf832e6b3 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1883,18 +1883,23 @@ fec_probe(struct platform_device *pdev)
goto failed_clk;
}
+ /* enet_out is optional, depends on board */
+ fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out");
+ if (IS_ERR(fep->clk_enet_out))
+ fep->clk_enet_out = NULL;
+
fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
fep->bufdesc_ex =
pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
if (IS_ERR(fep->clk_ptp)) {
- ret = PTR_ERR(fep->clk_ptp);
+ fep->clk_ptp = NULL;
fep->bufdesc_ex = 0;
}
clk_prepare_enable(fep->clk_ahb);
clk_prepare_enable(fep->clk_ipg);
- if (!IS_ERR(fep->clk_ptp))
- clk_prepare_enable(fep->clk_ptp);
+ clk_prepare_enable(fep->clk_enet_out);
+ clk_prepare_enable(fep->clk_ptp);
reg_phy = devm_regulator_get(&pdev->dev, "phy");
if (!IS_ERR(reg_phy)) {
@@ -1962,8 +1967,8 @@ failed_irq:
failed_regulator:
clk_disable_unprepare(fep->clk_ahb);
clk_disable_unprepare(fep->clk_ipg);
- if (!IS_ERR(fep->clk_ptp))
- clk_disable_unprepare(fep->clk_ptp);
+ clk_disable_unprepare(fep->clk_enet_out);
+ clk_disable_unprepare(fep->clk_ptp);
failed_pin:
failed_clk:
failed_ioremap:
@@ -1985,6 +1990,7 @@ fec_drv_remove(struct platform_device *pdev)
clk_disable_unprepare(fep->clk_ptp);
if (fep->ptp_clock)
ptp_clock_unregister(fep->ptp_clock);
+ clk_disable_unprepare(fep->clk_enet_out);
clk_disable_unprepare(fep->clk_ahb);
clk_disable_unprepare(fep->clk_ipg);
for (i = 0; i < FEC_IRQ_NUM; i++) {
@@ -2010,6 +2016,7 @@ fec_suspend(struct device *dev)
fec_stop(ndev);
netif_device_detach(ndev);
}
+ clk_disable_unprepare(fep->clk_enet_out);
clk_disable_unprepare(fep->clk_ahb);
clk_disable_unprepare(fep->clk_ipg);
@@ -2022,6 +2029,7 @@ fec_resume(struct device *dev)
struct net_device *ndev = dev_get_drvdata(dev);
struct fec_enet_private *fep = netdev_priv(ndev);
+ clk_prepare_enable(fep->clk_enet_out);
clk_prepare_enable(fep->clk_ahb);
clk_prepare_enable(fep->clk_ipg);
if (netif_running(ndev)) {
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 302d59401065..70fd55968844 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -1322,7 +1322,7 @@ static const struct net_device_ops ibmveth_netdev_ops = {
static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
{
- int rc, i;
+ int rc, i, mac_len;
struct net_device *netdev;
struct ibmveth_adapter *adapter;
unsigned char *mac_addr_p;
@@ -1332,11 +1332,19 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
dev->unit_address);
mac_addr_p = (unsigned char *)vio_get_attribute(dev, VETH_MAC_ADDR,
- NULL);
+ &mac_len);
if (!mac_addr_p) {
dev_err(&dev->dev, "Can't find VETH_MAC_ADDR attribute\n");
return -EINVAL;
}
+ /* Workaround for old/broken pHyp */
+ if (mac_len == 8)
+ mac_addr_p += 2;
+ else if (mac_len != 6) {
+ dev_err(&dev->dev, "VETH_MAC_ADDR attribute wrong len %d\n",
+ mac_len);
+ return -EINVAL;
+ }
mcastFilterSize_p = (unsigned int *)vio_get_attribute(dev,
VETH_MCAST_FILTER_SIZE, NULL);
@@ -1361,17 +1369,6 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
netif_napi_add(netdev, &adapter->napi, ibmveth_poll, 16);
- /*
- * Some older boxes running PHYP non-natively have an OF that returns
- * a 8-byte local-mac-address field (and the first 2 bytes have to be
- * ignored) while newer boxes' OF return a 6-byte field. Note that
- * IEEE 1275 specifies that local-mac-address must be a 6-byte field.
- * The RPA doc specifies that the first byte must be 10b, so we'll
- * just look for it to solve this 8 vs. 6 byte field issue
- */
- if ((*mac_addr_p & 0x3) != 0x02)
- mac_addr_p += 2;
-
adapter->mac_addr = 0;
memcpy(&adapter->mac_addr, mac_addr_p, 6);
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 82f1c84282db..ffbc08f56c40 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -600,7 +600,7 @@ static inline s32 __ew32_prepare(struct e1000_hw *hw)
s32 i = E1000_ICH_FWSM_PCIM2PCI_COUNT;
while ((er32(FWSM) & E1000_ICH_FWSM_PCIM2PCI) && --i)
- usleep_range(50, 100);
+ udelay(50);
return i;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index bcf4d118e98c..c9e6b62dd000 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -889,7 +889,7 @@ static int mlx4_en_flow_replace(struct net_device *dev,
.queue_mode = MLX4_NET_TRANS_Q_FIFO,
.exclusive = 0,
.allow_loopback = 1,
- .promisc_mode = MLX4_FS_PROMISC_NONE,
+ .promisc_mode = MLX4_FS_REGULAR,
};
rule.port = priv->port;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index a69a908614e6..b35f94700093 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -127,7 +127,7 @@ static void mlx4_en_filter_work(struct work_struct *work)
.queue_mode = MLX4_NET_TRANS_Q_LIFO,
.exclusive = 1,
.allow_loopback = 1,
- .promisc_mode = MLX4_FS_PROMISC_NONE,
+ .promisc_mode = MLX4_FS_REGULAR,
.port = priv->port,
.priority = MLX4_DOMAIN_RFS,
};
@@ -448,7 +448,7 @@ static int mlx4_en_uc_steer_add(struct mlx4_en_priv *priv,
.queue_mode = MLX4_NET_TRANS_Q_FIFO,
.exclusive = 0,
.allow_loopback = 1,
- .promisc_mode = MLX4_FS_PROMISC_NONE,
+ .promisc_mode = MLX4_FS_REGULAR,
.priority = MLX4_DOMAIN_NIC,
};
@@ -795,7 +795,7 @@ static void mlx4_en_set_promisc_mode(struct mlx4_en_priv *priv,
err = mlx4_flow_steer_promisc_add(mdev->dev,
priv->port,
priv->base_qpn,
- MLX4_FS_PROMISC_UPLINK);
+ MLX4_FS_ALL_DEFAULT);
if (err)
en_err(priv, "Failed enabling promiscuous mode\n");
priv->flags |= MLX4_EN_FLAG_MC_PROMISC;
@@ -858,7 +858,7 @@ static void mlx4_en_clear_promisc_mode(struct mlx4_en_priv *priv,
case MLX4_STEERING_MODE_DEVICE_MANAGED:
err = mlx4_flow_steer_promisc_remove(mdev->dev,
priv->port,
- MLX4_FS_PROMISC_UPLINK);
+ MLX4_FS_ALL_DEFAULT);
if (err)
en_err(priv, "Failed disabling promiscuous mode\n");
priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC;
@@ -919,7 +919,7 @@ static void mlx4_en_do_multicast(struct mlx4_en_priv *priv,
err = mlx4_flow_steer_promisc_add(mdev->dev,
priv->port,
priv->base_qpn,
- MLX4_FS_PROMISC_ALL_MULTI);
+ MLX4_FS_MC_DEFAULT);
break;
case MLX4_STEERING_MODE_B0:
@@ -942,7 +942,7 @@ static void mlx4_en_do_multicast(struct mlx4_en_priv *priv,
case MLX4_STEERING_MODE_DEVICE_MANAGED:
err = mlx4_flow_steer_promisc_remove(mdev->dev,
priv->port,
- MLX4_FS_PROMISC_ALL_MULTI);
+ MLX4_FS_MC_DEFAULT);
break;
case MLX4_STEERING_MODE_B0:
@@ -1621,10 +1621,10 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
MLX4_EN_FLAG_MC_PROMISC);
mlx4_flow_steer_promisc_remove(mdev->dev,
priv->port,
- MLX4_FS_PROMISC_UPLINK);
+ MLX4_FS_ALL_DEFAULT);
mlx4_flow_steer_promisc_remove(mdev->dev,
priv->port,
- MLX4_FS_PROMISC_ALL_MULTI);
+ MLX4_FS_MC_DEFAULT);
} else if (priv->flags & MLX4_EN_FLAG_PROMISC) {
priv->flags &= ~MLX4_EN_FLAG_PROMISC;
diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c
index 8e3123a1df88..6000342f9725 100644
--- a/drivers/net/ethernet/mellanox/mlx4/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/eq.c
@@ -497,8 +497,8 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
break;
case MLX4_EVENT_TYPE_SRQ_LIMIT:
- mlx4_warn(dev, "%s: MLX4_EVENT_TYPE_SRQ_LIMIT\n",
- __func__);
+ mlx4_dbg(dev, "%s: MLX4_EVENT_TYPE_SRQ_LIMIT\n",
+ __func__);
case MLX4_EVENT_TYPE_SRQ_CATAS_ERROR:
if (mlx4_is_master(dev)) {
/* forward only to slave owning the SRQ */
diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c
index ffc78d2cb0cf..f3e804f2a35f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mcg.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c
@@ -645,25 +645,37 @@ static int find_entry(struct mlx4_dev *dev, u8 port,
return err;
}
+static const u8 __promisc_mode[] = {
+ [MLX4_FS_REGULAR] = 0x0,
+ [MLX4_FS_ALL_DEFAULT] = 0x1,
+ [MLX4_FS_MC_DEFAULT] = 0x3,
+ [MLX4_FS_UC_SNIFFER] = 0x4,
+ [MLX4_FS_MC_SNIFFER] = 0x5,
+};
+
+int mlx4_map_sw_to_hw_steering_mode(struct mlx4_dev *dev,
+ enum mlx4_net_trans_promisc_mode flow_type)
+{
+ if (flow_type >= MLX4_FS_MODE_NUM || flow_type < 0) {
+ mlx4_err(dev, "Invalid flow type. type = %d\n", flow_type);
+ return -EINVAL;
+ }
+ return __promisc_mode[flow_type];
+}
+EXPORT_SYMBOL_GPL(mlx4_map_sw_to_hw_steering_mode);
+
static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl,
struct mlx4_net_trans_rule_hw_ctrl *hw)
{
- static const u8 __promisc_mode[] = {
- [MLX4_FS_PROMISC_NONE] = 0x0,
- [MLX4_FS_PROMISC_UPLINK] = 0x1,
- [MLX4_FS_PROMISC_FUNCTION_PORT] = 0x2,
- [MLX4_FS_PROMISC_ALL_MULTI] = 0x3,
- };
-
- u32 dw = 0;
-
- dw = ctrl->queue_mode == MLX4_NET_TRANS_Q_LIFO ? 1 : 0;
- dw |= ctrl->exclusive ? (1 << 2) : 0;
- dw |= ctrl->allow_loopback ? (1 << 3) : 0;
- dw |= __promisc_mode[ctrl->promisc_mode] << 8;
- dw |= ctrl->priority << 16;
-
- hw->ctrl = cpu_to_be32(dw);
+ u8 flags = 0;
+
+ flags = ctrl->queue_mode == MLX4_NET_TRANS_Q_LIFO ? 1 : 0;
+ flags |= ctrl->exclusive ? (1 << 2) : 0;
+ flags |= ctrl->allow_loopback ? (1 << 3) : 0;
+
+ hw->flags = flags;
+ hw->type = __promisc_mode[ctrl->promisc_mode];
+ hw->prio = cpu_to_be16(ctrl->priority);
hw->port = ctrl->port;
hw->qpn = cpu_to_be32(ctrl->qpn);
}
@@ -677,29 +689,51 @@ const u16 __sw_id_hw[] = {
[MLX4_NET_TRANS_RULE_ID_UDP] = 0xE006
};
+int mlx4_map_sw_to_hw_steering_id(struct mlx4_dev *dev,
+ enum mlx4_net_trans_rule_id id)
+{
+ if (id >= MLX4_NET_TRANS_RULE_NUM || id < 0) {
+ mlx4_err(dev, "Invalid network rule id. id = %d\n", id);
+ return -EINVAL;
+ }
+ return __sw_id_hw[id];
+}
+EXPORT_SYMBOL_GPL(mlx4_map_sw_to_hw_steering_id);
+
+static const int __rule_hw_sz[] = {
+ [MLX4_NET_TRANS_RULE_ID_ETH] =
+ sizeof(struct mlx4_net_trans_rule_hw_eth),
+ [MLX4_NET_TRANS_RULE_ID_IB] =
+ sizeof(struct mlx4_net_trans_rule_hw_ib),
+ [MLX4_NET_TRANS_RULE_ID_IPV6] = 0,
+ [MLX4_NET_TRANS_RULE_ID_IPV4] =
+ sizeof(struct mlx4_net_trans_rule_hw_ipv4),
+ [MLX4_NET_TRANS_RULE_ID_TCP] =
+ sizeof(struct mlx4_net_trans_rule_hw_tcp_udp),
+ [MLX4_NET_TRANS_RULE_ID_UDP] =
+ sizeof(struct mlx4_net_trans_rule_hw_tcp_udp)
+};
+
+int mlx4_hw_rule_sz(struct mlx4_dev *dev,
+ enum mlx4_net_trans_rule_id id)
+{
+ if (id >= MLX4_NET_TRANS_RULE_NUM || id < 0) {
+ mlx4_err(dev, "Invalid network rule id. id = %d\n", id);
+ return -EINVAL;
+ }
+
+ return __rule_hw_sz[id];
+}
+EXPORT_SYMBOL_GPL(mlx4_hw_rule_sz);
+
static int parse_trans_rule(struct mlx4_dev *dev, struct mlx4_spec_list *spec,
struct _rule_hw *rule_hw)
{
- static const size_t __rule_hw_sz[] = {
- [MLX4_NET_TRANS_RULE_ID_ETH] =
- sizeof(struct mlx4_net_trans_rule_hw_eth),
- [MLX4_NET_TRANS_RULE_ID_IB] =
- sizeof(struct mlx4_net_trans_rule_hw_ib),
- [MLX4_NET_TRANS_RULE_ID_IPV6] = 0,
- [MLX4_NET_TRANS_RULE_ID_IPV4] =
- sizeof(struct mlx4_net_trans_rule_hw_ipv4),
- [MLX4_NET_TRANS_RULE_ID_TCP] =
- sizeof(struct mlx4_net_trans_rule_hw_tcp_udp),
- [MLX4_NET_TRANS_RULE_ID_UDP] =
- sizeof(struct mlx4_net_trans_rule_hw_tcp_udp)
- };
- if (spec->id >= MLX4_NET_TRANS_RULE_NUM) {
- mlx4_err(dev, "Invalid network rule id. id = %d\n", spec->id);
+ if (mlx4_hw_rule_sz(dev, spec->id) < 0)
return -EINVAL;
- }
- memset(rule_hw, 0, __rule_hw_sz[spec->id]);
+ memset(rule_hw, 0, mlx4_hw_rule_sz(dev, spec->id));
rule_hw->id = cpu_to_be16(__sw_id_hw[spec->id]);
- rule_hw->size = __rule_hw_sz[spec->id] >> 2;
+ rule_hw->size = mlx4_hw_rule_sz(dev, spec->id) >> 2;
switch (spec->id) {
case MLX4_NET_TRANS_RULE_ID_ETH:
@@ -713,12 +747,12 @@ static int parse_trans_rule(struct mlx4_dev *dev, struct mlx4_spec_list *spec,
rule_hw->eth.ether_type_enable = 1;
rule_hw->eth.ether_type = spec->eth.ether_type;
}
- rule_hw->eth.vlan_id = spec->eth.vlan_id;
- rule_hw->eth.vlan_id_msk = spec->eth.vlan_id_msk;
+ rule_hw->eth.vlan_tag = spec->eth.vlan_id;
+ rule_hw->eth.vlan_tag_msk = spec->eth.vlan_id_msk;
break;
case MLX4_NET_TRANS_RULE_ID_IB:
- rule_hw->ib.qpn = spec->ib.r_qpn;
+ rule_hw->ib.l3_qpn = spec->ib.l3_qpn;
rule_hw->ib.qpn_mask = spec->ib.qpn_msk;
memcpy(&rule_hw->ib.dst_gid, &spec->ib.dst_gid, 16);
memcpy(&rule_hw->ib.dst_gid_msk, &spec->ib.dst_gid_msk, 16);
@@ -1136,7 +1170,7 @@ int mlx4_trans_to_dmfs_attach(struct mlx4_dev *dev, struct mlx4_qp *qp,
struct mlx4_net_trans_rule rule = {
.queue_mode = MLX4_NET_TRANS_Q_FIFO,
.exclusive = 0,
- .promisc_mode = MLX4_FS_PROMISC_NONE,
+ .promisc_mode = MLX4_FS_REGULAR,
.priority = MLX4_DOMAIN_NIC,
};
@@ -1229,11 +1263,10 @@ int mlx4_flow_steer_promisc_add(struct mlx4_dev *dev, u8 port,
u64 *regid_p;
switch (mode) {
- case MLX4_FS_PROMISC_UPLINK:
- case MLX4_FS_PROMISC_FUNCTION_PORT:
+ case MLX4_FS_ALL_DEFAULT:
regid_p = &dev->regid_promisc_array[port];
break;
- case MLX4_FS_PROMISC_ALL_MULTI:
+ case MLX4_FS_MC_DEFAULT:
regid_p = &dev->regid_allmulti_array[port];
break;
default:
@@ -1260,11 +1293,10 @@ int mlx4_flow_steer_promisc_remove(struct mlx4_dev *dev, u8 port,
u64 *regid_p;
switch (mode) {
- case MLX4_FS_PROMISC_UPLINK:
- case MLX4_FS_PROMISC_FUNCTION_PORT:
+ case MLX4_FS_ALL_DEFAULT:
regid_p = &dev->regid_promisc_array[port];
break;
- case MLX4_FS_PROMISC_ALL_MULTI:
+ case MLX4_FS_MC_DEFAULT:
regid_p = &dev->regid_allmulti_array[port];
break;
default:
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index eac3dae10efe..df15bb6631cc 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -730,85 +730,6 @@ struct mlx4_steer {
struct list_head steer_entries[MLX4_NUM_STEERS];
};
-struct mlx4_net_trans_rule_hw_ctrl {
- __be32 ctrl;
- u8 rsvd1;
- u8 funcid;
- u8 vep;
- u8 port;
- __be32 qpn;
- __be32 rsvd2;
-};
-
-struct mlx4_net_trans_rule_hw_ib {
- u8 size;
- u8 rsvd1;
- __be16 id;
- u32 rsvd2;
- __be32 qpn;
- __be32 qpn_mask;
- u8 dst_gid[16];
- u8 dst_gid_msk[16];
-} __packed;
-
-struct mlx4_net_trans_rule_hw_eth {
- u8 size;
- u8 rsvd;
- __be16 id;
- u8 rsvd1[6];
- u8 dst_mac[6];
- u16 rsvd2;
- u8 dst_mac_msk[6];
- u16 rsvd3;
- u8 src_mac[6];
- u16 rsvd4;
- u8 src_mac_msk[6];
- u8 rsvd5;
- u8 ether_type_enable;
- __be16 ether_type;
- __be16 vlan_id_msk;
- __be16 vlan_id;
-} __packed;
-
-struct mlx4_net_trans_rule_hw_tcp_udp {
- u8 size;
- u8 rsvd;
- __be16 id;
- __be16 rsvd1[3];
- __be16 dst_port;
- __be16 rsvd2;
- __be16 dst_port_msk;
- __be16 rsvd3;
- __be16 src_port;
- __be16 rsvd4;
- __be16 src_port_msk;
-} __packed;
-
-struct mlx4_net_trans_rule_hw_ipv4 {
- u8 size;
- u8 rsvd;
- __be16 id;
- __be32 rsvd1;
- __be32 dst_ip;
- __be32 dst_ip_msk;
- __be32 src_ip;
- __be32 src_ip_msk;
-} __packed;
-
-struct _rule_hw {
- union {
- struct {
- u8 size;
- u8 rsvd;
- __be16 id;
- };
- struct mlx4_net_trans_rule_hw_eth eth;
- struct mlx4_net_trans_rule_hw_ib ib;
- struct mlx4_net_trans_rule_hw_ipv4 ipv4;
- struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp;
- };
-};
-
enum {
MLX4_PCI_DEV_IS_VF = 1 << 0,
MLX4_PCI_DEV_FORCE_SENSE_PORT = 1 << 1,
diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c
index e329fe1f11b7..79fd269e2c54 100644
--- a/drivers/net/ethernet/mellanox/mlx4/srq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/srq.c
@@ -298,3 +298,18 @@ void mlx4_cleanup_srq_table(struct mlx4_dev *dev)
return;
mlx4_bitmap_cleanup(&mlx4_priv(dev)->srq_table.bitmap);
}
+
+struct mlx4_srq *mlx4_srq_lookup(struct mlx4_dev *dev, u32 srqn)
+{
+ struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table;
+ struct mlx4_srq *srq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&srq_table->lock, flags);
+ srq = radix_tree_lookup(&srq_table->tree,
+ srqn & (dev->caps.num_srqs - 1));
+ spin_unlock_irqrestore(&srq_table->lock, flags);
+
+ return srq;
+}
+EXPORT_SYMBOL_GPL(mlx4_srq_lookup);
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
index 49b8b58fc5c6..484f77ec2ce1 100644
--- a/drivers/net/hamradio/baycom_epp.c
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -449,7 +449,7 @@ static int transmit(struct baycom_state *bc, int cnt, unsigned char stat)
if ((--bc->hdlctx.slotcnt) > 0)
return 0;
bc->hdlctx.slotcnt = bc->ch_params.slottime;
- if ((random32() % 256) > bc->ch_params.ppersist)
+ if ((prandom_u32() % 256) > bc->ch_params.ppersist)
return 0;
}
}
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index a4a3516b6bbf..3169252613fa 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -389,7 +389,7 @@ void hdlcdrv_arbitrate(struct net_device *dev, struct hdlcdrv_state *s)
if ((--s->hdlctx.slotcnt) > 0)
return;
s->hdlctx.slotcnt = s->ch_params.slottime;
- if ((random32() % 256) > s->ch_params.ppersist)
+ if ((prandom_u32() % 256) > s->ch_params.ppersist)
return;
start_tx(dev, s);
}
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index b2d863f2ea42..0721e72f9299 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -638,7 +638,7 @@ static void yam_arbitrate(struct net_device *dev)
yp->slotcnt = yp->slot / 10;
/* is random > persist ? */
- if ((random32() % 256) > yp->pers)
+ if ((prandom_u32() % 256) > yp->pers)
return;
yam_start_tx(dev, yp);
diff --git a/drivers/net/team/team_mode_random.c b/drivers/net/team/team_mode_random.c
index 9eabfaa22f3e..5ca14d463ba7 100644
--- a/drivers/net/team/team_mode_random.c
+++ b/drivers/net/team/team_mode_random.c
@@ -18,7 +18,7 @@
static u32 random_N(unsigned int N)
{
- return reciprocal_divide(random32(), N);
+ return reciprocal_divide(prandom_u32(), N);
}
static bool rnd_transmit(struct team *team, struct sk_buff *skb)
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 7c769d8e25ad..287cc624b90b 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -93,6 +93,17 @@ config USB_RTL8150
To compile this driver as a module, choose M here: the
module will be called rtl8150.
+config USB_RTL8152
+ tristate "Realtek RTL8152 Based USB 2.0 Ethernet Adapters"
+ select NET_CORE
+ select MII
+ help
+ This option adds support for Realtek RTL8152 based USB 2.0
+ 10/100 Ethernet adapters.
+
+ To compile this driver as a module, choose M here: the
+ module will be called r8152.
+
config USB_USBNET
tristate "Multi-purpose USB Networking Framework"
select NET_CORE
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 119b06c9aa16..9ab5c9d4b45a 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_USB_CATC) += catc.o
obj-$(CONFIG_USB_KAWETH) += kaweth.o
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
obj-$(CONFIG_USB_RTL8150) += rtl8150.o
+obj-$(CONFIG_USB_RTL8152) += r8152.o
obj-$(CONFIG_USB_HSO) += hso.o
obj-$(CONFIG_USB_NET_AX8817X) += asix.o
asix-y := asix_devices.o asix_common.o ax88172a.o
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 4ff71d619cd8..24fbec27a22a 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -479,6 +479,7 @@ static const struct driver_info wwan_info = {
#define NOVATEL_VENDOR_ID 0x1410
#define ZTE_VENDOR_ID 0x19D2
#define DELL_VENDOR_ID 0x413C
+#define REALTEK_VENDOR_ID 0x0bda
static const struct usb_device_id products [] = {
/*
@@ -619,6 +620,15 @@ static const struct usb_device_id products [] = {
.driver_info = 0,
},
+/* Realtek RTL8152 Based USB 2.0 Ethernet Adapters */
+#if defined(CONFIG_USB_RTL8152) || defined(CONFIG_USB_RTL8152_MODULE)
+{
+ USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8152, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+#endif
+
/*
* WHITELIST!!!
*
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
new file mode 100644
index 000000000000..14e519888631
--- /dev/null
+++ b/drivers/net/usb/r8152.c
@@ -0,0 +1,1767 @@
+/*
+ * Copyright (c) 2013 Realtek Semiconductor Corp. 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/signal.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/if_vlan.h>
+#include <linux/uaccess.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v1.0.0 (2013/05/03)"
+#define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>"
+#define DRIVER_DESC "Realtek RTL8152 Based USB 2.0 Ethernet Adapters"
+#define MODULENAME "r8152"
+
+#define R8152_PHY_ID 32
+
+#define PLA_IDR 0xc000
+#define PLA_RCR 0xc010
+#define PLA_RMS 0xc016
+#define PLA_RXFIFO_CTRL0 0xc0a0
+#define PLA_RXFIFO_CTRL1 0xc0a4
+#define PLA_RXFIFO_CTRL2 0xc0a8
+#define PLA_FMC 0xc0b4
+#define PLA_CFG_WOL 0xc0b6
+#define PLA_MAR 0xcd00
+#define PAL_BDC_CR 0xd1a0
+#define PLA_LEDSEL 0xdd90
+#define PLA_LED_FEATURE 0xdd92
+#define PLA_PHYAR 0xde00
+#define PLA_GPHY_INTR_IMR 0xe022
+#define PLA_EEE_CR 0xe040
+#define PLA_EEEP_CR 0xe080
+#define PLA_MAC_PWR_CTRL 0xe0c0
+#define PLA_TCR0 0xe610
+#define PLA_TCR1 0xe612
+#define PLA_TXFIFO_CTRL 0xe618
+#define PLA_RSTTELLY 0xe800
+#define PLA_CR 0xe813
+#define PLA_CRWECR 0xe81c
+#define PLA_CONFIG5 0xe822
+#define PLA_PHY_PWR 0xe84c
+#define PLA_OOB_CTRL 0xe84f
+#define PLA_CPCR 0xe854
+#define PLA_MISC_0 0xe858
+#define PLA_MISC_1 0xe85a
+#define PLA_OCP_GPHY_BASE 0xe86c
+#define PLA_TELLYCNT 0xe890
+#define PLA_SFF_STS_7 0xe8de
+#define PLA_PHYSTATUS 0xe908
+#define PLA_BP_BA 0xfc26
+#define PLA_BP_0 0xfc28
+#define PLA_BP_1 0xfc2a
+#define PLA_BP_2 0xfc2c
+#define PLA_BP_3 0xfc2e
+#define PLA_BP_4 0xfc30
+#define PLA_BP_5 0xfc32
+#define PLA_BP_6 0xfc34
+#define PLA_BP_7 0xfc36
+
+#define USB_DEV_STAT 0xb808
+#define USB_USB_CTRL 0xd406
+#define USB_PHY_CTRL 0xd408
+#define USB_TX_AGG 0xd40a
+#define USB_RX_BUF_TH 0xd40c
+#define USB_USB_TIMER 0xd428
+#define USB_PM_CTRL_STATUS 0xd432
+#define USB_TX_DMA 0xd434
+#define USB_UPS_CTRL 0xd800
+#define USB_BP_BA 0xfc26
+#define USB_BP_0 0xfc28
+#define USB_BP_1 0xfc2a
+#define USB_BP_2 0xfc2c
+#define USB_BP_3 0xfc2e
+#define USB_BP_4 0xfc30
+#define USB_BP_5 0xfc32
+#define USB_BP_6 0xfc34
+#define USB_BP_7 0xfc36
+
+/* OCP Registers */
+#define OCP_ALDPS_CONFIG 0x2010
+#define OCP_EEE_CONFIG1 0x2080
+#define OCP_EEE_CONFIG2 0x2092
+#define OCP_EEE_CONFIG3 0x2094
+#define OCP_EEE_AR 0xa41a
+#define OCP_EEE_DATA 0xa41c
+
+/* PLA_RCR */
+#define RCR_AAP 0x00000001
+#define RCR_APM 0x00000002
+#define RCR_AM 0x00000004
+#define RCR_AB 0x00000008
+#define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB)
+
+/* PLA_RXFIFO_CTRL0 */
+#define RXFIFO_THR1_NORMAL 0x00080002
+#define RXFIFO_THR1_OOB 0x01800003
+
+/* PLA_RXFIFO_CTRL1 */
+#define RXFIFO_THR2_FULL 0x00000060
+#define RXFIFO_THR2_HIGH 0x00000038
+#define RXFIFO_THR2_OOB 0x0000004a
+
+/* PLA_RXFIFO_CTRL2 */
+#define RXFIFO_THR3_FULL 0x00000078
+#define RXFIFO_THR3_HIGH 0x00000048
+#define RXFIFO_THR3_OOB 0x0000005a
+
+/* PLA_TXFIFO_CTRL */
+#define TXFIFO_THR_NORMAL 0x00400008
+
+/* PLA_FMC */
+#define FMC_FCR_MCU_EN 0x0001
+
+/* PLA_EEEP_CR */
+#define EEEP_CR_EEEP_TX 0x0002
+
+/* PLA_TCR0 */
+#define TCR0_TX_EMPTY 0x0800
+#define TCR0_AUTO_FIFO 0x0080
+
+/* PLA_TCR1 */
+#define VERSION_MASK 0x7cf0
+
+/* PLA_CR */
+#define CR_RST 0x10
+#define CR_RE 0x08
+#define CR_TE 0x04
+
+/* PLA_CRWECR */
+#define CRWECR_NORAML 0x00
+#define CRWECR_CONFIG 0xc0
+
+/* PLA_OOB_CTRL */
+#define NOW_IS_OOB 0x80
+#define TXFIFO_EMPTY 0x20
+#define RXFIFO_EMPTY 0x10
+#define LINK_LIST_READY 0x02
+#define DIS_MCU_CLROOB 0x01
+#define FIFO_EMPTY (TXFIFO_EMPTY | RXFIFO_EMPTY)
+
+/* PLA_MISC_1 */
+#define RXDY_GATED_EN 0x0008
+
+/* PLA_SFF_STS_7 */
+#define RE_INIT_LL 0x8000
+#define MCU_BORW_EN 0x4000
+
+/* PLA_CPCR */
+#define CPCR_RX_VLAN 0x0040
+
+/* PLA_CFG_WOL */
+#define MAGIC_EN 0x0001
+
+/* PAL_BDC_CR */
+#define ALDPS_PROXY_MODE 0x0001
+
+/* PLA_CONFIG5 */
+#define LAN_WAKE_EN 0x0002
+
+/* PLA_LED_FEATURE */
+#define LED_MODE_MASK 0x0700
+
+/* PLA_PHY_PWR */
+#define TX_10M_IDLE_EN 0x0080
+#define PFM_PWM_SWITCH 0x0040
+
+/* PLA_MAC_PWR_CTRL */
+#define D3_CLK_GATED_EN 0x00004000
+#define MCU_CLK_RATIO 0x07010f07
+#define MCU_CLK_RATIO_MASK 0x0f0f0f0f
+
+/* PLA_GPHY_INTR_IMR */
+#define GPHY_STS_MSK 0x0001
+#define SPEED_DOWN_MSK 0x0002
+#define SPDWN_RXDV_MSK 0x0004
+#define SPDWN_LINKCHG_MSK 0x0008
+
+/* PLA_PHYAR */
+#define PHYAR_FLAG 0x80000000
+
+/* PLA_EEE_CR */
+#define EEE_RX_EN 0x0001
+#define EEE_TX_EN 0x0002
+
+/* USB_DEV_STAT */
+#define STAT_SPEED_MASK 0x0006
+#define STAT_SPEED_HIGH 0x0000
+#define STAT_SPEED_FULL 0x0001
+
+/* USB_TX_AGG */
+#define TX_AGG_MAX_THRESHOLD 0x03
+
+/* USB_RX_BUF_TH */
+#define RX_BUF_THR 0x7a120180
+
+/* USB_TX_DMA */
+#define TEST_MODE_DISABLE 0x00000001
+#define TX_SIZE_ADJUST1 0x00000100
+
+/* USB_UPS_CTRL */
+#define POWER_CUT 0x0100
+
+/* USB_PM_CTRL_STATUS */
+#define RWSUME_INDICATE 0x0001
+
+/* USB_USB_CTRL */
+#define RX_AGG_DISABLE 0x0010
+
+/* OCP_ALDPS_CONFIG */
+#define ENPWRSAVE 0x8000
+#define ENPDNPS 0x0200
+#define LINKENA 0x0100
+#define DIS_SDSAVE 0x0010
+
+/* OCP_EEE_CONFIG1 */
+#define RG_TXLPI_MSK_HFDUP 0x8000
+#define RG_MATCLR_EN 0x4000
+#define EEE_10_CAP 0x2000
+#define EEE_NWAY_EN 0x1000
+#define TX_QUIET_EN 0x0200
+#define RX_QUIET_EN 0x0100
+#define SDRISETIME 0x0010 /* bit 4 ~ 6 */
+#define RG_RXLPI_MSK_HFDUP 0x0008
+#define SDFALLTIME 0x0007 /* bit 0 ~ 2 */
+
+/* OCP_EEE_CONFIG2 */
+#define RG_LPIHYS_NUM 0x7000 /* bit 12 ~ 15 */
+#define RG_DACQUIET_EN 0x0400
+#define RG_LDVQUIET_EN 0x0200
+#define RG_CKRSEL 0x0020
+#define RG_EEEPRG_EN 0x0010
+
+/* OCP_EEE_CONFIG3 */
+#define FST_SNR_EYE_R 0x1500 /* bit 7 ~ 15 */
+#define RG_LFS_SEL 0x0060 /* bit 6 ~ 5 */
+#define MSK_PH 0x0006 /* bit 0 ~ 3 */
+
+/* OCP_EEE_AR */
+/* bit[15:14] function */
+#define FUN_ADDR 0x0000
+#define FUN_DATA 0x4000
+/* bit[4:0] device addr */
+#define DEVICE_ADDR 0x0007
+
+/* OCP_EEE_DATA */
+#define EEE_ADDR 0x003C
+#define EEE_DATA 0x0002
+
+enum rtl_register_content {
+ _100bps = 0x08,
+ _10bps = 0x04,
+ LINK_STATUS = 0x02,
+ FULL_DUP = 0x01,
+};
+
+#define RTL8152_REQT_READ 0xc0
+#define RTL8152_REQT_WRITE 0x40
+#define RTL8152_REQ_GET_REGS 0x05
+#define RTL8152_REQ_SET_REGS 0x05
+
+#define BYTE_EN_DWORD 0xff
+#define BYTE_EN_WORD 0x33
+#define BYTE_EN_BYTE 0x11
+#define BYTE_EN_SIX_BYTES 0x3f
+#define BYTE_EN_START_MASK 0x0f
+#define BYTE_EN_END_MASK 0xf0
+
+#define RTL8152_RMS (VLAN_ETH_FRAME_LEN + VLAN_HLEN)
+#define RTL8152_TX_TIMEOUT (HZ)
+
+/* rtl8152 flags */
+enum rtl8152_flags {
+ RTL8152_UNPLUG = 0,
+ RX_URB_FAIL,
+ RTL8152_SET_RX_MODE,
+ WORK_ENABLE
+};
+
+/* Define these values to match your device */
+#define VENDOR_ID_REALTEK 0x0bda
+#define PRODUCT_ID_RTL8152 0x8152
+
+#define MCU_TYPE_PLA 0x0100
+#define MCU_TYPE_USB 0x0000
+
+struct rx_desc {
+ u32 opts1;
+#define RX_LEN_MASK 0x7fff
+ u32 opts2;
+ u32 opts3;
+ u32 opts4;
+ u32 opts5;
+ u32 opts6;
+};
+
+struct tx_desc {
+ u32 opts1;
+#define TX_FS (1 << 31) /* First segment of a packet */
+#define TX_LS (1 << 30) /* Final segment of a packet */
+#define TX_LEN_MASK 0xffff
+ u32 opts2;
+};
+
+struct r8152 {
+ unsigned long flags;
+ struct usb_device *udev;
+ struct tasklet_struct tl;
+ struct net_device *netdev;
+ struct urb *rx_urb, *tx_urb;
+ struct sk_buff *tx_skb, *rx_skb;
+ struct delayed_work schedule;
+ struct mii_if_info mii;
+ u32 msg_enable;
+ u16 ocp_base;
+ u8 version;
+ u8 speed;
+};
+
+enum rtl_version {
+ RTL_VER_UNKNOWN = 0,
+ RTL_VER_01,
+ RTL_VER_02
+};
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ * The RTL chips use a 64 element hash table based on the Ethernet CRC.
+ */
+static const int multicast_filter_limit = 32;
+
+static
+int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
+{
+ return usb_control_msg(tp->udev, usb_rcvctrlpipe(tp->udev, 0),
+ RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
+ value, index, data, size, 500);
+}
+
+static
+int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
+{
+ return usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0),
+ RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
+ value, index, data, size, 500);
+}
+
+static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size,
+ void *data, u16 type)
+{
+ u16 limit = 64;
+ int ret = 0;
+
+ if (test_bit(RTL8152_UNPLUG, &tp->flags))
+ return -ENODEV;
+
+ /* both size and indix must be 4 bytes align */
+ if ((size & 3) || !size || (index & 3) || !data)
+ return -EPERM;
+
+ if ((u32)index + (u32)size > 0xffff)
+ return -EPERM;
+
+ while (size) {
+ if (size > limit) {
+ ret = get_registers(tp, index, type, limit, data);
+ if (ret < 0)
+ break;
+
+ index += limit;
+ data += limit;
+ size -= limit;
+ } else {
+ ret = get_registers(tp, index, type, size, data);
+ if (ret < 0)
+ break;
+
+ index += size;
+ data += size;
+ size = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen,
+ u16 size, void *data, u16 type)
+{
+ int ret;
+ u16 byteen_start, byteen_end, byen;
+ u16 limit = 512;
+
+ if (test_bit(RTL8152_UNPLUG, &tp->flags))
+ return -ENODEV;
+
+ /* both size and indix must be 4 bytes align */
+ if ((size & 3) || !size || (index & 3) || !data)
+ return -EPERM;
+
+ if ((u32)index + (u32)size > 0xffff)
+ return -EPERM;
+
+ byteen_start = byteen & BYTE_EN_START_MASK;
+ byteen_end = byteen & BYTE_EN_END_MASK;
+
+ byen = byteen_start | (byteen_start << 4);
+ ret = set_registers(tp, index, type | byen, 4, data);
+ if (ret < 0)
+ goto error1;
+
+ index += 4;
+ data += 4;
+ size -= 4;
+
+ if (size) {
+ size -= 4;
+
+ while (size) {
+ if (size > limit) {
+ ret = set_registers(tp, index,
+ type | BYTE_EN_DWORD,
+ limit, data);
+ if (ret < 0)
+ goto error1;
+
+ index += limit;
+ data += limit;
+ size -= limit;
+ } else {
+ ret = set_registers(tp, index,
+ type | BYTE_EN_DWORD,
+ size, data);
+ if (ret < 0)
+ goto error1;
+
+ index += size;
+ data += size;
+ size = 0;
+ break;
+ }
+ }
+
+ byen = byteen_end | (byteen_end >> 4);
+ ret = set_registers(tp, index, type | byen, 4, data);
+ if (ret < 0)
+ goto error1;
+ }
+
+error1:
+ return ret;
+}
+
+static inline
+int pla_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data)
+{
+ return generic_ocp_read(tp, index, size, data, MCU_TYPE_PLA);
+}
+
+static inline
+int pla_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data)
+{
+ return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_PLA);
+}
+
+static inline
+int usb_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data)
+{
+ return generic_ocp_read(tp, index, size, data, MCU_TYPE_USB);
+}
+
+static inline
+int usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data)
+{
+ return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_USB);
+}
+
+static u32 ocp_read_dword(struct r8152 *tp, u16 type, u16 index)
+{
+ u32 data;
+
+ if (type == MCU_TYPE_PLA)
+ pla_ocp_read(tp, index, sizeof(data), &data);
+ else
+ usb_ocp_read(tp, index, sizeof(data), &data);
+
+ return __le32_to_cpu(data);
+}
+
+static void ocp_write_dword(struct r8152 *tp, u16 type, u16 index, u32 data)
+{
+ if (type == MCU_TYPE_PLA)
+ pla_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(data), &data);
+ else
+ usb_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(data), &data);
+}
+
+static u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index)
+{
+ u32 data;
+ u8 shift = index & 2;
+
+ index &= ~3;
+
+ if (type == MCU_TYPE_PLA)
+ pla_ocp_read(tp, index, sizeof(data), &data);
+ else
+ usb_ocp_read(tp, index, sizeof(data), &data);
+
+ data = __le32_to_cpu(data);
+ data >>= (shift * 8);
+ data &= 0xffff;
+
+ return (u16)data;
+}
+
+static void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data)
+{
+ u32 tmp, mask = 0xffff;
+ u16 byen = BYTE_EN_WORD;
+ u8 shift = index & 2;
+
+ data &= mask;
+
+ if (index & 2) {
+ byen <<= shift;
+ mask <<= (shift * 8);
+ data <<= (shift * 8);
+ index &= ~3;
+ }
+
+ if (type == MCU_TYPE_PLA)
+ pla_ocp_read(tp, index, sizeof(tmp), &tmp);
+ else
+ usb_ocp_read(tp, index, sizeof(tmp), &tmp);
+
+ tmp = __le32_to_cpu(tmp) & ~mask;
+ tmp |= data;
+ tmp = __cpu_to_le32(tmp);
+
+ if (type == MCU_TYPE_PLA)
+ pla_ocp_write(tp, index, byen, sizeof(tmp), &tmp);
+ else
+ usb_ocp_write(tp, index, byen, sizeof(tmp), &tmp);
+}
+
+static u8 ocp_read_byte(struct r8152 *tp, u16 type, u16 index)
+{
+ u32 data;
+ u8 shift = index & 3;
+
+ index &= ~3;
+
+ if (type == MCU_TYPE_PLA)
+ pla_ocp_read(tp, index, sizeof(data), &data);
+ else
+ usb_ocp_read(tp, index, sizeof(data), &data);
+
+ data = __le32_to_cpu(data);
+ data >>= (shift * 8);
+ data &= 0xff;
+
+ return (u8)data;
+}
+
+static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data)
+{
+ u32 tmp, mask = 0xff;
+ u16 byen = BYTE_EN_BYTE;
+ u8 shift = index & 3;
+
+ data &= mask;
+
+ if (index & 3) {
+ byen <<= shift;
+ mask <<= (shift * 8);
+ data <<= (shift * 8);
+ index &= ~3;
+ }
+
+ if (type == MCU_TYPE_PLA)
+ pla_ocp_read(tp, index, sizeof(tmp), &tmp);
+ else
+ usb_ocp_read(tp, index, sizeof(tmp), &tmp);
+
+ tmp = __le32_to_cpu(tmp) & ~mask;
+ tmp |= data;
+ tmp = __cpu_to_le32(tmp);
+
+ if (type == MCU_TYPE_PLA)
+ pla_ocp_write(tp, index, byen, sizeof(tmp), &tmp);
+ else
+ usb_ocp_write(tp, index, byen, sizeof(tmp), &tmp);
+}
+
+static void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value)
+{
+ u32 ocp_data;
+ int i;
+
+ ocp_data = PHYAR_FLAG | ((reg_addr & 0x1f) << 16) |
+ (value & 0xffff);
+
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_PHYAR, ocp_data);
+
+ for (i = 20; i > 0; i--) {
+ udelay(25);
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_PHYAR);
+ if (!(ocp_data & PHYAR_FLAG))
+ break;
+ }
+ udelay(20);
+}
+
+static int r8152_mdio_read(struct r8152 *tp, u32 reg_addr)
+{
+ u32 ocp_data;
+ int i;
+
+ ocp_data = (reg_addr & 0x1f) << 16;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_PHYAR, ocp_data);
+
+ for (i = 20; i > 0; i--) {
+ udelay(25);
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_PHYAR);
+ if (ocp_data & PHYAR_FLAG)
+ break;
+ }
+ udelay(20);
+
+ if (!(ocp_data & PHYAR_FLAG))
+ return -EAGAIN;
+
+ return (u16)(ocp_data & 0xffff);
+}
+
+static int read_mii_word(struct net_device *netdev, int phy_id, int reg)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+
+ if (phy_id != R8152_PHY_ID)
+ return -EINVAL;
+
+ return r8152_mdio_read(tp, reg);
+}
+
+static
+void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+
+ if (phy_id != R8152_PHY_ID)
+ return;
+
+ r8152_mdio_write(tp, reg, val);
+}
+
+static void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data)
+{
+ u16 ocp_base, ocp_index;
+
+ ocp_base = addr & 0xf000;
+ if (ocp_base != tp->ocp_base) {
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base);
+ tp->ocp_base = ocp_base;
+ }
+
+ ocp_index = (addr & 0x0fff) | 0xb000;
+ ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data);
+}
+
+static inline void set_ethernet_addr(struct r8152 *tp)
+{
+ struct net_device *dev = tp->netdev;
+ u8 *node_id;
+
+ node_id = kmalloc(sizeof(u8) * 8, GFP_KERNEL);
+ if (!node_id) {
+ netif_err(tp, probe, dev, "out of memory");
+ return;
+ }
+
+ if (pla_ocp_read(tp, PLA_IDR, sizeof(u8) * 8, node_id) < 0)
+ netif_notice(tp, probe, dev, "inet addr fail\n");
+ else {
+ memcpy(dev->dev_addr, node_id, dev->addr_len);
+ memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+ }
+ kfree(node_id);
+}
+
+static int rtl8152_set_mac_address(struct net_device *netdev, void *p)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
+ pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, 8, addr->sa_data);
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
+
+ return 0;
+}
+
+static int alloc_all_urbs(struct r8152 *tp)
+{
+ tp->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!tp->rx_urb)
+ return 0;
+ tp->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!tp->tx_urb) {
+ usb_free_urb(tp->rx_urb);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void free_all_urbs(struct r8152 *tp)
+{
+ usb_free_urb(tp->rx_urb);
+ usb_free_urb(tp->tx_urb);
+}
+
+static struct net_device_stats *rtl8152_get_stats(struct net_device *dev)
+{
+ return &dev->stats;
+}
+
+static void read_bulk_callback(struct urb *urb)
+{
+ struct r8152 *tp;
+ unsigned pkt_len;
+ struct sk_buff *skb;
+ struct net_device *netdev;
+ struct net_device_stats *stats;
+ int status = urb->status;
+ int result;
+ struct rx_desc *rx_desc;
+
+ tp = urb->context;
+ if (!tp)
+ return;
+ if (test_bit(RTL8152_UNPLUG, &tp->flags))
+ return;
+ netdev = tp->netdev;
+ if (!netif_device_present(netdev))
+ return;
+
+ stats = rtl8152_get_stats(netdev);
+ switch (status) {
+ case 0:
+ break;
+ case -ESHUTDOWN:
+ set_bit(RTL8152_UNPLUG, &tp->flags);
+ netif_device_detach(tp->netdev);
+ case -ENOENT:
+ return; /* the urb is in unlink state */
+ case -ETIME:
+ pr_warn_ratelimited("may be reset is needed?..\n");
+ goto goon;
+ default:
+ pr_warn_ratelimited("Rx status %d\n", status);
+ goto goon;
+ }
+
+ /* protect against short packets (tell me why we got some?!?) */
+ if (urb->actual_length < sizeof(*rx_desc))
+ goto goon;
+
+
+ rx_desc = (struct rx_desc *)urb->transfer_buffer;
+ pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
+ if (urb->actual_length < sizeof(struct rx_desc) + pkt_len)
+ goto goon;
+
+ skb = netdev_alloc_skb_ip_align(netdev, pkt_len);
+ if (!skb)
+ goto goon;
+
+ memcpy(skb->data, tp->rx_skb->data + sizeof(struct rx_desc), pkt_len);
+ skb_put(skb, pkt_len);
+ skb->protocol = eth_type_trans(skb, netdev);
+ netif_rx(skb);
+ stats->rx_packets++;
+ stats->rx_bytes += pkt_len;
+goon:
+ usb_fill_bulk_urb(tp->rx_urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1),
+ tp->rx_skb->data, RTL8152_RMS + sizeof(struct rx_desc),
+ (usb_complete_t)read_bulk_callback, tp);
+ result = usb_submit_urb(tp->rx_urb, GFP_ATOMIC);
+ if (result == -ENODEV) {
+ netif_device_detach(tp->netdev);
+ } else if (result) {
+ set_bit(RX_URB_FAIL, &tp->flags);
+ goto resched;
+ } else {
+ clear_bit(RX_URB_FAIL, &tp->flags);
+ }
+
+ return;
+resched:
+ tasklet_schedule(&tp->tl);
+}
+
+static void rx_fixup(unsigned long data)
+{
+ struct r8152 *tp;
+ int status;
+
+ tp = (struct r8152 *)data;
+ if (!test_bit(WORK_ENABLE, &tp->flags))
+ return;
+
+ status = usb_submit_urb(tp->rx_urb, GFP_ATOMIC);
+ if (status == -ENODEV) {
+ netif_device_detach(tp->netdev);
+ } else if (status) {
+ set_bit(RX_URB_FAIL, &tp->flags);
+ goto tlsched;
+ } else {
+ clear_bit(RX_URB_FAIL, &tp->flags);
+ }
+
+ return;
+tlsched:
+ tasklet_schedule(&tp->tl);
+}
+
+static void write_bulk_callback(struct urb *urb)
+{
+ struct r8152 *tp;
+ int status = urb->status;
+
+ tp = urb->context;
+ if (!tp)
+ return;
+ dev_kfree_skb_irq(tp->tx_skb);
+ if (!netif_device_present(tp->netdev))
+ return;
+ if (status)
+ dev_info(&urb->dev->dev, "%s: Tx status %d\n",
+ tp->netdev->name, status);
+ tp->netdev->trans_start = jiffies;
+ netif_wake_queue(tp->netdev);
+}
+
+static void rtl8152_tx_timeout(struct net_device *netdev)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+ struct net_device_stats *stats = rtl8152_get_stats(netdev);
+ netif_warn(tp, tx_err, netdev, "Tx timeout.\n");
+ usb_unlink_urb(tp->tx_urb);
+ stats->tx_errors++;
+}
+
+static void rtl8152_set_rx_mode(struct net_device *netdev)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+
+ if (tp->speed & LINK_STATUS)
+ set_bit(RTL8152_SET_RX_MODE, &tp->flags);
+}
+
+static void _rtl8152_set_rx_mode(struct net_device *netdev)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+ u32 tmp, *mc_filter; /* Multicast hash filter */
+ u32 ocp_data;
+
+ mc_filter = kmalloc(sizeof(u32) * 2, GFP_KERNEL);
+ if (!mc_filter) {
+ netif_err(tp, link, netdev, "out of memory");
+ return;
+ }
+
+ clear_bit(RTL8152_SET_RX_MODE, &tp->flags);
+ netif_stop_queue(netdev);
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data &= ~RCR_ACPT_ALL;
+ ocp_data |= RCR_AB | RCR_APM;
+
+ if (netdev->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ netif_notice(tp, link, netdev, "Promiscuous mode enabled\n");
+ ocp_data |= RCR_AM | RCR_AAP;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else if ((netdev_mc_count(netdev) > multicast_filter_limit) ||
+ (netdev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ ocp_data |= RCR_AM;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else {
+ struct netdev_hw_addr *ha;
+
+ mc_filter[1] = mc_filter[0] = 0;
+ netdev_for_each_mc_addr(ha, netdev) {
+ int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+ ocp_data |= RCR_AM;
+ }
+ }
+
+ tmp = mc_filter[0];
+ mc_filter[0] = __cpu_to_le32(swab32(mc_filter[1]));
+ mc_filter[1] = __cpu_to_le32(swab32(tmp));
+
+ pla_ocp_write(tp, PLA_MAR, BYTE_EN_DWORD, sizeof(u32) * 2, mc_filter);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+ netif_wake_queue(netdev);
+ kfree(mc_filter);
+}
+
+static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+ struct net_device_stats *stats = rtl8152_get_stats(netdev);
+ struct tx_desc *tx_desc;
+ int len, res;
+
+ netif_stop_queue(netdev);
+ len = skb->len;
+ if (skb_header_cloned(skb) || skb_headroom(skb) < sizeof(*tx_desc)) {
+ struct sk_buff *tx_skb;
+
+ tx_skb = skb_copy_expand(skb, sizeof(*tx_desc), 0, GFP_ATOMIC);
+ dev_kfree_skb_any(skb);
+ if (!tx_skb) {
+ stats->tx_dropped++;
+ netif_wake_queue(netdev);
+ return NETDEV_TX_OK;
+ }
+ skb = tx_skb;
+ }
+ tx_desc = (struct tx_desc *)skb_push(skb, sizeof(*tx_desc));
+ memset(tx_desc, 0, sizeof(*tx_desc));
+ tx_desc->opts1 = cpu_to_le32((len & TX_LEN_MASK) | TX_FS | TX_LS);
+ tp->tx_skb = skb;
+ skb_tx_timestamp(skb);
+ usb_fill_bulk_urb(tp->tx_urb, tp->udev, usb_sndbulkpipe(tp->udev, 2),
+ skb->data, skb->len,
+ (usb_complete_t)write_bulk_callback, tp);
+ res = usb_submit_urb(tp->tx_urb, GFP_ATOMIC);
+ if (res) {
+ /* Can we get/handle EPIPE here? */
+ if (res == -ENODEV) {
+ netif_device_detach(tp->netdev);
+ } else {
+ netif_warn(tp, tx_err, netdev,
+ "failed tx_urb %d\n", res);
+ stats->tx_errors++;
+ netif_start_queue(netdev);
+ }
+ } else {
+ stats->tx_packets++;
+ stats->tx_bytes += skb->len;
+ }
+
+ return NETDEV_TX_OK;
+}
+
+static void r8152b_reset_packet_filter(struct r8152 *tp)
+{
+ u32 ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC);
+ ocp_data &= ~FMC_FCR_MCU_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data);
+ ocp_data |= FMC_FCR_MCU_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data);
+}
+
+static void rtl8152_nic_reset(struct r8152 *tp)
+{
+ int i;
+
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
+
+ for (i = 0; i < 1000; i++) {
+ if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
+ break;
+ udelay(100);
+ }
+}
+
+static inline u8 rtl8152_get_speed(struct r8152 *tp)
+{
+ return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS);
+}
+
+static int rtl8152_enable(struct r8152 *tp)
+{
+ u32 ocp_data;
+ u8 speed;
+
+ speed = rtl8152_get_speed(tp);
+ if (speed & _100bps) {
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
+ ocp_data &= ~EEEP_CR_EEEP_TX;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data);
+ } else {
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
+ ocp_data |= EEEP_CR_EEEP_TX;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data);
+ }
+
+ r8152b_reset_packet_filter(tp);
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR);
+ ocp_data |= CR_RE | CR_TE;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
+ ocp_data &= ~RXDY_GATED_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+
+ usb_fill_bulk_urb(tp->rx_urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1),
+ tp->rx_skb->data, RTL8152_RMS + sizeof(struct rx_desc),
+ (usb_complete_t)read_bulk_callback, tp);
+
+ return usb_submit_urb(tp->rx_urb, GFP_KERNEL);
+}
+
+static void rtl8152_disable(struct r8152 *tp)
+{
+ u32 ocp_data;
+ int i;
+
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data &= ~RCR_ACPT_ALL;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+
+ usb_kill_urb(tp->tx_urb);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
+ ocp_data |= RXDY_GATED_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+
+ for (i = 0; i < 1000; i++) {
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY)
+ break;
+ mdelay(1);
+ }
+
+ for (i = 0; i < 1000; i++) {
+ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY)
+ break;
+ mdelay(1);
+ }
+
+ usb_kill_urb(tp->rx_urb);
+
+ rtl8152_nic_reset(tp);
+}
+
+static void r8152b_exit_oob(struct r8152 *tp)
+{
+ u32 ocp_data;
+ int i;
+
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data &= ~RCR_ACPT_ALL;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
+ ocp_data |= RXDY_GATED_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, 0x00);
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ ocp_data &= ~NOW_IS_OOB;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
+ ocp_data &= ~MCU_BORW_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
+
+ for (i = 0; i < 1000; i++) {
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ if (ocp_data & LINK_LIST_READY)
+ break;
+ mdelay(1);
+ }
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
+ ocp_data |= RE_INIT_LL;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
+
+ for (i = 0; i < 1000; i++) {
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ if (ocp_data & LINK_LIST_READY)
+ break;
+ mdelay(1);
+ }
+
+ rtl8152_nic_reset(tp);
+
+ /* rx share fifo credit full threshold */
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_DEV_STAT);
+ ocp_data &= STAT_SPEED_MASK;
+ if (ocp_data == STAT_SPEED_FULL) {
+ /* rx share fifo credit near full threshold */
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1,
+ RXFIFO_THR2_FULL);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2,
+ RXFIFO_THR3_FULL);
+ } else {
+ /* rx share fifo credit near full threshold */
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1,
+ RXFIFO_THR2_HIGH);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2,
+ RXFIFO_THR3_HIGH);
+ }
+
+ /* TX share fifo free credit full threshold */
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL);
+
+ ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_BUF_THR);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_TX_DMA,
+ TEST_MODE_DISABLE | TX_SIZE_ADJUST1);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
+ ocp_data &= ~CPCR_RX_VLAN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
+
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0);
+ ocp_data |= TCR0_AUTO_FIFO;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data);
+}
+
+static void r8152b_enter_oob(struct r8152 *tp)
+{
+ u32 ocp_data;
+ int i;
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ ocp_data &= ~NOW_IS_OOB;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_OOB);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_OOB);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_OOB);
+
+ rtl8152_disable(tp);
+
+ for (i = 0; i < 1000; i++) {
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ if (ocp_data & LINK_LIST_READY)
+ break;
+ mdelay(1);
+ }
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
+ ocp_data |= RE_INIT_LL;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
+
+ for (i = 0; i < 1000; i++) {
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ if (ocp_data & LINK_LIST_READY)
+ break;
+ mdelay(1);
+ }
+
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL);
+ ocp_data |= MAGIC_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
+ ocp_data |= CPCR_RX_VLAN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR);
+ ocp_data |= ALDPS_PROXY_MODE;
+ ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data);
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5, LAN_WAKE_EN);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
+ ocp_data &= ~RXDY_GATED_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data |= RCR_APM | RCR_AM | RCR_AB;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+}
+
+static void r8152b_disable_aldps(struct r8152 *tp)
+{
+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE);
+ msleep(20);
+}
+
+static inline void r8152b_enable_aldps(struct r8152 *tp)
+{
+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS |
+ LINKENA | DIS_SDSAVE);
+}
+
+static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)
+{
+ u16 bmcr, anar;
+ int ret = 0;
+
+ cancel_delayed_work_sync(&tp->schedule);
+ anar = r8152_mdio_read(tp, MII_ADVERTISE);
+ anar &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
+ ADVERTISE_100HALF | ADVERTISE_100FULL);
+
+ if (autoneg == AUTONEG_DISABLE) {
+ if (speed == SPEED_10) {
+ bmcr = 0;
+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+ } else if (speed == SPEED_100) {
+ bmcr = BMCR_SPEED100;
+ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (duplex == DUPLEX_FULL)
+ bmcr |= BMCR_FULLDPLX;
+ } else {
+ if (speed == SPEED_10) {
+ if (duplex == DUPLEX_FULL)
+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+ else
+ anar |= ADVERTISE_10HALF;
+ } else if (speed == SPEED_100) {
+ if (duplex == DUPLEX_FULL) {
+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL;
+ } else {
+ anar |= ADVERTISE_10HALF;
+ anar |= ADVERTISE_100HALF;
+ }
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
+ }
+
+ r8152_mdio_write(tp, MII_ADVERTISE, anar);
+ r8152_mdio_write(tp, MII_BMCR, bmcr);
+
+out:
+ schedule_delayed_work(&tp->schedule, 5 * HZ);
+
+ return ret;
+}
+
+static void rtl8152_down(struct r8152 *tp)
+{
+ u32 ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL);
+ ocp_data &= ~POWER_CUT;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data);
+
+ r8152b_disable_aldps(tp);
+ r8152b_enter_oob(tp);
+ r8152b_enable_aldps(tp);
+}
+
+static void set_carrier(struct r8152 *tp)
+{
+ struct net_device *netdev = tp->netdev;
+ u8 speed;
+
+ speed = rtl8152_get_speed(tp);
+
+ if (speed & LINK_STATUS) {
+ if (!(tp->speed & LINK_STATUS)) {
+ rtl8152_enable(tp);
+ set_bit(RTL8152_SET_RX_MODE, &tp->flags);
+ netif_carrier_on(netdev);
+ }
+ } else {
+ if (tp->speed & LINK_STATUS) {
+ netif_carrier_off(netdev);
+ rtl8152_disable(tp);
+ }
+ }
+ tp->speed = speed;
+}
+
+static void rtl_work_func_t(struct work_struct *work)
+{
+ struct r8152 *tp = container_of(work, struct r8152, schedule.work);
+
+ if (!test_bit(WORK_ENABLE, &tp->flags))
+ goto out1;
+
+ if (test_bit(RTL8152_UNPLUG, &tp->flags))
+ goto out1;
+
+ set_carrier(tp);
+
+ if (test_bit(RTL8152_SET_RX_MODE, &tp->flags))
+ _rtl8152_set_rx_mode(tp->netdev);
+
+ schedule_delayed_work(&tp->schedule, HZ);
+
+out1:
+ return;
+}
+
+static int rtl8152_open(struct net_device *netdev)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+ int res = 0;
+
+ tp->speed = rtl8152_get_speed(tp);
+ if (tp->speed & LINK_STATUS) {
+ res = rtl8152_enable(tp);
+ if (res) {
+ if (res == -ENODEV)
+ netif_device_detach(tp->netdev);
+
+ netif_err(tp, ifup, netdev,
+ "rtl8152_open failed: %d\n", res);
+ return res;
+ }
+
+ netif_carrier_on(netdev);
+ } else {
+ netif_stop_queue(netdev);
+ netif_carrier_off(netdev);
+ }
+
+ rtl8152_set_speed(tp, AUTONEG_ENABLE, SPEED_100, DUPLEX_FULL);
+ netif_start_queue(netdev);
+ set_bit(WORK_ENABLE, &tp->flags);
+ schedule_delayed_work(&tp->schedule, 0);
+
+ return res;
+}
+
+static int rtl8152_close(struct net_device *netdev)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+ int res = 0;
+
+ clear_bit(WORK_ENABLE, &tp->flags);
+ cancel_delayed_work_sync(&tp->schedule);
+ netif_stop_queue(netdev);
+ rtl8152_disable(tp);
+
+ return res;
+}
+
+static void rtl_clear_bp(struct r8152 *tp)
+{
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_0, 0);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_2, 0);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_4, 0);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_6, 0);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_0, 0);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_2, 0);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_4, 0);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_6, 0);
+ mdelay(3);
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_BA, 0);
+ ocp_write_word(tp, MCU_TYPE_USB, USB_BP_BA, 0);
+}
+
+static void r8152b_enable_eee(struct r8152 *tp)
+{
+ u32 ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR);
+ ocp_data |= EEE_RX_EN | EEE_TX_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data);
+ ocp_reg_write(tp, OCP_EEE_CONFIG1, RG_TXLPI_MSK_HFDUP | RG_MATCLR_EN |
+ EEE_10_CAP | EEE_NWAY_EN |
+ TX_QUIET_EN | RX_QUIET_EN |
+ SDRISETIME | RG_RXLPI_MSK_HFDUP |
+ SDFALLTIME);
+ ocp_reg_write(tp, OCP_EEE_CONFIG2, RG_LPIHYS_NUM | RG_DACQUIET_EN |
+ RG_LDVQUIET_EN | RG_CKRSEL |
+ RG_EEEPRG_EN);
+ ocp_reg_write(tp, OCP_EEE_CONFIG3, FST_SNR_EYE_R | RG_LFS_SEL | MSK_PH);
+ ocp_reg_write(tp, OCP_EEE_AR, FUN_ADDR | DEVICE_ADDR);
+ ocp_reg_write(tp, OCP_EEE_DATA, EEE_ADDR);
+ ocp_reg_write(tp, OCP_EEE_AR, FUN_DATA | DEVICE_ADDR);
+ ocp_reg_write(tp, OCP_EEE_DATA, EEE_DATA);
+ ocp_reg_write(tp, OCP_EEE_AR, 0x0000);
+}
+
+static void r8152b_enable_fc(struct r8152 *tp)
+{
+ u16 anar;
+
+ anar = r8152_mdio_read(tp, MII_ADVERTISE);
+ anar |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+ r8152_mdio_write(tp, MII_ADVERTISE, anar);
+}
+
+static void r8152b_hw_phy_cfg(struct r8152 *tp)
+{
+ r8152_mdio_write(tp, MII_BMCR, BMCR_ANENABLE);
+ r8152b_disable_aldps(tp);
+}
+
+static void r8152b_init(struct r8152 *tp)
+{
+ u32 ocp_data;
+ int i;
+
+ rtl_clear_bp(tp);
+
+ if (tp->version == RTL_VER_01) {
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE);
+ ocp_data &= ~LED_MODE_MASK;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data);
+ }
+
+ r8152b_hw_phy_cfg(tp);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL);
+ ocp_data &= ~POWER_CUT;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS);
+ ocp_data &= ~RWSUME_INDICATE;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data);
+
+ r8152b_exit_oob(tp);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR);
+ ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data);
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL);
+ ocp_data &= ~MCU_CLK_RATIO_MASK;
+ ocp_data |= MCU_CLK_RATIO | D3_CLK_GATED_EN;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ocp_data);
+ ocp_data = GPHY_STS_MSK | SPEED_DOWN_MSK |
+ SPDWN_RXDV_MSK | SPDWN_LINKCHG_MSK;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data);
+
+ r8152b_enable_eee(tp);
+ r8152b_enable_aldps(tp);
+ r8152b_enable_fc(tp);
+
+ r8152_mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE |
+ BMCR_ANRESTART);
+ for (i = 0; i < 100; i++) {
+ udelay(100);
+ if (!(r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET))
+ break;
+ }
+
+ /* disable rx aggregation */
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+ ocp_data |= RX_AGG_DISABLE;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+}
+
+static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct r8152 *tp = usb_get_intfdata(intf);
+
+ netif_device_detach(tp->netdev);
+
+ if (netif_running(tp->netdev)) {
+ clear_bit(WORK_ENABLE, &tp->flags);
+ cancel_delayed_work_sync(&tp->schedule);
+ }
+
+ rtl8152_down(tp);
+
+ return 0;
+}
+
+static int rtl8152_resume(struct usb_interface *intf)
+{
+ struct r8152 *tp = usb_get_intfdata(intf);
+
+ r8152b_init(tp);
+ netif_device_attach(tp->netdev);
+ if (netif_running(tp->netdev)) {
+ rtl8152_enable(tp);
+ set_bit(WORK_ENABLE, &tp->flags);
+ set_bit(RTL8152_SET_RX_MODE, &tp->flags);
+ schedule_delayed_work(&tp->schedule, 0);
+ }
+
+ return 0;
+}
+
+static void rtl8152_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+
+ strncpy(info->driver, MODULENAME, ETHTOOL_BUSINFO_LEN);
+ strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);
+ usb_make_path(tp->udev, info->bus_info, sizeof(info->bus_info));
+}
+
+static
+int rtl8152_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+
+ if (!tp->mii.mdio_read)
+ return -EOPNOTSUPP;
+
+ return mii_ethtool_gset(&tp->mii, cmd);
+}
+
+static int rtl8152_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct r8152 *tp = netdev_priv(dev);
+
+ return rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex);
+}
+
+static struct ethtool_ops ops = {
+ .get_drvinfo = rtl8152_get_drvinfo,
+ .get_settings = rtl8152_get_settings,
+ .set_settings = rtl8152_set_settings,
+ .get_link = ethtool_op_get_link,
+};
+
+static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
+{
+ struct r8152 *tp = netdev_priv(netdev);
+ struct mii_ioctl_data *data = if_mii(rq);
+ int res = 0;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = R8152_PHY_ID; /* Internal PHY */
+ break;
+
+ case SIOCGMIIREG:
+ data->val_out = r8152_mdio_read(tp, data->reg_num);
+ break;
+
+ case SIOCSMIIREG:
+ if (!capable(CAP_NET_ADMIN)) {
+ res = -EPERM;
+ break;
+ }
+ r8152_mdio_write(tp, data->reg_num, data->val_in);
+ break;
+
+ default:
+ res = -EOPNOTSUPP;
+ }
+
+ return res;
+}
+
+static const struct net_device_ops rtl8152_netdev_ops = {
+ .ndo_open = rtl8152_open,
+ .ndo_stop = rtl8152_close,
+ .ndo_do_ioctl = rtl8152_ioctl,
+ .ndo_start_xmit = rtl8152_start_xmit,
+ .ndo_tx_timeout = rtl8152_tx_timeout,
+ .ndo_set_rx_mode = rtl8152_set_rx_mode,
+ .ndo_set_mac_address = rtl8152_set_mac_address,
+
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static void r8152b_get_version(struct r8152 *tp)
+{
+ u32 ocp_data;
+ u16 version;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1);
+ version = (u16)(ocp_data & VERSION_MASK);
+
+ switch (version) {
+ case 0x4c00:
+ tp->version = RTL_VER_01;
+ break;
+ case 0x4c10:
+ tp->version = RTL_VER_02;
+ break;
+ default:
+ netif_info(tp, probe, tp->netdev,
+ "Unknown version 0x%04x\n", version);
+ break;
+ }
+}
+
+static int rtl8152_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct r8152 *tp;
+ struct net_device *netdev;
+
+ if (udev->actconfig->desc.bConfigurationValue != 1) {
+ usb_driver_set_configuration(udev, 1);
+ return -ENODEV;
+ }
+
+ netdev = alloc_etherdev(sizeof(struct r8152));
+ if (!netdev) {
+ dev_err(&intf->dev, "Out of memory");
+ return -ENOMEM;
+ }
+
+ tp = netdev_priv(netdev);
+ tp->msg_enable = 0x7FFF;
+
+ tasklet_init(&tp->tl, rx_fixup, (unsigned long)tp);
+ INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t);
+
+ tp->udev = udev;
+ tp->netdev = netdev;
+ netdev->netdev_ops = &rtl8152_netdev_ops;
+ netdev->watchdog_timeo = RTL8152_TX_TIMEOUT;
+ netdev->features &= ~NETIF_F_IP_CSUM;
+ SET_ETHTOOL_OPS(netdev, &ops);
+ tp->speed = 0;
+
+ tp->mii.dev = netdev;
+ tp->mii.mdio_read = read_mii_word;
+ tp->mii.mdio_write = write_mii_word;
+ tp->mii.phy_id_mask = 0x3f;
+ tp->mii.reg_num_mask = 0x1f;
+ tp->mii.phy_id = R8152_PHY_ID;
+ tp->mii.supports_gmii = 0;
+
+ r8152b_get_version(tp);
+ r8152b_init(tp);
+ set_ethernet_addr(tp);
+
+ if (!alloc_all_urbs(tp)) {
+ netif_err(tp, probe, netdev, "out of memory");
+ goto out;
+ }
+
+ tp->rx_skb = netdev_alloc_skb(netdev,
+ RTL8152_RMS + sizeof(struct rx_desc));
+ if (!tp->rx_skb)
+ goto out1;
+
+ usb_set_intfdata(intf, tp);
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+
+ if (register_netdev(netdev) != 0) {
+ netif_err(tp, probe, netdev, "couldn't register the device");
+ goto out2;
+ }
+
+ netif_info(tp, probe, netdev, "%s", DRIVER_VERSION);
+
+ return 0;
+
+out2:
+ usb_set_intfdata(intf, NULL);
+ dev_kfree_skb(tp->rx_skb);
+out1:
+ free_all_urbs(tp);
+out:
+ free_netdev(netdev);
+ return -EIO;
+}
+
+static void rtl8152_unload(struct r8152 *tp)
+{
+ u32 ocp_data;
+
+ if (tp->version != RTL_VER_01) {
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL);
+ ocp_data |= POWER_CUT;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data);
+ }
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS);
+ ocp_data &= ~RWSUME_INDICATE;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data);
+}
+
+static void rtl8152_disconnect(struct usb_interface *intf)
+{
+ struct r8152 *tp = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (tp) {
+ set_bit(RTL8152_UNPLUG, &tp->flags);
+ tasklet_kill(&tp->tl);
+ unregister_netdev(tp->netdev);
+ rtl8152_unload(tp);
+ free_all_urbs(tp);
+ if (tp->rx_skb)
+ dev_kfree_skb(tp->rx_skb);
+ free_netdev(tp->netdev);
+ }
+}
+
+/* table of devices that work with this driver */
+static struct usb_device_id rtl8152_table[] = {
+ {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8152)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, rtl8152_table);
+
+static struct usb_driver rtl8152_driver = {
+ .name = MODULENAME,
+ .probe = rtl8152_probe,
+ .disconnect = rtl8152_disconnect,
+ .id_table = rtl8152_table,
+ .suspend = rtl8152_suspend,
+ .resume = rtl8152_resume
+};
+
+static int __init usb_rtl8152_init(void)
+{
+ return usb_register(&rtl8152_driver);
+}
+
+static void __exit usb_rtl8152_exit(void)
+{
+ usb_deregister(&rtl8152_driver);
+}
+
+module_init(usb_rtl8152_init);
+module_exit(usb_rtl8152_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
index 2b90da0d85f3..e7a1a4770996 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
@@ -1117,7 +1117,7 @@ static void brcmf_p2p_afx_handler(struct work_struct *work)
if (afx_hdl->is_listen && afx_hdl->my_listen_chan)
/* 100ms ~ 300ms */
err = brcmf_p2p_discover_listen(p2p, afx_hdl->my_listen_chan,
- 100 * (1 + (random32() % 3)));
+ 100 * (1 + prandom_u32() % 3));
else
err = brcmf_p2p_act_frm_search(p2p, afx_hdl->peer_listen_chan);
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index a0cb0770d319..d3c8ece980d8 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -216,7 +216,7 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
mwifiex_form_mgmt_frame(skb, buf, len);
mwifiex_queue_tx_pkt(priv, skb);
- *cookie = random32() | 1;
+ *cookie = prandom_u32() | 1;
cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, GFP_ATOMIC);
wiphy_dbg(wiphy, "info: management frame transmitted\n");
@@ -271,7 +271,7 @@ mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy,
duration);
if (!ret) {
- *cookie = random32() | 1;
+ *cookie = prandom_u32() | 1;
priv->roc_cfg.cookie = *cookie;
priv->roc_cfg.chan = *chan;
diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c
index 42cfcd9eb9aa..1ff1b67e8b27 100644
--- a/drivers/parisc/sba_iommu.c
+++ b/drivers/parisc/sba_iommu.c
@@ -575,7 +575,7 @@ sba_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba,
mtsp(sid,1);
asm("lci 0(%%sr1, %1), %0" : "=r" (ci) : "r" (vba));
- pa |= (ci >> 12) & 0xff; /* move CI (8 bits) into lowest byte */
+ pa |= (ci >> PAGE_SHIFT) & 0xff; /* move CI (8 bits) into lowest byte */
pa |= SBA_PDIR_VALID_BIT; /* set "valid" bit */
*pdir_ptr = cpu_to_le64(pa); /* swap and store into I/O Pdir */
@@ -1376,7 +1376,7 @@ static void
sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
{
u32 iova_space_size, iova_space_mask;
- unsigned int pdir_size, iov_order;
+ unsigned int pdir_size, iov_order, tcnfg;
/*
** Determine IOVA Space size from memory size.
@@ -1468,8 +1468,19 @@ sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE);
WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK);
- /* Set I/O PDIR Page size to 4K */
- WRITE_REG(0, ioc->ioc_hpa+IOC_TCNFG);
+ /* Set I/O PDIR Page size to system page size */
+ switch (PAGE_SHIFT) {
+ case 12: tcnfg = 0; break; /* 4K */
+ case 13: tcnfg = 1; break; /* 8K */
+ case 14: tcnfg = 2; break; /* 16K */
+ case 16: tcnfg = 3; break; /* 64K */
+ default:
+ panic(__FILE__ "Unsupported system page size %d",
+ 1 << PAGE_SHIFT);
+ break;
+ }
+ /* Set I/O PDIR Page size to PAGE_SIZE (4k/16k/...) */
+ WRITE_REG(tcnfg, ioc->ioc_hpa+IOC_TCNFG);
/*
** Clear I/O TLB of any possible entries.
diff --git a/drivers/pinctrl/sh-pfc/Kconfig b/drivers/pinctrl/sh-pfc/Kconfig
index af16f8f6ab6c..0e1f99c33d47 100644
--- a/drivers/pinctrl/sh-pfc/Kconfig
+++ b/drivers/pinctrl/sh-pfc/Kconfig
@@ -22,6 +22,11 @@ config GPIO_SH_PFC
This enables support for GPIOs within the SoC's pin function
controller.
+config PINCTRL_PFC_R8A73A4
+ def_bool y
+ depends on ARCH_R8A73A4
+ select PINCTRL_SH_PFC
+
config PINCTRL_PFC_R8A7740
def_bool y
depends on ARCH_R8A7740
diff --git a/drivers/pinctrl/sh-pfc/Makefile b/drivers/pinctrl/sh-pfc/Makefile
index e8b9562c47e1..211cd8e98a8a 100644
--- a/drivers/pinctrl/sh-pfc/Makefile
+++ b/drivers/pinctrl/sh-pfc/Makefile
@@ -3,6 +3,7 @@ ifeq ($(CONFIG_GPIO_SH_PFC),y)
sh-pfc-objs += gpio.o
endif
obj-$(CONFIG_PINCTRL_SH_PFC) += sh-pfc.o
+obj-$(CONFIG_PINCTRL_PFC_R8A73A4) += pfc-r8a73a4.o
obj-$(CONFIG_PINCTRL_PFC_R8A7740) += pfc-r8a7740.o
obj-$(CONFIG_PINCTRL_PFC_R8A7779) += pfc-r8a7779.o
obj-$(CONFIG_PINCTRL_PFC_SH7203) += pfc-sh7203.o
diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c
index feef89792568..b551336924a5 100644
--- a/drivers/pinctrl/sh-pfc/core.c
+++ b/drivers/pinctrl/sh-pfc/core.c
@@ -72,6 +72,7 @@ static void __iomem *sh_pfc_phys_to_virt(struct sh_pfc *pfc,
}
BUG();
+ return NULL;
}
int sh_pfc_get_pin_index(struct sh_pfc *pfc, unsigned int pin)
@@ -267,7 +268,7 @@ int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type)
int ret;
switch (pinmux_type) {
-
+ case PINMUX_TYPE_GPIO:
case PINMUX_TYPE_FUNCTION:
range = NULL;
break;
@@ -296,6 +297,8 @@ int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type)
enum_id = 0;
field = 0;
value = 0;
+
+ /* Iterate over all the configuration fields we need to update. */
while (1) {
pos = sh_pfc_mark_to_enum(pfc, mark, pos, &enum_id);
if (pos < 0)
@@ -304,18 +307,20 @@ int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type)
if (!enum_id)
break;
- /* first check if this is a function enum */
+ /* Check if the configuration field selects a function. If it
+ * doesn't, skip the field if it's not applicable to the
+ * requested pinmux type.
+ */
in_range = sh_pfc_enum_in_range(enum_id, &pfc->info->function);
if (!in_range) {
- /* not a function enum */
- if (range) {
- /*
- * other range exists, so this pin is
- * a regular GPIO pin that now is being
- * bound to a specific direction.
- *
- * for this case we only allow function enums
- * and the enums that match the other range.
+ if (pinmux_type == PINMUX_TYPE_FUNCTION) {
+ /* Functions are allowed to modify all
+ * fields.
+ */
+ in_range = 1;
+ } else if (pinmux_type != PINMUX_TYPE_GPIO) {
+ /* Input/output types can only modify fields
+ * that correspond to their respective ranges.
*/
in_range = sh_pfc_enum_in_range(enum_id, range);
@@ -326,17 +331,8 @@ int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type)
*/
if (in_range && enum_id == range->force)
continue;
- } else {
- /*
- * no other range exists, so this pin
- * must then be of the function type.
- *
- * allow function type pins to select
- * any combination of function/in/out
- * in their MARK lists.
- */
- in_range = 1;
}
+ /* GPIOs are only allowed to modify function fields. */
}
if (!in_range)
@@ -422,6 +418,9 @@ static int sh_pfc_remove(struct platform_device *pdev)
}
static const struct platform_device_id sh_pfc_id_table[] = {
+#ifdef CONFIG_PINCTRL_PFC_R8A73A4
+ { "pfc-r8a73a4", (kernel_ulong_t)&r8a73a4_pinmux_info },
+#endif
#ifdef CONFIG_PINCTRL_PFC_R8A7740
{ "pfc-r8a7740", (kernel_ulong_t)&r8a7740_pinmux_info },
#endif
diff --git a/drivers/pinctrl/sh-pfc/core.h b/drivers/pinctrl/sh-pfc/core.h
index 763d717ca979..89cb4289d761 100644
--- a/drivers/pinctrl/sh-pfc/core.h
+++ b/drivers/pinctrl/sh-pfc/core.h
@@ -54,6 +54,7 @@ void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned long reg_width,
int sh_pfc_get_pin_index(struct sh_pfc *pfc, unsigned int pin);
int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type);
+extern const struct sh_pfc_soc_info r8a73a4_pinmux_info;
extern const struct sh_pfc_soc_info r8a7740_pinmux_info;
extern const struct sh_pfc_soc_info r8a7779_pinmux_info;
extern const struct sh_pfc_soc_info sh7203_pinmux_info;
diff --git a/drivers/pinctrl/sh-pfc/gpio.c b/drivers/pinctrl/sh-pfc/gpio.c
index d7acb06d888c..d37efa7dcf90 100644
--- a/drivers/pinctrl/sh-pfc/gpio.c
+++ b/drivers/pinctrl/sh-pfc/gpio.c
@@ -101,24 +101,9 @@ static void gpio_setup_data_reg(struct sh_pfc_chip *chip, unsigned gpio)
static int gpio_setup_data_regs(struct sh_pfc_chip *chip)
{
struct sh_pfc *pfc = chip->pfc;
- unsigned long addr = pfc->info->data_regs[0].reg;
const struct pinmux_data_reg *dreg;
unsigned int i;
- /* Find the window that contain the GPIO registers. */
- for (i = 0; i < pfc->num_windows; ++i) {
- struct sh_pfc_window *window = &pfc->window[i];
-
- if (addr >= window->phys && addr < window->phys + window->size)
- break;
- }
-
- if (i == pfc->num_windows)
- return -EINVAL;
-
- /* GPIO data registers must be in the first memory resource. */
- chip->mem = &pfc->window[i];
-
/* Count the number of data registers, allocate memory and initialize
* them.
*/
@@ -319,7 +304,8 @@ static int gpio_function_setup(struct sh_pfc_chip *chip)
*/
static struct sh_pfc_chip *
-sh_pfc_add_gpiochip(struct sh_pfc *pfc, int(*setup)(struct sh_pfc_chip *))
+sh_pfc_add_gpiochip(struct sh_pfc *pfc, int(*setup)(struct sh_pfc_chip *),
+ struct sh_pfc_window *mem)
{
struct sh_pfc_chip *chip;
int ret;
@@ -328,6 +314,7 @@ sh_pfc_add_gpiochip(struct sh_pfc *pfc, int(*setup)(struct sh_pfc_chip *))
if (unlikely(!chip))
return ERR_PTR(-ENOMEM);
+ chip->mem = mem;
chip->pfc = pfc;
ret = setup(chip);
@@ -354,8 +341,27 @@ int sh_pfc_register_gpiochip(struct sh_pfc *pfc)
unsigned int i;
int ret;
+ if (pfc->info->data_regs == NULL)
+ return 0;
+
+ /* Find the memory window that contain the GPIO registers. Boards that
+ * register a separate GPIO device will not supply a memory resource
+ * that covers the data registers. In that case don't try to handle
+ * GPIOs.
+ */
+ for (i = 0; i < pfc->num_windows; ++i) {
+ struct sh_pfc_window *window = &pfc->window[i];
+
+ if (pfc->info->data_regs[0].reg >= window->phys &&
+ pfc->info->data_regs[0].reg < window->phys + window->size)
+ break;
+ }
+
+ if (i == pfc->num_windows)
+ return 0;
+
/* Register the real GPIOs chip. */
- chip = sh_pfc_add_gpiochip(pfc, gpio_pin_setup);
+ chip = sh_pfc_add_gpiochip(pfc, gpio_pin_setup, &pfc->window[i]);
if (IS_ERR(chip))
return PTR_ERR(chip);
@@ -384,7 +390,10 @@ int sh_pfc_register_gpiochip(struct sh_pfc *pfc)
}
/* Register the function GPIOs chip. */
- chip = sh_pfc_add_gpiochip(pfc, gpio_function_setup);
+ if (pfc->info->nr_func_gpios == 0)
+ return 0;
+
+ chip = sh_pfc_add_gpiochip(pfc, gpio_function_setup, NULL);
if (IS_ERR(chip))
return PTR_ERR(chip);
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a73a4.c b/drivers/pinctrl/sh-pfc/pfc-r8a73a4.c
new file mode 100644
index 000000000000..bbff5596e922
--- /dev/null
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a73a4.c
@@ -0,0 +1,2587 @@
+/*
+ * Copyright (C) 2012-2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Magnus Damm
+ * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <mach/irqs.h>
+#include <mach/r8a73a4.h>
+
+#include "core.h"
+#include "sh_pfc.h"
+
+#define CPU_ALL_PORT(fn, pfx, sfx) \
+ /* Port0 - Port30 */ \
+ PORT_10(fn, pfx, sfx), \
+ PORT_10(fn, pfx##1, sfx), \
+ PORT_10(fn, pfx##2, sfx), \
+ PORT_1(fn, pfx##30, sfx), \
+ /* Port32 - Port40 */ \
+ PORT_1(fn, pfx##32, sfx), PORT_1(fn, pfx##33, sfx), \
+ PORT_1(fn, pfx##34, sfx), PORT_1(fn, pfx##35, sfx), \
+ PORT_1(fn, pfx##36, sfx), PORT_1(fn, pfx##37, sfx), \
+ PORT_1(fn, pfx##38, sfx), PORT_1(fn, pfx##39, sfx), \
+ PORT_1(fn, pfx##40, sfx), \
+ /* Port64 - Port85 */ \
+ PORT_1(fn, pfx##64, sfx), PORT_1(fn, pfx##65, sfx), \
+ PORT_1(fn, pfx##66, sfx), PORT_1(fn, pfx##67, sfx), \
+ PORT_1(fn, pfx##68, sfx), PORT_1(fn, pfx##69, sfx), \
+ PORT_10(fn, pfx##7, sfx), \
+ PORT_1(fn, pfx##80, sfx), PORT_1(fn, pfx##81, sfx), \
+ PORT_1(fn, pfx##82, sfx), PORT_1(fn, pfx##83, sfx), \
+ PORT_1(fn, pfx##84, sfx), PORT_1(fn, pfx##85, sfx), \
+ /* Port96 - Port126 */ \
+ PORT_1(fn, pfx##96, sfx), PORT_1(fn, pfx##97, sfx), \
+ PORT_1(fn, pfx##98, sfx), PORT_1(fn, pfx##99, sfx), \
+ PORT_10(fn, pfx##10, sfx), \
+ PORT_10(fn, pfx##11, sfx), \
+ PORT_1(fn, pfx##120, sfx), PORT_1(fn, pfx##121, sfx), \
+ PORT_1(fn, pfx##122, sfx), PORT_1(fn, pfx##123, sfx), \
+ PORT_1(fn, pfx##124, sfx), PORT_1(fn, pfx##125, sfx), \
+ PORT_1(fn, pfx##126, sfx), \
+ /* Port128 - Port134 */ \
+ PORT_1(fn, pfx##128, sfx), PORT_1(fn, pfx##129, sfx), \
+ PORT_1(fn, pfx##130, sfx), PORT_1(fn, pfx##131, sfx), \
+ PORT_1(fn, pfx##132, sfx), PORT_1(fn, pfx##133, sfx), \
+ PORT_1(fn, pfx##134, sfx), \
+ /* Port160 - Port178 */ \
+ PORT_10(fn, pfx##16, sfx), \
+ PORT_1(fn, pfx##170, sfx), PORT_1(fn, pfx##171, sfx), \
+ PORT_1(fn, pfx##172, sfx), PORT_1(fn, pfx##173, sfx), \
+ PORT_1(fn, pfx##174, sfx), PORT_1(fn, pfx##175, sfx), \
+ PORT_1(fn, pfx##176, sfx), PORT_1(fn, pfx##177, sfx), \
+ PORT_1(fn, pfx##178, sfx), \
+ /* Port192 - Port222 */ \
+ PORT_1(fn, pfx##192, sfx), PORT_1(fn, pfx##193, sfx), \
+ PORT_1(fn, pfx##194, sfx), PORT_1(fn, pfx##195, sfx), \
+ PORT_1(fn, pfx##196, sfx), PORT_1(fn, pfx##197, sfx), \
+ PORT_1(fn, pfx##198, sfx), PORT_1(fn, pfx##199, sfx), \
+ PORT_10(fn, pfx##20, sfx), \
+ PORT_10(fn, pfx##21, sfx), \
+ PORT_1(fn, pfx##220, sfx), PORT_1(fn, pfx##221, sfx), \
+ PORT_1(fn, pfx##222, sfx), \
+ /* Port224 - Port250 */ \
+ PORT_1(fn, pfx##224, sfx), PORT_1(fn, pfx##225, sfx), \
+ PORT_1(fn, pfx##226, sfx), PORT_1(fn, pfx##227, sfx), \
+ PORT_1(fn, pfx##228, sfx), PORT_1(fn, pfx##229, sfx), \
+ PORT_10(fn, pfx##23, sfx), \
+ PORT_10(fn, pfx##24, sfx), \
+ PORT_1(fn, pfx##250, sfx), \
+ /* Port256 - Port283 */ \
+ PORT_1(fn, pfx##256, sfx), PORT_1(fn, pfx##257, sfx), \
+ PORT_1(fn, pfx##258, sfx), PORT_1(fn, pfx##259, sfx), \
+ PORT_10(fn, pfx##26, sfx), \
+ PORT_10(fn, pfx##27, sfx), \
+ PORT_1(fn, pfx##280, sfx), PORT_1(fn, pfx##281, sfx), \
+ PORT_1(fn, pfx##282, sfx), PORT_1(fn, pfx##283, sfx), \
+ /* Port288 - Port308 */ \
+ PORT_1(fn, pfx##288, sfx), PORT_1(fn, pfx##289, sfx), \
+ PORT_10(fn, pfx##29, sfx), \
+ PORT_1(fn, pfx##300, sfx), PORT_1(fn, pfx##301, sfx), \
+ PORT_1(fn, pfx##302, sfx), PORT_1(fn, pfx##303, sfx), \
+ PORT_1(fn, pfx##304, sfx), PORT_1(fn, pfx##305, sfx), \
+ PORT_1(fn, pfx##306, sfx), PORT_1(fn, pfx##307, sfx), \
+ PORT_1(fn, pfx##308, sfx), \
+ /* Port320 - Port329 */ \
+ PORT_10(fn, pfx##32, sfx)
+
+
+enum {
+ PINMUX_RESERVED = 0,
+
+ /* PORT0_DATA -> PORT329_DATA */
+ PINMUX_DATA_BEGIN,
+ PORT_ALL(DATA),
+ PINMUX_DATA_END,
+
+ /* PORT0_IN -> PORT329_IN */
+ PINMUX_INPUT_BEGIN,
+ PORT_ALL(IN),
+ PINMUX_INPUT_END,
+
+ /* PORT0_OUT -> PORT329_OUT */
+ PINMUX_OUTPUT_BEGIN,
+ PORT_ALL(OUT),
+ PINMUX_OUTPUT_END,
+
+ PINMUX_FUNCTION_BEGIN,
+ PORT_ALL(FN_IN), /* PORT0_FN_IN -> PORT329_FN_IN */
+ PORT_ALL(FN_OUT), /* PORT0_FN_OUT -> PORT329_FN_OUT */
+ PORT_ALL(FN0), /* PORT0_FN0 -> PORT329_FN0 */
+ PORT_ALL(FN1), /* PORT0_FN1 -> PORT329_FN1 */
+ PORT_ALL(FN2), /* PORT0_FN2 -> PORT329_FN2 */
+ PORT_ALL(FN3), /* PORT0_FN3 -> PORT329_FN3 */
+ PORT_ALL(FN4), /* PORT0_FN4 -> PORT329_FN4 */
+ PORT_ALL(FN5), /* PORT0_FN5 -> PORT329_FN5 */
+ PORT_ALL(FN6), /* PORT0_FN6 -> PORT329_FN6 */
+ PORT_ALL(FN7), /* PORT0_FN7 -> PORT329_FN7 */
+
+ MSEL1CR_31_0, MSEL1CR_31_1,
+ MSEL1CR_27_0, MSEL1CR_27_1,
+ MSEL1CR_25_0, MSEL1CR_25_1,
+ MSEL1CR_24_0, MSEL1CR_24_1,
+ MSEL1CR_22_0, MSEL1CR_22_1,
+ MSEL1CR_21_0, MSEL1CR_21_1,
+ MSEL1CR_20_0, MSEL1CR_20_1,
+ MSEL1CR_19_0, MSEL1CR_19_1,
+ MSEL1CR_18_0, MSEL1CR_18_1,
+ MSEL1CR_17_0, MSEL1CR_17_1,
+ MSEL1CR_16_0, MSEL1CR_16_1,
+ MSEL1CR_15_0, MSEL1CR_15_1,
+ MSEL1CR_14_0, MSEL1CR_14_1,
+ MSEL1CR_13_0, MSEL1CR_13_1,
+ MSEL1CR_12_0, MSEL1CR_12_1,
+ MSEL1CR_11_0, MSEL1CR_11_1,
+ MSEL1CR_10_0, MSEL1CR_10_1,
+ MSEL1CR_09_0, MSEL1CR_09_1,
+ MSEL1CR_08_0, MSEL1CR_08_1,
+ MSEL1CR_07_0, MSEL1CR_07_1,
+ MSEL1CR_06_0, MSEL1CR_06_1,
+ MSEL1CR_05_0, MSEL1CR_05_1,
+ MSEL1CR_04_0, MSEL1CR_04_1,
+ MSEL1CR_03_0, MSEL1CR_03_1,
+ MSEL1CR_02_0, MSEL1CR_02_1,
+ MSEL1CR_01_0, MSEL1CR_01_1,
+ MSEL1CR_00_0, MSEL1CR_00_1,
+
+ MSEL3CR_31_0, MSEL3CR_31_1,
+ MSEL3CR_28_0, MSEL3CR_28_1,
+ MSEL3CR_27_0, MSEL3CR_27_1,
+ MSEL3CR_26_0, MSEL3CR_26_1,
+ MSEL3CR_23_0, MSEL3CR_23_1,
+ MSEL3CR_22_0, MSEL3CR_22_1,
+ MSEL3CR_21_0, MSEL3CR_21_1,
+ MSEL3CR_20_0, MSEL3CR_20_1,
+ MSEL3CR_19_0, MSEL3CR_19_1,
+ MSEL3CR_18_0, MSEL3CR_18_1,
+ MSEL3CR_17_0, MSEL3CR_17_1,
+ MSEL3CR_16_0, MSEL3CR_16_1,
+ MSEL3CR_15_0, MSEL3CR_15_1,
+ MSEL3CR_12_0, MSEL3CR_12_1,
+ MSEL3CR_11_0, MSEL3CR_11_1,
+ MSEL3CR_10_0, MSEL3CR_10_1,
+ MSEL3CR_09_0, MSEL3CR_09_1,
+ MSEL3CR_06_0, MSEL3CR_06_1,
+ MSEL3CR_03_0, MSEL3CR_03_1,
+ MSEL3CR_01_0, MSEL3CR_01_1,
+ MSEL3CR_00_0, MSEL3CR_00_1,
+
+ MSEL4CR_30_0, MSEL4CR_30_1,
+ MSEL4CR_29_0, MSEL4CR_29_1,
+ MSEL4CR_28_0, MSEL4CR_28_1,
+ MSEL4CR_27_0, MSEL4CR_27_1,
+ MSEL4CR_26_0, MSEL4CR_26_1,
+ MSEL4CR_25_0, MSEL4CR_25_1,
+ MSEL4CR_24_0, MSEL4CR_24_1,
+ MSEL4CR_23_0, MSEL4CR_23_1,
+ MSEL4CR_22_0, MSEL4CR_22_1,
+ MSEL4CR_21_0, MSEL4CR_21_1,
+ MSEL4CR_20_0, MSEL4CR_20_1,
+ MSEL4CR_19_0, MSEL4CR_19_1,
+ MSEL4CR_18_0, MSEL4CR_18_1,
+ MSEL4CR_17_0, MSEL4CR_17_1,
+ MSEL4CR_16_0, MSEL4CR_16_1,
+ MSEL4CR_15_0, MSEL4CR_15_1,
+ MSEL4CR_14_0, MSEL4CR_14_1,
+ MSEL4CR_13_0, MSEL4CR_13_1,
+ MSEL4CR_12_0, MSEL4CR_12_1,
+ MSEL4CR_11_0, MSEL4CR_11_1,
+ MSEL4CR_10_0, MSEL4CR_10_1,
+ MSEL4CR_09_0, MSEL4CR_09_1,
+ MSEL4CR_07_0, MSEL4CR_07_1,
+ MSEL4CR_04_0, MSEL4CR_04_1,
+ MSEL4CR_01_0, MSEL4CR_01_1,
+
+ MSEL5CR_31_0, MSEL5CR_31_1,
+ MSEL5CR_30_0, MSEL5CR_30_1,
+ MSEL5CR_29_0, MSEL5CR_29_1,
+ MSEL5CR_28_0, MSEL5CR_28_1,
+ MSEL5CR_27_0, MSEL5CR_27_1,
+ MSEL5CR_26_0, MSEL5CR_26_1,
+ MSEL5CR_25_0, MSEL5CR_25_1,
+ MSEL5CR_24_0, MSEL5CR_24_1,
+ MSEL5CR_23_0, MSEL5CR_23_1,
+ MSEL5CR_22_0, MSEL5CR_22_1,
+ MSEL5CR_21_0, MSEL5CR_21_1,
+ MSEL5CR_20_0, MSEL5CR_20_1,
+ MSEL5CR_19_0, MSEL5CR_19_1,
+ MSEL5CR_18_0, MSEL5CR_18_1,
+ MSEL5CR_17_0, MSEL5CR_17_1,
+ MSEL5CR_16_0, MSEL5CR_16_1,
+ MSEL5CR_15_0, MSEL5CR_15_1,
+ MSEL5CR_14_0, MSEL5CR_14_1,
+ MSEL5CR_13_0, MSEL5CR_13_1,
+ MSEL5CR_12_0, MSEL5CR_12_1,
+ MSEL5CR_11_0, MSEL5CR_11_1,
+ MSEL5CR_10_0, MSEL5CR_10_1,
+ MSEL5CR_09_0, MSEL5CR_09_1,
+ MSEL5CR_08_0, MSEL5CR_08_1,
+ MSEL5CR_07_0, MSEL5CR_07_1,
+ MSEL5CR_06_0, MSEL5CR_06_1,
+
+ MSEL8CR_16_0, MSEL8CR_16_1,
+ MSEL8CR_01_0, MSEL8CR_01_1,
+ MSEL8CR_00_0, MSEL8CR_00_1,
+
+ PINMUX_FUNCTION_END,
+
+ PINMUX_MARK_BEGIN,
+
+
+#define F1(a) a##_MARK
+#define F2(a) a##_MARK
+#define F3(a) a##_MARK
+#define F4(a) a##_MARK
+#define F5(a) a##_MARK
+#define F6(a) a##_MARK
+#define F7(a) a##_MARK
+#define IRQ(a) IRQ##a##_MARK
+
+ F1(LCDD0), F3(PDM2_CLK_0), F7(DU0_DR0), IRQ(0), /* Port0 */
+ F1(LCDD1), F3(PDM2_DATA_1), F7(DU0_DR19), IRQ(1),
+ F1(LCDD2), F3(PDM3_CLK_2), F7(DU0_DR2), IRQ(2),
+ F1(LCDD3), F3(PDM3_DATA_3), F7(DU0_DR3), IRQ(3),
+ F1(LCDD4), F3(PDM4_CLK_4), F7(DU0_DR4), IRQ(4),
+ F1(LCDD5), F3(PDM4_DATA_5), F7(DU0_DR5), IRQ(5),
+ F1(LCDD6), F3(PDM0_OUTCLK_6), F7(DU0_DR6), IRQ(6),
+ F1(LCDD7), F3(PDM0_OUTDATA_7), F7(DU0_DR7), IRQ(7),
+ F1(LCDD8), F3(PDM1_OUTCLK_8), F7(DU0_DG0), IRQ(8),
+ F1(LCDD9), F3(PDM1_OUTDATA_9), F7(DU0_DG1), IRQ(9),
+ F1(LCDD10), F3(FSICCK), F7(DU0_DG2), IRQ(10), /* Port10 */
+ F1(LCDD11), F3(FSICISLD), F7(DU0_DG3), IRQ(11),
+ F1(LCDD12), F3(FSICOMC), F7(DU0_DG4), IRQ(12),
+ F1(LCDD13), F3(FSICOLR), F4(FSICILR), F7(DU0_DG5), IRQ(13),
+ F1(LCDD14), F3(FSICOBT), F4(FSICIBT), F7(DU0_DG6), IRQ(14),
+ F1(LCDD15), F3(FSICOSLD), F7(DU0_DG7), IRQ(15),
+ F1(LCDD16), F4(TPU1TO1), F7(DU0_DB0),
+ F1(LCDD17), F4(SF_IRQ_00), F7(DU0_DB1),
+ F1(LCDD18), F4(SF_IRQ_01), F7(DU0_DB2),
+ F1(LCDD19), F3(SCIFB3_RTS_19), F7(DU0_DB3),
+ F1(LCDD20), F3(SCIFB3_CTS_20), F7(DU0_DB4), /* Port20 */
+ F1(LCDD21), F3(SCIFB3_TXD_21), F7(DU0_DB5),
+ F1(LCDD22), F3(SCIFB3_RXD_22), F7(DU0_DB6),
+ F1(LCDD23), F3(SCIFB3_SCK_23), F7(DU0_DB7),
+ F1(LCDHSYN), F2(LCDCS), F3(SCIFB1_RTS_24),
+ F7(DU0_EXHSYNC_N_CSYNC_N_HSYNC_N),
+ F1(LCDVSYN), F3(SCIFB1_CTS_25), F7(DU0_EXVSYNC_N_VSYNC_N_CSYNC_N),
+ F1(LCDDCK), F2(LCDWR), F3(SCIFB1_TXD_26), F7(DU0_DOTCLKIN),
+ F1(LCDDISP), F2(LCDRS), F3(SCIFB1_RXD_27), F7(DU0_DOTCLKOUT),
+ F1(LCDRD_N), F3(SCIFB1_SCK_28), F7(DU0_DOTCLKOUTB),
+ F1(LCDLCLK), F4(SF_IRQ_02), F7(DU0_DISP_CSYNC_N_DE),
+ F1(LCDDON), F4(SF_IRQ_03), F7(DU0_ODDF_N_CLAMP), /* Port30 */
+
+ F1(SCIFA0_RTS), F5(SIM0_DET), F7(CSCIF0_RTS), /* Port32 */
+ F1(SCIFA0_CTS), F5(SIM1_DET), F7(CSCIF0_CTS),
+ F1(SCIFA0_SCK), F5(SIM0_PWRON), F7(CSCIF0_SCK),
+ F1(SCIFA1_RTS), F7(CSCIF1_RTS),
+ F1(SCIFA1_CTS), F7(CSCIF1_CTS),
+ F1(SCIFA1_SCK), F7(CSCIF1_SCK),
+ F1(SCIFB0_RTS), F3(TPU0TO1), F4(SCIFB3_RTS_38), F7(CHSCIF0_HRTS),
+ F1(SCIFB0_CTS), F3(TPU0TO2), F4(SCIFB3_CTS_39), F7(CHSCIF0_HCTS),
+ F1(SCIFB0_SCK), F3(TPU0TO3), F4(SCIFB3_SCK_40),
+ F7(CHSCIF0_HSCK), /* Port40 */
+
+ F1(PDM0_DATA), /* Port64 */
+ F1(PDM1_DATA),
+ F1(HSI_RX_WAKE), F2(SCIFB2_CTS_66), F3(MSIOF3_SYNC), F5(GenIO4),
+ IRQ(40),
+ F1(HSI_RX_READY), F2(SCIFB1_TXD_67), F5(GIO_OUT3_67), F7(CHSCIF1_HTX),
+ F1(HSI_RX_FLAG), F2(SCIFB2_TXD_68), F3(MSIOF3_TXD), F5(GIO_OUT4_68),
+ F1(HSI_RX_DATA), F2(SCIFB2_RXD_69), F3(MSIOF3_RXD), F5(GIO_OUT5_69),
+ F1(HSI_TX_FLAG), F2(SCIFB1_RTS_70), F5(GIO_OUT1_70), F6(HSIC_TSTCLK0),
+ F7(CHSCIF1_HRTS), /* Port70 */
+ F1(HSI_TX_DATA), F2(SCIFB1_CTS_71), F5(GIO_OUT2_71), F6(HSIC_TSTCLK1),
+ F7(CHSCIF1_HCTS),
+ F1(HSI_TX_WAKE), F2(SCIFB1_RXD_72), F5(GenIO8), F7(CHSCIF1_HRX),
+ F1(HSI_TX_READY), F2(SCIFB2_RTS_73), F3(MSIOF3_SCK), F5(GIO_OUT0_73),
+ F1(IRDA_OUT), F1(IRDA_IN), F1(IRDA_FIRSEL), F1(TPU0TO0),
+ F1(DIGRFEN), F1(GPS_TIMESTAMP), F1(TXP), /* Port80 */
+ F1(TXP2), F1(COEX_0), F1(COEX_1), IRQ(19), IRQ(18), /* Port85 */
+
+ F1(KEYIN0), /* Port96 */
+ F1(KEYIN1), F1(KEYIN2), F1(KEYIN3), F1(KEYIN4), /* Port100 */
+ F1(KEYIN5), F1(KEYIN6), IRQ(41), F1(KEYIN7), IRQ(42),
+ F2(KEYOUT0), F2(KEYOUT1), F2(KEYOUT2), F2(KEYOUT3),
+ F2(KEYOUT4), F2(KEYOUT5), IRQ(43), F2(KEYOUT6), IRQ(44), /* Port110 */
+ F2(KEYOUT7), F5(RFANAEN), IRQ(45),
+ F1(KEYIN8), F2(KEYOUT8), F4(SF_IRQ_04), IRQ(46),
+ F1(KEYIN9), F2(KEYOUT9), F4(SF_IRQ_05), IRQ(47),
+ F1(KEYIN10), F2(KEYOUT10), F4(SF_IRQ_06), IRQ(48),
+ F1(KEYIN11), F2(KEYOUT11), F4(SF_IRQ_07), IRQ(49),
+ F1(SCIFA0_TXD), F7(CSCIF0_TX), F1(SCIFA0_RXD), F7(CSCIF0_RX),
+ F1(SCIFA1_TXD), F7(CSCIF1_TX), F1(SCIFA1_RXD), F7(CSCIF1_RX),
+ F3(SF_PORT_1_120), F4(SCIFB3_RXD_120), F7(DU0_CDE), /* Port120 */
+ F3(SF_PORT_0_121), F4(SCIFB3_TXD_121),
+ F1(SCIFB0_TXD), F7(CHSCIF0_HTX),
+ F1(SCIFB0_RXD), F7(CHSCIF0_HRX), F3(ISP_STROBE_124),
+ F1(STP_ISD_0), F2(PDM4_CLK_125), F3(MSIOF2_TXD), F5(SIM0_VOLTSEL0),
+ F1(TS_SDEN), F2(MSIOF7_SYNC), F3(STP_ISEN_1),
+ F1(STP_ISEN_0), F2(PDM1_OUTDATA_128), F3(MSIOF2_SYNC),
+ F5(SIM1_VOLTSEL1), F1(TS_SPSYNC), F2(MSIOF7_RXD), F3(STP_ISSYNC_1),
+ F1(STP_ISSYNC_0), F2(PDM4_DATA_130), F3(MSIOF2_RXD),
+ F5(SIM0_VOLTSEL1), /* Port130 */
+ F1(STP_OPWM_0), F5(SIM1_PWRON), F1(TS_SCK), F2(MSIOF7_SCK),
+ F3(STP_ISCLK_1), F1(STP_ISCLK_0), F2(PDM1_OUTCLK_133), F3(MSIOF2_SCK),
+ F5(SIM1_VOLTSEL0), F1(TS_SDAT), F2(MSIOF7_TXD), F3(STP_ISD_1),
+ IRQ(20), /* Port160 */
+ IRQ(21), IRQ(22), IRQ(23),
+ F1(MMCD0_0), F1(MMCD0_1), F1(MMCD0_2), F1(MMCD0_3),
+ F1(MMCD0_4), F1(MMCD0_5), F1(MMCD0_6), /* Port170 */
+ F1(MMCD0_7), F1(MMCCMD0), F1(MMCCLK0), F1(MMCRST),
+ IRQ(24), IRQ(25), IRQ(26), IRQ(27),
+ F1(A10), F2(MMCD1_7), IRQ(31), /* Port192 */
+ F1(A9), F2(MMCD1_6), IRQ(32),
+ F1(A8), F2(MMCD1_5), IRQ(33),
+ F1(A7), F2(MMCD1_4), IRQ(34),
+ F1(A6), F2(MMCD1_3), IRQ(35),
+ F1(A5), F2(MMCD1_2), IRQ(36),
+ F1(A4), F2(MMCD1_1), IRQ(37),
+ F1(A3), F2(MMCD1_0), IRQ(38),
+ F1(A2), F2(MMCCMD1), IRQ(39), /* Port200 */
+ F1(A1),
+ F1(A0), F2(BS),
+ F1(CKO), F2(MMCCLK1),
+ F1(CS0_N), F5(SIM0_GPO1),
+ F1(CS2_N), F5(SIM0_GPO2),
+ F1(CS4_N), F2(VIO_VD), F5(SIM1_GPO0),
+ F1(D15), F5(GIO_OUT15),
+ F1(D14), F5(GIO_OUT14),
+ F1(D13), F5(GIO_OUT13),
+ F1(D12), F5(GIO_OUT12), /* Port210 */
+ F1(D11), F5(WGM_TXP2),
+ F1(D10), F5(WGM_GPS_TIMEM_ASK_RFCLK),
+ F1(D9), F2(VIO_D9), F5(GIO_OUT9),
+ F1(D8), F2(VIO_D8), F5(GIO_OUT8),
+ F1(D7), F2(VIO_D7), F5(GIO_OUT7),
+ F1(D6), F2(VIO_D6), F5(GIO_OUT6),
+ F1(D5), F2(VIO_D5), F5(GIO_OUT5_217),
+ F1(D4), F2(VIO_D4), F5(GIO_OUT4_218),
+ F1(D3), F2(VIO_D3), F5(GIO_OUT3_219),
+ F1(D2), F2(VIO_D2), F5(GIO_OUT2_220), /* Port220 */
+ F1(D1), F2(VIO_D1), F5(GIO_OUT1_221),
+ F1(D0), F2(VIO_D0), F5(GIO_OUT0_222),
+ F1(RDWR_224), F2(VIO_HD), F5(SIM1_GPO2),
+ F1(RD_N), F1(WAIT_N), F2(VIO_CLK), F5(SIM1_GPO1),
+ F1(WE0_N), F2(RDWR_227),
+ F1(WE1_N), F5(SIM0_GPO0),
+ F1(PWMO), F2(VIO_CKO1_229),
+ F1(SLIM_CLK), F2(VIO_CKO4_230), /* Port230 */
+ F1(SLIM_DATA), F2(VIO_CKO5_231), F2(VIO_CKO2_232), F4(SF_PORT_0_232),
+ F2(VIO_CKO3_233), F4(SF_PORT_1_233),
+ F1(FSIACK), F2(PDM3_CLK_234), F3(ISP_IRIS1_234),
+ F1(FSIAISLD), F2(PDM3_DATA_235),
+ F1(FSIAOMC), F2(PDM0_OUTCLK_236), F3(ISP_IRIS0_236),
+ F1(FSIAOLR), F2(FSIAILR), F1(FSIAOBT), F2(FSIAIBT),
+ F1(FSIAOSLD), F2(PDM0_OUTDATA_239),
+ F1(FSIBISLD), /* Port240 */
+ F1(FSIBOLR), F2(FSIBILR), F1(FSIBOMC), F3(ISP_SHUTTER1_242),
+ F1(FSIBOBT), F2(FSIBIBT), F1(FSIBOSLD), F2(FSIASPDIF),
+ F1(FSIBCK), F3(ISP_SHUTTER0_245),
+ F1(ISP_IRIS1_246), F1(ISP_IRIS0_247), F1(ISP_SHUTTER1_248),
+ F1(ISP_SHUTTER0_249), F1(ISP_STROBE_250), /* Port250 */
+ F1(MSIOF0_SYNC), F1(MSIOF0_RXD), F1(MSIOF0_SCK), F1(MSIOF0_SS2),
+ F3(VIO_CKO3_259), F1(MSIOF0_TXD), /* Port260 */
+ F2(SCIFB1_SCK_261), F7(CHSCIF1_HSCK), F2(SCIFB2_SCK_262),
+ F1(MSIOF1_SS2), F4(MSIOF5_SS2), F1(MSIOF1_TXD), F4(MSIOF5_TXD),
+ F1(MSIOF1_RXD), F4(MSIOF5_RXD), F1(MSIOF1_SS1), F4(MSIOF5_SS1),
+ F1(MSIOF0_SS1), F1(MSIOF1_SCK), F4(MSIOF5_SCK),
+ F1(MSIOF1_SYNC), F4(MSIOF5_SYNC),
+ F1(MSIOF2_SS1), F3(VIO_CKO5_270), /* Port270 */
+ F1(MSIOF2_SS2), F3(VIO_CKO2_271), F1(MSIOF3_SS2), F3(VIO_CKO1_272),
+ F1(MSIOF3_SS1), F3(VIO_CKO4_273), F1(MSIOF4_SS2), F4(TPU1TO0),
+ F1(IC_DP), F1(SIM0_RST), F1(IC_DM), F1(SIM0_BSICOMP),
+ F1(SIM0_CLK), F1(SIM0_IO), /* Port280 */
+ F1(SIM1_IO), F2(PDM2_DATA_281), F1(SIM1_CLK), F2(PDM2_CLK_282),
+ F1(SIM1_RST), F1(SDHID1_0), F3(STMDATA0_2),
+ F1(SDHID1_1), F3(STMDATA1_2), IRQ(51), /* Port290 */
+ F1(SDHID1_2), F3(STMDATA2_2), F1(SDHID1_3), F3(STMDATA3_2),
+ F1(SDHICLK1), F3(STMCLK_2), F1(SDHICMD1), F3(STMSIDI_2),
+ F1(SDHID2_0), F2(MSIOF4_TXD), F3(SCIFB2_TXD_295), F4(MSIOF6_TXD),
+ F1(SDHID2_1), F4(MSIOF6_SS2), IRQ(52),
+ F1(SDHID2_2), F2(MSIOF4_RXD), F3(SCIFB2_RXD_297), F4(MSIOF6_RXD),
+ F1(SDHID2_3), F2(MSIOF4_SYNC), F3(SCIFB2_CTS_298), F4(MSIOF6_SYNC),
+ F1(SDHICLK2), F2(MSIOF4_SCK), F3(SCIFB2_SCK_299), F4(MSIOF6_SCK),
+ F1(SDHICMD2), F2(MSIOF4_SS1), F3(SCIFB2_RTS_300),
+ F4(MSIOF6_SS1), /* Port300 */
+ F1(SDHICD0), IRQ(50), F1(SDHID0_0), F3(STMDATA0_1),
+ F1(SDHID0_1), F3(STMDATA1_1), F1(SDHID0_2), F3(STMDATA2_1),
+ F1(SDHID0_3), F3(STMDATA3_1), F1(SDHICMD0), F3(STMSIDI_1),
+ F1(SDHIWP0), F1(SDHICLK0), F3(STMCLK_1), IRQ(16), /* Port320 */
+ IRQ(17), IRQ(28), IRQ(29), IRQ(30), IRQ(53), IRQ(54),
+ IRQ(55), IRQ(56), IRQ(57),
+ PINMUX_MARK_END,
+};
+
+#define _PORT_DATA(pfx, sfx) PORT_DATA_IO(pfx)
+#define PINMUX_DATA_ALL() CPU_ALL_PORT(_PORT_DATA, , unused)
+
+static const pinmux_enum_t pinmux_data[] = {
+ /* specify valid pin states for each pin in GPIO mode */
+ PINMUX_DATA_ALL(),
+
+ /* Port0 */
+ PINMUX_DATA(LCDD0_MARK, PORT0_FN1),
+ PINMUX_DATA(PDM2_CLK_0_MARK, PORT0_FN3),
+ PINMUX_DATA(DU0_DR0_MARK, PORT0_FN7),
+ PINMUX_DATA(IRQ0_MARK, PORT0_FN0),
+
+ /* Port1 */
+ PINMUX_DATA(LCDD1_MARK, PORT1_FN1),
+ PINMUX_DATA(PDM2_DATA_1_MARK, PORT1_FN3, MSEL3CR_12_0),
+ PINMUX_DATA(DU0_DR19_MARK, PORT1_FN7),
+ PINMUX_DATA(IRQ1_MARK, PORT1_FN0),
+
+ /* Port2 */
+ PINMUX_DATA(LCDD2_MARK, PORT2_FN1),
+ PINMUX_DATA(PDM3_CLK_2_MARK, PORT2_FN3),
+ PINMUX_DATA(DU0_DR2_MARK, PORT2_FN7),
+ PINMUX_DATA(IRQ2_MARK, PORT2_FN0),
+
+ /* Port3 */
+ PINMUX_DATA(LCDD3_MARK, PORT3_FN1),
+ PINMUX_DATA(PDM3_DATA_3_MARK, PORT3_FN3, MSEL3CR_12_0),
+ PINMUX_DATA(DU0_DR3_MARK, PORT3_FN7),
+ PINMUX_DATA(IRQ3_MARK, PORT3_FN0),
+
+ /* Port4 */
+ PINMUX_DATA(LCDD4_MARK, PORT4_FN1),
+ PINMUX_DATA(PDM4_CLK_4_MARK, PORT4_FN3),
+ PINMUX_DATA(DU0_DR4_MARK, PORT4_FN7),
+ PINMUX_DATA(IRQ4_MARK, PORT4_FN0),
+
+ /* Port5 */
+ PINMUX_DATA(LCDD5_MARK, PORT5_FN1),
+ PINMUX_DATA(PDM4_DATA_5_MARK, PORT5_FN3, MSEL3CR_12_0),
+ PINMUX_DATA(DU0_DR5_MARK, PORT5_FN7),
+ PINMUX_DATA(IRQ5_MARK, PORT5_FN0),
+
+ /* Port6 */
+ PINMUX_DATA(LCDD6_MARK, PORT6_FN1),
+ PINMUX_DATA(PDM0_OUTCLK_6_MARK, PORT6_FN3),
+ PINMUX_DATA(DU0_DR6_MARK, PORT6_FN7),
+ PINMUX_DATA(IRQ6_MARK, PORT6_FN0),
+
+ /* Port7 */
+ PINMUX_DATA(LCDD7_MARK, PORT7_FN1),
+ PINMUX_DATA(PDM0_OUTDATA_7_MARK, PORT7_FN3),
+ PINMUX_DATA(DU0_DR7_MARK, PORT7_FN7),
+ PINMUX_DATA(IRQ7_MARK, PORT7_FN0),
+
+ /* Port8 */
+ PINMUX_DATA(LCDD8_MARK, PORT8_FN1),
+ PINMUX_DATA(PDM1_OUTCLK_8_MARK, PORT8_FN3),
+ PINMUX_DATA(DU0_DG0_MARK, PORT8_FN7),
+ PINMUX_DATA(IRQ8_MARK, PORT8_FN0),
+
+ /* Port9 */
+ PINMUX_DATA(LCDD9_MARK, PORT9_FN1),
+ PINMUX_DATA(PDM1_OUTDATA_9_MARK, PORT9_FN3),
+ PINMUX_DATA(DU0_DG1_MARK, PORT9_FN7),
+ PINMUX_DATA(IRQ9_MARK, PORT9_FN0),
+
+ /* Port10 */
+ PINMUX_DATA(LCDD10_MARK, PORT10_FN1),
+ PINMUX_DATA(FSICCK_MARK, PORT10_FN3),
+ PINMUX_DATA(DU0_DG2_MARK, PORT10_FN7),
+ PINMUX_DATA(IRQ10_MARK, PORT10_FN0),
+
+ /* Port11 */
+ PINMUX_DATA(LCDD11_MARK, PORT11_FN1),
+ PINMUX_DATA(FSICISLD_MARK, PORT11_FN3),
+ PINMUX_DATA(DU0_DG3_MARK, PORT11_FN7),
+ PINMUX_DATA(IRQ11_MARK, PORT11_FN0),
+
+ /* Port12 */
+ PINMUX_DATA(LCDD12_MARK, PORT12_FN1),
+ PINMUX_DATA(FSICOMC_MARK, PORT12_FN3),
+ PINMUX_DATA(DU0_DG4_MARK, PORT12_FN7),
+ PINMUX_DATA(IRQ12_MARK, PORT12_FN0),
+
+ /* Port13 */
+ PINMUX_DATA(LCDD13_MARK, PORT13_FN1),
+ PINMUX_DATA(FSICOLR_MARK, PORT13_FN3),
+ PINMUX_DATA(FSICILR_MARK, PORT13_FN4),
+ PINMUX_DATA(DU0_DG5_MARK, PORT13_FN7),
+ PINMUX_DATA(IRQ13_MARK, PORT13_FN0),
+
+ /* Port14 */
+ PINMUX_DATA(LCDD14_MARK, PORT14_FN1),
+ PINMUX_DATA(FSICOBT_MARK, PORT14_FN3),
+ PINMUX_DATA(FSICIBT_MARK, PORT14_FN4),
+ PINMUX_DATA(DU0_DG6_MARK, PORT14_FN7),
+ PINMUX_DATA(IRQ14_MARK, PORT14_FN0),
+
+ /* Port15 */
+ PINMUX_DATA(LCDD15_MARK, PORT15_FN1),
+ PINMUX_DATA(FSICOSLD_MARK, PORT15_FN3),
+ PINMUX_DATA(DU0_DG7_MARK, PORT15_FN7),
+ PINMUX_DATA(IRQ15_MARK, PORT15_FN0),
+
+ /* Port16 */
+ PINMUX_DATA(LCDD16_MARK, PORT16_FN1),
+ PINMUX_DATA(TPU1TO1_MARK, PORT16_FN4),
+ PINMUX_DATA(DU0_DB0_MARK, PORT16_FN7),
+
+ /* Port17 */
+ PINMUX_DATA(LCDD17_MARK, PORT17_FN1),
+ PINMUX_DATA(SF_IRQ_00_MARK, PORT17_FN4),
+ PINMUX_DATA(DU0_DB1_MARK, PORT17_FN7),
+
+ /* Port18 */
+ PINMUX_DATA(LCDD18_MARK, PORT18_FN1),
+ PINMUX_DATA(SF_IRQ_01_MARK, PORT18_FN4),
+ PINMUX_DATA(DU0_DB2_MARK, PORT18_FN7),
+
+ /* Port19 */
+ PINMUX_DATA(LCDD19_MARK, PORT19_FN1),
+ PINMUX_DATA(SCIFB3_RTS_19_MARK, PORT19_FN3),
+ PINMUX_DATA(DU0_DB3_MARK, PORT19_FN7),
+
+ /* Port20 */
+ PINMUX_DATA(LCDD20_MARK, PORT20_FN1),
+ PINMUX_DATA(SCIFB3_CTS_20_MARK, PORT20_FN3, MSEL3CR_09_0),
+ PINMUX_DATA(DU0_DB4_MARK, PORT20_FN7),
+
+ /* Port21 */
+ PINMUX_DATA(LCDD21_MARK, PORT21_FN1),
+ PINMUX_DATA(SCIFB3_TXD_21_MARK, PORT21_FN3, MSEL3CR_09_0),
+ PINMUX_DATA(DU0_DB5_MARK, PORT21_FN7),
+
+ /* Port22 */
+ PINMUX_DATA(LCDD22_MARK, PORT22_FN1),
+ PINMUX_DATA(SCIFB3_RXD_22_MARK, PORT22_FN3, MSEL3CR_09_0),
+ PINMUX_DATA(DU0_DB6_MARK, PORT22_FN7),
+
+ /* Port23 */
+ PINMUX_DATA(LCDD23_MARK, PORT23_FN1),
+ PINMUX_DATA(SCIFB3_SCK_23_MARK, PORT23_FN3),
+ PINMUX_DATA(DU0_DB7_MARK, PORT23_FN7),
+
+ /* Port24 */
+ PINMUX_DATA(LCDHSYN_MARK, PORT24_FN1),
+ PINMUX_DATA(LCDCS_MARK, PORT24_FN2),
+ PINMUX_DATA(SCIFB1_RTS_24_MARK, PORT24_FN3),
+ PINMUX_DATA(DU0_EXHSYNC_N_CSYNC_N_HSYNC_N_MARK, PORT24_FN7),
+
+ /* Port25 */
+ PINMUX_DATA(LCDVSYN_MARK, PORT25_FN1),
+ PINMUX_DATA(SCIFB1_CTS_25_MARK, PORT25_FN3, MSEL3CR_11_0),
+ PINMUX_DATA(DU0_EXVSYNC_N_VSYNC_N_CSYNC_N_MARK, PORT25_FN7),
+
+ /* Port26 */
+ PINMUX_DATA(LCDDCK_MARK, PORT26_FN1),
+ PINMUX_DATA(LCDWR_MARK, PORT26_FN2),
+ PINMUX_DATA(SCIFB1_TXD_26_MARK, PORT26_FN3, MSEL3CR_11_0),
+ PINMUX_DATA(DU0_DOTCLKIN_MARK, PORT26_FN7),
+
+ /* Port27 */
+ PINMUX_DATA(LCDDISP_MARK, PORT27_FN1),
+ PINMUX_DATA(LCDRS_MARK, PORT27_FN2),
+ PINMUX_DATA(SCIFB1_RXD_27_MARK, PORT27_FN3, MSEL3CR_11_0),
+ PINMUX_DATA(DU0_DOTCLKOUT_MARK, PORT27_FN7),
+
+ /* Port28 */
+ PINMUX_DATA(LCDRD_N_MARK, PORT28_FN1),
+ PINMUX_DATA(SCIFB1_SCK_28_MARK, PORT28_FN3),
+ PINMUX_DATA(DU0_DOTCLKOUTB_MARK, PORT28_FN7),
+
+ /* Port29 */
+ PINMUX_DATA(LCDLCLK_MARK, PORT29_FN1),
+ PINMUX_DATA(SF_IRQ_02_MARK, PORT29_FN4),
+ PINMUX_DATA(DU0_DISP_CSYNC_N_DE_MARK, PORT29_FN7),
+
+ /* Port30 */
+ PINMUX_DATA(LCDDON_MARK, PORT30_FN1),
+ PINMUX_DATA(SF_IRQ_03_MARK, PORT30_FN4),
+ PINMUX_DATA(DU0_ODDF_N_CLAMP_MARK, PORT30_FN7),
+
+ /* Port32 */
+ PINMUX_DATA(SCIFA0_RTS_MARK, PORT32_FN1),
+ PINMUX_DATA(SIM0_DET_MARK, PORT32_FN5),
+ PINMUX_DATA(CSCIF0_RTS_MARK, PORT32_FN7),
+
+ /* Port33 */
+ PINMUX_DATA(SCIFA0_CTS_MARK, PORT33_FN1),
+ PINMUX_DATA(SIM1_DET_MARK, PORT33_FN5),
+ PINMUX_DATA(CSCIF0_CTS_MARK, PORT33_FN7),
+
+ /* Port34 */
+ PINMUX_DATA(SCIFA0_SCK_MARK, PORT34_FN1),
+ PINMUX_DATA(SIM0_PWRON_MARK, PORT34_FN5),
+ PINMUX_DATA(CSCIF0_SCK_MARK, PORT34_FN7),
+
+ /* Port35 */
+ PINMUX_DATA(SCIFA1_RTS_MARK, PORT35_FN1),
+ PINMUX_DATA(CSCIF1_RTS_MARK, PORT35_FN7),
+
+ /* Port36 */
+ PINMUX_DATA(SCIFA1_CTS_MARK, PORT36_FN1),
+ PINMUX_DATA(CSCIF1_CTS_MARK, PORT36_FN7),
+
+ /* Port37 */
+ PINMUX_DATA(SCIFA1_SCK_MARK, PORT37_FN1),
+ PINMUX_DATA(CSCIF1_SCK_MARK, PORT37_FN7),
+
+ /* Port38 */
+ PINMUX_DATA(SCIFB0_RTS_MARK, PORT38_FN1),
+ PINMUX_DATA(TPU0TO1_MARK, PORT38_FN3),
+ PINMUX_DATA(SCIFB3_RTS_38_MARK, PORT38_FN4),
+ PINMUX_DATA(CHSCIF0_HRTS_MARK, PORT38_FN7),
+
+ /* Port39 */
+ PINMUX_DATA(SCIFB0_CTS_MARK, PORT39_FN1),
+ PINMUX_DATA(TPU0TO2_MARK, PORT39_FN3),
+ PINMUX_DATA(SCIFB3_CTS_39_MARK, PORT39_FN4, MSEL3CR_09_1),
+ PINMUX_DATA(CHSCIF0_HCTS_MARK, PORT39_FN7),
+
+ /* Port40 */
+ PINMUX_DATA(SCIFB0_SCK_MARK, PORT40_FN1),
+ PINMUX_DATA(TPU0TO3_MARK, PORT40_FN3),
+ PINMUX_DATA(SCIFB3_SCK_40_MARK, PORT40_FN4),
+ PINMUX_DATA(CHSCIF0_HSCK_MARK, PORT40_FN7),
+
+ /* Port64 */
+ PINMUX_DATA(PDM0_DATA_MARK, PORT64_FN1),
+
+ /* Port65 */
+ PINMUX_DATA(PDM1_DATA_MARK, PORT65_FN1),
+
+ /* Port66 */
+ PINMUX_DATA(HSI_RX_WAKE_MARK, PORT66_FN1),
+ PINMUX_DATA(SCIFB2_CTS_66_MARK, PORT66_FN2, MSEL3CR_10_0),
+ PINMUX_DATA(MSIOF3_SYNC_MARK, PORT66_FN3),
+ PINMUX_DATA(GenIO4_MARK, PORT66_FN5),
+ PINMUX_DATA(IRQ40_MARK, PORT66_FN0),
+
+ /* Port67 */
+ PINMUX_DATA(HSI_RX_READY_MARK, PORT67_FN1),
+ PINMUX_DATA(SCIFB1_TXD_67_MARK, PORT67_FN2, MSEL3CR_11_1),
+ PINMUX_DATA(GIO_OUT3_67_MARK, PORT67_FN5),
+ PINMUX_DATA(CHSCIF1_HTX_MARK, PORT67_FN7),
+
+ /* Port68 */
+ PINMUX_DATA(HSI_RX_FLAG_MARK, PORT68_FN1),
+ PINMUX_DATA(SCIFB2_TXD_68_MARK, PORT68_FN2, MSEL3CR_10_0),
+ PINMUX_DATA(MSIOF3_TXD_MARK, PORT68_FN3),
+ PINMUX_DATA(GIO_OUT4_68_MARK, PORT68_FN5),
+
+ /* Port69 */
+ PINMUX_DATA(HSI_RX_DATA_MARK, PORT69_FN1),
+ PINMUX_DATA(SCIFB2_RXD_69_MARK, PORT69_FN2, MSEL3CR_10_0),
+ PINMUX_DATA(MSIOF3_RXD_MARK, PORT69_FN3),
+ PINMUX_DATA(GIO_OUT5_69_MARK, PORT69_FN5),
+
+ /* Port70 */
+ PINMUX_DATA(HSI_TX_FLAG_MARK, PORT70_FN1),
+ PINMUX_DATA(SCIFB1_RTS_70_MARK, PORT70_FN2),
+ PINMUX_DATA(GIO_OUT1_70_MARK, PORT70_FN5),
+ PINMUX_DATA(HSIC_TSTCLK0_MARK, PORT70_FN6),
+ PINMUX_DATA(CHSCIF1_HRTS_MARK, PORT70_FN7),
+
+ /* Port71 */
+ PINMUX_DATA(HSI_TX_DATA_MARK, PORT71_FN1),
+ PINMUX_DATA(SCIFB1_CTS_71_MARK, PORT71_FN2, MSEL3CR_11_1),
+ PINMUX_DATA(GIO_OUT2_71_MARK, PORT71_FN5),
+ PINMUX_DATA(HSIC_TSTCLK1_MARK, PORT71_FN6),
+ PINMUX_DATA(CHSCIF1_HCTS_MARK, PORT71_FN7),
+
+ /* Port72 */
+ PINMUX_DATA(HSI_TX_WAKE_MARK, PORT72_FN1),
+ PINMUX_DATA(SCIFB1_RXD_72_MARK, PORT72_FN2, MSEL3CR_11_1),
+ PINMUX_DATA(GenIO8_MARK, PORT72_FN5),
+ PINMUX_DATA(CHSCIF1_HRX_MARK, PORT72_FN7),
+
+ /* Port73 */
+ PINMUX_DATA(HSI_TX_READY_MARK, PORT73_FN1),
+ PINMUX_DATA(SCIFB2_RTS_73_MARK, PORT73_FN2),
+ PINMUX_DATA(MSIOF3_SCK_MARK, PORT73_FN3),
+ PINMUX_DATA(GIO_OUT0_73_MARK, PORT73_FN5),
+
+ /* Port74 - Port85 */
+ PINMUX_DATA(IRDA_OUT_MARK, PORT74_FN1),
+ PINMUX_DATA(IRDA_IN_MARK, PORT75_FN1),
+ PINMUX_DATA(IRDA_FIRSEL_MARK, PORT76_FN1),
+ PINMUX_DATA(TPU0TO0_MARK, PORT77_FN1),
+ PINMUX_DATA(DIGRFEN_MARK, PORT78_FN1),
+ PINMUX_DATA(GPS_TIMESTAMP_MARK, PORT79_FN1),
+ PINMUX_DATA(TXP_MARK, PORT80_FN1),
+ PINMUX_DATA(TXP2_MARK, PORT81_FN1),
+ PINMUX_DATA(COEX_0_MARK, PORT82_FN1),
+ PINMUX_DATA(COEX_1_MARK, PORT83_FN1),
+ PINMUX_DATA(IRQ19_MARK, PORT84_FN0),
+ PINMUX_DATA(IRQ18_MARK, PORT85_FN0),
+
+ /* Port96 - Port101 */
+ PINMUX_DATA(KEYIN0_MARK, PORT96_FN1),
+ PINMUX_DATA(KEYIN1_MARK, PORT97_FN1),
+ PINMUX_DATA(KEYIN2_MARK, PORT98_FN1),
+ PINMUX_DATA(KEYIN3_MARK, PORT99_FN1),
+ PINMUX_DATA(KEYIN4_MARK, PORT100_FN1),
+ PINMUX_DATA(KEYIN5_MARK, PORT101_FN1),
+
+ /* Port102 */
+ PINMUX_DATA(KEYIN6_MARK, PORT102_FN1),
+ PINMUX_DATA(IRQ41_MARK, PORT102_FN0),
+
+ /* Port103 */
+ PINMUX_DATA(KEYIN7_MARK, PORT103_FN1),
+ PINMUX_DATA(IRQ42_MARK, PORT103_FN0),
+
+ /* Port104 - Port108 */
+ PINMUX_DATA(KEYOUT0_MARK, PORT104_FN2),
+ PINMUX_DATA(KEYOUT1_MARK, PORT105_FN2),
+ PINMUX_DATA(KEYOUT2_MARK, PORT106_FN2),
+ PINMUX_DATA(KEYOUT3_MARK, PORT107_FN2),
+ PINMUX_DATA(KEYOUT4_MARK, PORT108_FN2),
+
+ /* Port109 */
+ PINMUX_DATA(KEYOUT5_MARK, PORT109_FN2),
+ PINMUX_DATA(IRQ43_MARK, PORT109_FN0),
+
+ /* Port110 */
+ PINMUX_DATA(KEYOUT6_MARK, PORT110_FN2),
+ PINMUX_DATA(IRQ44_MARK, PORT110_FN0),
+
+ /* Port111 */
+ PINMUX_DATA(KEYOUT7_MARK, PORT111_FN2),
+ PINMUX_DATA(RFANAEN_MARK, PORT111_FN5),
+ PINMUX_DATA(IRQ45_MARK, PORT111_FN0),
+
+ /* Port112 */
+ PINMUX_DATA(KEYIN8_MARK, PORT112_FN1),
+ PINMUX_DATA(KEYOUT8_MARK, PORT112_FN2),
+ PINMUX_DATA(SF_IRQ_04_MARK, PORT112_FN4),
+ PINMUX_DATA(IRQ46_MARK, PORT112_FN0),
+
+ /* Port113 */
+ PINMUX_DATA(KEYIN9_MARK, PORT113_FN1),
+ PINMUX_DATA(KEYOUT9_MARK, PORT113_FN2),
+ PINMUX_DATA(SF_IRQ_05_MARK, PORT113_FN4),
+ PINMUX_DATA(IRQ47_MARK, PORT113_FN0),
+
+ /* Port114 */
+ PINMUX_DATA(KEYIN10_MARK, PORT114_FN1),
+ PINMUX_DATA(KEYOUT10_MARK, PORT114_FN2),
+ PINMUX_DATA(SF_IRQ_06_MARK, PORT114_FN4),
+ PINMUX_DATA(IRQ48_MARK, PORT114_FN0),
+
+ /* Port115 */
+ PINMUX_DATA(KEYIN11_MARK, PORT115_FN1),
+ PINMUX_DATA(KEYOUT11_MARK, PORT115_FN2),
+ PINMUX_DATA(SF_IRQ_07_MARK, PORT115_FN4),
+ PINMUX_DATA(IRQ49_MARK, PORT115_FN0),
+
+ /* Port116 */
+ PINMUX_DATA(SCIFA0_TXD_MARK, PORT116_FN1),
+ PINMUX_DATA(CSCIF0_TX_MARK, PORT116_FN7),
+
+ /* Port117 */
+ PINMUX_DATA(SCIFA0_RXD_MARK, PORT117_FN1),
+ PINMUX_DATA(CSCIF0_RX_MARK, PORT117_FN7),
+
+ /* Port118 */
+ PINMUX_DATA(SCIFA1_TXD_MARK, PORT118_FN1),
+ PINMUX_DATA(CSCIF1_TX_MARK, PORT118_FN7),
+
+ /* Port119 */
+ PINMUX_DATA(SCIFA1_RXD_MARK, PORT119_FN1),
+ PINMUX_DATA(CSCIF1_RX_MARK, PORT119_FN7),
+
+ /* Port120 */
+ PINMUX_DATA(SF_PORT_1_120_MARK, PORT120_FN3),
+ PINMUX_DATA(SCIFB3_RXD_120_MARK, PORT120_FN4, MSEL3CR_09_1),
+ PINMUX_DATA(DU0_CDE_MARK, PORT120_FN7),
+
+ /* Port121 */
+ PINMUX_DATA(SF_PORT_0_121_MARK, PORT121_FN3),
+ PINMUX_DATA(SCIFB3_TXD_121_MARK, PORT121_FN4, MSEL3CR_09_1),
+
+ /* Port122 */
+ PINMUX_DATA(SCIFB0_TXD_MARK, PORT122_FN1),
+ PINMUX_DATA(CHSCIF0_HTX_MARK, PORT122_FN7),
+
+ /* Port123 */
+ PINMUX_DATA(SCIFB0_RXD_MARK, PORT123_FN1),
+ PINMUX_DATA(CHSCIF0_HRX_MARK, PORT123_FN7),
+
+ /* Port124 */
+ PINMUX_DATA(ISP_STROBE_124_MARK, PORT124_FN3),
+
+ /* Port125 */
+ PINMUX_DATA(STP_ISD_0_MARK, PORT125_FN1),
+ PINMUX_DATA(PDM4_CLK_125_MARK, PORT125_FN2),
+ PINMUX_DATA(MSIOF2_TXD_MARK, PORT125_FN3),
+ PINMUX_DATA(SIM0_VOLTSEL0_MARK, PORT125_FN5),
+
+ /* Port126 */
+ PINMUX_DATA(TS_SDEN_MARK, PORT126_FN1),
+ PINMUX_DATA(MSIOF7_SYNC_MARK, PORT126_FN2),
+ PINMUX_DATA(STP_ISEN_1_MARK, PORT126_FN3),
+
+ /* Port128 */
+ PINMUX_DATA(STP_ISEN_0_MARK, PORT128_FN1),
+ PINMUX_DATA(PDM1_OUTDATA_128_MARK, PORT128_FN2),
+ PINMUX_DATA(MSIOF2_SYNC_MARK, PORT128_FN3),
+ PINMUX_DATA(SIM1_VOLTSEL1_MARK, PORT128_FN5),
+
+ /* Port129 */
+ PINMUX_DATA(TS_SPSYNC_MARK, PORT129_FN1),
+ PINMUX_DATA(MSIOF7_RXD_MARK, PORT129_FN2),
+ PINMUX_DATA(STP_ISSYNC_1_MARK, PORT129_FN3),
+
+ /* Port130 */
+ PINMUX_DATA(STP_ISSYNC_0_MARK, PORT130_FN1),
+ PINMUX_DATA(PDM4_DATA_130_MARK, PORT130_FN2, MSEL3CR_12_1),
+ PINMUX_DATA(MSIOF2_RXD_MARK, PORT130_FN3),
+ PINMUX_DATA(SIM0_VOLTSEL1_MARK, PORT130_FN5),
+
+ /* Port131 */
+ PINMUX_DATA(STP_OPWM_0_MARK, PORT131_FN1),
+ PINMUX_DATA(SIM1_PWRON_MARK, PORT131_FN5),
+
+ /* Port132 */
+ PINMUX_DATA(TS_SCK_MARK, PORT132_FN1),
+ PINMUX_DATA(MSIOF7_SCK_MARK, PORT132_FN2),
+ PINMUX_DATA(STP_ISCLK_1_MARK, PORT132_FN3),
+
+ /* Port133 */
+ PINMUX_DATA(STP_ISCLK_0_MARK, PORT133_FN1),
+ PINMUX_DATA(PDM1_OUTCLK_133_MARK, PORT133_FN2),
+ PINMUX_DATA(MSIOF2_SCK_MARK, PORT133_FN3),
+ PINMUX_DATA(SIM1_VOLTSEL0_MARK, PORT133_FN5),
+
+ /* Port134 */
+ PINMUX_DATA(TS_SDAT_MARK, PORT134_FN1),
+ PINMUX_DATA(MSIOF7_TXD_MARK, PORT134_FN2),
+ PINMUX_DATA(STP_ISD_1_MARK, PORT134_FN3),
+
+ /* Port160 - Port178 */
+ PINMUX_DATA(IRQ20_MARK, PORT160_FN0),
+ PINMUX_DATA(IRQ21_MARK, PORT161_FN0),
+ PINMUX_DATA(IRQ22_MARK, PORT162_FN0),
+ PINMUX_DATA(IRQ23_MARK, PORT163_FN0),
+ PINMUX_DATA(MMCD0_0_MARK, PORT164_FN1),
+ PINMUX_DATA(MMCD0_1_MARK, PORT165_FN1),
+ PINMUX_DATA(MMCD0_2_MARK, PORT166_FN1),
+ PINMUX_DATA(MMCD0_3_MARK, PORT167_FN1),
+ PINMUX_DATA(MMCD0_4_MARK, PORT168_FN1),
+ PINMUX_DATA(MMCD0_5_MARK, PORT169_FN1),
+ PINMUX_DATA(MMCD0_6_MARK, PORT170_FN1),
+ PINMUX_DATA(MMCD0_7_MARK, PORT171_FN1),
+ PINMUX_DATA(MMCCMD0_MARK, PORT172_FN1),
+ PINMUX_DATA(MMCCLK0_MARK, PORT173_FN1),
+ PINMUX_DATA(MMCRST_MARK, PORT174_FN1),
+ PINMUX_DATA(IRQ24_MARK, PORT175_FN0),
+ PINMUX_DATA(IRQ25_MARK, PORT176_FN0),
+ PINMUX_DATA(IRQ26_MARK, PORT177_FN0),
+ PINMUX_DATA(IRQ27_MARK, PORT178_FN0),
+
+ /* Port192 - Port200 FN1 */
+ PINMUX_DATA(A10_MARK, PORT192_FN1),
+ PINMUX_DATA(A9_MARK, PORT193_FN1),
+ PINMUX_DATA(A8_MARK, PORT194_FN1),
+ PINMUX_DATA(A7_MARK, PORT195_FN1),
+ PINMUX_DATA(A6_MARK, PORT196_FN1),
+ PINMUX_DATA(A5_MARK, PORT197_FN1),
+ PINMUX_DATA(A4_MARK, PORT198_FN1),
+ PINMUX_DATA(A3_MARK, PORT199_FN1),
+ PINMUX_DATA(A2_MARK, PORT200_FN1),
+
+ /* Port192 - Port200 FN2 */
+ PINMUX_DATA(MMCD1_7_MARK, PORT192_FN2),
+ PINMUX_DATA(MMCD1_6_MARK, PORT193_FN2),
+ PINMUX_DATA(MMCD1_5_MARK, PORT194_FN2),
+ PINMUX_DATA(MMCD1_4_MARK, PORT195_FN2),
+ PINMUX_DATA(MMCD1_3_MARK, PORT196_FN2),
+ PINMUX_DATA(MMCD1_2_MARK, PORT197_FN2),
+ PINMUX_DATA(MMCD1_1_MARK, PORT198_FN2),
+ PINMUX_DATA(MMCD1_0_MARK, PORT199_FN2),
+ PINMUX_DATA(MMCCMD1_MARK, PORT200_FN2),
+
+ /* Port192 - Port200 IRQ */
+ PINMUX_DATA(IRQ31_MARK, PORT192_FN0),
+ PINMUX_DATA(IRQ32_MARK, PORT193_FN0),
+ PINMUX_DATA(IRQ33_MARK, PORT194_FN0),
+ PINMUX_DATA(IRQ34_MARK, PORT195_FN0),
+ PINMUX_DATA(IRQ35_MARK, PORT196_FN0),
+ PINMUX_DATA(IRQ36_MARK, PORT197_FN0),
+ PINMUX_DATA(IRQ37_MARK, PORT198_FN0),
+ PINMUX_DATA(IRQ38_MARK, PORT199_FN0),
+ PINMUX_DATA(IRQ39_MARK, PORT200_FN0),
+
+ /* Port201 */
+ PINMUX_DATA(A1_MARK, PORT201_FN1),
+
+ /* Port202 */
+ PINMUX_DATA(A0_MARK, PORT202_FN1),
+ PINMUX_DATA(BS_MARK, PORT202_FN2),
+
+ /* Port203 */
+ PINMUX_DATA(CKO_MARK, PORT203_FN1),
+ PINMUX_DATA(MMCCLK1_MARK, PORT203_FN2),
+
+ /* Port204 */
+ PINMUX_DATA(CS0_N_MARK, PORT204_FN1),
+ PINMUX_DATA(SIM0_GPO1_MARK, PORT204_FN5),
+
+ /* Port205 */
+ PINMUX_DATA(CS2_N_MARK, PORT205_FN1),
+ PINMUX_DATA(SIM0_GPO2_MARK, PORT205_FN5),
+
+ /* Port206 */
+ PINMUX_DATA(CS4_N_MARK, PORT206_FN1),
+ PINMUX_DATA(VIO_VD_MARK, PORT206_FN2),
+ PINMUX_DATA(SIM1_GPO0_MARK, PORT206_FN5),
+
+ /* Port207 - Port212 FN1 */
+ PINMUX_DATA(D15_MARK, PORT207_FN1),
+ PINMUX_DATA(D14_MARK, PORT208_FN1),
+ PINMUX_DATA(D13_MARK, PORT209_FN1),
+ PINMUX_DATA(D12_MARK, PORT210_FN1),
+ PINMUX_DATA(D11_MARK, PORT211_FN1),
+ PINMUX_DATA(D10_MARK, PORT212_FN1),
+
+ /* Port207 - Port212 FN5 */
+ PINMUX_DATA(GIO_OUT15_MARK, PORT207_FN5),
+ PINMUX_DATA(GIO_OUT14_MARK, PORT208_FN5),
+ PINMUX_DATA(GIO_OUT13_MARK, PORT209_FN5),
+ PINMUX_DATA(GIO_OUT12_MARK, PORT210_FN5),
+ PINMUX_DATA(WGM_TXP2_MARK, PORT211_FN5),
+ PINMUX_DATA(WGM_GPS_TIMEM_ASK_RFCLK_MARK, PORT212_FN5),
+
+ /* Port213 - Port222 FN1 */
+ PINMUX_DATA(D9_MARK, PORT213_FN1),
+ PINMUX_DATA(D8_MARK, PORT214_FN1),
+ PINMUX_DATA(D7_MARK, PORT215_FN1),
+ PINMUX_DATA(D6_MARK, PORT216_FN1),
+ PINMUX_DATA(D5_MARK, PORT217_FN1),
+ PINMUX_DATA(D4_MARK, PORT218_FN1),
+ PINMUX_DATA(D3_MARK, PORT219_FN1),
+ PINMUX_DATA(D2_MARK, PORT220_FN1),
+ PINMUX_DATA(D1_MARK, PORT221_FN1),
+ PINMUX_DATA(D0_MARK, PORT222_FN1),
+
+ /* Port213 - Port222 FN2 */
+ PINMUX_DATA(VIO_D9_MARK, PORT213_FN2),
+ PINMUX_DATA(VIO_D8_MARK, PORT214_FN2),
+ PINMUX_DATA(VIO_D7_MARK, PORT215_FN2),
+ PINMUX_DATA(VIO_D6_MARK, PORT216_FN2),
+ PINMUX_DATA(VIO_D5_MARK, PORT217_FN2),
+ PINMUX_DATA(VIO_D4_MARK, PORT218_FN2),
+ PINMUX_DATA(VIO_D3_MARK, PORT219_FN2),
+ PINMUX_DATA(VIO_D2_MARK, PORT220_FN2),
+ PINMUX_DATA(VIO_D1_MARK, PORT221_FN2),
+ PINMUX_DATA(VIO_D0_MARK, PORT222_FN2),
+
+ /* Port213 - Port222 FN5 */
+ PINMUX_DATA(GIO_OUT9_MARK, PORT213_FN5),
+ PINMUX_DATA(GIO_OUT8_MARK, PORT214_FN5),
+ PINMUX_DATA(GIO_OUT7_MARK, PORT215_FN5),
+ PINMUX_DATA(GIO_OUT6_MARK, PORT216_FN5),
+ PINMUX_DATA(GIO_OUT5_217_MARK, PORT217_FN5),
+ PINMUX_DATA(GIO_OUT4_218_MARK, PORT218_FN5),
+ PINMUX_DATA(GIO_OUT3_219_MARK, PORT219_FN5),
+ PINMUX_DATA(GIO_OUT2_220_MARK, PORT220_FN5),
+ PINMUX_DATA(GIO_OUT1_221_MARK, PORT221_FN5),
+ PINMUX_DATA(GIO_OUT0_222_MARK, PORT222_FN5),
+
+ /* Port224 */
+ PINMUX_DATA(RDWR_224_MARK, PORT224_FN1),
+ PINMUX_DATA(VIO_HD_MARK, PORT224_FN2),
+ PINMUX_DATA(SIM1_GPO2_MARK, PORT224_FN5),
+
+ /* Port225 */
+ PINMUX_DATA(RD_N_MARK, PORT225_FN1),
+
+ /* Port226 */
+ PINMUX_DATA(WAIT_N_MARK, PORT226_FN1),
+ PINMUX_DATA(VIO_CLK_MARK, PORT226_FN2),
+ PINMUX_DATA(SIM1_GPO1_MARK, PORT226_FN5),
+
+ /* Port227 */
+ PINMUX_DATA(WE0_N_MARK, PORT227_FN1),
+ PINMUX_DATA(RDWR_227_MARK, PORT227_FN2),
+
+ /* Port228 */
+ PINMUX_DATA(WE1_N_MARK, PORT228_FN1),
+ PINMUX_DATA(SIM0_GPO0_MARK, PORT228_FN5),
+
+ /* Port229 */
+ PINMUX_DATA(PWMO_MARK, PORT229_FN1),
+ PINMUX_DATA(VIO_CKO1_229_MARK, PORT229_FN2),
+
+ /* Port230 */
+ PINMUX_DATA(SLIM_CLK_MARK, PORT230_FN1),
+ PINMUX_DATA(VIO_CKO4_230_MARK, PORT230_FN2),
+
+ /* Port231 */
+ PINMUX_DATA(SLIM_DATA_MARK, PORT231_FN1),
+ PINMUX_DATA(VIO_CKO5_231_MARK, PORT231_FN2),
+
+ /* Port232 */
+ PINMUX_DATA(VIO_CKO2_232_MARK, PORT232_FN2),
+ PINMUX_DATA(SF_PORT_0_232_MARK, PORT232_FN4),
+
+ /* Port233 */
+ PINMUX_DATA(VIO_CKO3_233_MARK, PORT233_FN2),
+ PINMUX_DATA(SF_PORT_1_233_MARK, PORT233_FN4),
+
+ /* Port234 */
+ PINMUX_DATA(FSIACK_MARK, PORT234_FN1),
+ PINMUX_DATA(PDM3_CLK_234_MARK, PORT234_FN2),
+ PINMUX_DATA(ISP_IRIS1_234_MARK, PORT234_FN3),
+
+ /* Port235 */
+ PINMUX_DATA(FSIAISLD_MARK, PORT235_FN1),
+ PINMUX_DATA(PDM3_DATA_235_MARK, PORT235_FN2, MSEL3CR_12_1),
+
+ /* Port236 */
+ PINMUX_DATA(FSIAOMC_MARK, PORT236_FN1),
+ PINMUX_DATA(PDM0_OUTCLK_236_MARK, PORT236_FN2),
+ PINMUX_DATA(ISP_IRIS0_236_MARK, PORT236_FN3),
+
+ /* Port237 */
+ PINMUX_DATA(FSIAOLR_MARK, PORT237_FN1),
+ PINMUX_DATA(FSIAILR_MARK, PORT237_FN2),
+
+ /* Port238 */
+ PINMUX_DATA(FSIAOBT_MARK, PORT238_FN1),
+ PINMUX_DATA(FSIAIBT_MARK, PORT238_FN2),
+
+ /* Port239 */
+ PINMUX_DATA(FSIAOSLD_MARK, PORT239_FN1),
+ PINMUX_DATA(PDM0_OUTDATA_239_MARK, PORT239_FN2),
+
+ /* Port240 */
+ PINMUX_DATA(FSIBISLD_MARK, PORT240_FN1),
+
+ /* Port241 */
+ PINMUX_DATA(FSIBOLR_MARK, PORT241_FN1),
+ PINMUX_DATA(FSIBILR_MARK, PORT241_FN2),
+
+ /* Port242 */
+ PINMUX_DATA(FSIBOMC_MARK, PORT242_FN1),
+ PINMUX_DATA(ISP_SHUTTER1_242_MARK, PORT242_FN3),
+
+ /* Port243 */
+ PINMUX_DATA(FSIBOBT_MARK, PORT243_FN1),
+ PINMUX_DATA(FSIBIBT_MARK, PORT243_FN2),
+
+ /* Port244 */
+ PINMUX_DATA(FSIBOSLD_MARK, PORT244_FN1),
+ PINMUX_DATA(FSIASPDIF_MARK, PORT244_FN2),
+
+ /* Port245 */
+ PINMUX_DATA(FSIBCK_MARK, PORT245_FN1),
+ PINMUX_DATA(ISP_SHUTTER0_245_MARK, PORT245_FN3),
+
+ /* Port246 - Port250 FN1 */
+ PINMUX_DATA(ISP_IRIS1_246_MARK, PORT246_FN1),
+ PINMUX_DATA(ISP_IRIS0_247_MARK, PORT247_FN1),
+ PINMUX_DATA(ISP_SHUTTER1_248_MARK, PORT248_FN1),
+ PINMUX_DATA(ISP_SHUTTER0_249_MARK, PORT249_FN1),
+ PINMUX_DATA(ISP_STROBE_250_MARK, PORT250_FN1),
+
+ /* Port256 - Port258 */
+ PINMUX_DATA(MSIOF0_SYNC_MARK, PORT256_FN1),
+ PINMUX_DATA(MSIOF0_RXD_MARK, PORT257_FN1),
+ PINMUX_DATA(MSIOF0_SCK_MARK, PORT258_FN1),
+
+ /* Port259 */
+ PINMUX_DATA(MSIOF0_SS2_MARK, PORT259_FN1),
+ PINMUX_DATA(VIO_CKO3_259_MARK, PORT259_FN3),
+
+ /* Port260 */
+ PINMUX_DATA(MSIOF0_TXD_MARK, PORT260_FN1),
+
+ /* Port261 */
+ PINMUX_DATA(SCIFB1_SCK_261_MARK, PORT261_FN2),
+ PINMUX_DATA(CHSCIF1_HSCK_MARK, PORT261_FN7),
+
+ /* Port262 */
+ PINMUX_DATA(SCIFB2_SCK_262_MARK, PORT262_FN2),
+
+ /* Port263 - Port266 FN1 */
+ PINMUX_DATA(MSIOF1_SS2_MARK, PORT263_FN1),
+ PINMUX_DATA(MSIOF1_TXD_MARK, PORT264_FN1),
+ PINMUX_DATA(MSIOF1_RXD_MARK, PORT265_FN1),
+ PINMUX_DATA(MSIOF1_SS1_MARK, PORT266_FN1),
+
+ /* Port263 - Port266 FN4 */
+ PINMUX_DATA(MSIOF5_SS2_MARK, PORT263_FN4),
+ PINMUX_DATA(MSIOF5_TXD_MARK, PORT264_FN4),
+ PINMUX_DATA(MSIOF5_RXD_MARK, PORT265_FN4),
+ PINMUX_DATA(MSIOF5_SS1_MARK, PORT266_FN4),
+
+ /* Port267 */
+ PINMUX_DATA(MSIOF0_SS1_MARK, PORT267_FN1),
+
+ /* Port268 */
+ PINMUX_DATA(MSIOF1_SCK_MARK, PORT268_FN1),
+ PINMUX_DATA(MSIOF5_SCK_MARK, PORT268_FN4),
+
+ /* Port269 */
+ PINMUX_DATA(MSIOF1_SYNC_MARK, PORT269_FN1),
+ PINMUX_DATA(MSIOF5_SYNC_MARK, PORT269_FN4),
+
+ /* Port270 - Port273 FN1 */
+ PINMUX_DATA(MSIOF2_SS1_MARK, PORT270_FN1),
+ PINMUX_DATA(MSIOF2_SS2_MARK, PORT271_FN1),
+ PINMUX_DATA(MSIOF3_SS2_MARK, PORT272_FN1),
+ PINMUX_DATA(MSIOF3_SS1_MARK, PORT273_FN1),
+
+ /* Port270 - Port273 FN3 */
+ PINMUX_DATA(VIO_CKO5_270_MARK, PORT270_FN3),
+ PINMUX_DATA(VIO_CKO2_271_MARK, PORT271_FN3),
+ PINMUX_DATA(VIO_CKO1_272_MARK, PORT272_FN3),
+ PINMUX_DATA(VIO_CKO4_273_MARK, PORT273_FN3),
+
+ /* Port274 */
+ PINMUX_DATA(MSIOF4_SS2_MARK, PORT274_FN1),
+ PINMUX_DATA(TPU1TO0_MARK, PORT274_FN4),
+
+ /* Port275 - Port280 */
+ PINMUX_DATA(IC_DP_MARK, PORT275_FN1),
+ PINMUX_DATA(SIM0_RST_MARK, PORT276_FN1),
+ PINMUX_DATA(IC_DM_MARK, PORT277_FN1),
+ PINMUX_DATA(SIM0_BSICOMP_MARK, PORT278_FN1),
+ PINMUX_DATA(SIM0_CLK_MARK, PORT279_FN1),
+ PINMUX_DATA(SIM0_IO_MARK, PORT280_FN1),
+
+ /* Port281 */
+ PINMUX_DATA(SIM1_IO_MARK, PORT281_FN1),
+ PINMUX_DATA(PDM2_DATA_281_MARK, PORT281_FN2, MSEL3CR_12_1),
+
+ /* Port282 */
+ PINMUX_DATA(SIM1_CLK_MARK, PORT282_FN1),
+ PINMUX_DATA(PDM2_CLK_282_MARK, PORT282_FN2),
+
+ /* Port283 */
+ PINMUX_DATA(SIM1_RST_MARK, PORT283_FN1),
+
+ /* Port289 */
+ PINMUX_DATA(SDHID1_0_MARK, PORT289_FN1),
+ PINMUX_DATA(STMDATA0_2_MARK, PORT289_FN3),
+
+ /* Port290 */
+ PINMUX_DATA(SDHID1_1_MARK, PORT290_FN1),
+ PINMUX_DATA(STMDATA1_2_MARK, PORT290_FN3),
+ PINMUX_DATA(IRQ51_MARK, PORT290_FN0),
+
+ /* Port291 - Port294 FN1 */
+ PINMUX_DATA(SDHID1_2_MARK, PORT291_FN1),
+ PINMUX_DATA(SDHID1_3_MARK, PORT292_FN1),
+ PINMUX_DATA(SDHICLK1_MARK, PORT293_FN1),
+ PINMUX_DATA(SDHICMD1_MARK, PORT294_FN1),
+
+ /* Port291 - Port294 FN3 */
+ PINMUX_DATA(STMDATA2_2_MARK, PORT291_FN3),
+ PINMUX_DATA(STMDATA3_2_MARK, PORT292_FN3),
+ PINMUX_DATA(STMCLK_2_MARK, PORT293_FN3),
+ PINMUX_DATA(STMSIDI_2_MARK, PORT294_FN3),
+
+ /* Port295 */
+ PINMUX_DATA(SDHID2_0_MARK, PORT295_FN1),
+ PINMUX_DATA(MSIOF4_TXD_MARK, PORT295_FN2),
+ PINMUX_DATA(SCIFB2_TXD_295_MARK, PORT295_FN3, MSEL3CR_10_1),
+ PINMUX_DATA(MSIOF6_TXD_MARK, PORT295_FN4),
+
+ /* Port296 */
+ PINMUX_DATA(SDHID2_1_MARK, PORT296_FN1),
+ PINMUX_DATA(MSIOF6_SS2_MARK, PORT296_FN4),
+ PINMUX_DATA(IRQ52_MARK, PORT296_FN0),
+
+ /* Port297 - Port300 FN1 */
+ PINMUX_DATA(SDHID2_2_MARK, PORT297_FN1),
+ PINMUX_DATA(SDHID2_3_MARK, PORT298_FN1),
+ PINMUX_DATA(SDHICLK2_MARK, PORT299_FN1),
+ PINMUX_DATA(SDHICMD2_MARK, PORT300_FN1),
+
+ /* Port297 - Port300 FN2 */
+ PINMUX_DATA(MSIOF4_RXD_MARK, PORT297_FN2),
+ PINMUX_DATA(MSIOF4_SYNC_MARK, PORT298_FN2),
+ PINMUX_DATA(MSIOF4_SCK_MARK, PORT299_FN2),
+ PINMUX_DATA(MSIOF4_SS1_MARK, PORT300_FN2),
+
+ /* Port297 - Port300 FN3 */
+ PINMUX_DATA(SCIFB2_RXD_297_MARK, PORT297_FN3, MSEL3CR_10_1),
+ PINMUX_DATA(SCIFB2_CTS_298_MARK, PORT298_FN3, MSEL3CR_10_1),
+ PINMUX_DATA(SCIFB2_SCK_299_MARK, PORT299_FN3),
+ PINMUX_DATA(SCIFB2_RTS_300_MARK, PORT300_FN3),
+
+ /* Port297 - Port300 FN4 */
+ PINMUX_DATA(MSIOF6_RXD_MARK, PORT297_FN4),
+ PINMUX_DATA(MSIOF6_SYNC_MARK, PORT298_FN4),
+ PINMUX_DATA(MSIOF6_SCK_MARK, PORT299_FN4),
+ PINMUX_DATA(MSIOF6_SS1_MARK, PORT300_FN4),
+
+ /* Port301 */
+ PINMUX_DATA(SDHICD0_MARK, PORT301_FN1),
+ PINMUX_DATA(IRQ50_MARK, PORT301_FN0),
+
+ /* Port302 - Port306 FN1 */
+ PINMUX_DATA(SDHID0_0_MARK, PORT302_FN1),
+ PINMUX_DATA(SDHID0_1_MARK, PORT303_FN1),
+ PINMUX_DATA(SDHID0_2_MARK, PORT304_FN1),
+ PINMUX_DATA(SDHID0_3_MARK, PORT305_FN1),
+ PINMUX_DATA(SDHICMD0_MARK, PORT306_FN1),
+
+ /* Port302 - Port306 FN3 */
+ PINMUX_DATA(STMDATA0_1_MARK, PORT302_FN3),
+ PINMUX_DATA(STMDATA1_1_MARK, PORT303_FN3),
+ PINMUX_DATA(STMDATA2_1_MARK, PORT304_FN3),
+ PINMUX_DATA(STMDATA3_1_MARK, PORT305_FN3),
+ PINMUX_DATA(STMSIDI_1_MARK, PORT306_FN3),
+
+ /* Port307 */
+ PINMUX_DATA(SDHIWP0_MARK, PORT307_FN1),
+
+ /* Port308 */
+ PINMUX_DATA(SDHICLK0_MARK, PORT308_FN1),
+ PINMUX_DATA(STMCLK_1_MARK, PORT308_FN3),
+
+ /* Port320 - Port329 */
+ PINMUX_DATA(IRQ16_MARK, PORT320_FN0),
+ PINMUX_DATA(IRQ17_MARK, PORT321_FN0),
+ PINMUX_DATA(IRQ28_MARK, PORT322_FN0),
+ PINMUX_DATA(IRQ29_MARK, PORT323_FN0),
+ PINMUX_DATA(IRQ30_MARK, PORT324_FN0),
+ PINMUX_DATA(IRQ53_MARK, PORT325_FN0),
+ PINMUX_DATA(IRQ54_MARK, PORT326_FN0),
+ PINMUX_DATA(IRQ55_MARK, PORT327_FN0),
+ PINMUX_DATA(IRQ56_MARK, PORT328_FN0),
+ PINMUX_DATA(IRQ57_MARK, PORT329_FN0),
+};
+
+#define R8A73A4_PIN(pin, cfgs) \
+ { \
+ .name = __stringify(PORT##pin), \
+ .enum_id = PORT##pin##_DATA, \
+ .configs = cfgs, \
+ }
+
+#define __O (SH_PFC_PIN_CFG_OUTPUT)
+#define __IO (SH_PFC_PIN_CFG_INPUT | SH_PFC_PIN_CFG_OUTPUT)
+#define __PUD (SH_PFC_PIN_CFG_PULL_DOWN | SH_PFC_PIN_CFG_PULL_UP)
+
+#define R8A73A4_PIN_IO_PU_PD(pin) R8A73A4_PIN(pin, __IO | __PUD)
+#define R8A73A4_PIN_O(pin) R8A73A4_PIN(pin, __O)
+
+static struct sh_pfc_pin pinmux_pins[] = {
+ R8A73A4_PIN_IO_PU_PD(0), R8A73A4_PIN_IO_PU_PD(1),
+ R8A73A4_PIN_IO_PU_PD(2), R8A73A4_PIN_IO_PU_PD(3),
+ R8A73A4_PIN_IO_PU_PD(4), R8A73A4_PIN_IO_PU_PD(5),
+ R8A73A4_PIN_IO_PU_PD(6), R8A73A4_PIN_IO_PU_PD(7),
+ R8A73A4_PIN_IO_PU_PD(8), R8A73A4_PIN_IO_PU_PD(9),
+ R8A73A4_PIN_IO_PU_PD(10), R8A73A4_PIN_IO_PU_PD(11),
+ R8A73A4_PIN_IO_PU_PD(12), R8A73A4_PIN_IO_PU_PD(13),
+ R8A73A4_PIN_IO_PU_PD(14), R8A73A4_PIN_IO_PU_PD(15),
+ R8A73A4_PIN_IO_PU_PD(16), R8A73A4_PIN_IO_PU_PD(17),
+ R8A73A4_PIN_IO_PU_PD(18), R8A73A4_PIN_IO_PU_PD(19),
+ R8A73A4_PIN_IO_PU_PD(20), R8A73A4_PIN_IO_PU_PD(21),
+ R8A73A4_PIN_IO_PU_PD(22), R8A73A4_PIN_IO_PU_PD(23),
+ R8A73A4_PIN_IO_PU_PD(24), R8A73A4_PIN_IO_PU_PD(25),
+ R8A73A4_PIN_IO_PU_PD(26), R8A73A4_PIN_IO_PU_PD(27),
+ R8A73A4_PIN_IO_PU_PD(28), R8A73A4_PIN_IO_PU_PD(29),
+ R8A73A4_PIN_IO_PU_PD(30),
+ R8A73A4_PIN_IO_PU_PD(32), R8A73A4_PIN_IO_PU_PD(33),
+ R8A73A4_PIN_IO_PU_PD(34), R8A73A4_PIN_IO_PU_PD(35),
+ R8A73A4_PIN_IO_PU_PD(36), R8A73A4_PIN_IO_PU_PD(37),
+ R8A73A4_PIN_IO_PU_PD(38), R8A73A4_PIN_IO_PU_PD(39),
+ R8A73A4_PIN_IO_PU_PD(40),
+ R8A73A4_PIN_IO_PU_PD(64), R8A73A4_PIN_IO_PU_PD(65),
+ R8A73A4_PIN_IO_PU_PD(66), R8A73A4_PIN_IO_PU_PD(67),
+ R8A73A4_PIN_IO_PU_PD(68), R8A73A4_PIN_IO_PU_PD(69),
+ R8A73A4_PIN_IO_PU_PD(70), R8A73A4_PIN_IO_PU_PD(71),
+ R8A73A4_PIN_IO_PU_PD(72), R8A73A4_PIN_IO_PU_PD(73),
+ R8A73A4_PIN_O(74), R8A73A4_PIN_IO_PU_PD(75),
+ R8A73A4_PIN_IO_PU_PD(76), R8A73A4_PIN_IO_PU_PD(77),
+ R8A73A4_PIN_IO_PU_PD(78), R8A73A4_PIN_IO_PU_PD(79),
+ R8A73A4_PIN_IO_PU_PD(80), R8A73A4_PIN_IO_PU_PD(81),
+ R8A73A4_PIN_IO_PU_PD(82), R8A73A4_PIN_IO_PU_PD(83),
+ R8A73A4_PIN_IO_PU_PD(84), R8A73A4_PIN_IO_PU_PD(85),
+ R8A73A4_PIN_IO_PU_PD(96), R8A73A4_PIN_IO_PU_PD(97),
+ R8A73A4_PIN_IO_PU_PD(98), R8A73A4_PIN_IO_PU_PD(99),
+ R8A73A4_PIN_IO_PU_PD(100), R8A73A4_PIN_IO_PU_PD(101),
+ R8A73A4_PIN_IO_PU_PD(102), R8A73A4_PIN_IO_PU_PD(103),
+ R8A73A4_PIN_IO_PU_PD(104), R8A73A4_PIN_IO_PU_PD(105),
+ R8A73A4_PIN_IO_PU_PD(106), R8A73A4_PIN_IO_PU_PD(107),
+ R8A73A4_PIN_IO_PU_PD(108), R8A73A4_PIN_IO_PU_PD(109),
+ R8A73A4_PIN_IO_PU_PD(110), R8A73A4_PIN_IO_PU_PD(111),
+ R8A73A4_PIN_IO_PU_PD(112), R8A73A4_PIN_IO_PU_PD(113),
+ R8A73A4_PIN_IO_PU_PD(114), R8A73A4_PIN_IO_PU_PD(115),
+ R8A73A4_PIN_IO_PU_PD(116), R8A73A4_PIN_IO_PU_PD(117),
+ R8A73A4_PIN_IO_PU_PD(118), R8A73A4_PIN_IO_PU_PD(119),
+ R8A73A4_PIN_IO_PU_PD(120), R8A73A4_PIN_IO_PU_PD(121),
+ R8A73A4_PIN_IO_PU_PD(122), R8A73A4_PIN_IO_PU_PD(123),
+ R8A73A4_PIN_IO_PU_PD(124), R8A73A4_PIN_IO_PU_PD(125),
+ R8A73A4_PIN_IO_PU_PD(126),
+ R8A73A4_PIN_IO_PU_PD(128), R8A73A4_PIN_IO_PU_PD(129),
+ R8A73A4_PIN_IO_PU_PD(130), R8A73A4_PIN_IO_PU_PD(131),
+ R8A73A4_PIN_IO_PU_PD(132), R8A73A4_PIN_IO_PU_PD(133),
+ R8A73A4_PIN_IO_PU_PD(134),
+ R8A73A4_PIN_IO_PU_PD(160), R8A73A4_PIN_IO_PU_PD(161),
+ R8A73A4_PIN_IO_PU_PD(162), R8A73A4_PIN_IO_PU_PD(163),
+ R8A73A4_PIN_IO_PU_PD(164), R8A73A4_PIN_IO_PU_PD(165),
+ R8A73A4_PIN_IO_PU_PD(166), R8A73A4_PIN_IO_PU_PD(167),
+ R8A73A4_PIN_IO_PU_PD(168), R8A73A4_PIN_IO_PU_PD(169),
+ R8A73A4_PIN_IO_PU_PD(170), R8A73A4_PIN_IO_PU_PD(171),
+ R8A73A4_PIN_IO_PU_PD(172), R8A73A4_PIN_IO_PU_PD(173),
+ R8A73A4_PIN_IO_PU_PD(174), R8A73A4_PIN_IO_PU_PD(175),
+ R8A73A4_PIN_IO_PU_PD(176), R8A73A4_PIN_IO_PU_PD(177),
+ R8A73A4_PIN_IO_PU_PD(178),
+ R8A73A4_PIN_IO_PU_PD(192), R8A73A4_PIN_IO_PU_PD(193),
+ R8A73A4_PIN_IO_PU_PD(194), R8A73A4_PIN_IO_PU_PD(195),
+ R8A73A4_PIN_IO_PU_PD(196), R8A73A4_PIN_IO_PU_PD(197),
+ R8A73A4_PIN_IO_PU_PD(198), R8A73A4_PIN_IO_PU_PD(199),
+ R8A73A4_PIN_IO_PU_PD(200), R8A73A4_PIN_IO_PU_PD(201),
+ R8A73A4_PIN_IO_PU_PD(202), R8A73A4_PIN_IO_PU_PD(203),
+ R8A73A4_PIN_IO_PU_PD(204), R8A73A4_PIN_IO_PU_PD(205),
+ R8A73A4_PIN_IO_PU_PD(206), R8A73A4_PIN_IO_PU_PD(207),
+ R8A73A4_PIN_IO_PU_PD(208), R8A73A4_PIN_IO_PU_PD(209),
+ R8A73A4_PIN_IO_PU_PD(210), R8A73A4_PIN_IO_PU_PD(211),
+ R8A73A4_PIN_IO_PU_PD(212), R8A73A4_PIN_IO_PU_PD(213),
+ R8A73A4_PIN_IO_PU_PD(214), R8A73A4_PIN_IO_PU_PD(215),
+ R8A73A4_PIN_IO_PU_PD(216), R8A73A4_PIN_IO_PU_PD(217),
+ R8A73A4_PIN_IO_PU_PD(218), R8A73A4_PIN_IO_PU_PD(219),
+ R8A73A4_PIN_IO_PU_PD(220), R8A73A4_PIN_IO_PU_PD(221),
+ R8A73A4_PIN_IO_PU_PD(222),
+ R8A73A4_PIN_IO_PU_PD(224), R8A73A4_PIN_IO_PU_PD(225),
+ R8A73A4_PIN_IO_PU_PD(226), R8A73A4_PIN_IO_PU_PD(227),
+ R8A73A4_PIN_IO_PU_PD(228), R8A73A4_PIN_IO_PU_PD(229),
+ R8A73A4_PIN_IO_PU_PD(230), R8A73A4_PIN_IO_PU_PD(231),
+ R8A73A4_PIN_IO_PU_PD(232), R8A73A4_PIN_IO_PU_PD(233),
+ R8A73A4_PIN_IO_PU_PD(234), R8A73A4_PIN_IO_PU_PD(235),
+ R8A73A4_PIN_IO_PU_PD(236), R8A73A4_PIN_IO_PU_PD(237),
+ R8A73A4_PIN_IO_PU_PD(238), R8A73A4_PIN_IO_PU_PD(239),
+ R8A73A4_PIN_IO_PU_PD(240), R8A73A4_PIN_IO_PU_PD(241),
+ R8A73A4_PIN_IO_PU_PD(242), R8A73A4_PIN_IO_PU_PD(243),
+ R8A73A4_PIN_IO_PU_PD(244), R8A73A4_PIN_IO_PU_PD(245),
+ R8A73A4_PIN_IO_PU_PD(246), R8A73A4_PIN_IO_PU_PD(247),
+ R8A73A4_PIN_IO_PU_PD(248), R8A73A4_PIN_IO_PU_PD(249),
+ R8A73A4_PIN_IO_PU_PD(250),
+ R8A73A4_PIN_IO_PU_PD(256), R8A73A4_PIN_IO_PU_PD(257),
+ R8A73A4_PIN_IO_PU_PD(258), R8A73A4_PIN_IO_PU_PD(259),
+ R8A73A4_PIN_IO_PU_PD(260), R8A73A4_PIN_IO_PU_PD(261),
+ R8A73A4_PIN_IO_PU_PD(262), R8A73A4_PIN_IO_PU_PD(263),
+ R8A73A4_PIN_IO_PU_PD(264), R8A73A4_PIN_IO_PU_PD(265),
+ R8A73A4_PIN_IO_PU_PD(266), R8A73A4_PIN_IO_PU_PD(267),
+ R8A73A4_PIN_IO_PU_PD(268), R8A73A4_PIN_IO_PU_PD(269),
+ R8A73A4_PIN_IO_PU_PD(270), R8A73A4_PIN_IO_PU_PD(271),
+ R8A73A4_PIN_IO_PU_PD(272), R8A73A4_PIN_IO_PU_PD(273),
+ R8A73A4_PIN_IO_PU_PD(274), R8A73A4_PIN_IO_PU_PD(275),
+ R8A73A4_PIN_IO_PU_PD(276), R8A73A4_PIN_IO_PU_PD(277),
+ R8A73A4_PIN_IO_PU_PD(278), R8A73A4_PIN_IO_PU_PD(279),
+ R8A73A4_PIN_IO_PU_PD(280), R8A73A4_PIN_IO_PU_PD(281),
+ R8A73A4_PIN_IO_PU_PD(282), R8A73A4_PIN_IO_PU_PD(283),
+ R8A73A4_PIN_O(288), R8A73A4_PIN_IO_PU_PD(289),
+ R8A73A4_PIN_IO_PU_PD(290), R8A73A4_PIN_IO_PU_PD(291),
+ R8A73A4_PIN_IO_PU_PD(292), R8A73A4_PIN_IO_PU_PD(293),
+ R8A73A4_PIN_IO_PU_PD(294), R8A73A4_PIN_IO_PU_PD(295),
+ R8A73A4_PIN_IO_PU_PD(296), R8A73A4_PIN_IO_PU_PD(297),
+ R8A73A4_PIN_IO_PU_PD(298), R8A73A4_PIN_IO_PU_PD(299),
+ R8A73A4_PIN_IO_PU_PD(300), R8A73A4_PIN_IO_PU_PD(301),
+ R8A73A4_PIN_IO_PU_PD(302), R8A73A4_PIN_IO_PU_PD(303),
+ R8A73A4_PIN_IO_PU_PD(304), R8A73A4_PIN_IO_PU_PD(305),
+ R8A73A4_PIN_IO_PU_PD(306), R8A73A4_PIN_IO_PU_PD(307),
+ R8A73A4_PIN_IO_PU_PD(308),
+ R8A73A4_PIN_IO_PU_PD(320), R8A73A4_PIN_IO_PU_PD(321),
+ R8A73A4_PIN_IO_PU_PD(322), R8A73A4_PIN_IO_PU_PD(323),
+ R8A73A4_PIN_IO_PU_PD(324), R8A73A4_PIN_IO_PU_PD(325),
+ R8A73A4_PIN_IO_PU_PD(326), R8A73A4_PIN_IO_PU_PD(327),
+ R8A73A4_PIN_IO_PU_PD(328), R8A73A4_PIN_IO_PU_PD(329),
+};
+
+static const struct pinmux_range pinmux_ranges[] = {
+ {.begin = 0, .end = 30,},
+ {.begin = 32, .end = 40,},
+ {.begin = 64, .end = 85,},
+ {.begin = 96, .end = 126,},
+ {.begin = 128, .end = 134,},
+ {.begin = 160, .end = 178,},
+ {.begin = 192, .end = 222,},
+ {.begin = 224, .end = 250,},
+ {.begin = 256, .end = 283,},
+ {.begin = 288, .end = 308,},
+ {.begin = 320, .end = 329,},
+};
+
+/* - IRQC ------------------------------------------------------------------- */
+#define IRQC_PINS_MUX(pin, irq_mark) \
+static const unsigned int irqc_irq##irq_mark##_pins[] = { \
+ pin, \
+}; \
+static const unsigned int irqc_irq##irq_mark##_mux[] = { \
+ IRQ##irq_mark##_MARK, \
+}
+IRQC_PINS_MUX(0, 0);
+IRQC_PINS_MUX(1, 1);
+IRQC_PINS_MUX(2, 2);
+IRQC_PINS_MUX(3, 3);
+IRQC_PINS_MUX(4, 4);
+IRQC_PINS_MUX(5, 5);
+IRQC_PINS_MUX(6, 6);
+IRQC_PINS_MUX(7, 7);
+IRQC_PINS_MUX(8, 8);
+IRQC_PINS_MUX(9, 9);
+IRQC_PINS_MUX(10, 10);
+IRQC_PINS_MUX(11, 11);
+IRQC_PINS_MUX(12, 12);
+IRQC_PINS_MUX(13, 13);
+IRQC_PINS_MUX(14, 14);
+IRQC_PINS_MUX(15, 15);
+IRQC_PINS_MUX(66, 40);
+IRQC_PINS_MUX(84, 19);
+IRQC_PINS_MUX(85, 18);
+IRQC_PINS_MUX(102, 41);
+IRQC_PINS_MUX(103, 42);
+IRQC_PINS_MUX(109, 43);
+IRQC_PINS_MUX(110, 44);
+IRQC_PINS_MUX(111, 45);
+IRQC_PINS_MUX(112, 46);
+IRQC_PINS_MUX(113, 47);
+IRQC_PINS_MUX(114, 48);
+IRQC_PINS_MUX(115, 49);
+IRQC_PINS_MUX(160, 20);
+IRQC_PINS_MUX(161, 21);
+IRQC_PINS_MUX(162, 22);
+IRQC_PINS_MUX(163, 23);
+IRQC_PINS_MUX(175, 24);
+IRQC_PINS_MUX(176, 25);
+IRQC_PINS_MUX(177, 26);
+IRQC_PINS_MUX(178, 27);
+IRQC_PINS_MUX(192, 31);
+IRQC_PINS_MUX(193, 32);
+IRQC_PINS_MUX(194, 33);
+IRQC_PINS_MUX(195, 34);
+IRQC_PINS_MUX(196, 35);
+IRQC_PINS_MUX(197, 36);
+IRQC_PINS_MUX(198, 37);
+IRQC_PINS_MUX(199, 38);
+IRQC_PINS_MUX(200, 39);
+IRQC_PINS_MUX(290, 51);
+IRQC_PINS_MUX(296, 52);
+IRQC_PINS_MUX(301, 50);
+IRQC_PINS_MUX(320, 16);
+IRQC_PINS_MUX(321, 17);
+IRQC_PINS_MUX(322, 28);
+IRQC_PINS_MUX(323, 29);
+IRQC_PINS_MUX(324, 30);
+IRQC_PINS_MUX(325, 53);
+IRQC_PINS_MUX(326, 54);
+IRQC_PINS_MUX(327, 55);
+IRQC_PINS_MUX(328, 56);
+IRQC_PINS_MUX(329, 57);
+/* - SCIFA0 ----------------------------------------------------------------- */
+static const unsigned int scifa0_data_pins[] = {
+ /* SCIFA0_RXD, SCIFA0_TXD */
+ 117, 116,
+};
+static const unsigned int scifa0_data_mux[] = {
+ SCIFA0_RXD_MARK, SCIFA0_TXD_MARK,
+};
+static const unsigned int scifa0_clk_pins[] = {
+ /* SCIFA0_SCK */
+ 34,
+};
+static const unsigned int scifa0_clk_mux[] = {
+ SCIFA0_SCK_MARK,
+};
+static const unsigned int scifa0_ctrl_pins[] = {
+ /* SCIFA0_RTS, SCIFA0_CTS */
+ 32, 33,
+};
+static const unsigned int scifa0_ctrl_mux[] = {
+ SCIFA0_RTS_MARK, SCIFA0_CTS_MARK,
+};
+/* - SCIFA1 ----------------------------------------------------------------- */
+static const unsigned int scifa1_data_pins[] = {
+ /* SCIFA1_RXD, SCIFA1_TXD */
+ 119, 118,
+};
+static const unsigned int scifa1_data_mux[] = {
+ SCIFA1_RXD_MARK, SCIFA1_TXD_MARK,
+};
+static const unsigned int scifa1_clk_pins[] = {
+ /* SCIFA1_SCK */
+ 37,
+};
+static const unsigned int scifa1_clk_mux[] = {
+ SCIFA1_SCK_MARK,
+};
+static const unsigned int scifa1_ctrl_pins[] = {
+ /* SCIFA1_RTS, SCIFA1_CTS */
+ 35, 36,
+};
+static const unsigned int scifa1_ctrl_mux[] = {
+ SCIFA1_RTS_MARK, SCIFA1_CTS_MARK,
+};
+/* - SCIFB0 ----------------------------------------------------------------- */
+static const unsigned int scifb0_data_pins[] = {
+ /* SCIFB0_RXD, SCIFB0_TXD */
+ 123, 122,
+};
+static const unsigned int scifb0_data_mux[] = {
+ SCIFB0_RXD_MARK, SCIFB0_TXD_MARK,
+};
+static const unsigned int scifb0_clk_pins[] = {
+ /* SCIFB0_SCK */
+ 40,
+};
+static const unsigned int scifb0_clk_mux[] = {
+ SCIFB0_SCK_MARK,
+};
+static const unsigned int scifb0_ctrl_pins[] = {
+ /* SCIFB0_RTS, SCIFB0_CTS */
+ 38, 39,
+};
+static const unsigned int scifb0_ctrl_mux[] = {
+ SCIFB0_RTS_MARK, SCIFB0_CTS_MARK,
+};
+/* - SCIFB1 ----------------------------------------------------------------- */
+static const unsigned int scifb1_data_pins[] = {
+ /* SCIFB1_RXD, SCIFB1_TXD */
+ 27, 26,
+};
+static const unsigned int scifb1_data_mux[] = {
+ SCIFB1_RXD_27_MARK, SCIFB1_TXD_26_MARK,
+};
+static const unsigned int scifb1_clk_pins[] = {
+ /* SCIFB1_SCK */
+ 28,
+};
+static const unsigned int scifb1_clk_mux[] = {
+ SCIFB1_SCK_28_MARK,
+};
+static const unsigned int scifb1_ctrl_pins[] = {
+ /* SCIFB1_RTS, SCIFB1_CTS */
+ 24, 25,
+};
+static const unsigned int scifb1_ctrl_mux[] = {
+ SCIFB1_RTS_24_MARK, SCIFB1_CTS_25_MARK,
+};
+static const unsigned int scifb1_data_b_pins[] = {
+ /* SCIFB1_RXD, SCIFB1_TXD */
+ 72, 67,
+};
+static const unsigned int scifb1_data_b_mux[] = {
+ SCIFB1_RXD_72_MARK, SCIFB1_TXD_67_MARK,
+};
+static const unsigned int scifb1_clk_b_pins[] = {
+ /* SCIFB1_SCK */
+ 261,
+};
+static const unsigned int scifb1_clk_b_mux[] = {
+ SCIFB1_SCK_261_MARK,
+};
+static const unsigned int scifb1_ctrl_b_pins[] = {
+ /* SCIFB1_RTS, SCIFB1_CTS */
+ 70, 71,
+};
+static const unsigned int scifb1_ctrl_b_mux[] = {
+ SCIFB1_RTS_70_MARK, SCIFB1_CTS_71_MARK,
+};
+/* - SCIFB2 ----------------------------------------------------------------- */
+static const unsigned int scifb2_data_pins[] = {
+ /* SCIFB2_RXD, SCIFB2_TXD */
+ 69, 68,
+};
+static const unsigned int scifb2_data_mux[] = {
+ SCIFB2_RXD_69_MARK, SCIFB2_TXD_68_MARK,
+};
+static const unsigned int scifb2_clk_pins[] = {
+ /* SCIFB2_SCK */
+ 262,
+};
+static const unsigned int scifb2_clk_mux[] = {
+ SCIFB2_SCK_262_MARK,
+};
+static const unsigned int scifb2_ctrl_pins[] = {
+ /* SCIFB2_RTS, SCIFB2_CTS */
+ 73, 66,
+};
+static const unsigned int scifb2_ctrl_mux[] = {
+ SCIFB2_RTS_73_MARK, SCIFB2_CTS_66_MARK,
+};
+static const unsigned int scifb2_data_b_pins[] = {
+ /* SCIFB2_RXD, SCIFB2_TXD */
+ 297, 295,
+};
+static const unsigned int scifb2_data_b_mux[] = {
+ SCIFB2_RXD_297_MARK, SCIFB2_TXD_295_MARK,
+};
+static const unsigned int scifb2_clk_b_pins[] = {
+ /* SCIFB2_SCK */
+ 299,
+};
+static const unsigned int scifb2_clk_b_mux[] = {
+ SCIFB2_SCK_299_MARK,
+};
+static const unsigned int scifb2_ctrl_b_pins[] = {
+ /* SCIFB2_RTS, SCIFB2_CTS */
+ 300, 298,
+};
+static const unsigned int scifb2_ctrl_b_mux[] = {
+ SCIFB2_RTS_300_MARK, SCIFB2_CTS_298_MARK,
+};
+/* - SCIFB3 ----------------------------------------------------------------- */
+static const unsigned int scifb3_data_pins[] = {
+ /* SCIFB3_RXD, SCIFB3_TXD */
+ 22, 21,
+};
+static const unsigned int scifb3_data_mux[] = {
+ SCIFB3_RXD_22_MARK, SCIFB3_TXD_21_MARK,
+};
+static const unsigned int scifb3_clk_pins[] = {
+ /* SCIFB3_SCK */
+ 23,
+};
+static const unsigned int scifb3_clk_mux[] = {
+ SCIFB3_SCK_23_MARK,
+};
+static const unsigned int scifb3_ctrl_pins[] = {
+ /* SCIFB3_RTS, SCIFB3_CTS */
+ 19, 20,
+};
+static const unsigned int scifb3_ctrl_mux[] = {
+ SCIFB3_RTS_19_MARK, SCIFB3_CTS_20_MARK,
+};
+static const unsigned int scifb3_data_b_pins[] = {
+ /* SCIFB3_RXD, SCIFB3_TXD */
+ 120, 121,
+};
+static const unsigned int scifb3_data_b_mux[] = {
+ SCIFB3_RXD_120_MARK, SCIFB3_TXD_121_MARK,
+};
+static const unsigned int scifb3_clk_b_pins[] = {
+ /* SCIFB3_SCK */
+ 40,
+};
+static const unsigned int scifb3_clk_b_mux[] = {
+ SCIFB3_SCK_40_MARK,
+};
+static const unsigned int scifb3_ctrl_b_pins[] = {
+ /* SCIFB3_RTS, SCIFB3_CTS */
+ 38, 39,
+};
+static const unsigned int scifb3_ctrl_b_mux[] = {
+ SCIFB3_RTS_38_MARK, SCIFB3_CTS_39_MARK,
+};
+
+static const struct sh_pfc_pin_group pinmux_groups[] = {
+ SH_PFC_PIN_GROUP(irqc_irq0),
+ SH_PFC_PIN_GROUP(irqc_irq1),
+ SH_PFC_PIN_GROUP(irqc_irq2),
+ SH_PFC_PIN_GROUP(irqc_irq3),
+ SH_PFC_PIN_GROUP(irqc_irq4),
+ SH_PFC_PIN_GROUP(irqc_irq5),
+ SH_PFC_PIN_GROUP(irqc_irq6),
+ SH_PFC_PIN_GROUP(irqc_irq7),
+ SH_PFC_PIN_GROUP(irqc_irq8),
+ SH_PFC_PIN_GROUP(irqc_irq9),
+ SH_PFC_PIN_GROUP(irqc_irq10),
+ SH_PFC_PIN_GROUP(irqc_irq11),
+ SH_PFC_PIN_GROUP(irqc_irq12),
+ SH_PFC_PIN_GROUP(irqc_irq13),
+ SH_PFC_PIN_GROUP(irqc_irq14),
+ SH_PFC_PIN_GROUP(irqc_irq15),
+ SH_PFC_PIN_GROUP(irqc_irq16),
+ SH_PFC_PIN_GROUP(irqc_irq17),
+ SH_PFC_PIN_GROUP(irqc_irq18),
+ SH_PFC_PIN_GROUP(irqc_irq19),
+ SH_PFC_PIN_GROUP(irqc_irq20),
+ SH_PFC_PIN_GROUP(irqc_irq21),
+ SH_PFC_PIN_GROUP(irqc_irq22),
+ SH_PFC_PIN_GROUP(irqc_irq23),
+ SH_PFC_PIN_GROUP(irqc_irq24),
+ SH_PFC_PIN_GROUP(irqc_irq25),
+ SH_PFC_PIN_GROUP(irqc_irq26),
+ SH_PFC_PIN_GROUP(irqc_irq27),
+ SH_PFC_PIN_GROUP(irqc_irq28),
+ SH_PFC_PIN_GROUP(irqc_irq29),
+ SH_PFC_PIN_GROUP(irqc_irq30),
+ SH_PFC_PIN_GROUP(irqc_irq31),
+ SH_PFC_PIN_GROUP(irqc_irq32),
+ SH_PFC_PIN_GROUP(irqc_irq33),
+ SH_PFC_PIN_GROUP(irqc_irq34),
+ SH_PFC_PIN_GROUP(irqc_irq35),
+ SH_PFC_PIN_GROUP(irqc_irq36),
+ SH_PFC_PIN_GROUP(irqc_irq37),
+ SH_PFC_PIN_GROUP(irqc_irq38),
+ SH_PFC_PIN_GROUP(irqc_irq39),
+ SH_PFC_PIN_GROUP(irqc_irq40),
+ SH_PFC_PIN_GROUP(irqc_irq41),
+ SH_PFC_PIN_GROUP(irqc_irq42),
+ SH_PFC_PIN_GROUP(irqc_irq43),
+ SH_PFC_PIN_GROUP(irqc_irq44),
+ SH_PFC_PIN_GROUP(irqc_irq45),
+ SH_PFC_PIN_GROUP(irqc_irq46),
+ SH_PFC_PIN_GROUP(irqc_irq47),
+ SH_PFC_PIN_GROUP(irqc_irq48),
+ SH_PFC_PIN_GROUP(irqc_irq49),
+ SH_PFC_PIN_GROUP(irqc_irq50),
+ SH_PFC_PIN_GROUP(irqc_irq51),
+ SH_PFC_PIN_GROUP(irqc_irq52),
+ SH_PFC_PIN_GROUP(irqc_irq53),
+ SH_PFC_PIN_GROUP(irqc_irq54),
+ SH_PFC_PIN_GROUP(irqc_irq55),
+ SH_PFC_PIN_GROUP(irqc_irq56),
+ SH_PFC_PIN_GROUP(irqc_irq57),
+ SH_PFC_PIN_GROUP(scifa0_data),
+ SH_PFC_PIN_GROUP(scifa0_clk),
+ SH_PFC_PIN_GROUP(scifa0_ctrl),
+ SH_PFC_PIN_GROUP(scifa1_data),
+ SH_PFC_PIN_GROUP(scifa1_clk),
+ SH_PFC_PIN_GROUP(scifa1_ctrl),
+ SH_PFC_PIN_GROUP(scifb0_data),
+ SH_PFC_PIN_GROUP(scifb0_clk),
+ SH_PFC_PIN_GROUP(scifb0_ctrl),
+ SH_PFC_PIN_GROUP(scifb1_data),
+ SH_PFC_PIN_GROUP(scifb1_clk),
+ SH_PFC_PIN_GROUP(scifb1_ctrl),
+ SH_PFC_PIN_GROUP(scifb1_data_b),
+ SH_PFC_PIN_GROUP(scifb1_clk_b),
+ SH_PFC_PIN_GROUP(scifb1_ctrl_b),
+ SH_PFC_PIN_GROUP(scifb2_data),
+ SH_PFC_PIN_GROUP(scifb2_clk),
+ SH_PFC_PIN_GROUP(scifb2_ctrl),
+ SH_PFC_PIN_GROUP(scifb2_data_b),
+ SH_PFC_PIN_GROUP(scifb2_clk_b),
+ SH_PFC_PIN_GROUP(scifb2_ctrl_b),
+ SH_PFC_PIN_GROUP(scifb3_data),
+ SH_PFC_PIN_GROUP(scifb3_clk),
+ SH_PFC_PIN_GROUP(scifb3_ctrl),
+ SH_PFC_PIN_GROUP(scifb3_data_b),
+ SH_PFC_PIN_GROUP(scifb3_clk_b),
+ SH_PFC_PIN_GROUP(scifb3_ctrl_b),
+};
+
+static const char * const irqc_groups[] = {
+ "irqc_irq0",
+ "irqc_irq1",
+ "irqc_irq2",
+ "irqc_irq3",
+ "irqc_irq4",
+ "irqc_irq5",
+ "irqc_irq6",
+ "irqc_irq7",
+ "irqc_irq8",
+ "irqc_irq9",
+ "irqc_irq10",
+ "irqc_irq11",
+ "irqc_irq12",
+ "irqc_irq13",
+ "irqc_irq14",
+ "irqc_irq15",
+ "irqc_irq16",
+ "irqc_irq17",
+ "irqc_irq18",
+ "irqc_irq19",
+ "irqc_irq20",
+ "irqc_irq21",
+ "irqc_irq22",
+ "irqc_irq23",
+ "irqc_irq24",
+ "irqc_irq25",
+ "irqc_irq26",
+ "irqc_irq27",
+ "irqc_irq28",
+ "irqc_irq29",
+ "irqc_irq30",
+ "irqc_irq31",
+ "irqc_irq32",
+ "irqc_irq33",
+ "irqc_irq34",
+ "irqc_irq35",
+ "irqc_irq36",
+ "irqc_irq37",
+ "irqc_irq38",
+ "irqc_irq39",
+ "irqc_irq40",
+ "irqc_irq41",
+ "irqc_irq42",
+ "irqc_irq43",
+ "irqc_irq44",
+ "irqc_irq45",
+ "irqc_irq46",
+ "irqc_irq47",
+ "irqc_irq48",
+ "irqc_irq49",
+ "irqc_irq50",
+ "irqc_irq51",
+ "irqc_irq52",
+ "irqc_irq53",
+ "irqc_irq54",
+ "irqc_irq55",
+ "irqc_irq56",
+ "irqc_irq57",
+};
+
+static const char * const scifa0_groups[] = {
+ "scifa0_data",
+ "scifa0_clk",
+ "scifa0_ctrl",
+};
+
+static const char * const scifa1_groups[] = {
+ "scifa1_data",
+ "scifa1_clk",
+ "scifa1_ctrl",
+};
+
+static const char * const scifb0_groups[] = {
+ "scifb0_data",
+ "scifb0_clk",
+ "scifb0_ctrl",
+};
+
+static const char * const scifb1_groups[] = {
+ "scifb1_data",
+ "scifb1_clk",
+ "scifb1_ctrl",
+ "scifb1_data_b",
+ "scifb1_clk_b",
+ "scifb1_ctrl_b",
+};
+
+static const char * const scifb2_groups[] = {
+ "scifb2_data",
+ "scifb2_clk",
+ "scifb2_ctrl",
+ "scifb2_data_b",
+ "scifb2_clk_b",
+ "scifb2_ctrl_b",
+};
+
+static const char * const scifb3_groups[] = {
+ "scifb3_data",
+ "scifb3_clk",
+ "scifb3_ctrl",
+ "scifb3_data_b",
+ "scifb3_clk_b",
+ "scifb3_ctrl_b",
+};
+
+static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(irqc),
+ SH_PFC_FUNCTION(scifa0),
+ SH_PFC_FUNCTION(scifa1),
+ SH_PFC_FUNCTION(scifb0),
+ SH_PFC_FUNCTION(scifb1),
+ SH_PFC_FUNCTION(scifb2),
+ SH_PFC_FUNCTION(scifb3),
+};
+
+#undef PORTCR
+#define PORTCR(nr, reg) \
+ { \
+ PINMUX_CFG_REG("PORT" nr "CR", reg, 8, 4) { \
+ _PCRH(PORT##nr##_IN, 0, 0, PORT##nr##_OUT), \
+ PORT##nr##_FN0, PORT##nr##_FN1, \
+ PORT##nr##_FN2, PORT##nr##_FN3, \
+ PORT##nr##_FN4, PORT##nr##_FN5, \
+ PORT##nr##_FN6, PORT##nr##_FN7 } \
+ }
+
+static const struct pinmux_cfg_reg pinmux_config_regs[] = {
+ PORTCR(0, 0xe6050000),
+ PORTCR(1, 0xe6050001),
+ PORTCR(2, 0xe6050002),
+ PORTCR(3, 0xe6050003),
+ PORTCR(4, 0xe6050004),
+ PORTCR(5, 0xe6050005),
+ PORTCR(6, 0xe6050006),
+ PORTCR(7, 0xe6050007),
+ PORTCR(8, 0xe6050008),
+ PORTCR(9, 0xe6050009),
+ PORTCR(10, 0xe605000A),
+ PORTCR(11, 0xe605000B),
+ PORTCR(12, 0xe605000C),
+ PORTCR(13, 0xe605000D),
+ PORTCR(14, 0xe605000E),
+ PORTCR(15, 0xe605000F),
+ PORTCR(16, 0xe6050010),
+ PORTCR(17, 0xe6050011),
+ PORTCR(18, 0xe6050012),
+ PORTCR(19, 0xe6050013),
+ PORTCR(20, 0xe6050014),
+ PORTCR(21, 0xe6050015),
+ PORTCR(22, 0xe6050016),
+ PORTCR(23, 0xe6050017),
+ PORTCR(24, 0xe6050018),
+ PORTCR(25, 0xe6050019),
+ PORTCR(26, 0xe605001A),
+ PORTCR(27, 0xe605001B),
+ PORTCR(28, 0xe605001C),
+ PORTCR(29, 0xe605001D),
+ PORTCR(30, 0xe605001E),
+ PORTCR(32, 0xe6051020),
+ PORTCR(33, 0xe6051021),
+ PORTCR(34, 0xe6051022),
+ PORTCR(35, 0xe6051023),
+ PORTCR(36, 0xe6051024),
+ PORTCR(37, 0xe6051025),
+ PORTCR(38, 0xe6051026),
+ PORTCR(39, 0xe6051027),
+ PORTCR(40, 0xe6051028),
+ PORTCR(64, 0xe6050040),
+ PORTCR(65, 0xe6050041),
+ PORTCR(66, 0xe6050042),
+ PORTCR(67, 0xe6050043),
+ PORTCR(68, 0xe6050044),
+ PORTCR(69, 0xe6050045),
+ PORTCR(70, 0xe6050046),
+ PORTCR(71, 0xe6050047),
+ PORTCR(72, 0xe6050048),
+ PORTCR(73, 0xe6050049),
+ PORTCR(74, 0xe605004A),
+ PORTCR(75, 0xe605004B),
+ PORTCR(76, 0xe605004C),
+ PORTCR(77, 0xe605004D),
+ PORTCR(78, 0xe605004E),
+ PORTCR(79, 0xe605004F),
+ PORTCR(80, 0xe6050050),
+ PORTCR(81, 0xe6050051),
+ PORTCR(82, 0xe6050052),
+ PORTCR(83, 0xe6050053),
+ PORTCR(84, 0xe6050054),
+ PORTCR(85, 0xe6050055),
+ PORTCR(96, 0xe6051060),
+ PORTCR(97, 0xe6051061),
+ PORTCR(98, 0xe6051062),
+ PORTCR(99, 0xe6051063),
+ PORTCR(100, 0xe6051064),
+ PORTCR(101, 0xe6051065),
+ PORTCR(102, 0xe6051066),
+ PORTCR(103, 0xe6051067),
+ PORTCR(104, 0xe6051068),
+ PORTCR(105, 0xe6051069),
+ PORTCR(106, 0xe605106A),
+ PORTCR(107, 0xe605106B),
+ PORTCR(108, 0xe605106C),
+ PORTCR(109, 0xe605106D),
+ PORTCR(110, 0xe605106E),
+ PORTCR(111, 0xe605106F),
+ PORTCR(112, 0xe6051070),
+ PORTCR(113, 0xe6051071),
+ PORTCR(114, 0xe6051072),
+ PORTCR(115, 0xe6051073),
+ PORTCR(116, 0xe6051074),
+ PORTCR(117, 0xe6051075),
+ PORTCR(118, 0xe6051076),
+ PORTCR(119, 0xe6051077),
+ PORTCR(120, 0xe6051078),
+ PORTCR(121, 0xe6051079),
+ PORTCR(122, 0xe605107A),
+ PORTCR(123, 0xe605107B),
+ PORTCR(124, 0xe605107C),
+ PORTCR(125, 0xe605107D),
+ PORTCR(126, 0xe605107E),
+ PORTCR(128, 0xe6051080),
+ PORTCR(129, 0xe6051081),
+ PORTCR(130, 0xe6051082),
+ PORTCR(131, 0xe6051083),
+ PORTCR(132, 0xe6051084),
+ PORTCR(133, 0xe6051085),
+ PORTCR(134, 0xe6051086),
+ PORTCR(160, 0xe60520A0),
+ PORTCR(161, 0xe60520A1),
+ PORTCR(162, 0xe60520A2),
+ PORTCR(163, 0xe60520A3),
+ PORTCR(164, 0xe60520A4),
+ PORTCR(165, 0xe60520A5),
+ PORTCR(166, 0xe60520A6),
+ PORTCR(167, 0xe60520A7),
+ PORTCR(168, 0xe60520A8),
+ PORTCR(169, 0xe60520A9),
+ PORTCR(170, 0xe60520AA),
+ PORTCR(171, 0xe60520AB),
+ PORTCR(172, 0xe60520AC),
+ PORTCR(173, 0xe60520AD),
+ PORTCR(174, 0xe60520AE),
+ PORTCR(175, 0xe60520AF),
+ PORTCR(176, 0xe60520B0),
+ PORTCR(177, 0xe60520B1),
+ PORTCR(178, 0xe60520B2),
+ PORTCR(192, 0xe60520C0),
+ PORTCR(193, 0xe60520C1),
+ PORTCR(194, 0xe60520C2),
+ PORTCR(195, 0xe60520C3),
+ PORTCR(196, 0xe60520C4),
+ PORTCR(197, 0xe60520C5),
+ PORTCR(198, 0xe60520C6),
+ PORTCR(199, 0xe60520C7),
+ PORTCR(200, 0xe60520C8),
+ PORTCR(201, 0xe60520C9),
+ PORTCR(202, 0xe60520CA),
+ PORTCR(203, 0xe60520CB),
+ PORTCR(204, 0xe60520CC),
+ PORTCR(205, 0xe60520CD),
+ PORTCR(206, 0xe60520CE),
+ PORTCR(207, 0xe60520CF),
+ PORTCR(208, 0xe60520D0),
+ PORTCR(209, 0xe60520D1),
+ PORTCR(210, 0xe60520D2),
+ PORTCR(211, 0xe60520D3),
+ PORTCR(212, 0xe60520D4),
+ PORTCR(213, 0xe60520D5),
+ PORTCR(214, 0xe60520D6),
+ PORTCR(215, 0xe60520D7),
+ PORTCR(216, 0xe60520D8),
+ PORTCR(217, 0xe60520D9),
+ PORTCR(218, 0xe60520DA),
+ PORTCR(219, 0xe60520DB),
+ PORTCR(220, 0xe60520DC),
+ PORTCR(221, 0xe60520DD),
+ PORTCR(222, 0xe60520DE),
+ PORTCR(224, 0xe60520E0),
+ PORTCR(225, 0xe60520E1),
+ PORTCR(226, 0xe60520E2),
+ PORTCR(227, 0xe60520E3),
+ PORTCR(228, 0xe60520E4),
+ PORTCR(229, 0xe60520E5),
+ PORTCR(230, 0xe60520e6),
+ PORTCR(231, 0xe60520E7),
+ PORTCR(232, 0xe60520E8),
+ PORTCR(233, 0xe60520E9),
+ PORTCR(234, 0xe60520EA),
+ PORTCR(235, 0xe60520EB),
+ PORTCR(236, 0xe60520EC),
+ PORTCR(237, 0xe60520ED),
+ PORTCR(238, 0xe60520EE),
+ PORTCR(239, 0xe60520EF),
+ PORTCR(240, 0xe60520F0),
+ PORTCR(241, 0xe60520F1),
+ PORTCR(242, 0xe60520F2),
+ PORTCR(243, 0xe60520F3),
+ PORTCR(244, 0xe60520F4),
+ PORTCR(245, 0xe60520F5),
+ PORTCR(246, 0xe60520F6),
+ PORTCR(247, 0xe60520F7),
+ PORTCR(248, 0xe60520F8),
+ PORTCR(249, 0xe60520F9),
+ PORTCR(250, 0xe60520FA),
+ PORTCR(256, 0xe6052100),
+ PORTCR(257, 0xe6052101),
+ PORTCR(258, 0xe6052102),
+ PORTCR(259, 0xe6052103),
+ PORTCR(260, 0xe6052104),
+ PORTCR(261, 0xe6052105),
+ PORTCR(262, 0xe6052106),
+ PORTCR(263, 0xe6052107),
+ PORTCR(264, 0xe6052108),
+ PORTCR(265, 0xe6052109),
+ PORTCR(266, 0xe605210A),
+ PORTCR(267, 0xe605210B),
+ PORTCR(268, 0xe605210C),
+ PORTCR(269, 0xe605210D),
+ PORTCR(270, 0xe605210E),
+ PORTCR(271, 0xe605210F),
+ PORTCR(272, 0xe6052110),
+ PORTCR(273, 0xe6052111),
+ PORTCR(274, 0xe6052112),
+ PORTCR(275, 0xe6052113),
+ PORTCR(276, 0xe6052114),
+ PORTCR(277, 0xe6052115),
+ PORTCR(278, 0xe6052116),
+ PORTCR(279, 0xe6052117),
+ PORTCR(280, 0xe6052118),
+ PORTCR(281, 0xe6052119),
+ PORTCR(282, 0xe605211A),
+ PORTCR(283, 0xe605211B),
+ PORTCR(288, 0xe6053120),
+ PORTCR(289, 0xe6053121),
+ PORTCR(290, 0xe6053122),
+ PORTCR(291, 0xe6053123),
+ PORTCR(292, 0xe6053124),
+ PORTCR(293, 0xe6053125),
+ PORTCR(294, 0xe6053126),
+ PORTCR(295, 0xe6053127),
+ PORTCR(296, 0xe6053128),
+ PORTCR(297, 0xe6053129),
+ PORTCR(298, 0xe605312A),
+ PORTCR(299, 0xe605312B),
+ PORTCR(300, 0xe605312C),
+ PORTCR(301, 0xe605312D),
+ PORTCR(302, 0xe605312E),
+ PORTCR(303, 0xe605312F),
+ PORTCR(304, 0xe6053130),
+ PORTCR(305, 0xe6053131),
+ PORTCR(306, 0xe6053132),
+ PORTCR(307, 0xe6053133),
+ PORTCR(308, 0xe6053134),
+ PORTCR(320, 0xe6053140),
+ PORTCR(321, 0xe6053141),
+ PORTCR(322, 0xe6053142),
+ PORTCR(323, 0xe6053143),
+ PORTCR(324, 0xe6053144),
+ PORTCR(325, 0xe6053145),
+ PORTCR(326, 0xe6053146),
+ PORTCR(327, 0xe6053147),
+ PORTCR(328, 0xe6053148),
+ PORTCR(329, 0xe6053149),
+
+ { PINMUX_CFG_REG("MSEL1CR", 0xe605800c, 32, 1) {
+ MSEL1CR_31_0, MSEL1CR_31_1,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ MSEL1CR_27_0, MSEL1CR_27_1,
+ 0, 0,
+ MSEL1CR_25_0, MSEL1CR_25_1,
+ MSEL1CR_24_0, MSEL1CR_24_1,
+ 0, 0,
+ MSEL1CR_22_0, MSEL1CR_22_1,
+ MSEL1CR_21_0, MSEL1CR_21_1,
+ MSEL1CR_20_0, MSEL1CR_20_1,
+ MSEL1CR_19_0, MSEL1CR_19_1,
+ MSEL1CR_18_0, MSEL1CR_18_1,
+ MSEL1CR_17_0, MSEL1CR_17_1,
+ MSEL1CR_16_0, MSEL1CR_16_1,
+ MSEL1CR_15_0, MSEL1CR_15_1,
+ MSEL1CR_14_0, MSEL1CR_14_1,
+ MSEL1CR_13_0, MSEL1CR_13_1,
+ MSEL1CR_12_0, MSEL1CR_12_1,
+ MSEL1CR_11_0, MSEL1CR_11_1,
+ MSEL1CR_10_0, MSEL1CR_10_1,
+ MSEL1CR_09_0, MSEL1CR_09_1,
+ MSEL1CR_08_0, MSEL1CR_08_1,
+ MSEL1CR_07_0, MSEL1CR_07_1,
+ MSEL1CR_06_0, MSEL1CR_06_1,
+ MSEL1CR_05_0, MSEL1CR_05_1,
+ MSEL1CR_04_0, MSEL1CR_04_1,
+ MSEL1CR_03_0, MSEL1CR_03_1,
+ MSEL1CR_02_0, MSEL1CR_02_1,
+ MSEL1CR_01_0, MSEL1CR_01_1,
+ MSEL1CR_00_0, MSEL1CR_00_1,
+ }
+ },
+ { PINMUX_CFG_REG("MSEL3CR", 0xe6058020, 32, 1) {
+ MSEL3CR_31_0, MSEL3CR_31_1,
+ 0, 0,
+ 0, 0,
+ MSEL3CR_28_0, MSEL3CR_28_1,
+ MSEL3CR_27_0, MSEL3CR_27_1,
+ MSEL3CR_26_0, MSEL3CR_26_1,
+ 0, 0,
+ 0, 0,
+ MSEL3CR_23_0, MSEL3CR_23_1,
+ MSEL3CR_22_0, MSEL3CR_22_1,
+ MSEL3CR_21_0, MSEL3CR_21_1,
+ MSEL3CR_20_0, MSEL3CR_20_1,
+ MSEL3CR_19_0, MSEL3CR_19_1,
+ MSEL3CR_18_0, MSEL3CR_18_1,
+ MSEL3CR_17_0, MSEL3CR_17_1,
+ MSEL3CR_16_0, MSEL3CR_16_1,
+ MSEL3CR_15_0, MSEL3CR_15_1,
+ 0, 0,
+ 0, 0,
+ MSEL3CR_12_0, MSEL3CR_12_1,
+ MSEL3CR_11_0, MSEL3CR_11_1,
+ MSEL3CR_10_0, MSEL3CR_10_1,
+ MSEL3CR_09_0, MSEL3CR_09_1,
+ 0, 0,
+ 0, 0,
+ MSEL3CR_06_0, MSEL3CR_06_1,
+ 0, 0,
+ 0, 0,
+ MSEL3CR_03_0, MSEL3CR_03_1,
+ 0, 0,
+ MSEL3CR_01_0, MSEL3CR_01_1,
+ MSEL3CR_00_0, MSEL3CR_00_1,
+ }
+ },
+ { PINMUX_CFG_REG("MSEL4CR", 0xe6058024, 32, 1) {
+ 0, 0,
+ MSEL4CR_30_0, MSEL4CR_30_1,
+ MSEL4CR_29_0, MSEL4CR_29_1,
+ MSEL4CR_28_0, MSEL4CR_28_1,
+ MSEL4CR_27_0, MSEL4CR_27_1,
+ MSEL4CR_26_0, MSEL4CR_26_1,
+ MSEL4CR_25_0, MSEL4CR_25_1,
+ MSEL4CR_24_0, MSEL4CR_24_1,
+ MSEL4CR_23_0, MSEL4CR_23_1,
+ MSEL4CR_22_0, MSEL4CR_22_1,
+ MSEL4CR_21_0, MSEL4CR_21_1,
+ MSEL4CR_20_0, MSEL4CR_20_1,
+ MSEL4CR_19_0, MSEL4CR_19_1,
+ MSEL4CR_18_0, MSEL4CR_18_1,
+ MSEL4CR_17_0, MSEL4CR_17_1,
+ MSEL4CR_16_0, MSEL4CR_16_1,
+ MSEL4CR_15_0, MSEL4CR_15_1,
+ MSEL4CR_14_0, MSEL4CR_14_1,
+ MSEL4CR_13_0, MSEL4CR_13_1,
+ MSEL4CR_12_0, MSEL4CR_12_1,
+ MSEL4CR_11_0, MSEL4CR_11_1,
+ MSEL4CR_10_0, MSEL4CR_10_1,
+ MSEL4CR_09_0, MSEL4CR_09_1,
+ 0, 0,
+ MSEL4CR_07_0, MSEL4CR_07_1,
+ 0, 0,
+ 0, 0,
+ MSEL4CR_04_0, MSEL4CR_04_1,
+ 0, 0,
+ 0, 0,
+ MSEL4CR_01_0, MSEL4CR_01_1,
+ 0, 0,
+ }
+ },
+ { PINMUX_CFG_REG("MSEL5CR", 0xe6058028, 32, 1) {
+ MSEL5CR_31_0, MSEL5CR_31_1,
+ MSEL5CR_30_0, MSEL5CR_30_1,
+ MSEL5CR_29_0, MSEL5CR_29_1,
+ MSEL5CR_28_0, MSEL5CR_28_1,
+ MSEL5CR_27_0, MSEL5CR_27_1,
+ MSEL5CR_26_0, MSEL5CR_26_1,
+ MSEL5CR_25_0, MSEL5CR_25_1,
+ MSEL5CR_24_0, MSEL5CR_24_1,
+ MSEL5CR_23_0, MSEL5CR_23_1,
+ MSEL5CR_22_0, MSEL5CR_22_1,
+ MSEL5CR_21_0, MSEL5CR_21_1,
+ MSEL5CR_20_0, MSEL5CR_20_1,
+ MSEL5CR_19_0, MSEL5CR_19_1,
+ MSEL5CR_18_0, MSEL5CR_18_1,
+ MSEL5CR_17_0, MSEL5CR_17_1,
+ MSEL5CR_16_0, MSEL5CR_16_1,
+ MSEL5CR_15_0, MSEL5CR_15_1,
+ MSEL5CR_14_0, MSEL5CR_14_1,
+ MSEL5CR_13_0, MSEL5CR_13_1,
+ MSEL5CR_12_0, MSEL5CR_12_1,
+ MSEL5CR_11_0, MSEL5CR_11_1,
+ MSEL5CR_10_0, MSEL5CR_10_1,
+ MSEL5CR_09_0, MSEL5CR_09_1,
+ MSEL5CR_08_0, MSEL5CR_08_1,
+ MSEL5CR_07_0, MSEL5CR_07_1,
+ MSEL5CR_06_0, MSEL5CR_06_1,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ }
+ },
+ { PINMUX_CFG_REG("MSEL8CR", 0xe6058034, 32, 1) {
+ 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,
+ MSEL8CR_16_0, MSEL8CR_16_1,
+ 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,
+ MSEL8CR_01_0, MSEL8CR_01_1,
+ MSEL8CR_00_0, MSEL8CR_00_1,
+ }
+ },
+ { },
+};
+
+static const struct pinmux_data_reg pinmux_data_regs[] = {
+
+ { PINMUX_DATA_REG("PORTL031_000DR", 0xe6054000, 32) {
+ 0, PORT30_DATA, PORT29_DATA, PORT28_DATA,
+ PORT27_DATA, PORT26_DATA, PORT25_DATA, PORT24_DATA,
+ PORT23_DATA, PORT22_DATA, PORT21_DATA, PORT20_DATA,
+ PORT19_DATA, PORT18_DATA, PORT17_DATA, PORT16_DATA,
+ PORT15_DATA, PORT14_DATA, PORT13_DATA, PORT12_DATA,
+ PORT11_DATA, PORT10_DATA, PORT9_DATA, PORT8_DATA,
+ PORT7_DATA, PORT6_DATA, PORT5_DATA, PORT4_DATA,
+ PORT3_DATA, PORT2_DATA, PORT1_DATA, PORT0_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTD063_032DR", 0xe6055000, 32) {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, PORT40_DATA,
+ PORT39_DATA, PORT38_DATA, PORT37_DATA, PORT36_DATA,
+ PORT35_DATA, PORT34_DATA, PORT33_DATA, PORT32_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTL095_064DR", 0xe6054004, 32) {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, PORT85_DATA, PORT84_DATA,
+ PORT83_DATA, PORT82_DATA, PORT81_DATA, PORT80_DATA,
+ PORT79_DATA, PORT78_DATA, PORT77_DATA, PORT76_DATA,
+ PORT75_DATA, PORT74_DATA, PORT73_DATA, PORT72_DATA,
+ PORT71_DATA, PORT70_DATA, PORT69_DATA, PORT68_DATA,
+ PORT67_DATA, PORT66_DATA, PORT65_DATA, PORT64_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTD127_096DR", 0xe6055004, 32) {
+ 0, PORT126_DATA, PORT125_DATA, PORT124_DATA,
+ PORT123_DATA, PORT122_DATA, PORT121_DATA, PORT120_DATA,
+ PORT119_DATA, PORT118_DATA, PORT117_DATA, PORT116_DATA,
+ PORT115_DATA, PORT114_DATA, PORT113_DATA, PORT112_DATA,
+ PORT111_DATA, PORT110_DATA, PORT109_DATA, PORT108_DATA,
+ PORT107_DATA, PORT106_DATA, PORT105_DATA, PORT104_DATA,
+ PORT103_DATA, PORT102_DATA, PORT101_DATA, PORT100_DATA,
+ PORT99_DATA, PORT98_DATA, PORT97_DATA, PORT96_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTD159_128DR", 0xe6055008, 32) {
+ 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, PORT134_DATA, PORT133_DATA, PORT132_DATA,
+ PORT131_DATA, PORT130_DATA, PORT129_DATA, PORT128_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTR191_160DR", 0xe6056000, 32) {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, PORT178_DATA, PORT177_DATA, PORT176_DATA,
+ PORT175_DATA, PORT174_DATA, PORT173_DATA, PORT172_DATA,
+ PORT171_DATA, PORT170_DATA, PORT169_DATA, PORT168_DATA,
+ PORT167_DATA, PORT166_DATA, PORT165_DATA, PORT164_DATA,
+ PORT163_DATA, PORT162_DATA, PORT161_DATA, PORT160_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTR223_192DR", 0xe6056004, 32) {
+ 0, PORT222_DATA, PORT221_DATA, PORT220_DATA,
+ PORT219_DATA, PORT218_DATA, PORT217_DATA, PORT216_DATA,
+ PORT215_DATA, PORT214_DATA, PORT213_DATA, PORT212_DATA,
+ PORT211_DATA, PORT210_DATA, PORT209_DATA, PORT208_DATA,
+ PORT207_DATA, PORT206_DATA, PORT205_DATA, PORT204_DATA,
+ PORT203_DATA, PORT202_DATA, PORT201_DATA, PORT200_DATA,
+ PORT199_DATA, PORT198_DATA, PORT197_DATA, PORT196_DATA,
+ PORT195_DATA, PORT194_DATA, PORT193_DATA, PORT192_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTR255_224DR", 0xe6056008, 32) {
+ 0, 0, 0, 0,
+ 0, PORT250_DATA, PORT249_DATA, PORT248_DATA,
+ PORT247_DATA, PORT246_DATA, PORT245_DATA, PORT244_DATA,
+ PORT243_DATA, PORT242_DATA, PORT241_DATA, PORT240_DATA,
+ PORT239_DATA, PORT238_DATA, PORT237_DATA, PORT236_DATA,
+ PORT235_DATA, PORT234_DATA, PORT233_DATA, PORT232_DATA,
+ PORT231_DATA, PORT230_DATA, PORT229_DATA, PORT228_DATA,
+ PORT227_DATA, PORT226_DATA, PORT225_DATA, PORT224_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTR287_256DR", 0xe605600C, 32) {
+ 0, 0, 0, 0,
+ PORT283_DATA, PORT282_DATA, PORT281_DATA, PORT280_DATA,
+ PORT279_DATA, PORT278_DATA, PORT277_DATA, PORT276_DATA,
+ PORT275_DATA, PORT274_DATA, PORT273_DATA, PORT272_DATA,
+ PORT271_DATA, PORT270_DATA, PORT269_DATA, PORT268_DATA,
+ PORT267_DATA, PORT266_DATA, PORT265_DATA, PORT264_DATA,
+ PORT263_DATA, PORT262_DATA, PORT261_DATA, PORT260_DATA,
+ PORT259_DATA, PORT258_DATA, PORT257_DATA, PORT256_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTU319_288DR", 0xe6057000, 32) {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, PORT308_DATA,
+ PORT307_DATA, PORT306_DATA, PORT305_DATA, PORT304_DATA,
+ PORT303_DATA, PORT302_DATA, PORT301_DATA, PORT300_DATA,
+ PORT299_DATA, PORT298_DATA, PORT297_DATA, PORT296_DATA,
+ PORT295_DATA, PORT294_DATA, PORT293_DATA, PORT292_DATA,
+ PORT291_DATA, PORT290_DATA, PORT289_DATA, PORT288_DATA,
+ }
+ },
+ { PINMUX_DATA_REG("PORTU351_320DR", 0xe6057004, 32) {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, PORT329_DATA, PORT328_DATA,
+ PORT327_DATA, PORT326_DATA, PORT325_DATA, PORT324_DATA,
+ PORT323_DATA, PORT322_DATA, PORT321_DATA, PORT320_DATA,
+ }
+ },
+ { },
+};
+
+static const struct pinmux_irq pinmux_irqs[] = {
+ PINMUX_IRQ(irq_pin(0), 0),
+ PINMUX_IRQ(irq_pin(1), 1),
+ PINMUX_IRQ(irq_pin(2), 2),
+ PINMUX_IRQ(irq_pin(3), 3),
+ PINMUX_IRQ(irq_pin(4), 4),
+ PINMUX_IRQ(irq_pin(5), 5),
+ PINMUX_IRQ(irq_pin(6), 6),
+ PINMUX_IRQ(irq_pin(7), 7),
+ PINMUX_IRQ(irq_pin(8), 8),
+ PINMUX_IRQ(irq_pin(9), 9),
+ PINMUX_IRQ(irq_pin(10), 10),
+ PINMUX_IRQ(irq_pin(11), 11),
+ PINMUX_IRQ(irq_pin(12), 12),
+ PINMUX_IRQ(irq_pin(13), 13),
+ PINMUX_IRQ(irq_pin(14), 14),
+ PINMUX_IRQ(irq_pin(15), 15),
+ PINMUX_IRQ(irq_pin(16), 320),
+ PINMUX_IRQ(irq_pin(17), 321),
+ PINMUX_IRQ(irq_pin(18), 85),
+ PINMUX_IRQ(irq_pin(19), 84),
+ PINMUX_IRQ(irq_pin(20), 160),
+ PINMUX_IRQ(irq_pin(21), 161),
+ PINMUX_IRQ(irq_pin(22), 162),
+ PINMUX_IRQ(irq_pin(23), 163),
+ PINMUX_IRQ(irq_pin(24), 175),
+ PINMUX_IRQ(irq_pin(25), 176),
+ PINMUX_IRQ(irq_pin(26), 177),
+ PINMUX_IRQ(irq_pin(27), 178),
+ PINMUX_IRQ(irq_pin(28), 322),
+ PINMUX_IRQ(irq_pin(29), 323),
+ PINMUX_IRQ(irq_pin(30), 324),
+ PINMUX_IRQ(irq_pin(31), 192),
+ PINMUX_IRQ(irq_pin(32), 193),
+ PINMUX_IRQ(irq_pin(33), 194),
+ PINMUX_IRQ(irq_pin(34), 195),
+ PINMUX_IRQ(irq_pin(35), 196),
+ PINMUX_IRQ(irq_pin(36), 197),
+ PINMUX_IRQ(irq_pin(37), 198),
+ PINMUX_IRQ(irq_pin(38), 199),
+ PINMUX_IRQ(irq_pin(39), 200),
+ PINMUX_IRQ(irq_pin(40), 66),
+ PINMUX_IRQ(irq_pin(41), 102),
+ PINMUX_IRQ(irq_pin(42), 103),
+ PINMUX_IRQ(irq_pin(43), 109),
+ PINMUX_IRQ(irq_pin(44), 110),
+ PINMUX_IRQ(irq_pin(45), 111),
+ PINMUX_IRQ(irq_pin(46), 112),
+ PINMUX_IRQ(irq_pin(47), 113),
+ PINMUX_IRQ(irq_pin(48), 114),
+ PINMUX_IRQ(irq_pin(49), 115),
+ PINMUX_IRQ(irq_pin(50), 301),
+ PINMUX_IRQ(irq_pin(51), 290),
+ PINMUX_IRQ(irq_pin(52), 296),
+ PINMUX_IRQ(irq_pin(53), 325),
+ PINMUX_IRQ(irq_pin(54), 326),
+ PINMUX_IRQ(irq_pin(55), 327),
+ PINMUX_IRQ(irq_pin(56), 328),
+ PINMUX_IRQ(irq_pin(57), 329),
+};
+
+#define PORTCR_PULMD_OFF (0 << 6)
+#define PORTCR_PULMD_DOWN (2 << 6)
+#define PORTCR_PULMD_UP (3 << 6)
+#define PORTCR_PULMD_MASK (3 << 6)
+
+static const unsigned int r8a73a4_portcr_offsets[] = {
+ 0x00000000, 0x00001000, 0x00000000, 0x00001000,
+ 0x00001000, 0x00002000, 0x00002000, 0x00002000,
+ 0x00002000, 0x00003000, 0x00003000,
+};
+
+static unsigned int r8a73a4_pinmux_get_bias(struct sh_pfc *pfc,
+ unsigned int pin)
+{
+ void __iomem *addr;
+
+ addr = pfc->window->virt + r8a73a4_portcr_offsets[pin >> 5] + pin;
+
+ switch (ioread8(addr) & PORTCR_PULMD_MASK) {
+ case PORTCR_PULMD_UP:
+ return PIN_CONFIG_BIAS_PULL_UP;
+ case PORTCR_PULMD_DOWN:
+ return PIN_CONFIG_BIAS_PULL_DOWN;
+ case PORTCR_PULMD_OFF:
+ default:
+ return PIN_CONFIG_BIAS_DISABLE;
+ }
+}
+
+static void r8a73a4_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin,
+ unsigned int bias)
+{
+ void __iomem *addr;
+ u32 value;
+
+ addr = pfc->window->virt + r8a73a4_portcr_offsets[pin >> 5] + pin;
+ value = ioread8(addr) & ~PORTCR_PULMD_MASK;
+
+ switch (bias) {
+ case PIN_CONFIG_BIAS_PULL_UP:
+ value |= PORTCR_PULMD_UP;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ value |= PORTCR_PULMD_DOWN;
+ break;
+ }
+
+ iowrite8(value, addr);
+}
+
+static const struct sh_pfc_soc_operations r8a73a4_pinmux_ops = {
+ .get_bias = r8a73a4_pinmux_get_bias,
+ .set_bias = r8a73a4_pinmux_set_bias,
+};
+
+const struct sh_pfc_soc_info r8a73a4_pinmux_info = {
+ .name = "r8a73a4_pfc",
+ .ops = &r8a73a4_pinmux_ops,
+
+ .input = { PINMUX_INPUT_BEGIN, PINMUX_INPUT_END },
+ .output = { PINMUX_OUTPUT_BEGIN, PINMUX_OUTPUT_END },
+ .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END },
+
+ .pins = pinmux_pins,
+ .nr_pins = ARRAY_SIZE(pinmux_pins),
+
+ .ranges = pinmux_ranges,
+ .nr_ranges = ARRAY_SIZE(pinmux_ranges),
+
+ .groups = pinmux_groups,
+ .nr_groups = ARRAY_SIZE(pinmux_groups),
+ .functions = pinmux_functions,
+ .nr_functions = ARRAY_SIZE(pinmux_functions),
+
+ .cfg_regs = pinmux_config_regs,
+ .data_regs = pinmux_data_regs,
+
+ .gpio_data = pinmux_data,
+ .gpio_data_size = ARRAY_SIZE(pinmux_data),
+
+ .gpio_irq = pinmux_irqs,
+ .gpio_irq_size = ARRAY_SIZE(pinmux_irqs),
+};
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7740.c b/drivers/pinctrl/sh-pfc/pfc-r8a7740.c
index 3621d3e81fc3..bbd87d29bfd0 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7740.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7740.c
@@ -2994,38 +2994,38 @@ static const struct pinmux_data_reg pinmux_data_regs[] = {
};
static const struct pinmux_irq pinmux_irqs[] = {
- PINMUX_IRQ(evt2irq(0x0200), GPIO_PORT2, GPIO_PORT13), /* IRQ0A */
- PINMUX_IRQ(evt2irq(0x0220), GPIO_PORT20), /* IRQ1A */
- PINMUX_IRQ(evt2irq(0x0240), GPIO_PORT11, GPIO_PORT12), /* IRQ2A */
- PINMUX_IRQ(evt2irq(0x0260), GPIO_PORT10, GPIO_PORT14), /* IRQ3A */
- PINMUX_IRQ(evt2irq(0x0280), GPIO_PORT15, GPIO_PORT172),/* IRQ4A */
- PINMUX_IRQ(evt2irq(0x02A0), GPIO_PORT0, GPIO_PORT1), /* IRQ5A */
- PINMUX_IRQ(evt2irq(0x02C0), GPIO_PORT121, GPIO_PORT173),/* IRQ6A */
- PINMUX_IRQ(evt2irq(0x02E0), GPIO_PORT120, GPIO_PORT209),/* IRQ7A */
- PINMUX_IRQ(evt2irq(0x0300), GPIO_PORT119), /* IRQ8A */
- PINMUX_IRQ(evt2irq(0x0320), GPIO_PORT118, GPIO_PORT210),/* IRQ9A */
- PINMUX_IRQ(evt2irq(0x0340), GPIO_PORT19), /* IRQ10A */
- PINMUX_IRQ(evt2irq(0x0360), GPIO_PORT104), /* IRQ11A */
- PINMUX_IRQ(evt2irq(0x0380), GPIO_PORT42, GPIO_PORT97), /* IRQ12A */
- PINMUX_IRQ(evt2irq(0x03A0), GPIO_PORT64, GPIO_PORT98), /* IRQ13A */
- PINMUX_IRQ(evt2irq(0x03C0), GPIO_PORT63, GPIO_PORT99), /* IRQ14A */
- PINMUX_IRQ(evt2irq(0x03E0), GPIO_PORT62, GPIO_PORT100),/* IRQ15A */
- PINMUX_IRQ(evt2irq(0x3200), GPIO_PORT68, GPIO_PORT211),/* IRQ16A */
- PINMUX_IRQ(evt2irq(0x3220), GPIO_PORT69), /* IRQ17A */
- PINMUX_IRQ(evt2irq(0x3240), GPIO_PORT70), /* IRQ18A */
- PINMUX_IRQ(evt2irq(0x3260), GPIO_PORT71), /* IRQ19A */
- PINMUX_IRQ(evt2irq(0x3280), GPIO_PORT67), /* IRQ20A */
- PINMUX_IRQ(evt2irq(0x32A0), GPIO_PORT202), /* IRQ21A */
- PINMUX_IRQ(evt2irq(0x32C0), GPIO_PORT95), /* IRQ22A */
- PINMUX_IRQ(evt2irq(0x32E0), GPIO_PORT96), /* IRQ23A */
- PINMUX_IRQ(evt2irq(0x3300), GPIO_PORT180), /* IRQ24A */
- PINMUX_IRQ(evt2irq(0x3320), GPIO_PORT38), /* IRQ25A */
- PINMUX_IRQ(evt2irq(0x3340), GPIO_PORT58, GPIO_PORT81), /* IRQ26A */
- PINMUX_IRQ(evt2irq(0x3360), GPIO_PORT57, GPIO_PORT168),/* IRQ27A */
- PINMUX_IRQ(evt2irq(0x3380), GPIO_PORT56, GPIO_PORT169),/* IRQ28A */
- PINMUX_IRQ(evt2irq(0x33A0), GPIO_PORT50, GPIO_PORT170),/* IRQ29A */
- PINMUX_IRQ(evt2irq(0x33C0), GPIO_PORT49, GPIO_PORT171),/* IRQ30A */
- PINMUX_IRQ(evt2irq(0x33E0), GPIO_PORT41, GPIO_PORT167),/* IRQ31A */
+ PINMUX_IRQ(irq_pin(0), GPIO_PORT2, GPIO_PORT13), /* IRQ0A */
+ PINMUX_IRQ(irq_pin(1), GPIO_PORT20), /* IRQ1A */
+ PINMUX_IRQ(irq_pin(2), GPIO_PORT11, GPIO_PORT12), /* IRQ2A */
+ PINMUX_IRQ(irq_pin(3), GPIO_PORT10, GPIO_PORT14), /* IRQ3A */
+ PINMUX_IRQ(irq_pin(4), GPIO_PORT15, GPIO_PORT172),/* IRQ4A */
+ PINMUX_IRQ(irq_pin(5), GPIO_PORT0, GPIO_PORT1), /* IRQ5A */
+ PINMUX_IRQ(irq_pin(6), GPIO_PORT121, GPIO_PORT173),/* IRQ6A */
+ PINMUX_IRQ(irq_pin(7), GPIO_PORT120, GPIO_PORT209),/* IRQ7A */
+ PINMUX_IRQ(irq_pin(8), GPIO_PORT119), /* IRQ8A */
+ PINMUX_IRQ(irq_pin(9), GPIO_PORT118, GPIO_PORT210),/* IRQ9A */
+ PINMUX_IRQ(irq_pin(10), GPIO_PORT19), /* IRQ10A */
+ PINMUX_IRQ(irq_pin(11), GPIO_PORT104), /* IRQ11A */
+ PINMUX_IRQ(irq_pin(12), GPIO_PORT42, GPIO_PORT97), /* IRQ12A */
+ PINMUX_IRQ(irq_pin(13), GPIO_PORT64, GPIO_PORT98), /* IRQ13A */
+ PINMUX_IRQ(irq_pin(14), GPIO_PORT63, GPIO_PORT99), /* IRQ14A */
+ PINMUX_IRQ(irq_pin(15), GPIO_PORT62, GPIO_PORT100),/* IRQ15A */
+ PINMUX_IRQ(irq_pin(16), GPIO_PORT68, GPIO_PORT211),/* IRQ16A */
+ PINMUX_IRQ(irq_pin(17), GPIO_PORT69), /* IRQ17A */
+ PINMUX_IRQ(irq_pin(18), GPIO_PORT70), /* IRQ18A */
+ PINMUX_IRQ(irq_pin(19), GPIO_PORT71), /* IRQ19A */
+ PINMUX_IRQ(irq_pin(20), GPIO_PORT67), /* IRQ20A */
+ PINMUX_IRQ(irq_pin(21), GPIO_PORT202), /* IRQ21A */
+ PINMUX_IRQ(irq_pin(22), GPIO_PORT95), /* IRQ22A */
+ PINMUX_IRQ(irq_pin(23), GPIO_PORT96), /* IRQ23A */
+ PINMUX_IRQ(irq_pin(24), GPIO_PORT180), /* IRQ24A */
+ PINMUX_IRQ(irq_pin(25), GPIO_PORT38), /* IRQ25A */
+ PINMUX_IRQ(irq_pin(26), GPIO_PORT58, GPIO_PORT81), /* IRQ26A */
+ PINMUX_IRQ(irq_pin(27), GPIO_PORT57, GPIO_PORT168),/* IRQ27A */
+ PINMUX_IRQ(irq_pin(28), GPIO_PORT56, GPIO_PORT169),/* IRQ28A */
+ PINMUX_IRQ(irq_pin(29), GPIO_PORT50, GPIO_PORT170),/* IRQ29A */
+ PINMUX_IRQ(irq_pin(30), GPIO_PORT49, GPIO_PORT171),/* IRQ30A */
+ PINMUX_IRQ(irq_pin(31), GPIO_PORT41, GPIO_PORT167),/* IRQ31A */
};
const struct sh_pfc_soc_info r8a7740_pinmux_info = {
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7779.c b/drivers/pinctrl/sh-pfc/pfc-r8a7779.c
index 1d7b0dfbbb21..791a6719d8a9 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7779.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7779.c
@@ -19,39 +19,77 @@
*/
#include <linux/kernel.h>
-#include <mach/r8a7779.h>
#include "sh_pfc.h"
-#define CPU_32_PORT6(fn, pfx, sfx) \
- PORT_1(fn, pfx##0, sfx), PORT_1(fn, pfx##1, sfx), \
- PORT_1(fn, pfx##2, sfx), PORT_1(fn, pfx##3, sfx), \
- PORT_1(fn, pfx##4, sfx), PORT_1(fn, pfx##5, sfx), \
- PORT_1(fn, pfx##6, sfx), PORT_1(fn, pfx##7, sfx), \
- PORT_1(fn, pfx##8, sfx)
-
-#define CPU_ALL_PORT(fn, pfx, sfx) \
- PORT_32(fn, pfx##_0_, sfx), \
- PORT_32(fn, pfx##_1_, sfx), \
- PORT_32(fn, pfx##_2_, sfx), \
- PORT_32(fn, pfx##_3_, sfx), \
- PORT_32(fn, pfx##_4_, sfx), \
- PORT_32(fn, pfx##_5_, sfx), \
- CPU_32_PORT6(fn, pfx##_6_, sfx)
-
-#define _GP_GPIO(pfx, sfx) PINMUX_GPIO(GPIO_GP##pfx, GP##pfx##_DATA)
-#define _GP_DATA(pfx, sfx) PINMUX_DATA(GP##pfx##_DATA, GP##pfx##_FN, \
- GP##pfx##_IN, GP##pfx##_OUT)
-
-#define _GP_INOUTSEL(pfx, sfx) GP##pfx##_IN, GP##pfx##_OUT
-#define _GP_INDT(pfx, sfx) GP##pfx##_DATA
-
-#define GP_ALL(str) CPU_ALL_PORT(_PORT_ALL, GP, str)
-#define PINMUX_GPIO_GP_ALL() CPU_ALL_PORT(_GP_GPIO, , unused)
-#define PINMUX_DATA_GP_ALL() CPU_ALL_PORT(_GP_DATA, , unused)
-
-#define GP_INOUTSEL(bank) PORT_32_REV(_GP_INOUTSEL, _##bank##_, unused)
-#define GP_INDT(bank) PORT_32_REV(_GP_INDT, _##bank##_, unused)
+#define PORT_GP_1(bank, pin, fn, sfx) fn(bank, pin, GP_##bank##_##pin, sfx)
+
+#define PORT_GP_32(bank, fn, sfx) \
+ PORT_GP_1(bank, 0, fn, sfx), PORT_GP_1(bank, 1, fn, sfx), \
+ PORT_GP_1(bank, 2, fn, sfx), PORT_GP_1(bank, 3, fn, sfx), \
+ PORT_GP_1(bank, 4, fn, sfx), PORT_GP_1(bank, 5, fn, sfx), \
+ PORT_GP_1(bank, 6, fn, sfx), PORT_GP_1(bank, 7, fn, sfx), \
+ PORT_GP_1(bank, 8, fn, sfx), PORT_GP_1(bank, 9, fn, sfx), \
+ PORT_GP_1(bank, 10, fn, sfx), PORT_GP_1(bank, 11, fn, sfx), \
+ PORT_GP_1(bank, 12, fn, sfx), PORT_GP_1(bank, 13, fn, sfx), \
+ PORT_GP_1(bank, 14, fn, sfx), PORT_GP_1(bank, 15, fn, sfx), \
+ PORT_GP_1(bank, 16, fn, sfx), PORT_GP_1(bank, 17, fn, sfx), \
+ PORT_GP_1(bank, 18, fn, sfx), PORT_GP_1(bank, 19, fn, sfx), \
+ PORT_GP_1(bank, 20, fn, sfx), PORT_GP_1(bank, 21, fn, sfx), \
+ PORT_GP_1(bank, 22, fn, sfx), PORT_GP_1(bank, 23, fn, sfx), \
+ PORT_GP_1(bank, 24, fn, sfx), PORT_GP_1(bank, 25, fn, sfx), \
+ PORT_GP_1(bank, 26, fn, sfx), PORT_GP_1(bank, 27, fn, sfx), \
+ PORT_GP_1(bank, 28, fn, sfx), PORT_GP_1(bank, 29, fn, sfx), \
+ PORT_GP_1(bank, 30, fn, sfx), PORT_GP_1(bank, 31, fn, sfx)
+
+#define PORT_GP_32_9(bank, fn, sfx) \
+ PORT_GP_1(bank, 0, fn, sfx), PORT_GP_1(bank, 1, fn, sfx), \
+ PORT_GP_1(bank, 2, fn, sfx), PORT_GP_1(bank, 3, fn, sfx), \
+ PORT_GP_1(bank, 4, fn, sfx), PORT_GP_1(bank, 5, fn, sfx), \
+ PORT_GP_1(bank, 6, fn, sfx), PORT_GP_1(bank, 7, fn, sfx), \
+ PORT_GP_1(bank, 8, fn, sfx)
+
+#define PORT_GP_32_REV(bank, fn, sfx) \
+ PORT_GP_1(bank, 31, fn, sfx), PORT_GP_1(bank, 30, fn, sfx), \
+ PORT_GP_1(bank, 29, fn, sfx), PORT_GP_1(bank, 28, fn, sfx), \
+ PORT_GP_1(bank, 27, fn, sfx), PORT_GP_1(bank, 26, fn, sfx), \
+ PORT_GP_1(bank, 25, fn, sfx), PORT_GP_1(bank, 24, fn, sfx), \
+ PORT_GP_1(bank, 23, fn, sfx), PORT_GP_1(bank, 22, fn, sfx), \
+ PORT_GP_1(bank, 21, fn, sfx), PORT_GP_1(bank, 20, fn, sfx), \
+ PORT_GP_1(bank, 19, fn, sfx), PORT_GP_1(bank, 18, fn, sfx), \
+ PORT_GP_1(bank, 17, fn, sfx), PORT_GP_1(bank, 16, fn, sfx), \
+ PORT_GP_1(bank, 15, fn, sfx), PORT_GP_1(bank, 14, fn, sfx), \
+ PORT_GP_1(bank, 13, fn, sfx), PORT_GP_1(bank, 12, fn, sfx), \
+ PORT_GP_1(bank, 11, fn, sfx), PORT_GP_1(bank, 10, fn, sfx), \
+ PORT_GP_1(bank, 9, fn, sfx), PORT_GP_1(bank, 8, fn, sfx), \
+ PORT_GP_1(bank, 7, fn, sfx), PORT_GP_1(bank, 6, fn, sfx), \
+ PORT_GP_1(bank, 5, fn, sfx), PORT_GP_1(bank, 4, fn, sfx), \
+ PORT_GP_1(bank, 3, fn, sfx), PORT_GP_1(bank, 2, fn, sfx), \
+ PORT_GP_1(bank, 1, fn, sfx), PORT_GP_1(bank, 0, fn, sfx)
+
+#define CPU_ALL_PORT(fn, sfx) \
+ PORT_GP_32(0, fn, sfx), \
+ PORT_GP_32(1, fn, sfx), \
+ PORT_GP_32(2, fn, sfx), \
+ PORT_GP_32(3, fn, sfx), \
+ PORT_GP_32(4, fn, sfx), \
+ PORT_GP_32(5, fn, sfx), \
+ PORT_GP_32_9(6, fn, sfx)
+
+#define _GP_PORT_ALL(bank, pin, name, sfx) name##_##sfx
+
+#define _GP_GPIO(bank, pin, _name, sfx) \
+ [(bank * 32) + pin] = { \
+ .name = __stringify(_name), \
+ .enum_id = _name##_DATA, \
+ }
+
+#define _GP_DATA(bank, pin, name, sfx) \
+ PINMUX_DATA(name##_DATA, name##_FN)
+
+#define GP_ALL(str) CPU_ALL_PORT(_GP_PORT_ALL, str)
+#define PINMUX_GPIO_GP_ALL() CPU_ALL_PORT(_GP_GPIO, unused)
+#define PINMUX_DATA_GP_ALL() CPU_ALL_PORT(_GP_DATA, unused)
#define PINMUX_IPSR_DATA(ipsr, fn) PINMUX_DATA(fn##_MARK, FN_##ipsr, FN_##fn)
#define PINMUX_IPSR_MODSEL_DATA(ipsr, fn, ms) PINMUX_DATA(fn##_MARK, FN_##ms, \
@@ -64,14 +102,6 @@ enum {
GP_ALL(DATA), /* GP_0_0_DATA -> GP_6_8_DATA */
PINMUX_DATA_END,
- PINMUX_INPUT_BEGIN,
- GP_ALL(IN), /* GP_0_0_IN -> GP_6_8_IN */
- PINMUX_INPUT_END,
-
- PINMUX_OUTPUT_BEGIN,
- GP_ALL(OUT), /* GP_0_0_OUT -> GP_6_8_OUT */
- PINMUX_OUTPUT_END,
-
PINMUX_FUNCTION_BEGIN,
GP_ALL(FN), /* GP_0_0_FN -> GP_6_8_FN */
@@ -1468,19 +1498,26 @@ static const unsigned int du0_rgb888_mux[] = {
DU0_DB7_MARK, DU0_DB6_MARK, DU0_DB5_MARK, DU0_DB4_MARK,
DU0_DB3_MARK, DU0_DB2_MARK, DU0_DB1_MARK, DU0_DB0_MARK,
};
-static const unsigned int du0_clk_0_pins[] = {
- /* CLKIN, CLKOUT */
- 29, 180,
+static const unsigned int du0_clk_in_pins[] = {
+ /* CLKIN */
+ 29,
};
-static const unsigned int du0_clk_0_mux[] = {
- DU0_DOTCLKIN_MARK, DU0_DOTCLKOUT0_MARK,
+static const unsigned int du0_clk_in_mux[] = {
+ DU0_DOTCLKIN_MARK,
};
-static const unsigned int du0_clk_1_pins[] = {
- /* CLKIN, CLKOUT */
- 29, 30,
+static const unsigned int du0_clk_out_0_pins[] = {
+ /* CLKOUT */
+ 180,
};
-static const unsigned int du0_clk_1_mux[] = {
- DU0_DOTCLKIN_MARK, DU0_DOTCLKOUT1_MARK,
+static const unsigned int du0_clk_out_0_mux[] = {
+ DU0_DOTCLKOUT0_MARK,
+};
+static const unsigned int du0_clk_out_1_pins[] = {
+ /* CLKOUT */
+ 30,
+};
+static const unsigned int du0_clk_out_1_mux[] = {
+ DU0_DOTCLKOUT1_MARK,
};
static const unsigned int du0_sync_0_pins[] = {
/* VSYNC, HSYNC, DISP */
@@ -1541,12 +1578,19 @@ static const unsigned int du1_rgb888_mux[] = {
DU1_DB7_MARK, DU1_DB6_MARK, DU1_DB5_MARK, DU1_DB4_MARK,
DU1_DB3_MARK, DU1_DB2_MARK, DU1_DB1_MARK, DU1_DB0_MARK,
};
-static const unsigned int du1_clk_pins[] = {
- /* CLKIN, CLKOUT */
- 58, 59,
+static const unsigned int du1_clk_in_pins[] = {
+ /* CLKIN */
+ 58,
+};
+static const unsigned int du1_clk_in_mux[] = {
+ DU1_DOTCLKIN_MARK,
+};
+static const unsigned int du1_clk_out_pins[] = {
+ /* CLKOUT */
+ 59,
};
-static const unsigned int du1_clk_mux[] = {
- DU1_DOTCLKIN_MARK, DU1_DOTCLKOUT_MARK,
+static const unsigned int du1_clk_out_mux[] = {
+ DU1_DOTCLKOUT_MARK,
};
static const unsigned int du1_sync_0_pins[] = {
/* VSYNC, HSYNC, DISP */
@@ -2339,15 +2383,17 @@ static const unsigned int usb2_mux[] = {
static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(du0_rgb666),
SH_PFC_PIN_GROUP(du0_rgb888),
- SH_PFC_PIN_GROUP(du0_clk_0),
- SH_PFC_PIN_GROUP(du0_clk_1),
+ SH_PFC_PIN_GROUP(du0_clk_in),
+ SH_PFC_PIN_GROUP(du0_clk_out_0),
+ SH_PFC_PIN_GROUP(du0_clk_out_1),
SH_PFC_PIN_GROUP(du0_sync_0),
SH_PFC_PIN_GROUP(du0_sync_1),
SH_PFC_PIN_GROUP(du0_oddf),
SH_PFC_PIN_GROUP(du0_cde),
SH_PFC_PIN_GROUP(du1_rgb666),
SH_PFC_PIN_GROUP(du1_rgb888),
- SH_PFC_PIN_GROUP(du1_clk),
+ SH_PFC_PIN_GROUP(du1_clk_in),
+ SH_PFC_PIN_GROUP(du1_clk_out),
SH_PFC_PIN_GROUP(du1_sync_0),
SH_PFC_PIN_GROUP(du1_sync_1),
SH_PFC_PIN_GROUP(du1_oddf),
@@ -2462,8 +2508,9 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
static const char * const du0_groups[] = {
"du0_rgb666",
"du0_rgb888",
- "du0_clk_0",
- "du0_clk_1",
+ "du0_clk_in",
+ "du0_clk_out_0",
+ "du0_clk_out_1",
"du0_sync_0",
"du0_sync_1",
"du0_oddf",
@@ -2473,7 +2520,8 @@ static const char * const du0_groups[] = {
static const char * const du1_groups[] = {
"du1_rgb666",
"du1_rgb888",
- "du1_clk",
+ "du1_clk_in",
+ "du1_clk_out",
"du1_sync_0",
"du1_sync_1",
"du1_oddf",
@@ -2504,7 +2552,7 @@ static const char * const intc_groups[] = {
"intc_irq2",
"intc_irq2_b",
"intc_irq3",
- "intc_irq4_b",
+ "intc_irq3_b",
};
static const char * const lbsc_groups[] = {
@@ -2670,274 +2718,6 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(usb2),
};
-#define PINMUX_FN_BASE ARRAY_SIZE(pinmux_pins)
-
-static const struct pinmux_func pinmux_func_gpios[] = {
- GPIO_FN(AVS1), GPIO_FN(AVS2), GPIO_FN(A17), GPIO_FN(A18),
- GPIO_FN(A19),
-
- /* IPSR0 */
- GPIO_FN(PWM1), GPIO_FN(PWMFSW0),
- GPIO_FN(SCIF_CLK), GPIO_FN(TCLK0_C), GPIO_FN(BS),
- GPIO_FN(FD2), GPIO_FN(ATADIR0), GPIO_FN(SDSELF),
- GPIO_FN(HCTS1), GPIO_FN(A0),
- GPIO_FN(FD3), GPIO_FN(A20),
- GPIO_FN(A21),
- GPIO_FN(A22),
- GPIO_FN(VI1_R0), GPIO_FN(A23), GPIO_FN(FCLE),
- GPIO_FN(VI1_R1), GPIO_FN(A24),
- GPIO_FN(FD4), GPIO_FN(VI1_R2),
- GPIO_FN(SSI_WS78_B), GPIO_FN(A25),
- GPIO_FN(FD5), GPIO_FN(VI1_R3),
- GPIO_FN(SSI_SDATA7_B), GPIO_FN(CLKOUT),
- GPIO_FN(PWM0_B),
- GPIO_FN(SDSELF_B), GPIO_FN(RD_WR), GPIO_FN(FWE), GPIO_FN(ATAG0),
- GPIO_FN(VI1_R7), GPIO_FN(HRTS1),
-
- /* IPSR1 */
- GPIO_FN(FD6), GPIO_FN(FD7),
- GPIO_FN(FALE),
- GPIO_FN(ATACS00),
- GPIO_FN(FRE), GPIO_FN(ATACS10), GPIO_FN(VI1_R4),
- GPIO_FN(HSCK1), GPIO_FN(SSI_SDATA8_B),
- GPIO_FN(SSI_SDATA9),
- GPIO_FN(FD0), GPIO_FN(ATARD0), GPIO_FN(VI1_R5),
- GPIO_FN(HTX1),
- GPIO_FN(SSI_SCK9),
- GPIO_FN(FD1), GPIO_FN(ATAWR0), GPIO_FN(VI1_R6),
- GPIO_FN(HRX1), GPIO_FN(SSI_WS9),
- GPIO_FN(MLB_CLK), GPIO_FN(PWM2), GPIO_FN(MLB_SIG),
- GPIO_FN(PWM3), GPIO_FN(MLB_DAT), GPIO_FN(PWM4),
- GPIO_FN(HTX0), GPIO_FN(SDATA),
- GPIO_FN(SUB_TCK), GPIO_FN(CC5_STATE2),
- GPIO_FN(CC5_STATE10), GPIO_FN(CC5_STATE18), GPIO_FN(CC5_STATE26),
- GPIO_FN(CC5_STATE34),
-
- /* IPSR2 */
- GPIO_FN(HRX0), GPIO_FN(SCKZ),
- GPIO_FN(SUB_TDI), GPIO_FN(CC5_STATE3), GPIO_FN(CC5_STATE11),
- GPIO_FN(CC5_STATE19), GPIO_FN(CC5_STATE27), GPIO_FN(CC5_STATE35),
- GPIO_FN(HSCK0), GPIO_FN(MTS), GPIO_FN(PWM5),
- GPIO_FN(SSI_SDATA9_B), GPIO_FN(SUB_TDO),
- GPIO_FN(CC5_STATE0), GPIO_FN(CC5_STATE8), GPIO_FN(CC5_STATE16),
- GPIO_FN(CC5_STATE24), GPIO_FN(CC5_STATE32), GPIO_FN(HCTS0),
- GPIO_FN(STM), GPIO_FN(PWM0_D),
- GPIO_FN(SCIF_CLK_C), GPIO_FN(SUB_TRST), GPIO_FN(TCLK1_B),
- GPIO_FN(CC5_OSCOUT), GPIO_FN(HRTS0),
- GPIO_FN(MDATA), GPIO_FN(SUB_TMS), GPIO_FN(CC5_STATE1),
- GPIO_FN(CC5_STATE9), GPIO_FN(CC5_STATE17), GPIO_FN(CC5_STATE25),
- GPIO_FN(CC5_STATE33), GPIO_FN(LCDOUT0),
- GPIO_FN(DREQ0), GPIO_FN(GPS_CLK_B), GPIO_FN(AUDATA0),
- GPIO_FN(LCDOUT1), GPIO_FN(DACK0),
- GPIO_FN(DRACK0), GPIO_FN(GPS_SIGN_B), GPIO_FN(AUDATA1),
- GPIO_FN(LCDOUT2), GPIO_FN(LCDOUT3),
- GPIO_FN(LCDOUT4), GPIO_FN(LCDOUT5),
- GPIO_FN(LCDOUT6), GPIO_FN(LCDOUT7),
- GPIO_FN(LCDOUT8), GPIO_FN(DREQ1), GPIO_FN(SCL2),
- GPIO_FN(AUDATA2),
-
- /* IPSR3 */
- GPIO_FN(LCDOUT9), GPIO_FN(DACK1), GPIO_FN(SDA2),
- GPIO_FN(AUDATA3), GPIO_FN(LCDOUT10),
- GPIO_FN(LCDOUT11),
- GPIO_FN(LCDOUT12), GPIO_FN(LCDOUT13),
- GPIO_FN(LCDOUT14),
- GPIO_FN(LCDOUT15), GPIO_FN(LCDOUT16),
- GPIO_FN(EX_WAIT1), GPIO_FN(SCL1), GPIO_FN(TCLK1), GPIO_FN(AUDATA4),
- GPIO_FN(LCDOUT17), GPIO_FN(EX_WAIT2), GPIO_FN(SDA1),
- GPIO_FN(GPS_MAG_B), GPIO_FN(AUDATA5),
- GPIO_FN(LCDOUT18),
- GPIO_FN(LCDOUT19), GPIO_FN(LCDOUT20),
- GPIO_FN(LCDOUT21),
- GPIO_FN(LCDOUT22), GPIO_FN(LCDOUT23),
- GPIO_FN(QSTVA_QVS),
- GPIO_FN(SCL3_B), GPIO_FN(QCLK),
- GPIO_FN(QSTVB_QVE),
- GPIO_FN(SDA3_B), GPIO_FN(SDA2_C), GPIO_FN(DACK0_B), GPIO_FN(DRACK0_B),
- GPIO_FN(QSTH_QHS),
- GPIO_FN(QSTB_QHE),
- GPIO_FN(QCPV_QDE),
- GPIO_FN(CAN1_TX), GPIO_FN(SCL2_C), GPIO_FN(REMOCON),
-
- /* IPSR4 */
- GPIO_FN(QPOLA), GPIO_FN(CAN_CLK_C),
- GPIO_FN(QPOLB), GPIO_FN(CAN1_RX),
- GPIO_FN(DREQ0_B), GPIO_FN(SSI_SCK78_B),
- GPIO_FN(VI2_DATA0_VI2_B0), GPIO_FN(PWM6),
- GPIO_FN(AUDCK),
- GPIO_FN(PWMFSW0_B), GPIO_FN(VI2_DATA1_VI2_B1),
- GPIO_FN(PWM0),
- GPIO_FN(AUDSYNC), GPIO_FN(VI2_G0),
- GPIO_FN(VI2_G1), GPIO_FN(VI2_G2),
- GPIO_FN(VI2_G3), GPIO_FN(VI2_G4),
- GPIO_FN(VI2_G5),
- GPIO_FN(VI2_DATA2_VI2_B2), GPIO_FN(SCL1_B),
- GPIO_FN(AUDATA6),
- GPIO_FN(VI2_DATA3_VI2_B3), GPIO_FN(SDA1_B),
- GPIO_FN(AUDATA7),
- GPIO_FN(VI2_G6), GPIO_FN(VI2_G7),
- GPIO_FN(VI2_R0), GPIO_FN(VI2_R1),
- GPIO_FN(VI2_R2), GPIO_FN(VI2_R3),
- GPIO_FN(VI2_DATA4_VI2_B4), GPIO_FN(SCL2_B),
-
- /* IPSR5 */
- GPIO_FN(VI2_DATA5_VI2_B5), GPIO_FN(SDA2_B),
- GPIO_FN(VI2_R4), GPIO_FN(VI2_R5),
- GPIO_FN(VI2_R6), GPIO_FN(VI2_R7),
- GPIO_FN(SCL2_D), GPIO_FN(SDA2_D),
- GPIO_FN(VI2_CLKENB),
- GPIO_FN(SCL1_D), GPIO_FN(VI2_FIELD),
- GPIO_FN(SDA1_D), GPIO_FN(VI2_HSYNC),
- GPIO_FN(VI3_HSYNC), GPIO_FN(VI2_VSYNC),
- GPIO_FN(VI3_VSYNC),
- GPIO_FN(VI2_CLK),
- GPIO_FN(VI1_CLKENB), GPIO_FN(VI3_CLKENB),
- GPIO_FN(AUDIO_CLKC), GPIO_FN(SPEEDIN),
- GPIO_FN(GPS_SIGN_D), GPIO_FN(VI2_DATA6_VI2_B6),
- GPIO_FN(TCLK0), GPIO_FN(QSTVA_B_QVS_B),
- GPIO_FN(AUDIO_CLKOUT_B), GPIO_FN(GPS_MAG_D),
- GPIO_FN(VI2_DATA7_VI2_B7),
- GPIO_FN(VI1_FIELD),
- GPIO_FN(VI3_FIELD), GPIO_FN(AUDIO_CLKOUT),
- GPIO_FN(GPS_CLK_C), GPIO_FN(GPS_CLK_D), GPIO_FN(AUDIO_CLKA),
- GPIO_FN(CAN_TXCLK), GPIO_FN(AUDIO_CLKB),
- GPIO_FN(CAN_DEBUGOUT0), GPIO_FN(MOUT0),
-
- /* IPSR6 */
- GPIO_FN(SSI_SCK0129), GPIO_FN(CAN_DEBUGOUT1), GPIO_FN(MOUT1),
- GPIO_FN(SSI_WS0129), GPIO_FN(CAN_DEBUGOUT2), GPIO_FN(MOUT2),
- GPIO_FN(SSI_SDATA0), GPIO_FN(CAN_DEBUGOUT3), GPIO_FN(MOUT5),
- GPIO_FN(SSI_SDATA1), GPIO_FN(CAN_DEBUGOUT4), GPIO_FN(MOUT6),
- GPIO_FN(SSI_SDATA2), GPIO_FN(CAN_DEBUGOUT5), GPIO_FN(SSI_SCK34),
- GPIO_FN(CAN_DEBUGOUT6), GPIO_FN(CAN0_TX_B), GPIO_FN(IERX),
- GPIO_FN(SSI_SCK9_C), GPIO_FN(SSI_WS34), GPIO_FN(CAN_DEBUGOUT7),
- GPIO_FN(CAN0_RX_B), GPIO_FN(IETX), GPIO_FN(SSI_WS9_C),
- GPIO_FN(SSI_SDATA3), GPIO_FN(PWM0_C), GPIO_FN(CAN_DEBUGOUT8),
- GPIO_FN(CAN_CLK_B), GPIO_FN(IECLK), GPIO_FN(SCIF_CLK_B),
- GPIO_FN(TCLK0_B), GPIO_FN(SSI_SDATA4), GPIO_FN(CAN_DEBUGOUT9),
- GPIO_FN(SSI_SDATA9_C), GPIO_FN(SSI_SCK5), GPIO_FN(ADICLK),
- GPIO_FN(CAN_DEBUGOUT10), GPIO_FN(TCLK0_D),
- GPIO_FN(SSI_WS5), GPIO_FN(ADICS_SAMP), GPIO_FN(CAN_DEBUGOUT11),
- GPIO_FN(SSI_SDATA5), GPIO_FN(ADIDATA),
- GPIO_FN(CAN_DEBUGOUT12), GPIO_FN(SSI_SCK6),
- GPIO_FN(ADICHS0), GPIO_FN(CAN0_TX), GPIO_FN(IERX_B),
-
- /* IPSR7 */
- GPIO_FN(SSI_WS6), GPIO_FN(ADICHS1), GPIO_FN(CAN0_RX), GPIO_FN(IETX_B),
- GPIO_FN(SSI_SDATA6), GPIO_FN(ADICHS2), GPIO_FN(CAN_CLK),
- GPIO_FN(IECLK_B), GPIO_FN(SSI_SCK78), GPIO_FN(CAN_DEBUGOUT13),
- GPIO_FN(SSI_SCK9_B),
- GPIO_FN(SSI_WS78), GPIO_FN(CAN_DEBUGOUT14),
- GPIO_FN(SSI_WS9_B), GPIO_FN(SSI_SDATA7),
- GPIO_FN(CAN_DEBUGOUT15), GPIO_FN(TCLK1_C),
- GPIO_FN(SSI_SDATA8), GPIO_FN(VSP),
- GPIO_FN(ATACS01), GPIO_FN(ATACS11),
- GPIO_FN(CC5_TDO), GPIO_FN(ATADIR1),
- GPIO_FN(CC5_TRST), GPIO_FN(ATAG1),
- GPIO_FN(CC5_TMS), GPIO_FN(ATARD1),
- GPIO_FN(CC5_TCK), GPIO_FN(ATAWR1),
- GPIO_FN(CC5_TDI), GPIO_FN(DREQ2),
- GPIO_FN(DACK2),
-
- /* IPSR8 */
- GPIO_FN(AD_CLK),
- GPIO_FN(CC5_STATE4), GPIO_FN(CC5_STATE12), GPIO_FN(CC5_STATE20),
- GPIO_FN(CC5_STATE28), GPIO_FN(CC5_STATE36),
- GPIO_FN(AD_DI),
- GPIO_FN(CC5_STATE5), GPIO_FN(CC5_STATE13), GPIO_FN(CC5_STATE21),
- GPIO_FN(CC5_STATE29), GPIO_FN(CC5_STATE37),
- GPIO_FN(CAN_DEBUG_HW_TRIGGER), GPIO_FN(AD_DO),
- GPIO_FN(CC5_STATE6), GPIO_FN(CC5_STATE14), GPIO_FN(CC5_STATE22),
- GPIO_FN(CC5_STATE30), GPIO_FN(CC5_STATE38),
- GPIO_FN(CAN_STEP0), GPIO_FN(AD_NCS), GPIO_FN(CC5_STATE7),
- GPIO_FN(CC5_STATE15), GPIO_FN(CC5_STATE23), GPIO_FN(CC5_STATE31),
- GPIO_FN(CC5_STATE39), GPIO_FN(FMCLK), GPIO_FN(RDS_CLK), GPIO_FN(PCMOE),
- GPIO_FN(BPFCLK), GPIO_FN(PCMWE), GPIO_FN(FMIN), GPIO_FN(RDS_DATA),
- GPIO_FN(VI0_CLK), GPIO_FN(VI0_CLKENB),
- GPIO_FN(HTX1_B), GPIO_FN(MT1_SYNC),
- GPIO_FN(VI0_FIELD), GPIO_FN(HRX1_B),
- GPIO_FN(VI0_HSYNC), GPIO_FN(VI0_DATA0_B_VI0_B0_B),
- GPIO_FN(HSCK1_B),
- GPIO_FN(VI0_VSYNC), GPIO_FN(VI0_DATA1_B_VI0_B1_B),
- GPIO_FN(PWMFSW0_C),
-
- /* IPSR9 */
- GPIO_FN(VI0_DATA0_VI0_B0), GPIO_FN(HRTS1_B), GPIO_FN(MT1_VCXO),
- GPIO_FN(VI0_DATA1_VI0_B1), GPIO_FN(HCTS1_B), GPIO_FN(MT1_PWM),
- GPIO_FN(VI0_DATA2_VI0_B2), GPIO_FN(VI0_DATA3_VI0_B3),
- GPIO_FN(VI0_DATA4_VI0_B4),
- GPIO_FN(VI0_DATA5_VI0_B5), GPIO_FN(VI0_DATA6_VI0_B6),
- GPIO_FN(ARM_TRACEDATA_0), GPIO_FN(VI0_DATA7_VI0_B7),
- GPIO_FN(ARM_TRACEDATA_1), GPIO_FN(VI0_G0),
- GPIO_FN(SSI_SCK78_C), GPIO_FN(ARM_TRACEDATA_2),
- GPIO_FN(VI0_G1), GPIO_FN(SSI_WS78_C),
- GPIO_FN(ARM_TRACEDATA_3), GPIO_FN(VI0_G2), GPIO_FN(ETH_TXD1),
- GPIO_FN(ARM_TRACEDATA_4), GPIO_FN(TS_SPSYNC0),
- GPIO_FN(VI0_G3), GPIO_FN(ETH_CRS_DV),
- GPIO_FN(ARM_TRACEDATA_5), GPIO_FN(TS_SDAT0), GPIO_FN(VI0_G4),
- GPIO_FN(ETH_TX_EN), GPIO_FN(ARM_TRACEDATA_6),
- GPIO_FN(VI0_G5), GPIO_FN(ETH_RX_ER),
- GPIO_FN(ARM_TRACEDATA_7), GPIO_FN(VI0_G6), GPIO_FN(ETH_RXD0),
- GPIO_FN(ARM_TRACEDATA_8), GPIO_FN(VI0_G7),
- GPIO_FN(ETH_RXD1), GPIO_FN(ARM_TRACEDATA_9),
-
- /* IPSR10 */
- GPIO_FN(VI0_R0), GPIO_FN(SSI_SDATA7_C),
- GPIO_FN(DREQ1_B), GPIO_FN(ARM_TRACEDATA_10), GPIO_FN(DREQ0_C),
- GPIO_FN(VI0_R1), GPIO_FN(SSI_SDATA8_C), GPIO_FN(DACK1_B),
- GPIO_FN(ARM_TRACEDATA_11), GPIO_FN(DACK0_C), GPIO_FN(DRACK0_C),
- GPIO_FN(VI0_R2), GPIO_FN(ETH_LINK),
- GPIO_FN(ARM_TRACEDATA_12), GPIO_FN(VI0_R3), GPIO_FN(ETH_MAGIC),
- GPIO_FN(ARM_TRACEDATA_13),
- GPIO_FN(VI0_R4), GPIO_FN(ETH_REFCLK),
- GPIO_FN(ARM_TRACEDATA_14), GPIO_FN(MT1_CLK),
- GPIO_FN(TS_SCK0), GPIO_FN(VI0_R5), GPIO_FN(ETH_TXD0),
- GPIO_FN(ARM_TRACEDATA_15),
- GPIO_FN(MT1_D), GPIO_FN(TS_SDEN0), GPIO_FN(VI0_R6), GPIO_FN(ETH_MDC),
- GPIO_FN(DREQ2_C), GPIO_FN(TRACECLK),
- GPIO_FN(MT1_BEN), GPIO_FN(PWMFSW0_D), GPIO_FN(VI0_R7),
- GPIO_FN(ETH_MDIO), GPIO_FN(DACK2_C),
- GPIO_FN(SCIF_CLK_D), GPIO_FN(TRACECTL), GPIO_FN(MT1_PEN),
- GPIO_FN(VI1_CLK), GPIO_FN(SIM_D), GPIO_FN(SDA3), GPIO_FN(VI1_HSYNC),
- GPIO_FN(VI3_CLK), GPIO_FN(SSI_SCK4), GPIO_FN(GPS_SIGN_C),
- GPIO_FN(PWMFSW0_E), GPIO_FN(VI1_VSYNC), GPIO_FN(AUDIO_CLKOUT_C),
- GPIO_FN(SSI_WS4), GPIO_FN(SIM_CLK), GPIO_FN(GPS_MAG_C),
- GPIO_FN(SPV_TRST), GPIO_FN(SCL3),
-
- /* IPSR11 */
- GPIO_FN(VI1_DATA0_VI1_B0), GPIO_FN(SIM_RST),
- GPIO_FN(SPV_TCK), GPIO_FN(ADICLK_B), GPIO_FN(VI1_DATA1_VI1_B1),
- GPIO_FN(MT0_CLK), GPIO_FN(SPV_TMS),
- GPIO_FN(ADICS_B_SAMP_B), GPIO_FN(VI1_DATA2_VI1_B2),
- GPIO_FN(MT0_D), GPIO_FN(SPVTDI), GPIO_FN(ADIDATA_B),
- GPIO_FN(VI1_DATA3_VI1_B3), GPIO_FN(MT0_BEN),
- GPIO_FN(SPV_TDO), GPIO_FN(ADICHS0_B), GPIO_FN(VI1_DATA4_VI1_B4),
- GPIO_FN(MT0_PEN), GPIO_FN(SPA_TRST),
- GPIO_FN(ADICHS1_B), GPIO_FN(VI1_DATA5_VI1_B5),
- GPIO_FN(MT0_SYNC), GPIO_FN(SPA_TCK),
- GPIO_FN(ADICHS2_B), GPIO_FN(VI1_DATA6_VI1_B6),
- GPIO_FN(MT0_VCXO), GPIO_FN(SPA_TMS),
- GPIO_FN(VI1_DATA7_VI1_B7),
- GPIO_FN(MT0_PWM), GPIO_FN(SPA_TDI),
- GPIO_FN(VI1_G0), GPIO_FN(VI3_DATA0),
- GPIO_FN(TS_SCK1), GPIO_FN(DREQ2_B), GPIO_FN(SPA_TDO),
- GPIO_FN(HCTS0_B), GPIO_FN(VI1_G1), GPIO_FN(VI3_DATA1),
- GPIO_FN(SSI_SCK1), GPIO_FN(TS_SDEN1), GPIO_FN(DACK2_B),
- GPIO_FN(HRTS0_B),
-
- /* IPSR12 */
- GPIO_FN(VI1_G2), GPIO_FN(VI3_DATA2), GPIO_FN(SSI_WS1),
- GPIO_FN(TS_SPSYNC1), GPIO_FN(HSCK0_B), GPIO_FN(VI1_G3),
- GPIO_FN(VI3_DATA3), GPIO_FN(SSI_SCK2), GPIO_FN(TS_SDAT1),
- GPIO_FN(SCL1_C), GPIO_FN(HTX0_B), GPIO_FN(VI1_G4), GPIO_FN(VI3_DATA4),
- GPIO_FN(SSI_WS2), GPIO_FN(SDA1_C), GPIO_FN(SIM_RST_B),
- GPIO_FN(HRX0_B), GPIO_FN(VI1_G5), GPIO_FN(VI3_DATA5),
- GPIO_FN(GPS_CLK), GPIO_FN(FSE), GPIO_FN(SIM_D_B),
- GPIO_FN(VI1_G6), GPIO_FN(VI3_DATA6), GPIO_FN(GPS_SIGN), GPIO_FN(FRB),
- GPIO_FN(SIM_CLK_B), GPIO_FN(VI1_G7),
- GPIO_FN(VI3_DATA7), GPIO_FN(GPS_MAG), GPIO_FN(FCE),
-};
-
static const struct pinmux_cfg_reg pinmux_config_regs[] = {
{ PINMUX_CFG_REG("GPSR0", 0xfffc0004, 32, 1) {
GP_0_31_FN, FN_IP3_31_29,
@@ -3773,45 +3553,6 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
/* SEL_I2C1 [2] */
FN_SEL_I2C1_0, FN_SEL_I2C1_1, FN_SEL_I2C1_2, FN_SEL_I2C1_3 }
},
- { PINMUX_CFG_REG("INOUTSEL0", 0xffc40004, 32, 1) { GP_INOUTSEL(0) } },
- { PINMUX_CFG_REG("INOUTSEL1", 0xffc41004, 32, 1) { GP_INOUTSEL(1) } },
- { PINMUX_CFG_REG("INOUTSEL2", 0xffc42004, 32, 1) { GP_INOUTSEL(2) } },
- { PINMUX_CFG_REG("INOUTSEL3", 0xffc43004, 32, 1) { GP_INOUTSEL(3) } },
- { PINMUX_CFG_REG("INOUTSEL4", 0xffc44004, 32, 1) { GP_INOUTSEL(4) } },
- { PINMUX_CFG_REG("INOUTSEL5", 0xffc45004, 32, 1) { GP_INOUTSEL(5) } },
- { PINMUX_CFG_REG("INOUTSEL6", 0xffc46004, 32, 1) {
- 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, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- GP_6_8_IN, GP_6_8_OUT,
- GP_6_7_IN, GP_6_7_OUT,
- GP_6_6_IN, GP_6_6_OUT,
- GP_6_5_IN, GP_6_5_OUT,
- GP_6_4_IN, GP_6_4_OUT,
- GP_6_3_IN, GP_6_3_OUT,
- GP_6_2_IN, GP_6_2_OUT,
- GP_6_1_IN, GP_6_1_OUT,
- GP_6_0_IN, GP_6_0_OUT, }
- },
- { },
-};
-
-static const struct pinmux_data_reg pinmux_data_regs[] = {
- { PINMUX_DATA_REG("INDT0", 0xffc40008, 32) { GP_INDT(0) } },
- { PINMUX_DATA_REG("INDT1", 0xffc41008, 32) { GP_INDT(1) } },
- { PINMUX_DATA_REG("INDT2", 0xffc42008, 32) { GP_INDT(2) } },
- { PINMUX_DATA_REG("INDT3", 0xffc43008, 32) { GP_INDT(3) } },
- { PINMUX_DATA_REG("INDT4", 0xffc44008, 32) { GP_INDT(4) } },
- { PINMUX_DATA_REG("INDT5", 0xffc45008, 32) { GP_INDT(5) } },
- { PINMUX_DATA_REG("INDT6", 0xffc46008, 32) {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, GP_6_8_DATA,
- GP_6_7_DATA, GP_6_6_DATA, GP_6_5_DATA, GP_6_4_DATA,
- GP_6_3_DATA, GP_6_2_DATA, GP_6_1_DATA, GP_6_0_DATA }
- },
{ },
};
@@ -3820,8 +3561,6 @@ const struct sh_pfc_soc_info r8a7779_pinmux_info = {
.unlock_reg = 0xfffc0000, /* PMMR */
- .input = { PINMUX_INPUT_BEGIN, PINMUX_INPUT_END },
- .output = { PINMUX_OUTPUT_BEGIN, PINMUX_OUTPUT_END },
.function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END },
.pins = pinmux_pins,
@@ -3831,11 +3570,7 @@ const struct sh_pfc_soc_info r8a7779_pinmux_info = {
.functions = pinmux_functions,
.nr_functions = ARRAY_SIZE(pinmux_functions),
- .func_gpios = pinmux_func_gpios,
- .nr_func_gpios = ARRAY_SIZE(pinmux_func_gpios),
-
.cfg_regs = pinmux_config_regs,
- .data_regs = pinmux_data_regs,
.gpio_data = pinmux_data,
.gpio_data_size = ARRAY_SIZE(pinmux_data),
diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c
index aef268bc17ba..3492ec9a33b7 100644
--- a/drivers/pinctrl/sh-pfc/pinctrl.c
+++ b/drivers/pinctrl/sh-pfc/pinctrl.c
@@ -182,6 +182,17 @@ static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev,
goto done;
}
+ if (!pfc->gpio) {
+ /* If GPIOs are handled externally the pin mux type need to be
+ * set to GPIO here.
+ */
+ const struct sh_pfc_pin *pin = &pfc->info->pins[idx];
+
+ ret = sh_pfc_config_mux(pfc, pin->enum_id, PINMUX_TYPE_GPIO);
+ if (ret < 0)
+ goto done;
+ }
+
cfg->type = PINMUX_TYPE_GPIO;
ret = 0;
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0e0bfa035083..115b64453493 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -147,8 +147,7 @@ config PWM_TEGRA
config PWM_TIECAP
tristate "ECAP PWM support"
- depends on SOC_AM33XX
- select PWM_TIPWMSS
+ depends on SOC_AM33XX || ARCH_DAVINCI_DA8XX
help
PWM driver support for the ECAP APWM controller found on AM33XX
TI SOC
@@ -158,8 +157,7 @@ config PWM_TIECAP
config PWM_TIEHRPWM
tristate "EHRPWM PWM support"
- depends on SOC_AM33XX
- select PWM_TIPWMSS
+ depends on SOC_AM33XX || ARCH_DAVINCI_DA8XX
help
PWM driver support for the EHRPWM controller found on AM33XX
TI SOC
@@ -169,7 +167,7 @@ config PWM_TIEHRPWM
config PWM_TIPWMSS
bool
- depends on SOC_AM33XX && (PWM_TIEHRPWM || PWM_TIECAP)
+ default y if SOC_AM33XX && (PWM_TIECAP || PWM_TIEHRPWM)
help
PWM Subsystem driver support for AM33xx SOC.
diff --git a/drivers/pwm/pwm-ab8500.c b/drivers/pwm/pwm-ab8500.c
index 4248d0418273..1d07a6f99375 100644
--- a/drivers/pwm/pwm-ab8500.c
+++ b/drivers/pwm/pwm-ab8500.c
@@ -66,7 +66,7 @@ static int ab8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
1 << (chip->base - 1), ENABLE_PWM);
if (ret < 0)
- dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n",
+ dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n",
pwm->label, ret);
return ret;
}
@@ -88,6 +88,7 @@ static const struct pwm_ops ab8500_pwm_ops = {
.config = ab8500_pwm_config,
.enable = ab8500_pwm_enable,
.disable = ab8500_pwm_disable,
+ .owner = THIS_MODULE,
};
static int ab8500_pwm_probe(struct platform_device *pdev)
@@ -99,7 +100,7 @@ static int ab8500_pwm_probe(struct platform_device *pdev)
* Nothing to be done in probe, this is required to get the
* device which is required for ab8500 read and write
*/
- ab8500 = kzalloc(sizeof(*ab8500), GFP_KERNEL);
+ ab8500 = devm_kzalloc(&pdev->dev, sizeof(*ab8500), GFP_KERNEL);
if (ab8500 == NULL) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
@@ -111,10 +112,8 @@ static int ab8500_pwm_probe(struct platform_device *pdev)
ab8500->chip.npwm = 1;
err = pwmchip_add(&ab8500->chip);
- if (err < 0) {
- kfree(ab8500);
+ if (err < 0)
return err;
- }
dev_dbg(&pdev->dev, "pwm probe successful\n");
platform_set_drvdata(pdev, ab8500);
@@ -132,7 +131,6 @@ static int ab8500_pwm_remove(struct platform_device *pdev)
return err;
dev_dbg(&pdev->dev, "pwm driver removed\n");
- kfree(ab8500);
return 0;
}
diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 16cb53092857..0a7b6582edb1 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -358,6 +358,7 @@ static const struct pwm_ops atmel_tcb_pwm_ops = {
.set_polarity = atmel_tcb_pwm_set_polarity,
.enable = atmel_tcb_pwm_enable,
.disable = atmel_tcb_pwm_disable,
+ .owner = THIS_MODULE,
};
static int atmel_tcb_pwm_probe(struct platform_device *pdev)
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index 3f5677b7690e..ec287989eafc 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -43,7 +43,6 @@ struct imx_chip {
struct clk *clk_per;
struct clk *clk_ipg;
- int enabled;
void __iomem *mmio_base;
struct pwm_chip chip;
@@ -135,7 +134,7 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH;
- if (imx->enabled)
+ if (test_bit(PWMF_ENABLED, &pwm->flags))
cr |= MX3_PWMCR_EN;
writel(cr, imx->mmio_base + MX3_PWMCR);
@@ -186,8 +185,6 @@ static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
imx->set_enable(chip, true);
- imx->enabled = 1;
-
return 0;
}
@@ -198,7 +195,6 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
imx->set_enable(chip, false);
clk_disable_unprepare(imx->clk_per);
- imx->enabled = 0;
}
static struct pwm_ops imx_pwm_ops = {
diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c
index b3f0d0dfd748..8272883c0d05 100644
--- a/drivers/pwm/pwm-lpc32xx.c
+++ b/drivers/pwm/pwm-lpc32xx.c
@@ -37,6 +37,7 @@ static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
unsigned long long c;
int period_cycles, duty_cycles;
+ u32 val;
c = clk_get_rate(lpc32xx->clk) / 256;
c = c * period_ns;
@@ -68,8 +69,10 @@ static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
c = 255;
duty_cycles = 256 - c;
- writel(PWM_ENABLE | PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles),
- lpc32xx->base + (pwm->hwpwm << 2));
+ val = readl(lpc32xx->base + (pwm->hwpwm << 2));
+ val &= ~0xFFFF;
+ val |= PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles);
+ writel(val, lpc32xx->base + (pwm->hwpwm << 2));
return 0;
}
@@ -77,15 +80,29 @@ static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
static int lpc32xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+ u32 val;
+ int ret;
+
+ ret = clk_enable(lpc32xx->clk);
+ if (ret)
+ return ret;
- return clk_enable(lpc32xx->clk);
+ val = readl(lpc32xx->base + (pwm->hwpwm << 2));
+ val |= PWM_ENABLE;
+ writel(val, lpc32xx->base + (pwm->hwpwm << 2));
+
+ return 0;
}
static void lpc32xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+ u32 val;
+
+ val = readl(lpc32xx->base + (pwm->hwpwm << 2));
+ val &= ~PWM_ENABLE;
+ writel(val, lpc32xx->base + (pwm->hwpwm << 2));
- writel(0, lpc32xx->base + (pwm->hwpwm << 2));
clk_disable(lpc32xx->clk);
}
@@ -145,7 +162,7 @@ static int lpc32xx_pwm_remove(struct platform_device *pdev)
return pwmchip_remove(&lpc32xx->chip);
}
-static struct of_device_id lpc32xx_pwm_dt_ids[] = {
+static const struct of_device_id lpc32xx_pwm_dt_ids[] = {
{ .compatible = "nxp,lpc3220-pwm", },
{ /* sentinel */ }
};
diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c
index a53d3094b75a..3febdddf71f9 100644
--- a/drivers/pwm/pwm-mxs.c
+++ b/drivers/pwm/pwm-mxs.c
@@ -38,7 +38,6 @@
struct mxs_pwm_chip {
struct pwm_chip chip;
- struct device *dev;
struct clk *clk;
void __iomem *base;
};
@@ -166,7 +165,6 @@ static int mxs_pwm_probe(struct platform_device *pdev)
return ret;
}
- mxs->dev = &pdev->dev;
platform_set_drvdata(pdev, mxs);
stmp_reset_block(mxs->base);
@@ -181,7 +179,7 @@ static int mxs_pwm_remove(struct platform_device *pdev)
return pwmchip_remove(&mxs->chip);
}
-static struct of_device_id mxs_pwm_dt_ids[] = {
+static const struct of_device_id mxs_pwm_dt_ids[] = {
{ .compatible = "fsl,imx23-pwm", },
{ /* sentinel */ }
};
diff --git a/drivers/pwm/pwm-puv3.c b/drivers/pwm/pwm-puv3.c
index db964e6ecf5c..d1eb499fb15d 100644
--- a/drivers/pwm/pwm-puv3.c
+++ b/drivers/pwm/pwm-puv3.c
@@ -27,7 +27,6 @@ struct puv3_pwm_chip {
struct pwm_chip chip;
void __iomem *base;
struct clk *clk;
- bool enabled;
};
static inline struct puv3_pwm_chip *to_puv3(struct pwm_chip *chip)
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
index 20370e61de5a..dee6ab552a0a 100644
--- a/drivers/pwm/pwm-pxa.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -23,14 +23,13 @@
#include <asm/div64.h>
#define HAS_SECONDARY_PWM 0x10
-#define PWM_ID_BASE(d) ((d) & 0xf)
static const struct platform_device_id pwm_id_table[] = {
/* PWM has_secondary_pwm? */
{ "pxa25x-pwm", 0 },
- { "pxa27x-pwm", 0 | HAS_SECONDARY_PWM },
- { "pxa168-pwm", 1 },
- { "pxa910-pwm", 1 },
+ { "pxa27x-pwm", HAS_SECONDARY_PWM },
+ { "pxa168-pwm", 0 },
+ { "pxa910-pwm", 0 },
{ },
};
MODULE_DEVICE_TABLE(platform, pwm_id_table);
@@ -48,7 +47,6 @@ struct pxa_pwm_chip {
struct device *dev;
struct clk *clk;
- int clk_enabled;
void __iomem *mmio_base;
};
@@ -108,24 +106,15 @@ static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
- int rc = 0;
- if (!pc->clk_enabled) {
- rc = clk_prepare_enable(pc->clk);
- if (!rc)
- pc->clk_enabled++;
- }
- return rc;
+ return clk_prepare_enable(pc->clk);
}
static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
- if (pc->clk_enabled) {
- clk_disable_unprepare(pc->clk);
- pc->clk_enabled--;
- }
+ clk_disable_unprepare(pc->clk);
}
static struct pwm_ops pxa_pwm_ops = {
@@ -152,8 +141,6 @@ static int pwm_probe(struct platform_device *pdev)
if (IS_ERR(pwm->clk))
return PTR_ERR(pwm->clk);
- pwm->clk_enabled = 0;
-
pwm->chip.dev = &pdev->dev;
pwm->chip.ops = &pxa_pwm_ops;
pwm->chip.base = -1;
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
index 5207e6cd8648..a0ece50d70bb 100644
--- a/drivers/pwm/pwm-samsung.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -289,10 +289,10 @@ static int s3c_pwm_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int s3c_pwm_suspend(struct device *dev)
{
- struct s3c_chip *s3c = platform_get_drvdata(pdev);
+ struct s3c_chip *s3c = dev_get_drvdata(dev);
/* No one preserve these values during suspend so reset them
* Otherwise driver leaves PWM unconfigured if same values
@@ -304,9 +304,9 @@ static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int s3c_pwm_resume(struct platform_device *pdev)
+static int s3c_pwm_resume(struct device *dev)
{
- struct s3c_chip *s3c = platform_get_drvdata(pdev);
+ struct s3c_chip *s3c = dev_get_drvdata(dev);
unsigned long tcon;
/* Restore invertion */
@@ -316,21 +316,19 @@ static int s3c_pwm_resume(struct platform_device *pdev)
return 0;
}
-
-#else
-#define s3c_pwm_suspend NULL
-#define s3c_pwm_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(s3c_pwm_pm_ops, s3c_pwm_suspend,
+ s3c_pwm_resume);
+
static struct platform_driver s3c_pwm_driver = {
.driver = {
.name = "s3c24xx-pwm",
.owner = THIS_MODULE,
+ .pm = &s3c_pwm_pm_ops,
},
.probe = s3c_pwm_probe,
.remove = s3c_pwm_remove,
- .suspend = s3c_pwm_suspend,
- .resume = s3c_pwm_resume,
};
static int __init pwm_init(void)
diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c
index 69a2d9eb34db..6d99e2cbdc73 100644
--- a/drivers/pwm/pwm-spear.c
+++ b/drivers/pwm/pwm-spear.c
@@ -49,13 +49,11 @@
* @mmio_base: base address of pwm chip
* @clk: pointer to clk structure of pwm chip
* @chip: linux pwm chip representation
- * @dev: pointer to device structure of pwm chip
*/
struct spear_pwm_chip {
void __iomem *mmio_base;
struct clk *clk;
struct pwm_chip chip;
- struct device *dev;
};
static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip)
@@ -143,7 +141,7 @@ static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
u32 val;
rc = clk_enable(pc->clk);
- if (!rc)
+ if (rc)
return rc;
val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR);
@@ -200,7 +198,6 @@ static int spear_pwm_probe(struct platform_device *pdev)
if (IS_ERR(pc->clk))
return PTR_ERR(pc->clk);
- pc->dev = &pdev->dev;
platform_set_drvdata(pdev, pc);
pc->chip.dev = &pdev->dev;
@@ -209,12 +206,12 @@ static int spear_pwm_probe(struct platform_device *pdev)
pc->chip.npwm = NUM_PWM;
ret = clk_prepare(pc->clk);
- if (!ret)
+ if (ret)
return ret;
if (of_device_is_compatible(np, "st,spear1340-pwm")) {
ret = clk_enable(pc->clk);
- if (!ret) {
+ if (ret) {
clk_unprepare(pc->clk);
return ret;
}
@@ -251,7 +248,7 @@ static int spear_pwm_remove(struct platform_device *pdev)
return pwmchip_remove(&pc->chip);
}
-static struct of_device_id spear_pwm_of_match[] = {
+static const struct of_device_id spear_pwm_of_match[] = {
{ .compatible = "st,spear320-pwm" },
{ .compatible = "st,spear1340-pwm" },
{ }
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index af3ab48cb7ef..3d75f4a88f98 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -233,7 +233,7 @@ static int tegra_pwm_remove(struct platform_device *pdev)
return pwmchip_remove(&pc->chip);
}
-static struct of_device_id tegra_pwm_of_match[] = {
+static const struct of_device_id tegra_pwm_of_match[] = {
{ .compatible = "nvidia,tegra20-pwm" },
{ .compatible = "nvidia,tegra30-pwm" },
{ }
diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c
index 22e96e2bffd3..0d65fb2e02c7 100644
--- a/drivers/pwm/pwm-tiecap.c
+++ b/drivers/pwm/pwm-tiecap.c
@@ -295,7 +295,7 @@ static int ecap_pwm_remove(struct platform_device *pdev)
return pwmchip_remove(&pc->chip);
}
-void ecap_pwm_save_context(struct ecap_pwm_chip *pc)
+static void ecap_pwm_save_context(struct ecap_pwm_chip *pc)
{
pm_runtime_get_sync(pc->chip.dev);
pc->ctx.ecctl2 = readw(pc->mmio_base + ECCTL2);
@@ -304,13 +304,14 @@ void ecap_pwm_save_context(struct ecap_pwm_chip *pc)
pm_runtime_put_sync(pc->chip.dev);
}
-void ecap_pwm_restore_context(struct ecap_pwm_chip *pc)
+static void ecap_pwm_restore_context(struct ecap_pwm_chip *pc)
{
writel(pc->ctx.cap3, pc->mmio_base + CAP3);
writel(pc->ctx.cap4, pc->mmio_base + CAP4);
writew(pc->ctx.ecctl2, pc->mmio_base + ECCTL2);
}
+#ifdef CONFIG_PM_SLEEP
static int ecap_pwm_suspend(struct device *dev)
{
struct ecap_pwm_chip *pc = dev_get_drvdata(dev);
@@ -337,6 +338,7 @@ static int ecap_pwm_resume(struct device *dev)
ecap_pwm_restore_context(pc);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume);
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index 8b4c86fa99c8..6a217596942f 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -533,7 +533,7 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev)
return pwmchip_remove(&pc->chip);
}
-void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc)
+static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc)
{
pm_runtime_get_sync(pc->chip.dev);
pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL);
@@ -547,7 +547,7 @@ void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc)
pm_runtime_put_sync(pc->chip.dev);
}
-void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc)
+static void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc)
{
ehrpwm_write(pc->mmio_base, TBPRD, pc->ctx.tbprd);
ehrpwm_write(pc->mmio_base, CMPA, pc->ctx.cmpa);
@@ -559,6 +559,7 @@ void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc)
ehrpwm_write(pc->mmio_base, TBCTL, pc->ctx.tbctl);
}
+#ifdef CONFIG_PM_SLEEP
static int ehrpwm_pwm_suspend(struct device *dev)
{
struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev);
@@ -594,6 +595,7 @@ static int ehrpwm_pwm_resume(struct device *dev)
ehrpwm_pwm_restore_context(pc);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend,
ehrpwm_pwm_resume);
diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/pwm/pwm-tipwmss.c
index 17cbc59660ec..c9c3d3a1e0eb 100644
--- a/drivers/pwm/pwm-tipwmss.c
+++ b/drivers/pwm/pwm-tipwmss.c
@@ -101,6 +101,7 @@ static int pwmss_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int pwmss_suspend(struct device *dev)
{
struct pwmss_info *info = dev_get_drvdata(dev);
@@ -118,6 +119,7 @@ static int pwmss_resume(struct device *dev)
writew(info->pwmss_clkconfig, info->mmio_base + PWMSS_CLKCONFIG);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(pwmss_pm_ops, pwmss_suspend, pwmss_resume);
diff --git a/drivers/pwm/pwm-tipwmss.h b/drivers/pwm/pwm-tipwmss.h
index 11f76a1e266b..10ad8040408b 100644
--- a/drivers/pwm/pwm-tipwmss.h
+++ b/drivers/pwm/pwm-tipwmss.h
@@ -18,7 +18,6 @@
#ifndef __TIPWMSS_H
#define __TIPWMSS_H
-#ifdef CONFIG_PWM_TIPWMSS
/* PWM substem clock gating */
#define PWMSS_ECAPCLK_EN BIT(0)
#define PWMSS_ECAPCLK_STOP_REQ BIT(1)
@@ -28,6 +27,7 @@
#define PWMSS_ECAPCLK_EN_ACK BIT(0)
#define PWMSS_EPWMCLK_EN_ACK BIT(8)
+#ifdef CONFIG_PWM_TIPWMSS
extern u16 pwmss_submodule_state_change(struct device *dev, int set);
#else
static inline u16 pwmss_submodule_state_change(struct device *dev, int set)
diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c
index 83e25d45d640..29d1bba4804e 100644
--- a/drivers/pwm/pwm-twl-led.c
+++ b/drivers/pwm/pwm-twl-led.c
@@ -271,6 +271,7 @@ static const struct pwm_ops twl4030_pwmled_ops = {
.enable = twl4030_pwmled_enable,
.disable = twl4030_pwmled_disable,
.config = twl4030_pwmled_config,
+ .owner = THIS_MODULE,
};
static const struct pwm_ops twl6030_pwmled_ops = {
@@ -279,6 +280,7 @@ static const struct pwm_ops twl6030_pwmled_ops = {
.config = twl6030_pwmled_config,
.request = twl6030_pwmled_request,
.free = twl6030_pwmled_free,
+ .owner = THIS_MODULE,
};
static int twl_pwmled_probe(struct platform_device *pdev)
@@ -321,7 +323,7 @@ static int twl_pwmled_remove(struct platform_device *pdev)
}
#ifdef CONFIG_OF
-static struct of_device_id twl_pwmled_of_match[] = {
+static const struct of_device_id twl_pwmled_of_match[] = {
{ .compatible = "ti,twl4030-pwmled" },
{ .compatible = "ti,twl6030-pwmled" },
{ },
diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c
index bf3fda294223..eef910580eae 100644
--- a/drivers/pwm/pwm-twl.c
+++ b/drivers/pwm/pwm-twl.c
@@ -248,7 +248,7 @@ static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
twl->twl6030_toggle3 = val;
out:
mutex_unlock(&twl->mutex);
- return 0;
+ return ret;
}
static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
@@ -287,12 +287,14 @@ static const struct pwm_ops twl4030_pwm_ops = {
.disable = twl4030_pwm_disable,
.request = twl4030_pwm_request,
.free = twl4030_pwm_free,
+ .owner = THIS_MODULE,
};
static const struct pwm_ops twl6030_pwm_ops = {
.config = twl_pwm_config,
.enable = twl6030_pwm_enable,
.disable = twl6030_pwm_disable,
+ .owner = THIS_MODULE,
};
static int twl_pwm_probe(struct platform_device *pdev)
@@ -333,7 +335,7 @@ static int twl_pwm_remove(struct platform_device *pdev)
}
#ifdef CONFIG_OF
-static struct of_device_id twl_pwm_of_match[] = {
+static const struct of_device_id twl_pwm_of_match[] = {
{ .compatible = "ti,twl4030-pwm" },
{ .compatible = "ti,twl6030-pwm" },
{ },
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index c6d77e20622c..d4d377c40ec9 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -4,13 +4,15 @@ menu "Remoteproc drivers"
config REMOTEPROC
tristate
depends on HAS_DMA
+ select CRC32
select FW_LOADER
select VIRTIO
+ select VIRTUALIZATION
config OMAP_REMOTEPROC
tristate "OMAP remoteproc support"
depends on HAS_DMA
- depends on ARCH_OMAP4
+ depends on ARCH_OMAP4 || SOC_OMAP5
depends on OMAP_IOMMU
depends on OMAP_MBOX_FWK
select REMOTEPROC
@@ -38,4 +40,27 @@ config STE_MODEM_RPROC
This can be either built-in or a loadable module.
If unsure say N.
+config DA8XX_REMOTEPROC
+ tristate "DA8xx/OMAP-L13x remoteproc support"
+ depends on ARCH_DAVINCI_DA8XX
+ select CMA
+ select REMOTEPROC
+ select RPMSG
+ help
+ Say y here to support DA8xx/OMAP-L13x remote processors via the
+ remote processor framework.
+
+ You want to say y here in order to enable AMP
+ use-cases to run on your platform (multimedia codecs are
+ offloaded to remote DSP processors using this framework).
+
+ This module controls the name of the firmware file that gets
+ loaded on the DSP. This file must reside in the /lib/firmware
+ directory. It can be specified via the module parameter
+ da8xx_fw_name=<filename>, and if not specified will default to
+ "rproc-dsp-fw".
+
+ It's safe to say n here if you're not interested in multimedia
+ offloading.
+
endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 391b65181c05..ac2ff75686d2 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -9,3 +9,4 @@ remoteproc-y += remoteproc_virtio.o
remoteproc-y += remoteproc_elf_loader.o
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o
+obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c
new file mode 100644
index 000000000000..9b2e60afa1a6
--- /dev/null
+++ b/drivers/remoteproc/da8xx_remoteproc.c
@@ -0,0 +1,324 @@
+/*
+ * Remote processor machine-specific module for DA8XX
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+
+#include <mach/clock.h> /* for davinci_clk_reset_assert/deassert() */
+
+#include "remoteproc_internal.h"
+
+static char *da8xx_fw_name;
+module_param(da8xx_fw_name, charp, S_IRUGO);
+MODULE_PARM_DESC(da8xx_fw_name,
+ "\n\t\tName of DSP firmware file in /lib/firmware"
+ " (if not specified defaults to 'rproc-dsp-fw')");
+
+/*
+ * OMAP-L138 Technical References:
+ * http://www.ti.com/product/omap-l138
+ */
+#define SYSCFG_CHIPSIG0 BIT(0)
+#define SYSCFG_CHIPSIG1 BIT(1)
+#define SYSCFG_CHIPSIG2 BIT(2)
+#define SYSCFG_CHIPSIG3 BIT(3)
+#define SYSCFG_CHIPSIG4 BIT(4)
+
+/**
+ * struct da8xx_rproc - da8xx remote processor instance state
+ * @rproc: rproc handle
+ * @dsp_clk: placeholder for platform's DSP clk
+ * @ack_fxn: chip-specific ack function for ack'ing irq
+ * @irq_data: ack_fxn function parameter
+ * @chipsig: virt ptr to DSP interrupt registers (CHIPSIG & CHIPSIG_CLR)
+ * @bootreg: virt ptr to DSP boot address register (HOST1CFG)
+ * @irq: irq # used by this instance
+ */
+struct da8xx_rproc {
+ struct rproc *rproc;
+ struct clk *dsp_clk;
+ void (*ack_fxn)(struct irq_data *data);
+ struct irq_data *irq_data;
+ void __iomem *chipsig;
+ void __iomem *bootreg;
+ int irq;
+};
+
+/**
+ * handle_event() - inbound virtqueue message workqueue function
+ *
+ * This function is registered as a kernel thread and is scheduled by the
+ * kernel handler.
+ */
+static irqreturn_t handle_event(int irq, void *p)
+{
+ struct rproc *rproc = (struct rproc *)p;
+
+ /* Process incoming buffers on all our vrings */
+ rproc_vq_interrupt(rproc, 0);
+ rproc_vq_interrupt(rproc, 1);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * da8xx_rproc_callback() - inbound virtqueue message handler
+ *
+ * This handler is invoked directly by the kernel whenever the remote
+ * core (DSP) has modified the state of a virtqueue. There is no
+ * "payload" message indicating the virtqueue index as is the case with
+ * mailbox-based implementations on OMAP4. As such, this handler "polls"
+ * each known virtqueue index for every invocation.
+ */
+static irqreturn_t da8xx_rproc_callback(int irq, void *p)
+{
+ struct rproc *rproc = (struct rproc *)p;
+ struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+ u32 chipsig;
+
+ chipsig = readl(drproc->chipsig);
+ if (chipsig & SYSCFG_CHIPSIG0) {
+ /* Clear interrupt level source */
+ writel(SYSCFG_CHIPSIG0, drproc->chipsig + 4);
+
+ /*
+ * ACK intr to AINTC.
+ *
+ * It has already been ack'ed by the kernel before calling
+ * this function, but since the ARM<->DSP interrupts in the
+ * CHIPSIG register are "level" instead of "pulse" variety,
+ * we need to ack it after taking down the level else we'll
+ * be called again immediately after returning.
+ */
+ drproc->ack_fxn(drproc->irq_data);
+
+ return IRQ_WAKE_THREAD;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int da8xx_rproc_start(struct rproc *rproc)
+{
+ struct device *dev = rproc->dev.parent;
+ struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+ struct clk *dsp_clk = drproc->dsp_clk;
+
+ /* hw requires the start (boot) address be on 1KB boundary */
+ if (rproc->bootaddr & 0x3ff) {
+ dev_err(dev, "invalid boot address: must be aligned to 1KB\n");
+
+ return -EINVAL;
+ }
+
+ writel(rproc->bootaddr, drproc->bootreg);
+
+ clk_enable(dsp_clk);
+ davinci_clk_reset_deassert(dsp_clk);
+
+ return 0;
+}
+
+static int da8xx_rproc_stop(struct rproc *rproc)
+{
+ struct da8xx_rproc *drproc = rproc->priv;
+
+ clk_disable(drproc->dsp_clk);
+
+ return 0;
+}
+
+/* kick a virtqueue */
+static void da8xx_rproc_kick(struct rproc *rproc, int vqid)
+{
+ struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+
+ /* Interupt remote proc */
+ writel(SYSCFG_CHIPSIG2, drproc->chipsig);
+}
+
+static struct rproc_ops da8xx_rproc_ops = {
+ .start = da8xx_rproc_start,
+ .stop = da8xx_rproc_stop,
+ .kick = da8xx_rproc_kick,
+};
+
+static int reset_assert(struct device *dev)
+{
+ struct clk *dsp_clk;
+
+ dsp_clk = clk_get(dev, NULL);
+ if (IS_ERR(dsp_clk)) {
+ dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
+ return PTR_RET(dsp_clk);
+ }
+
+ davinci_clk_reset_assert(dsp_clk);
+ clk_put(dsp_clk);
+
+ return 0;
+}
+
+static int da8xx_rproc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da8xx_rproc *drproc;
+ struct rproc *rproc;
+ struct irq_data *irq_data;
+ struct resource *bootreg_res;
+ struct resource *chipsig_res;
+ struct clk *dsp_clk;
+ void __iomem *chipsig;
+ void __iomem *bootreg;
+ int irq;
+ int ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n", irq);
+ return irq;
+ }
+
+ irq_data = irq_get_irq_data(irq);
+ if (!irq_data) {
+ dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq);
+ return -EINVAL;
+ }
+
+ bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!bootreg_res) {
+ dev_err(dev,
+ "platform_get_resource(IORESOURCE_MEM, 0): NULL\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!chipsig_res) {
+ dev_err(dev,
+ "platform_get_resource(IORESOURCE_MEM, 1): NULL\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ bootreg = devm_ioremap_resource(dev, bootreg_res);
+ if (IS_ERR(bootreg))
+ return PTR_ERR(bootreg);
+
+ chipsig = devm_ioremap_resource(dev, chipsig_res);
+ if (IS_ERR(chipsig))
+ return PTR_ERR(chipsig);
+
+ dsp_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(dsp_clk)) {
+ dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
+
+ return PTR_ERR(dsp_clk);
+ }
+
+ rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name,
+ sizeof(*drproc));
+ if (!rproc)
+ return -ENOMEM;
+
+ drproc = rproc->priv;
+ drproc->rproc = rproc;
+
+ platform_set_drvdata(pdev, rproc);
+
+ /* everything the ISR needs is now setup, so hook it up */
+ ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback,
+ handle_event, 0, "da8xx-remoteproc",
+ rproc);
+ if (ret) {
+ dev_err(dev, "devm_request_threaded_irq error: %d\n", ret);
+ goto free_rproc;
+ }
+
+ /*
+ * rproc_add() can end up enabling the DSP's clk with the DSP
+ * *not* in reset, but da8xx_rproc_start() needs the DSP to be
+ * held in reset at the time it is called.
+ */
+ ret = reset_assert(dev);
+ if (ret)
+ goto free_rproc;
+
+ drproc->chipsig = chipsig;
+ drproc->bootreg = bootreg;
+ drproc->ack_fxn = irq_data->chip->irq_ack;
+ drproc->irq_data = irq_data;
+ drproc->irq = irq;
+ drproc->dsp_clk = dsp_clk;
+
+ ret = rproc_add(rproc);
+ if (ret) {
+ dev_err(dev, "rproc_add failed: %d\n", ret);
+ goto free_rproc;
+ }
+
+ return 0;
+
+free_rproc:
+ rproc_put(rproc);
+
+ return ret;
+}
+
+static int da8xx_rproc_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rproc *rproc = platform_get_drvdata(pdev);
+ struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+
+ /*
+ * It's important to place the DSP in reset before going away,
+ * since a subsequent insmod of this module may enable the DSP's
+ * clock before its program/boot-address has been loaded and
+ * before this module's probe has had a chance to reset the DSP.
+ * Without the reset, the DSP can lockup permanently when it
+ * begins executing garbage.
+ */
+ reset_assert(dev);
+
+ /*
+ * The devm subsystem might end up releasing things before
+ * freeing the irq, thus allowing an interrupt to sneak in while
+ * the device is being removed. This should prevent that.
+ */
+ disable_irq(drproc->irq);
+
+ devm_clk_put(dev, drproc->dsp_clk);
+
+ rproc_del(rproc);
+ rproc_put(rproc);
+
+ return 0;
+}
+
+static struct platform_driver da8xx_rproc_driver = {
+ .probe = da8xx_rproc_probe,
+ .remove = da8xx_rproc_remove,
+ .driver = {
+ .name = "davinci-rproc",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(da8xx_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DA8XX Remote Processor control driver");
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 814af5ab8a72..022dc635d01e 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -37,6 +37,7 @@
#include <linux/iommu.h>
#include <linux/idr.h>
#include <linux/elf.h>
+#include <linux/crc32.h>
#include <linux/virtio_ids.h>
#include <linux/virtio_ring.h>
#include <asm/byteorder.h>
@@ -45,7 +46,8 @@
typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
struct resource_table *table, int len);
-typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
+typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
+ void *, int offset, int avail);
/* Unique indices for remoteproc devices */
static DEFINE_IDA(rproc_dev_index);
@@ -192,6 +194,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
struct rproc *rproc = rvdev->rproc;
struct device *dev = &rproc->dev;
struct rproc_vring *rvring = &rvdev->vring[i];
+ struct fw_rsc_vdev *rsc;
dma_addr_t dma;
void *va;
int ret, size, notifyid;
@@ -202,7 +205,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
/*
* Allocate non-cacheable memory for the vring. In the future
* this call will also configure the IOMMU for us
- * TODO: let the rproc know the da of this vring
*/
va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL);
if (!va) {
@@ -213,7 +215,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
/*
* Assign an rproc-wide unique index for this vring
* TODO: assign a notifyid for rvdev updates as well
- * TODO: let the rproc know the notifyid of this vring
* TODO: support predefined notifyids (via resource table)
*/
ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL);
@@ -224,9 +225,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
}
notifyid = ret;
- /* Store largest notifyid */
- rproc->max_notifyid = max(rproc->max_notifyid, notifyid);
-
dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va,
(unsigned long long)dma, size, notifyid);
@@ -234,6 +232,15 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
rvring->dma = dma;
rvring->notifyid = notifyid;
+ /*
+ * Let the rproc know the notifyid and da of this vring.
+ * Not all platforms use dma_alloc_coherent to automatically
+ * set up the iommu. In this case the device address (da) will
+ * hold the physical address and not the device address.
+ */
+ rsc = (void *)rproc->table_ptr + rvdev->rsc_offset;
+ rsc->vring[i].da = dma;
+ rsc->vring[i].notifyid = notifyid;
return 0;
}
@@ -268,25 +275,20 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
return 0;
}
-static int rproc_max_notifyid(int id, void *p, void *data)
-{
- int *maxid = data;
- *maxid = max(*maxid, id);
- return 0;
-}
-
void rproc_free_vring(struct rproc_vring *rvring)
{
int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
struct rproc *rproc = rvring->rvdev->rproc;
- int maxid = 0;
+ int idx = rvring->rvdev->vring - rvring;
+ struct fw_rsc_vdev *rsc;
dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma);
idr_remove(&rproc->notifyids, rvring->notifyid);
- /* Find the largest remaining notifyid */
- idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid);
- rproc->max_notifyid = maxid;
+ /* reset resource entry info */
+ rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset;
+ rsc->vring[idx].da = 0;
+ rsc->vring[idx].notifyid = -1;
}
/**
@@ -317,7 +319,7 @@ void rproc_free_vring(struct rproc_vring *rvring)
* Returns 0 on success, or an appropriate error code otherwise
*/
static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
- int avail)
+ int offset, int avail)
{
struct device *dev = &rproc->dev;
struct rproc_vdev *rvdev;
@@ -358,8 +360,8 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
goto free_rvdev;
}
- /* remember the device features */
- rvdev->dfeatures = rsc->dfeatures;
+ /* remember the resource offset*/
+ rvdev->rsc_offset = offset;
list_add_tail(&rvdev->node, &rproc->rvdevs);
@@ -394,7 +396,7 @@ free_rvdev:
* Returns 0 on success, or an appropriate error code otherwise
*/
static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
- int avail)
+ int offset, int avail)
{
struct rproc_mem_entry *trace;
struct device *dev = &rproc->dev;
@@ -476,7 +478,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
* are outside those ranges.
*/
static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
- int avail)
+ int offset, int avail)
{
struct rproc_mem_entry *mapping;
struct device *dev = &rproc->dev;
@@ -549,7 +551,9 @@ out:
* pressure is important; it may have a substantial impact on performance.
*/
static int rproc_handle_carveout(struct rproc *rproc,
- struct fw_rsc_carveout *rsc, int avail)
+ struct fw_rsc_carveout *rsc,
+ int offset, int avail)
+
{
struct rproc_mem_entry *carveout, *mapping;
struct device *dev = &rproc->dev;
@@ -671,28 +675,45 @@ free_carv:
return ret;
}
+static int rproc_count_vrings(struct rproc *rproc, struct fw_rsc_vdev *rsc,
+ int offset, int avail)
+{
+ /* Summarize the number of notification IDs */
+ rproc->max_notifyid += rsc->num_of_vrings;
+
+ return 0;
+}
+
/*
* A lookup table for resource handlers. The indices are defined in
* enum fw_resource_type.
*/
-static rproc_handle_resource_t rproc_handle_rsc[] = {
+static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
[RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
[RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
[RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
[RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */
};
+static rproc_handle_resource_t rproc_vdev_handler[RSC_LAST] = {
+ [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev,
+};
+
+static rproc_handle_resource_t rproc_count_vrings_handler[RSC_LAST] = {
+ [RSC_VDEV] = (rproc_handle_resource_t)rproc_count_vrings,
+};
+
/* handle firmware resource entries before booting the remote processor */
-static int
-rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len)
+static int rproc_handle_resources(struct rproc *rproc, int len,
+ rproc_handle_resource_t handlers[RSC_LAST])
{
struct device *dev = &rproc->dev;
rproc_handle_resource_t handler;
int ret = 0, i;
- for (i = 0; i < table->num; i++) {
- int offset = table->offset[i];
- struct fw_rsc_hdr *hdr = (void *)table + offset;
+ for (i = 0; i < rproc->table_ptr->num; i++) {
+ int offset = rproc->table_ptr->offset[i];
+ struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset;
int avail = len - offset - sizeof(*hdr);
void *rsc = (void *)hdr + sizeof(*hdr);
@@ -709,45 +730,11 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len
continue;
}
- handler = rproc_handle_rsc[hdr->type];
+ handler = handlers[hdr->type];
if (!handler)
continue;
- ret = handler(rproc, rsc, avail);
- if (ret)
- break;
- }
-
- return ret;
-}
-
-/* handle firmware resource entries while registering the remote processor */
-static int
-rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len)
-{
- struct device *dev = &rproc->dev;
- int ret = 0, i;
-
- for (i = 0; i < table->num; i++) {
- int offset = table->offset[i];
- struct fw_rsc_hdr *hdr = (void *)table + offset;
- int avail = len - offset - sizeof(*hdr);
- struct fw_rsc_vdev *vrsc;
-
- /* make sure table isn't truncated */
- if (avail < 0) {
- dev_err(dev, "rsc table is truncated\n");
- return -EINVAL;
- }
-
- dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type);
-
- if (hdr->type != RSC_VDEV)
- continue;
-
- vrsc = (struct fw_rsc_vdev *)hdr->data;
-
- ret = rproc_handle_vdev(rproc, vrsc, avail);
+ ret = handler(rproc, rsc, offset + sizeof(*hdr), avail);
if (ret)
break;
}
@@ -805,9 +792,12 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
{
struct device *dev = &rproc->dev;
const char *name = rproc->firmware;
- struct resource_table *table;
+ struct resource_table *table, *loaded_table;
int ret, tablesz;
+ if (!rproc->table_ptr)
+ return -ENOMEM;
+
ret = rproc_fw_sanity_check(rproc, fw);
if (ret)
return ret;
@@ -833,8 +823,15 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
goto clean_up;
}
+ /* Verify that resource table in loaded fw is unchanged */
+ if (rproc->table_csum != crc32(0, table, tablesz)) {
+ dev_err(dev, "resource checksum failed, fw changed?\n");
+ ret = -EINVAL;
+ goto clean_up;
+ }
+
/* handle fw resources which are required to boot rproc */
- ret = rproc_handle_boot_rsc(rproc, table, tablesz);
+ ret = rproc_handle_resources(rproc, tablesz, rproc_loading_handlers);
if (ret) {
dev_err(dev, "Failed to process resources: %d\n", ret);
goto clean_up;
@@ -847,6 +844,19 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
goto clean_up;
}
+ /*
+ * The starting device has been given the rproc->cached_table as the
+ * resource table. The address of the vring along with the other
+ * allocated resources (carveouts etc) is stored in cached_table.
+ * In order to pass this information to the remote device we must
+ * copy this information to device memory.
+ */
+ loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
+ if (!loaded_table)
+ goto clean_up;
+
+ memcpy(loaded_table, rproc->cached_table, tablesz);
+
/* power up the remote processor */
ret = rproc->ops->start(rproc);
if (ret) {
@@ -854,6 +864,13 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
goto clean_up;
}
+ /*
+ * Update table_ptr so that all subsequent vring allocations and
+ * virtio fields manipulation update the actual loaded resource table
+ * in device memory.
+ */
+ rproc->table_ptr = loaded_table;
+
rproc->state = RPROC_RUNNING;
dev_info(dev, "remote processor %s is now up\n", rproc->name);
@@ -888,11 +905,30 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
if (!table)
goto out;
- /* look for virtio devices and register them */
- ret = rproc_handle_virtio_rsc(rproc, table, tablesz);
+ rproc->table_csum = crc32(0, table, tablesz);
+
+ /*
+ * Create a copy of the resource table. When a virtio device starts
+ * and calls vring_new_virtqueue() the address of the allocated vring
+ * will be stored in the cached_table. Before the device is started,
+ * cached_table will be copied into devic memory.
+ */
+ rproc->cached_table = kmalloc(tablesz, GFP_KERNEL);
+ if (!rproc->cached_table)
+ goto out;
+
+ memcpy(rproc->cached_table, table, tablesz);
+ rproc->table_ptr = rproc->cached_table;
+
+ /* count the number of notify-ids */
+ rproc->max_notifyid = -1;
+ ret = rproc_handle_resources(rproc, tablesz, rproc_count_vrings_handler);
if (ret)
goto out;
+ /* look for virtio devices and register them */
+ ret = rproc_handle_resources(rproc, tablesz, rproc_vdev_handler);
+
out:
release_firmware(fw);
/* allow rproc_del() contexts, if any, to proceed */
@@ -950,6 +986,9 @@ int rproc_trigger_recovery(struct rproc *rproc)
/* wait until there is no more rproc users */
wait_for_completion(&rproc->crash_comp);
+ /* Free the copy of the resource table */
+ kfree(rproc->cached_table);
+
return rproc_add_virtio_devices(rproc);
}
@@ -1105,6 +1144,9 @@ void rproc_shutdown(struct rproc *rproc)
rproc_disable_iommu(rproc);
+ /* Give the next start a clean resource table */
+ rproc->table_ptr = rproc->cached_table;
+
/* if in crash state, unlock crash handler */
if (rproc->state == RPROC_CRASHED)
complete_all(&rproc->crash_comp);
@@ -1196,11 +1238,11 @@ static struct device_type rproc_type = {
* @dev: the underlying device
* @name: name of this remote processor
* @ops: platform-specific handlers (mainly start/stop)
- * @firmware: name of firmware file to load
+ * @firmware: name of firmware file to load, can be NULL
* @len: length of private data needed by the rproc driver (in bytes)
*
* Allocates a new remote processor handle, but does not register
- * it yet.
+ * it yet. if @firmware is NULL, a default name is used.
*
* This function should be used by rproc implementations during initialization
* of the remote processor.
@@ -1219,19 +1261,39 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
const char *firmware, int len)
{
struct rproc *rproc;
+ char *p, *template = "rproc-%s-fw";
+ int name_len = 0;
if (!dev || !name || !ops)
return NULL;
- rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
+ if (!firmware)
+ /*
+ * Make room for default firmware name (minus %s plus '\0').
+ * If the caller didn't pass in a firmware name then
+ * construct a default name. We're already glomming 'len'
+ * bytes onto the end of the struct rproc allocation, so do
+ * a few more for the default firmware name (but only if
+ * the caller doesn't pass one).
+ */
+ name_len = strlen(name) + strlen(template) - 2 + 1;
+
+ rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL);
if (!rproc) {
dev_err(dev, "%s: kzalloc failed\n", __func__);
return NULL;
}
+ if (!firmware) {
+ p = (char *)rproc + sizeof(struct rproc) + len;
+ snprintf(p, name_len, template, name);
+ } else {
+ p = (char *)firmware;
+ }
+
+ rproc->firmware = p;
rproc->name = name;
rproc->ops = ops;
- rproc->firmware = firmware;
rproc->priv = &rproc[1];
device_initialize(&rproc->dev);
@@ -1315,6 +1377,9 @@ int rproc_del(struct rproc *rproc)
list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node)
rproc_remove_virtio_dev(rvdev);
+ /* Free the copy of the resource table */
+ kfree(rproc->cached_table);
+
device_del(&rproc->dev);
return 0;
diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c
index 0d36f94ab51d..ce283a5b42a1 100644
--- a/drivers/remoteproc/remoteproc_elf_loader.c
+++ b/drivers/remoteproc/remoteproc_elf_loader.c
@@ -208,41 +208,22 @@ rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
return ret;
}
-/**
- * rproc_elf_find_rsc_table() - find the resource table
- * @rproc: the rproc handle
- * @fw: the ELF firmware image
- * @tablesz: place holder for providing back the table size
- *
- * This function finds the resource table inside the remote processor's
- * firmware. It is used both upon the registration of @rproc (in order
- * to look for and register the supported virito devices), and when the
- * @rproc is booted.
- *
- * Returns the pointer to the resource table if it is found, and write its
- * size into @tablesz. If a valid table isn't found, NULL is returned
- * (and @tablesz isn't set).
- */
-static struct resource_table *
-rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
- int *tablesz)
+static struct elf32_shdr *
+find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
{
- struct elf32_hdr *ehdr;
struct elf32_shdr *shdr;
+ int i;
const char *name_table;
- struct device *dev = &rproc->dev;
struct resource_table *table = NULL;
- int i;
- const u8 *elf_data = fw->data;
+ const u8 *elf_data = (void *)ehdr;
- ehdr = (struct elf32_hdr *)elf_data;
+ /* look for the resource table and handle it */
shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
- /* look for the resource table and handle it */
for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
- int size = shdr->sh_size;
- int offset = shdr->sh_offset;
+ u32 size = shdr->sh_size;
+ u32 offset = shdr->sh_offset;
if (strcmp(name_table + shdr->sh_name, ".resource_table"))
continue;
@@ -250,7 +231,7 @@ rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
table = (struct resource_table *)(elf_data + offset);
/* make sure we have the entire table */
- if (offset + size > fw->size) {
+ if (offset + size > fw_size || offset + size < size) {
dev_err(dev, "resource table truncated\n");
return NULL;
}
@@ -280,16 +261,77 @@ rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
return NULL;
}
- *tablesz = shdr->sh_size;
- break;
+ return shdr;
}
+ return NULL;
+}
+
+/**
+ * rproc_elf_find_rsc_table() - find the resource table
+ * @rproc: the rproc handle
+ * @fw: the ELF firmware image
+ * @tablesz: place holder for providing back the table size
+ *
+ * This function finds the resource table inside the remote processor's
+ * firmware. It is used both upon the registration of @rproc (in order
+ * to look for and register the supported virito devices), and when the
+ * @rproc is booted.
+ *
+ * Returns the pointer to the resource table if it is found, and write its
+ * size into @tablesz. If a valid table isn't found, NULL is returned
+ * (and @tablesz isn't set).
+ */
+static struct resource_table *
+rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
+ int *tablesz)
+{
+ struct elf32_hdr *ehdr;
+ struct elf32_shdr *shdr;
+ struct device *dev = &rproc->dev;
+ struct resource_table *table = NULL;
+ const u8 *elf_data = fw->data;
+
+ ehdr = (struct elf32_hdr *)elf_data;
+
+ shdr = find_table(dev, ehdr, fw->size);
+ if (!shdr)
+ return NULL;
+
+ table = (struct resource_table *)(elf_data + shdr->sh_offset);
+ *tablesz = shdr->sh_size;
+
return table;
}
+/**
+ * rproc_elf_find_loaded_rsc_table() - find the loaded resource table
+ * @rproc: the rproc handle
+ * @fw: the ELF firmware image
+ *
+ * This function finds the location of the loaded resource table. Don't
+ * call this function if the table wasn't loaded yet - it's a bug if you do.
+ *
+ * Returns the pointer to the resource table if it is found or NULL otherwise.
+ * If the table wasn't loaded yet the result is unspecified.
+ */
+static struct resource_table *
+rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
+{
+ struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
+ struct elf32_shdr *shdr;
+
+ shdr = find_table(&rproc->dev, ehdr, fw->size);
+ if (!shdr)
+ return NULL;
+
+ return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size);
+}
+
const struct rproc_fw_ops rproc_elf_fw_ops = {
.load = rproc_elf_load_segments,
.find_rsc_table = rproc_elf_find_rsc_table,
+ .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
.sanity_check = rproc_elf_sanity_check,
.get_boot_addr = rproc_elf_get_boot_addr
};
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 7bb66482d061..157e762c1571 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -27,7 +27,8 @@ struct rproc;
/**
* struct rproc_fw_ops - firmware format specific operations.
- * @find_rsc_table: finds the resource table inside the firmware image
+ * @find_rsc_table: find the resource table inside the firmware image
+ * @find_loaded_rsc_table: find the loaded resouce table
* @load: load firmeware to memory, where the remote processor
* expects to find it
* @sanity_check: sanity check the fw image
@@ -37,6 +38,8 @@ struct rproc_fw_ops {
struct resource_table *(*find_rsc_table) (struct rproc *rproc,
const struct firmware *fw,
int *tablesz);
+ struct resource_table *(*find_loaded_rsc_table)(struct rproc *rproc,
+ const struct firmware *fw);
int (*load)(struct rproc *rproc, const struct firmware *fw);
int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);
u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
@@ -102,6 +105,16 @@ struct resource_table *rproc_find_rsc_table(struct rproc *rproc,
return NULL;
}
+static inline
+struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc,
+ const struct firmware *fw)
+{
+ if (rproc->fw_ops->find_loaded_rsc_table)
+ return rproc->fw_ops->find_loaded_rsc_table(rproc, fw);
+
+ return NULL;
+}
+
extern const struct rproc_fw_ops rproc_elf_fw_ops;
#endif /* REMOTEPROC_INTERNAL_H */
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
index afed9b7731c4..b09c75c21b60 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -173,25 +173,35 @@ error:
return ret;
}
-/*
- * We don't support yet real virtio status semantics.
- *
- * The plan is to provide this via the VDEV resource entry
- * which is part of the firmware: this way the remote processor
- * will be able to access the status values as set by us.
- */
static u8 rproc_virtio_get_status(struct virtio_device *vdev)
{
- return 0;
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+ struct fw_rsc_vdev *rsc;
+
+ rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+
+ return rsc->status;
}
static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
{
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+ struct fw_rsc_vdev *rsc;
+
+ rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+
+ rsc->status = status;
dev_dbg(&vdev->dev, "status: %d\n", status);
}
static void rproc_virtio_reset(struct virtio_device *vdev)
{
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+ struct fw_rsc_vdev *rsc;
+
+ rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+
+ rsc->status = 0;
dev_dbg(&vdev->dev, "reset !\n");
}
@@ -199,13 +209,19 @@ static void rproc_virtio_reset(struct virtio_device *vdev)
static u32 rproc_virtio_get_features(struct virtio_device *vdev)
{
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+ struct fw_rsc_vdev *rsc;
+
+ rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
- return rvdev->dfeatures;
+ return rsc->dfeatures;
}
static void rproc_virtio_finalize_features(struct virtio_device *vdev)
{
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+ struct fw_rsc_vdev *rsc;
+
+ rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
/* Give virtio_ring a chance to accept features */
vring_transport_features(vdev);
@@ -213,13 +229,44 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev)
/*
* Remember the finalized features of our vdev, and provide it
* to the remote processor once it is powered on.
- *
- * Similarly to the status field, we don't expose yet the negotiated
- * features to the remote processors at this point. This will be
- * fixed as part of a small resource table overhaul and then an
- * extension of the virtio resource entries.
*/
- rvdev->gfeatures = vdev->features[0];
+ rsc->gfeatures = vdev->features[0];
+}
+
+static void rproc_virtio_get(struct virtio_device *vdev, unsigned offset,
+ void *buf, unsigned len)
+{
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+ struct fw_rsc_vdev *rsc;
+ void *cfg;
+
+ rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+ cfg = &rsc->vring[rsc->num_of_vrings];
+
+ if (offset + len > rsc->config_len || offset + len < len) {
+ dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n");
+ return;
+ }
+
+ memcpy(buf, cfg + offset, len);
+}
+
+static void rproc_virtio_set(struct virtio_device *vdev, unsigned offset,
+ const void *buf, unsigned len)
+{
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+ struct fw_rsc_vdev *rsc;
+ void *cfg;
+
+ rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+ cfg = &rsc->vring[rsc->num_of_vrings];
+
+ if (offset + len > rsc->config_len || offset + len < len) {
+ dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n");
+ return;
+ }
+
+ memcpy(cfg + offset, buf, len);
}
static const struct virtio_config_ops rproc_virtio_config_ops = {
@@ -230,6 +277,8 @@ static const struct virtio_config_ops rproc_virtio_config_ops = {
.reset = rproc_virtio_reset,
.set_status = rproc_virtio_set_status,
.get_status = rproc_virtio_get_status,
+ .get = rproc_virtio_get,
+ .set = rproc_virtio_set,
};
/*
diff --git a/drivers/remoteproc/ste_modem_rproc.c b/drivers/remoteproc/ste_modem_rproc.c
index fb95c4220052..1ec39a4c0b3e 100644
--- a/drivers/remoteproc/ste_modem_rproc.c
+++ b/drivers/remoteproc/ste_modem_rproc.c
@@ -64,26 +64,18 @@ static int sproc_load_segments(struct rproc *rproc, const struct firmware *fw)
}
/* Find the entry for resource table in the Table of Content */
-static struct ste_toc_entry *sproc_find_rsc_entry(const struct firmware *fw)
+static const struct ste_toc_entry *sproc_find_rsc_entry(const void *data)
{
int i;
- struct ste_toc *toc;
-
- if (!fw)
- return NULL;
-
- toc = (void *)fw->data;
+ const struct ste_toc *toc;
+ toc = data;
/* Search the table for the resource table */
for (i = 0; i < SPROC_MAX_TOC_ENTRIES &&
toc->table[i].start != 0xffffffff; i++) {
-
if (!strncmp(toc->table[i].name, SPROC_RESOURCE_NAME,
- sizeof(toc->table[i].name))) {
- if (toc->table[i].start > fw->size)
- return NULL;
+ sizeof(toc->table[i].name)))
return &toc->table[i];
- }
}
return NULL;
@@ -96,9 +88,12 @@ sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
{
struct sproc *sproc = rproc->priv;
struct resource_table *table;
- struct ste_toc_entry *entry;
+ const struct ste_toc_entry *entry;
- entry = sproc_find_rsc_entry(fw);
+ if (!fw)
+ return NULL;
+
+ entry = sproc_find_rsc_entry(fw->data);
if (!entry) {
sproc_err(sproc, "resource table not found in fw\n");
return NULL;
@@ -149,10 +144,30 @@ sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
return table;
}
+/* Find the resource table inside the remote processor's firmware. */
+static struct resource_table *
+sproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
+{
+ struct sproc *sproc = rproc->priv;
+ const struct ste_toc_entry *entry;
+
+ if (!fw || !sproc->fw_addr)
+ return NULL;
+
+ entry = sproc_find_rsc_entry(sproc->fw_addr);
+ if (!entry) {
+ sproc_err(sproc, "resource table not found in fw\n");
+ return NULL;
+ }
+
+ return sproc->fw_addr + entry->start;
+}
+
/* STE modem firmware handler operations */
const struct rproc_fw_ops sproc_fw_ops = {
.load = sproc_load_segments,
.find_rsc_table = sproc_find_rsc_table,
+ .find_loaded_rsc_table = sproc_find_loaded_rsc_table,
};
/* Kick the modem with specified notification id */
@@ -198,7 +213,7 @@ static int sproc_start(struct rproc *rproc)
}
/* Subscribe to notifications */
- for (i = 0; i < rproc->max_notifyid; i++) {
+ for (i = 0; i <= rproc->max_notifyid; i++) {
err = sproc->mdev->ops.kick_subscribe(sproc->mdev, i);
if (err) {
sproc_err(sproc,
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index f6e0ea6ffda5..69a219387582 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -4,5 +4,6 @@ menu "Rpmsg drivers"
config RPMSG
tristate
select VIRTIO
+ select VIRTUALIZATION
endmenu
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 56fceafec9ec..b6135d4d54eb 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -776,23 +776,13 @@ out:
}
EXPORT_SYMBOL(rpmsg_send_offchannel_raw);
-/* called when an rx buffer is used, and it's time to digest a message */
-static void rpmsg_recv_done(struct virtqueue *rvq)
+static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
+ struct rpmsg_hdr *msg, unsigned int len)
{
- struct rpmsg_hdr *msg;
- unsigned int len;
struct rpmsg_endpoint *ept;
struct scatterlist sg;
- struct virtproc_info *vrp = rvq->vdev->priv;
- struct device *dev = &rvq->vdev->dev;
int err;
- msg = virtqueue_get_buf(rvq, &len);
- if (!msg) {
- dev_err(dev, "uhm, incoming signal, but no used buffer ?\n");
- return;
- }
-
dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n",
msg->src, msg->dst, msg->len,
msg->flags, msg->reserved);
@@ -806,7 +796,7 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
if (len > RPMSG_BUF_SIZE ||
msg->len > (len - sizeof(struct rpmsg_hdr))) {
dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len);
- return;
+ return -EINVAL;
}
/* use the dst addr to fetch the callback of the appropriate user */
@@ -842,11 +832,42 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL);
if (err < 0) {
dev_err(dev, "failed to add a virtqueue buffer: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+/* called when an rx buffer is used, and it's time to digest a message */
+static void rpmsg_recv_done(struct virtqueue *rvq)
+{
+ struct virtproc_info *vrp = rvq->vdev->priv;
+ struct device *dev = &rvq->vdev->dev;
+ struct rpmsg_hdr *msg;
+ unsigned int len, msgs_received = 0;
+ int err;
+
+ msg = virtqueue_get_buf(rvq, &len);
+ if (!msg) {
+ dev_err(dev, "uhm, incoming signal, but no used buffer ?\n");
return;
}
+ while (msg) {
+ err = rpmsg_recv_single(vrp, dev, msg, len);
+ if (err)
+ break;
+
+ msgs_received++;
+
+ msg = virtqueue_get_buf(rvq, &len);
+ };
+
+ dev_dbg(dev, "Received %u messages\n", msgs_received);
+
/* tell the remote processor we added another available rx buffer */
- virtqueue_kick(vrp->rvq);
+ if (msgs_received)
+ virtqueue_kick(vrp->rvq);
}
/*
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index 224d634322b4..ccf54f06396b 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -68,6 +68,7 @@
enum rtc_type {
rtc_undef = 0,
rtc_r2025sd,
+ rtc_r2221tl,
rtc_rs5c372a,
rtc_rs5c372b,
rtc_rv5c386,
@@ -76,6 +77,7 @@ enum rtc_type {
static const struct i2c_device_id rs5c372_id[] = {
{ "r2025sd", rtc_r2025sd },
+ { "r2221tl", rtc_r2221tl },
{ "rs5c372a", rtc_rs5c372a },
{ "rs5c372b", rtc_rs5c372b },
{ "rv5c386", rtc_rv5c386 },
@@ -529,6 +531,7 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
rs5c372->time24 = 1;
break;
case rtc_r2025sd:
+ case rtc_r2221tl:
case rtc_rv5c386:
case rtc_rv5c387a:
buf[0] |= RV5C387_CTRL1_24;
@@ -609,6 +612,7 @@ static int rs5c372_probe(struct i2c_client *client,
rs5c372->time24 = 1;
break;
case rtc_r2025sd:
+ case rtc_r2221tl:
case rtc_rv5c386:
case rtc_rv5c387a:
if (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24)
@@ -640,6 +644,7 @@ static int rs5c372_probe(struct i2c_client *client,
dev_info(&client->dev, "%s found, %s, driver version " DRV_VERSION "\n",
({ char *s; switch (rs5c372->type) {
case rtc_r2025sd: s = "r2025sd"; break;
+ case rtc_r2221tl: s = "r2221tl"; break;
case rtc_rs5c372a: s = "rs5c372a"; break;
case rtc_rs5c372b: s = "rs5c372b"; break;
case rtc_rv5c386: s = "rv5c386"; break;
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 82758cbb220b..4361d9772c42 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -2997,18 +2997,14 @@ unlock:
return rc;
}
-static int dasd_release(struct gendisk *disk, fmode_t mode)
+static void dasd_release(struct gendisk *disk, fmode_t mode)
{
- struct dasd_device *base;
-
- base = dasd_device_from_gendisk(disk);
- if (!base)
- return -ENODEV;
-
- atomic_dec(&base->block->open_count);
- module_put(base->discipline->owner);
- dasd_put_device(base);
- return 0;
+ struct dasd_device *base = dasd_device_from_gendisk(disk);
+ if (base) {
+ atomic_dec(&base->block->open_count);
+ module_put(base->discipline->owner);
+ dasd_put_device(base);
+ }
}
/*
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index b6ad0de07930..6eca019bcf30 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -26,7 +26,7 @@
#define DCSS_BUS_ID_SIZE 20
static int dcssblk_open(struct block_device *bdev, fmode_t mode);
-static int dcssblk_release(struct gendisk *disk, fmode_t mode);
+static void dcssblk_release(struct gendisk *disk, fmode_t mode);
static void dcssblk_make_request(struct request_queue *q, struct bio *bio);
static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum,
void **kaddr, unsigned long *pfn);
@@ -781,16 +781,15 @@ out:
return rc;
}
-static int
+static void
dcssblk_release(struct gendisk *disk, fmode_t mode)
{
struct dcssblk_dev_info *dev_info = disk->private_data;
struct segment_info *entry;
- int rc;
if (!dev_info) {
- rc = -ENODEV;
- goto out;
+ WARN_ON(1);
+ return;
}
down_write(&dcssblk_devices_sem);
if (atomic_dec_and_test(&dev_info->use_count)
@@ -803,9 +802,6 @@ dcssblk_release(struct gendisk *disk, fmode_t mode)
dev_info->save_pending = 0;
}
up_write(&dcssblk_devices_sem);
- rc = 0;
-out:
- return rc;
}
static void
@@ -826,8 +822,7 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0)
/* Request is not page-aligned. */
goto fail;
- if (((bio->bi_size >> 9) + bio->bi_sector)
- > get_capacity(bio->bi_bdev->bd_disk)) {
+ if (bio_end_sector(bio) > get_capacity(bio->bi_bdev->bd_disk)) {
/* Request beyond end of DCSS segment. */
goto fail;
}
diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c
index b303cab76a7f..5d73e6e49af6 100644
--- a/drivers/s390/block/scm_blk.c
+++ b/drivers/s390/block/scm_blk.c
@@ -123,10 +123,9 @@ static int scm_open(struct block_device *blkdev, fmode_t mode)
return scm_get_ref();
}
-static int scm_release(struct gendisk *gendisk, fmode_t mode)
+static void scm_release(struct gendisk *gendisk, fmode_t mode)
{
scm_put_ref();
- return 0;
}
static const struct block_device_operations scm_blk_devops = {
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 55cbd0180159..f42b0e15410f 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -2163,10 +2163,10 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
}
/* do we need to support multiple segments? */
- if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
+ if (bio_segments(req->bio) > 1 || bio_segments(rsp->bio) > 1) {
printk("%s: multiple segments req %u %u, rsp %u %u\n",
- __func__, req->bio->bi_vcnt, blk_rq_bytes(req),
- rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
+ __func__, bio_segments(req->bio), blk_rq_bytes(req),
+ bio_segments(rsp->bio), blk_rq_bytes(rsp));
return -EINVAL;
}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c
index 8c2ffbe6af0f..193e7ae90c3b 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_transport.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c
@@ -1939,7 +1939,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
ioc->transport_cmds.status = MPT2_CMD_PENDING;
/* Check if the request is split across multiple segments */
- if (req->bio->bi_vcnt > 1) {
+ if (bio_segments(req->bio) > 1) {
u32 offset = 0;
/* Allocate memory and copy the request */
@@ -1971,7 +1971,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
/* Check if the response needs to be populated across
* multiple segments */
- if (rsp->bio->bi_vcnt > 1) {
+ if (bio_segments(rsp->bio) > 1) {
pci_addr_in = pci_alloc_consistent(ioc->pdev, blk_rq_bytes(rsp),
&pci_dma_in);
if (!pci_addr_in) {
@@ -2038,7 +2038,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- if (req->bio->bi_vcnt > 1) {
+ if (bio_segments(req->bio) > 1) {
ioc->base_add_sg_single(psge, sgl_flags |
(blk_rq_bytes(req) - 4), pci_dma_out);
} else {
@@ -2054,7 +2054,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
MPI2_SGE_FLAGS_END_OF_LIST);
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- if (rsp->bio->bi_vcnt > 1) {
+ if (bio_segments(rsp->bio) > 1) {
ioc->base_add_sg_single(psge, sgl_flags |
(blk_rq_bytes(rsp) + 4), pci_dma_in);
} else {
@@ -2099,7 +2099,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
le16_to_cpu(mpi_reply->ResponseDataLength);
/* check if the resp needs to be copied from the allocated
* pci mem */
- if (rsp->bio->bi_vcnt > 1) {
+ if (bio_segments(rsp->bio) > 1) {
u32 offset = 0;
u32 bytes_to_copy =
le16_to_cpu(mpi_reply->ResponseDataLength);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 7992635d405f..e6689776b4f6 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1188,7 +1188,7 @@ error_autopm:
*
* Locking: called with bdev->bd_mutex held.
**/
-static int sd_release(struct gendisk *disk, fmode_t mode)
+static void sd_release(struct gendisk *disk, fmode_t mode)
{
struct scsi_disk *sdkp = scsi_disk(disk);
struct scsi_device *sdev = sdkp->device;
@@ -1207,7 +1207,6 @@ static int sd_release(struct gendisk *disk, fmode_t mode)
scsi_autopm_put_device(sdev);
scsi_disk_put(sdkp);
- return 0;
}
static int sd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 9f0c46547459..df5e961484e1 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -35,6 +35,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
+#include <linux/aio.h>
#include <linux/errno.h>
#include <linux/mtio.h>
#include <linux/ioctl.h>
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index f2884ee90710..119d67f9c47e 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -541,14 +541,13 @@ static int sr_block_open(struct block_device *bdev, fmode_t mode)
return ret;
}
-static int sr_block_release(struct gendisk *disk, fmode_t mode)
+static void sr_block_release(struct gendisk *disk, fmode_t mode)
{
struct scsi_cd *cd = scsi_cd(disk);
mutex_lock(&sr_mutex);
cdrom_release(&cd->cdi, mode);
scsi_cd_put(cd);
mutex_unlock(&sr_mutex);
- return 0;
}
static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 8234d2259722..2e8f24a1fb95 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -776,10 +776,10 @@ rx_dma_failed:
#if defined(CONFIG_OF)
static const struct of_device_id davinci_spi_of_match[] = {
{
- .compatible = "ti,dm644x-spi",
+ .compatible = "ti,dm6441-spi",
},
{
- .compatible = "ti,da8xx-spi",
+ .compatible = "ti,da830-spi",
.data = (void *)SPI_VERSION_2,
},
{ },
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c
index a1d5778e2bbb..84982768cd10 100644
--- a/drivers/spi/spi-mxs.c
+++ b/drivers/spi/spi-mxs.c
@@ -490,21 +490,6 @@ static int mxs_spi_transfer_one(struct spi_master *master,
return status;
}
-static bool mxs_ssp_dma_filter(struct dma_chan *chan, void *param)
-{
- struct mxs_ssp *ssp = param;
-
- if (!mxs_dma_is_apbh(chan))
- return false;
-
- if (chan->chan_id != ssp->dma_channel)
- return false;
-
- chan->private = &ssp->dma_data;
-
- return true;
-}
-
static const struct of_device_id mxs_spi_dt_ids[] = {
{ .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, },
{ .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, },
@@ -520,13 +505,12 @@ static int mxs_spi_probe(struct platform_device *pdev)
struct spi_master *master;
struct mxs_spi *spi;
struct mxs_ssp *ssp;
- struct resource *iores, *dmares;
+ struct resource *iores;
struct pinctrl *pinctrl;
struct clk *clk;
void __iomem *base;
- int devid, dma_channel, clk_freq;
- int ret = 0, irq_err, irq_dma;
- dma_cap_mask_t mask;
+ int devid, clk_freq;
+ int ret = 0, irq_err;
/*
* Default clock speed for the SPI core. 160MHz seems to
@@ -537,8 +521,7 @@ static int mxs_spi_probe(struct platform_device *pdev)
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq_err = platform_get_irq(pdev, 0);
- irq_dma = platform_get_irq(pdev, 1);
- if (!iores || irq_err < 0 || irq_dma < 0)
+ if (!iores || irq_err < 0)
return -EINVAL;
base = devm_ioremap_resource(&pdev->dev, iores);
@@ -553,32 +536,11 @@ static int mxs_spi_probe(struct platform_device *pdev)
if (IS_ERR(clk))
return PTR_ERR(clk);
- if (np) {
- devid = (enum mxs_ssp_id) of_id->data;
- /*
- * TODO: This is a temporary solution and should be changed
- * to use generic DMA binding later when the helpers get in.
- */
- ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
- &dma_channel);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to get DMA channel\n");
- return -EINVAL;
- }
-
- ret = of_property_read_u32(np, "clock-frequency",
- &clk_freq);
- if (ret)
- clk_freq = clk_freq_default;
- } else {
- dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmares)
- return -EINVAL;
- devid = pdev->id_entry->driver_data;
- dma_channel = dmares->start;
+ devid = (enum mxs_ssp_id) of_id->data;
+ ret = of_property_read_u32(np, "clock-frequency",
+ &clk_freq);
+ if (ret)
clk_freq = clk_freq_default;
- }
master = spi_alloc_master(&pdev->dev, sizeof(*spi));
if (!master)
@@ -597,7 +559,6 @@ static int mxs_spi_probe(struct platform_device *pdev)
ssp->clk = clk;
ssp->base = base;
ssp->devid = devid;
- ssp->dma_channel = dma_channel;
init_completion(&spi->c);
@@ -606,10 +567,7 @@ static int mxs_spi_probe(struct platform_device *pdev)
if (ret)
goto out_master_free;
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- ssp->dma_data.chan_irq = irq_dma;
- ssp->dmach = dma_request_channel(mask, mxs_ssp_dma_filter, ssp);
+ ssp->dmach = dma_request_slave_channel(&pdev->dev, "rx-tx");
if (!ssp->dmach) {
dev_err(ssp->dev, "Failed to request DMA\n");
ret = -ENODEV;
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index b0fe393c882c..371cc66f1a0e 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -1139,6 +1139,35 @@ err_no_rxchan:
return -ENODEV;
}
+static int pl022_dma_autoprobe(struct pl022 *pl022)
+{
+ struct device *dev = &pl022->adev->dev;
+
+ /* automatically configure DMA channels from platform, normally using DT */
+ pl022->dma_rx_channel = dma_request_slave_channel(dev, "rx");
+ if (!pl022->dma_rx_channel)
+ goto err_no_rxchan;
+
+ pl022->dma_tx_channel = dma_request_slave_channel(dev, "tx");
+ if (!pl022->dma_tx_channel)
+ goto err_no_txchan;
+
+ pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!pl022->dummypage)
+ goto err_no_dummypage;
+
+ return 0;
+
+err_no_dummypage:
+ dma_release_channel(pl022->dma_tx_channel);
+ pl022->dma_tx_channel = NULL;
+err_no_txchan:
+ dma_release_channel(pl022->dma_rx_channel);
+ pl022->dma_rx_channel = NULL;
+err_no_rxchan:
+ return -ENODEV;
+}
+
static void terminate_dma(struct pl022 *pl022)
{
struct dma_chan *rxchan = pl022->dma_rx_channel;
@@ -1167,6 +1196,11 @@ static inline int configure_dma(struct pl022 *pl022)
return -ENODEV;
}
+static inline int pl022_dma_autoprobe(struct pl022 *pl022)
+{
+ return 0;
+}
+
static inline int pl022_dma_probe(struct pl022 *pl022)
{
return 0;
@@ -2226,8 +2260,13 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
goto err_no_irq;
}
- /* Get DMA channels */
- if (platform_info->enable_dma) {
+ /* Get DMA channels, try autoconfiguration first */
+ status = pl022_dma_autoprobe(pl022);
+
+ /* If that failed, use channels from platform_info */
+ if (status == 0)
+ platform_info->enable_dma = 1;
+ else if (platform_info->enable_dma) {
status = pl022_dma_probe(pl022);
if (status != 0)
platform_info->enable_dma = 0;
diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c
index b14a55742559..b040200a5a55 100644
--- a/drivers/staging/android/logger.c
+++ b/drivers/staging/android/logger.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
+#include <linux/aio.h>
#include "logger.h"
#include <asm/ioctls.h>
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c
index 0880ef1a01ba..0127601c26c7 100644
--- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c
@@ -16,6 +16,7 @@
#include <linux/export.h>
#include <linux/types.h>
#include <linux/init.h>
+#include <linux/reset.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/spinlock.h>
@@ -661,7 +662,7 @@ int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
}
EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
-static int ipu_reset(struct ipu_soc *ipu)
+static int ipu_memory_reset(struct ipu_soc *ipu)
{
unsigned long timeout;
@@ -1105,7 +1106,12 @@ static int ipu_probe(struct platform_device *pdev)
if (ret)
goto out_failed_irq;
- ret = ipu_reset(ipu);
+ ret = device_reset(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to reset: %d\n", ret);
+ goto out_failed_reset;
+ }
+ ret = ipu_memory_reset(ipu);
if (ret)
goto out_failed_reset;
@@ -1131,8 +1137,8 @@ static int ipu_probe(struct platform_device *pdev)
failed_add_clients:
ipu_submodules_exit(ipu);
failed_submodules_init:
- ipu_irq_exit(ipu);
out_failed_reset:
+ ipu_irq_exit(ipu);
out_failed_irq:
clk_disable_unprepare(ipu->clk);
failed_clk_get:
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index b2e9e177a354..8ab70a620919 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -267,7 +267,7 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg,
}
}
-static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
+static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap)
{
/* DMA is the sole user of the platform data right now */
struct amba_pl011_data *plat = uap->port.dev->platform_data;
@@ -281,20 +281,25 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
struct dma_chan *chan;
dma_cap_mask_t mask;
- /* We need platform data */
- if (!plat || !plat->dma_filter) {
- dev_info(uap->port.dev, "no DMA platform data\n");
- return;
- }
+ chan = dma_request_slave_channel(dev, "tx");
- /* Try to acquire a generic DMA engine slave TX channel */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- chan = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param);
if (!chan) {
- dev_err(uap->port.dev, "no TX DMA channel!\n");
- return;
+ /* We need platform data */
+ if (!plat || !plat->dma_filter) {
+ dev_info(uap->port.dev, "no DMA platform data\n");
+ return;
+ }
+
+ /* Try to acquire a generic DMA engine slave TX channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ chan = dma_request_channel(mask, plat->dma_filter,
+ plat->dma_tx_param);
+ if (!chan) {
+ dev_err(uap->port.dev, "no TX DMA channel!\n");
+ return;
+ }
}
dmaengine_slave_config(chan, &tx_conf);
@@ -304,7 +309,18 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
dma_chan_name(uap->dmatx.chan));
/* Optionally make use of an RX channel as well */
- if (plat->dma_rx_param) {
+ chan = dma_request_slave_channel(dev, "rx");
+
+ if (!chan && plat->dma_rx_param) {
+ chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param);
+
+ if (!chan) {
+ dev_err(uap->port.dev, "no RX DMA channel!\n");
+ return;
+ }
+ }
+
+ if (chan) {
struct dma_slave_config rx_conf = {
.src_addr = uap->port.mapbase + UART01x_DR,
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
@@ -313,12 +329,6 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
.device_fc = false,
};
- chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param);
- if (!chan) {
- dev_err(uap->port.dev, "no RX DMA channel!\n");
- return;
- }
-
dmaengine_slave_config(chan, &rx_conf);
uap->dmarx.chan = chan;
@@ -360,6 +370,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
struct dma_uap {
struct list_head node;
struct uart_amba_port *uap;
+ struct device *dev;
};
static LIST_HEAD(pl011_dma_uarts);
@@ -370,7 +381,7 @@ static int __init pl011_dma_initcall(void)
list_for_each_safe(node, tmp, &pl011_dma_uarts) {
struct dma_uap *dmau = list_entry(node, struct dma_uap, node);
- pl011_dma_probe_initcall(dmau->uap);
+ pl011_dma_probe_initcall(dmau->dev, dmau->uap);
list_del(node);
kfree(dmau);
}
@@ -379,18 +390,19 @@ static int __init pl011_dma_initcall(void)
device_initcall(pl011_dma_initcall);
-static void pl011_dma_probe(struct uart_amba_port *uap)
+static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
{
struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL);
if (dmau) {
dmau->uap = uap;
+ dmau->dev = dev;
list_add_tail(&dmau->node, &pl011_dma_uarts);
}
}
#else
-static void pl011_dma_probe(struct uart_amba_port *uap)
+static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
{
- pl011_dma_probe_initcall(uap);
+ pl011_dma_probe_initcall(dev, uap);
}
#endif
@@ -1096,7 +1108,7 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
#else
/* Blank functions if the DMA engine is not available */
-static inline void pl011_dma_probe(struct uart_amba_port *uap)
+static inline void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
{
}
@@ -2155,7 +2167,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
uap->port.ops = &amba_pl011_pops;
uap->port.flags = UPF_BOOT_AUTOCONF;
uap->port.line = i;
- pl011_dma_probe(uap);
+ pl011_dma_probe(&dev->dev, uap);
/* Ensure interrupts from this UART are masked and cleared */
writew(0, uap->port.membase + UART011_IMSC);
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 62e7d3b015a1..4f5f161896a1 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -35,7 +35,7 @@
#include <linux/pinctrl/consumer.h>
#include <linux/of_device.h>
#include <linux/dma-mapping.h>
-#include <linux/fsl/mxs-dma.h>
+#include <linux/dmaengine.h>
#include <asm/cacheflush.h>
@@ -148,11 +148,6 @@ struct mxs_auart_port {
struct device *dev;
/* for DMA */
- struct mxs_dma_data dma_data;
- int dma_channel_rx, dma_channel_tx;
- int dma_irq_rx, dma_irq_tx;
- int dma_channel;
-
struct scatterlist tx_sgl;
struct dma_chan *tx_dma_chan;
void *tx_dma_buf;
@@ -440,20 +435,6 @@ static u32 mxs_auart_get_mctrl(struct uart_port *u)
return mctrl;
}
-static bool mxs_auart_dma_filter(struct dma_chan *chan, void *param)
-{
- struct mxs_auart_port *s = param;
-
- if (!mxs_dma_is_apbx(chan))
- return false;
-
- if (s->dma_channel == chan->chan_id) {
- chan->private = &s->dma_data;
- return true;
- }
- return false;
-}
-
static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s);
static void dma_rx_callback(void *arg)
{
@@ -545,21 +526,11 @@ static void mxs_auart_dma_exit(struct mxs_auart_port *s)
static int mxs_auart_dma_init(struct mxs_auart_port *s)
{
- dma_cap_mask_t mask;
-
if (auart_dma_enabled(s))
return 0;
- /* We do not get the right DMA channels. */
- if (s->dma_channel_rx == -1 || s->dma_channel_tx == -1)
- return -EINVAL;
-
/* init for RX */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- s->dma_channel = s->dma_channel_rx;
- s->dma_data.chan_irq = s->dma_irq_rx;
- s->rx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s);
+ s->rx_dma_chan = dma_request_slave_channel(s->dev, "rx");
if (!s->rx_dma_chan)
goto err_out;
s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA);
@@ -567,9 +538,7 @@ static int mxs_auart_dma_init(struct mxs_auart_port *s)
goto err_out;
/* init for TX */
- s->dma_channel = s->dma_channel_tx;
- s->dma_data.chan_irq = s->dma_irq_tx;
- s->tx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s);
+ s->tx_dma_chan = dma_request_slave_channel(s->dev, "tx");
if (!s->tx_dma_chan)
goto err_out;
s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA);
@@ -1020,7 +989,6 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s,
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- u32 dma_channel[2];
int ret;
if (!np)
@@ -1034,20 +1002,8 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s,
}
s->port.line = ret;
- s->dma_irq_rx = platform_get_irq(pdev, 1);
- s->dma_irq_tx = platform_get_irq(pdev, 2);
+ s->flags |= MXS_AUART_DMA_CONFIG;
- ret = of_property_read_u32_array(np, "fsl,auart-dma-channel",
- dma_channel, 2);
- if (ret == 0) {
- s->dma_channel_rx = dma_channel[0];
- s->dma_channel_tx = dma_channel[1];
-
- s->flags |= MXS_AUART_DMA_CONFIG;
- } else {
- s->dma_channel_rx = -1;
- s->dma_channel_tx = -1;
- }
return 0;
}
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index dda0dc4a5567..570c005062ab 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -24,6 +24,8 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
+#include <linux/mmu_context.h>
+#include <linux/aio.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@@ -513,6 +515,9 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
struct kiocb_priv {
struct usb_request *req;
struct ep_data *epdata;
+ struct kiocb *iocb;
+ struct mm_struct *mm;
+ struct work_struct work;
void *buf;
const struct iovec *iv;
unsigned long nr_segs;
@@ -528,7 +533,6 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e)
local_irq_disable();
epdata = priv->epdata;
// spin_lock(&epdata->dev->lock);
- kiocbSetCancelled(iocb);
if (likely(epdata && epdata->ep && priv->req))
value = usb_ep_dequeue (epdata->ep, priv->req);
else
@@ -540,15 +544,12 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e)
return value;
}
-static ssize_t ep_aio_read_retry(struct kiocb *iocb)
+static ssize_t ep_copy_to_user(struct kiocb_priv *priv)
{
- struct kiocb_priv *priv = iocb->private;
ssize_t len, total;
void *to_copy;
int i;
- /* we "retry" to get the right mm context for this: */
-
/* copy stuff into user buffers */
total = priv->actual;
len = 0;
@@ -568,9 +569,26 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb)
if (total == 0)
break;
}
+
+ return len;
+}
+
+static void ep_user_copy_worker(struct work_struct *work)
+{
+ struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
+ struct mm_struct *mm = priv->mm;
+ struct kiocb *iocb = priv->iocb;
+ size_t ret;
+
+ use_mm(mm);
+ ret = ep_copy_to_user(priv);
+ unuse_mm(mm);
+
+ /* completing the iocb can drop the ctx and mm, don't touch mm after */
+ aio_complete(iocb, ret, ret);
+
kfree(priv->buf);
kfree(priv);
- return len;
}
static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
@@ -596,14 +614,14 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
aio_complete(iocb, req->actual ? req->actual : req->status,
req->status);
} else {
- /* retry() won't report both; so we hide some faults */
+ /* ep_copy_to_user() won't report both; we hide some faults */
if (unlikely(0 != req->status))
DBG(epdata->dev, "%s fault %d len %d\n",
ep->name, req->status, req->actual);
priv->buf = req->buf;
priv->actual = req->actual;
- kick_iocb(iocb);
+ schedule_work(&priv->work);
}
spin_unlock(&epdata->dev->lock);
@@ -633,8 +651,10 @@ fail:
return value;
}
iocb->private = priv;
+ priv->iocb = iocb;
priv->iv = iv;
priv->nr_segs = nr_segs;
+ INIT_WORK(&priv->work, ep_user_copy_worker);
value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
if (unlikely(value < 0)) {
@@ -642,10 +662,11 @@ fail:
goto fail;
}
- iocb->ki_cancel = ep_aio_cancel;
+ kiocb_set_cancel_fn(iocb, ep_aio_cancel);
get_ep(epdata);
priv->epdata = epdata;
priv->actual = 0;
+ priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */
/* each kiocb is coupled to one usb_request, but we can't
* allocate or submit those if the host disconnected.
@@ -674,7 +695,7 @@ fail:
kfree(priv);
put_ep(epdata);
} else
- value = (iv ? -EIOCBRETRY : -EIOCBQUEUED);
+ value = -EIOCBQUEUED;
return value;
}
@@ -692,7 +713,6 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
if (unlikely(!buf))
return -ENOMEM;
- iocb->ki_retry = ep_aio_read_retry;
return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs);
}
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index a3645bd163d8..2b51e2336aa2 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -59,12 +59,18 @@ MODULE_PARM_DESC(experimental_zcopytx, "Enable Zero Copy TX;"
#define VHOST_DMA_IS_DONE(len) ((len) >= VHOST_DMA_DONE_LEN)
enum {
+ VHOST_NET_FEATURES = VHOST_FEATURES |
+ (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) |
+ (1ULL << VIRTIO_NET_F_MRG_RXBUF),
+};
+
+enum {
VHOST_NET_VQ_RX = 0,
VHOST_NET_VQ_TX = 1,
VHOST_NET_VQ_MAX = 2,
};
-struct vhost_ubuf_ref {
+struct vhost_net_ubuf_ref {
struct kref kref;
wait_queue_head_t wait;
struct vhost_virtqueue *vq;
@@ -87,7 +93,7 @@ struct vhost_net_virtqueue {
struct ubuf_info *ubuf_info;
/* Reference counting for outstanding ubufs.
* Protected by vq mutex. Writers must also take device mutex. */
- struct vhost_ubuf_ref *ubufs;
+ struct vhost_net_ubuf_ref *ubufs;
};
struct vhost_net {
@@ -104,24 +110,25 @@ struct vhost_net {
bool tx_flush;
};
-static unsigned vhost_zcopy_mask __read_mostly;
+static unsigned vhost_net_zcopy_mask __read_mostly;
-void vhost_enable_zcopy(int vq)
+static void vhost_net_enable_zcopy(int vq)
{
- vhost_zcopy_mask |= 0x1 << vq;
+ vhost_net_zcopy_mask |= 0x1 << vq;
}
-static void vhost_zerocopy_done_signal(struct kref *kref)
+static void vhost_net_zerocopy_done_signal(struct kref *kref)
{
- struct vhost_ubuf_ref *ubufs = container_of(kref, struct vhost_ubuf_ref,
- kref);
+ struct vhost_net_ubuf_ref *ubufs;
+
+ ubufs = container_of(kref, struct vhost_net_ubuf_ref, kref);
wake_up(&ubufs->wait);
}
-struct vhost_ubuf_ref *vhost_ubuf_alloc(struct vhost_virtqueue *vq,
- bool zcopy)
+static struct vhost_net_ubuf_ref *
+vhost_net_ubuf_alloc(struct vhost_virtqueue *vq, bool zcopy)
{
- struct vhost_ubuf_ref *ubufs;
+ struct vhost_net_ubuf_ref *ubufs;
/* No zero copy backend? Nothing to count. */
if (!zcopy)
return NULL;
@@ -134,25 +141,38 @@ struct vhost_ubuf_ref *vhost_ubuf_alloc(struct vhost_virtqueue *vq,
return ubufs;
}
-void vhost_ubuf_put(struct vhost_ubuf_ref *ubufs)
+static void vhost_net_ubuf_put(struct vhost_net_ubuf_ref *ubufs)
{
- kref_put(&ubufs->kref, vhost_zerocopy_done_signal);
+ kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal);
}
-void vhost_ubuf_put_and_wait(struct vhost_ubuf_ref *ubufs)
+static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs)
{
- kref_put(&ubufs->kref, vhost_zerocopy_done_signal);
+ kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal);
wait_event(ubufs->wait, !atomic_read(&ubufs->kref.refcount));
kfree(ubufs);
}
+static void vhost_net_clear_ubuf_info(struct vhost_net *n)
+{
+
+ bool zcopy;
+ int i;
+
+ for (i = 0; i < n->dev.nvqs; ++i) {
+ zcopy = vhost_net_zcopy_mask & (0x1 << i);
+ if (zcopy)
+ kfree(n->vqs[i].ubuf_info);
+ }
+}
+
int vhost_net_set_ubuf_info(struct vhost_net *n)
{
bool zcopy;
int i;
for (i = 0; i < n->dev.nvqs; ++i) {
- zcopy = vhost_zcopy_mask & (0x1 << i);
+ zcopy = vhost_net_zcopy_mask & (0x1 << i);
if (!zcopy)
continue;
n->vqs[i].ubuf_info = kmalloc(sizeof(*n->vqs[i].ubuf_info) *
@@ -164,7 +184,7 @@ int vhost_net_set_ubuf_info(struct vhost_net *n)
err:
while (i--) {
- zcopy = vhost_zcopy_mask & (0x1 << i);
+ zcopy = vhost_net_zcopy_mask & (0x1 << i);
if (!zcopy)
continue;
kfree(n->vqs[i].ubuf_info);
@@ -286,7 +306,7 @@ static int vhost_zerocopy_signal_used(struct vhost_net *net,
static void vhost_zerocopy_callback(struct ubuf_info *ubuf, bool success)
{
- struct vhost_ubuf_ref *ubufs = ubuf->ctx;
+ struct vhost_net_ubuf_ref *ubufs = ubuf->ctx;
struct vhost_virtqueue *vq = ubufs->vq;
int cnt = atomic_read(&ubufs->kref.refcount);
@@ -303,7 +323,7 @@ static void vhost_zerocopy_callback(struct ubuf_info *ubuf, bool success)
/* set len to mark this desc buffers done DMA */
vq->heads[ubuf->desc].len = success ?
VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN;
- vhost_ubuf_put(ubufs);
+ vhost_net_ubuf_put(ubufs);
}
/* Expects to be always run from workqueue - which acts as
@@ -326,7 +346,7 @@ static void handle_tx(struct vhost_net *net)
int err;
size_t hdr_size;
struct socket *sock;
- struct vhost_ubuf_ref *uninitialized_var(ubufs);
+ struct vhost_net_ubuf_ref *uninitialized_var(ubufs);
bool zcopy, zcopy_used;
/* TODO: check that we are running from vhost_worker? */
@@ -422,7 +442,7 @@ static void handle_tx(struct vhost_net *net)
if (unlikely(err < 0)) {
if (zcopy_used) {
if (ubufs)
- vhost_ubuf_put(ubufs);
+ vhost_net_ubuf_put(ubufs);
nvq->upend_idx = ((unsigned)nvq->upend_idx - 1)
% UIO_MAXIOV;
}
@@ -776,7 +796,7 @@ static void vhost_net_flush(struct vhost_net *n)
n->tx_flush = true;
mutex_unlock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex);
/* Wait for all lower device DMAs done. */
- vhost_ubuf_put_and_wait(n->vqs[VHOST_NET_VQ_TX].ubufs);
+ vhost_net_ubuf_put_and_wait(n->vqs[VHOST_NET_VQ_TX].ubufs);
mutex_lock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex);
n->tx_flush = false;
kref_init(&n->vqs[VHOST_NET_VQ_TX].ubufs->kref);
@@ -877,7 +897,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
struct socket *sock, *oldsock;
struct vhost_virtqueue *vq;
struct vhost_net_virtqueue *nvq;
- struct vhost_ubuf_ref *ubufs, *oldubufs = NULL;
+ struct vhost_net_ubuf_ref *ubufs, *oldubufs = NULL;
int r;
mutex_lock(&n->dev.mutex);
@@ -908,7 +928,8 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
oldsock = rcu_dereference_protected(vq->private_data,
lockdep_is_held(&vq->mutex));
if (sock != oldsock) {
- ubufs = vhost_ubuf_alloc(vq, sock && vhost_sock_zcopy(sock));
+ ubufs = vhost_net_ubuf_alloc(vq,
+ sock && vhost_sock_zcopy(sock));
if (IS_ERR(ubufs)) {
r = PTR_ERR(ubufs);
goto err_ubufs;
@@ -934,7 +955,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
mutex_unlock(&vq->mutex);
if (oldubufs) {
- vhost_ubuf_put_and_wait(oldubufs);
+ vhost_net_ubuf_put_and_wait(oldubufs);
mutex_lock(&vq->mutex);
vhost_zerocopy_signal_used(n, vq);
mutex_unlock(&vq->mutex);
@@ -952,7 +973,7 @@ err_used:
rcu_assign_pointer(vq->private_data, oldsock);
vhost_net_enable_vq(n, vq);
if (ubufs)
- vhost_ubuf_put_and_wait(ubufs);
+ vhost_net_ubuf_put_and_wait(ubufs);
err_ubufs:
fput(sock->file);
err_vq:
@@ -1027,6 +1048,23 @@ static int vhost_net_set_features(struct vhost_net *n, u64 features)
return 0;
}
+static long vhost_net_set_owner(struct vhost_net *n)
+{
+ int r;
+
+ mutex_lock(&n->dev.mutex);
+ r = vhost_net_set_ubuf_info(n);
+ if (r)
+ goto out;
+ r = vhost_dev_set_owner(&n->dev);
+ if (r)
+ vhost_net_clear_ubuf_info(n);
+ vhost_net_flush(n);
+out:
+ mutex_unlock(&n->dev.mutex);
+ return r;
+}
+
static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg)
{
@@ -1055,19 +1093,15 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
return vhost_net_set_features(n, features);
case VHOST_RESET_OWNER:
return vhost_net_reset_owner(n);
+ case VHOST_SET_OWNER:
+ return vhost_net_set_owner(n);
default:
mutex_lock(&n->dev.mutex);
- if (ioctl == VHOST_SET_OWNER) {
- r = vhost_net_set_ubuf_info(n);
- if (r)
- goto out;
- }
r = vhost_dev_ioctl(&n->dev, ioctl, argp);
if (r == -ENOIOCTLCMD)
r = vhost_vring_ioctl(&n->dev, ioctl, argp);
else
vhost_net_flush(n);
-out:
mutex_unlock(&n->dev.mutex);
return r;
}
@@ -1101,7 +1135,7 @@ static struct miscdevice vhost_net_misc = {
static int vhost_net_init(void)
{
if (experimental_zcopytx)
- vhost_enable_zcopy(VHOST_NET_VQ_TX);
+ vhost_net_enable_zcopy(VHOST_NET_VQ_TX);
return misc_register(&vhost_net_misc);
}
module_init(vhost_net_init);
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 5179f7aa1b0b..701420297225 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -162,14 +162,8 @@ enum {
VHOST_SCSI_VQ_IO = 2,
};
-/*
- * VIRTIO_RING_F_EVENT_IDX seems broken. Not sure the bug is in
- * kernel but disabling it helps.
- * TODO: debug and remove the workaround.
- */
enum {
- VHOST_SCSI_FEATURES = (VHOST_FEATURES & (~VIRTIO_RING_F_EVENT_IDX)) |
- (1ULL << VIRTIO_SCSI_F_HOTPLUG)
+ VHOST_SCSI_FEATURES = VHOST_FEATURES | (1ULL << VIRTIO_SCSI_F_HOTPLUG)
};
#define VHOST_SCSI_MAX_TARGET 256
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 749b5ab5bfbb..beee7f5787e6 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -13,7 +13,7 @@
#include <linux/eventfd.h>
#include <linux/vhost.h>
-#include <linux/virtio_net.h>
+#include <linux/socket.h> /* memcpy_fromiovec */
#include <linux/mm.h>
#include <linux/mmu_context.h>
#include <linux/miscdevice.h>
@@ -344,7 +344,7 @@ static int vhost_attach_cgroups(struct vhost_dev *dev)
}
/* Caller should have device mutex */
-static long vhost_dev_set_owner(struct vhost_dev *dev)
+long vhost_dev_set_owner(struct vhost_dev *dev)
{
struct task_struct *worker;
int err;
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index b58f4ae82cb8..a7ad63592987 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -99,9 +99,6 @@ struct vhost_virtqueue {
u64 log_addr;
struct iovec iov[UIO_MAXIOV];
- /* hdr is used to store the virtio header.
- * Since each iovec has >= 1 byte length, we never need more than
- * header length entries to store the header. */
struct iovec *indirect;
struct vring_used_elem *heads;
/* We use a kind of RCU to access private pointer.
@@ -135,6 +132,7 @@ struct vhost_dev {
};
long vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue **vqs, int nvqs);
+long vhost_dev_set_owner(struct vhost_dev *dev);
long vhost_dev_check_owner(struct vhost_dev *);
struct vhost_memory *vhost_dev_reset_owner_prepare(void);
void vhost_dev_reset_owner(struct vhost_dev *, struct vhost_memory *);
@@ -177,9 +175,6 @@ enum {
(1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
(1ULL << VIRTIO_RING_F_EVENT_IDX) |
(1ULL << VHOST_F_LOG_ALL),
- VHOST_NET_FEATURES = VHOST_FEATURES |
- (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) |
- (1ULL << VIRTIO_NET_F_MRG_RXBUF),
};
static inline int vhost_has_feature(struct vhost_dev *dev, int bit)
@@ -191,7 +186,4 @@ static inline int vhost_has_feature(struct vhost_dev *dev, int bit)
acked_features = rcu_dereference_index_check(dev->acked_features, 1);
return acked_features & (1 << bit);
}
-
-void vhost_enable_zcopy(int vq);
-
#endif
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 688ff4e32e2b..ffc468407bed 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2430,6 +2430,8 @@ config FB_MXS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
+ select FB_MODE_HELPERS
+ select OF_VIDEOMODE
help
Framebuffer support for the MXS SoC.
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index fa00304a63d8..1fea627394d7 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -274,7 +274,7 @@ static int pwm_backlight_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int pwm_backlight_suspend(struct device *dev)
{
struct backlight_device *bl = dev_get_drvdata(dev);
@@ -296,19 +296,16 @@ static int pwm_backlight_resume(struct device *dev)
backlight_update_status(bl);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
pwm_backlight_resume);
-#endif
-
static struct platform_driver pwm_backlight_driver = {
.driver = {
.name = "pwm-backlight",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
.pm = &pwm_backlight_pm_ops,
-#endif
.of_match_table = of_match_ptr(pwm_backlight_of_match),
},
.probe = pwm_backlight_probe,
diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c
index 45169cbaba6e..1b2c26d1658c 100644
--- a/drivers/video/mxsfb.c
+++ b/drivers/video/mxsfb.c
@@ -42,13 +42,15 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <video/of_display_timing.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/pinctrl/consumer.h>
-#include <linux/mxsfb.h>
+#include <linux/fb.h>
+#include <linux/regulator/consumer.h>
+#include <video/videomode.h>
#define REG_SET 4
#define REG_CLR 8
@@ -107,7 +109,7 @@
#define VDCTRL0_ENABLE_PRESENT (1 << 28)
#define VDCTRL0_VSYNC_ACT_HIGH (1 << 27)
#define VDCTRL0_HSYNC_ACT_HIGH (1 << 26)
-#define VDCTRL0_DOTCLK_ACT_FAILING (1 << 25)
+#define VDCTRL0_DOTCLK_ACT_FALLING (1 << 25)
#define VDCTRL0_ENABLE_ACT_HIGH (1 << 24)
#define VDCTRL0_VSYNC_PERIOD_UNIT (1 << 21)
#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20)
@@ -142,6 +144,14 @@
#define BLUE 2
#define TRANSP 3
+#define STMLCDIF_8BIT 1 /** pixel data bus to the display is of 8 bit width */
+#define STMLCDIF_16BIT 0 /** pixel data bus to the display is of 16 bit width */
+#define STMLCDIF_18BIT 2 /** pixel data bus to the display is of 18 bit width */
+#define STMLCDIF_24BIT 3 /** pixel data bus to the display is of 24 bit width */
+
+#define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6)
+#define MXSFB_SYNC_DOTCLK_FALLING_ACT (1 << 7) /* negtive edge sampling */
+
enum mxsfb_devtype {
MXSFB_V3,
MXSFB_V4,
@@ -168,8 +178,8 @@ struct mxsfb_info {
unsigned ld_intf_width;
unsigned dotclk_delay;
const struct mxsfb_devdata *devdata;
- int mapped;
u32 sync;
+ struct regulator *reg_lcd;
};
#define mxsfb_is_v3(host) (host->devdata->ipversion == 3)
@@ -329,9 +339,19 @@ static void mxsfb_enable_controller(struct fb_info *fb_info)
{
struct mxsfb_info *host = to_imxfb_host(fb_info);
u32 reg;
+ int ret;
dev_dbg(&host->pdev->dev, "%s\n", __func__);
+ if (host->reg_lcd) {
+ ret = regulator_enable(host->reg_lcd);
+ if (ret) {
+ dev_err(&host->pdev->dev,
+ "lcd regulator enable failed: %d\n", ret);
+ return;
+ }
+ }
+
clk_prepare_enable(host->clk);
clk_set_rate(host->clk, PICOS2KHZ(fb_info->var.pixclock) * 1000U);
@@ -353,6 +373,7 @@ static void mxsfb_disable_controller(struct fb_info *fb_info)
struct mxsfb_info *host = to_imxfb_host(fb_info);
unsigned loop;
u32 reg;
+ int ret;
dev_dbg(&host->pdev->dev, "%s\n", __func__);
@@ -376,6 +397,13 @@ static void mxsfb_disable_controller(struct fb_info *fb_info)
clk_disable_unprepare(host->clk);
host->enabled = 0;
+
+ if (host->reg_lcd) {
+ ret = regulator_disable(host->reg_lcd);
+ if (ret)
+ dev_err(&host->pdev->dev,
+ "lcd regulator disable failed: %d\n", ret);
+ }
}
static int mxsfb_set_par(struct fb_info *fb_info)
@@ -459,8 +487,8 @@ static int mxsfb_set_par(struct fb_info *fb_info)
vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
if (host->sync & MXSFB_SYNC_DATA_ENABLE_HIGH_ACT)
vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
- if (host->sync & MXSFB_SYNC_DOTCLK_FAILING_ACT)
- vdctrl0 |= VDCTRL0_DOTCLK_ACT_FAILING;
+ if (host->sync & MXSFB_SYNC_DOTCLK_FALLING_ACT)
+ vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
writel(vdctrl0, host->base + LCDC_VDCTRL0);
@@ -679,14 +707,105 @@ static int mxsfb_restore_mode(struct mxsfb_info *host)
return 0;
}
+static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host)
+{
+ struct fb_info *fb_info = &host->fb_info;
+ struct fb_var_screeninfo *var = &fb_info->var;
+ struct device *dev = &host->pdev->dev;
+ struct device_node *np = host->pdev->dev.of_node;
+ struct device_node *display_np;
+ struct device_node *timings_np;
+ struct display_timings *timings;
+ u32 width;
+ int i;
+ int ret = 0;
+
+ display_np = of_parse_phandle(np, "display", 0);
+ if (!display_np) {
+ dev_err(dev, "failed to find display phandle\n");
+ return -ENOENT;
+ }
+
+ ret = of_property_read_u32(display_np, "bus-width", &width);
+ if (ret < 0) {
+ dev_err(dev, "failed to get property bus-width\n");
+ goto put_display_node;
+ }
+
+ switch (width) {
+ case 8:
+ host->ld_intf_width = STMLCDIF_8BIT;
+ break;
+ case 16:
+ host->ld_intf_width = STMLCDIF_16BIT;
+ break;
+ case 18:
+ host->ld_intf_width = STMLCDIF_18BIT;
+ break;
+ case 24:
+ host->ld_intf_width = STMLCDIF_24BIT;
+ break;
+ default:
+ dev_err(dev, "invalid bus-width value\n");
+ ret = -EINVAL;
+ goto put_display_node;
+ }
+
+ ret = of_property_read_u32(display_np, "bits-per-pixel",
+ &var->bits_per_pixel);
+ if (ret < 0) {
+ dev_err(dev, "failed to get property bits-per-pixel\n");
+ goto put_display_node;
+ }
+
+ timings = of_get_display_timings(display_np);
+ if (!timings) {
+ dev_err(dev, "failed to get display timings\n");
+ ret = -ENOENT;
+ goto put_display_node;
+ }
+
+ timings_np = of_find_node_by_name(display_np,
+ "display-timings");
+ if (!timings_np) {
+ dev_err(dev, "failed to find display-timings node\n");
+ ret = -ENOENT;
+ goto put_display_node;
+ }
+
+ for (i = 0; i < of_get_child_count(timings_np); i++) {
+ struct videomode vm;
+ struct fb_videomode fb_vm;
+
+ ret = videomode_from_timing(timings, &vm, i);
+ if (ret < 0)
+ goto put_timings_node;
+ ret = fb_videomode_from_videomode(&vm, &fb_vm);
+ if (ret < 0)
+ goto put_timings_node;
+
+ if (vm.data_flags & DISPLAY_FLAGS_DE_HIGH)
+ host->sync |= MXSFB_SYNC_DATA_ENABLE_HIGH_ACT;
+ if (vm.data_flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+ host->sync |= MXSFB_SYNC_DOTCLK_FALLING_ACT;
+ fb_add_videomode(&fb_vm, &fb_info->modelist);
+ }
+
+put_timings_node:
+ of_node_put(timings_np);
+put_display_node:
+ of_node_put(display_np);
+ return ret;
+}
+
static int mxsfb_init_fbinfo(struct mxsfb_info *host)
{
struct fb_info *fb_info = &host->fb_info;
struct fb_var_screeninfo *var = &fb_info->var;
- struct mxsfb_platform_data *pdata = host->pdev->dev.platform_data;
dma_addr_t fb_phys;
void *fb_virt;
- unsigned fb_size = pdata->fb_size;
+ unsigned fb_size;
+ int ret;
fb_info->fbops = &mxsfb_ops;
fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST;
@@ -696,40 +815,22 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host)
fb_info->fix.visual = FB_VISUAL_TRUECOLOR,
fb_info->fix.accel = FB_ACCEL_NONE;
- var->bits_per_pixel = pdata->default_bpp ? pdata->default_bpp : 16;
+ ret = mxsfb_init_fbinfo_dt(host);
+ if (ret)
+ return ret;
+
var->nonstd = 0;
var->activate = FB_ACTIVATE_NOW;
var->accel_flags = 0;
var->vmode = FB_VMODE_NONINTERLACED;
- host->dotclk_delay = pdata->dotclk_delay;
- host->ld_intf_width = pdata->ld_intf_width;
-
/* Memory allocation for framebuffer */
- if (pdata->fb_phys) {
- if (!fb_size)
- return -EINVAL;
-
- fb_phys = pdata->fb_phys;
-
- if (!request_mem_region(fb_phys, fb_size, host->pdev->name))
- return -ENOMEM;
+ fb_size = SZ_2M;
+ fb_virt = alloc_pages_exact(fb_size, GFP_DMA);
+ if (!fb_virt)
+ return -ENOMEM;
- fb_virt = ioremap(fb_phys, fb_size);
- if (!fb_virt) {
- release_mem_region(fb_phys, fb_size);
- return -ENOMEM;
- }
- host->mapped = 1;
- } else {
- if (!fb_size)
- fb_size = SZ_2M; /* default */
- fb_virt = alloc_pages_exact(fb_size, GFP_DMA);
- if (!fb_virt)
- return -ENOMEM;
-
- fb_phys = virt_to_phys(fb_virt);
- }
+ fb_phys = virt_to_phys(fb_virt);
fb_info->fix.smem_start = fb_phys;
fb_info->screen_base = fb_virt;
@@ -745,13 +846,7 @@ static void mxsfb_free_videomem(struct mxsfb_info *host)
{
struct fb_info *fb_info = &host->fb_info;
- if (host->mapped) {
- iounmap(fb_info->screen_base);
- release_mem_region(fb_info->fix.smem_start,
- fb_info->screen_size);
- } else {
- free_pages_exact(fb_info->screen_base, fb_info->fix.smem_len);
- }
+ free_pages_exact(fb_info->screen_base, fb_info->fix.smem_len);
}
static struct platform_device_id mxsfb_devtype[] = {
@@ -778,47 +873,35 @@ static int mxsfb_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxsfb_dt_ids, &pdev->dev);
- struct mxsfb_platform_data *pdata = pdev->dev.platform_data;
struct resource *res;
struct mxsfb_info *host;
struct fb_info *fb_info;
struct fb_modelist *modelist;
struct pinctrl *pinctrl;
- int panel_enable;
- enum of_gpio_flags flags;
- int i, ret;
+ int ret;
if (of_id)
pdev->id_entry = of_id->data;
- if (!pdata) {
- dev_err(&pdev->dev, "No platformdata. Giving up\n");
- return -ENODEV;
- }
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Cannot get memory IO resource\n");
return -ENODEV;
}
- if (!request_mem_region(res->start, resource_size(res), pdev->name))
- return -EBUSY;
-
fb_info = framebuffer_alloc(sizeof(struct mxsfb_info), &pdev->dev);
if (!fb_info) {
dev_err(&pdev->dev, "Failed to allocate fbdev\n");
- ret = -ENOMEM;
- goto error_alloc_info;
+ return -ENOMEM;
}
host = to_imxfb_host(fb_info);
- host->base = ioremap(res->start, resource_size(res));
- if (!host->base) {
+ host->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->base)) {
dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
- goto error_ioremap;
+ ret = PTR_ERR(host->base);
+ goto fb_release;
}
host->pdev = pdev;
@@ -829,47 +912,31 @@ static int mxsfb_probe(struct platform_device *pdev)
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
ret = PTR_ERR(pinctrl);
- goto error_getpin;
+ goto fb_release;
}
- host->clk = clk_get(&host->pdev->dev, NULL);
+ host->clk = devm_clk_get(&host->pdev->dev, NULL);
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
- goto error_getclock;
+ goto fb_release;
}
- panel_enable = of_get_named_gpio_flags(pdev->dev.of_node,
- "panel-enable-gpios", 0, &flags);
- if (gpio_is_valid(panel_enable)) {
- unsigned long f = GPIOF_OUT_INIT_HIGH;
- if (flags == OF_GPIO_ACTIVE_LOW)
- f = GPIOF_OUT_INIT_LOW;
- ret = devm_gpio_request_one(&pdev->dev, panel_enable,
- f, "panel-enable");
- if (ret) {
- dev_err(&pdev->dev,
- "failed to request gpio %d: %d\n",
- panel_enable, ret);
- goto error_panel_enable;
- }
- }
+ host->reg_lcd = devm_regulator_get(&pdev->dev, "lcd");
+ if (IS_ERR(host->reg_lcd))
+ host->reg_lcd = NULL;
- fb_info->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
+ fb_info->pseudo_palette = devm_kzalloc(&pdev->dev, sizeof(u32) * 16,
+ GFP_KERNEL);
if (!fb_info->pseudo_palette) {
ret = -ENOMEM;
- goto error_pseudo_pallette;
+ goto fb_release;
}
INIT_LIST_HEAD(&fb_info->modelist);
- host->sync = pdata->sync;
-
ret = mxsfb_init_fbinfo(host);
if (ret != 0)
- goto error_init_fb;
-
- for (i = 0; i < pdata->mode_count; i++)
- fb_add_videomode(&pdata->mode_list[i], &fb_info->modelist);
+ goto fb_release;
modelist = list_first_entry(&fb_info->modelist,
struct fb_modelist, list);
@@ -883,7 +950,7 @@ static int mxsfb_probe(struct platform_device *pdev)
ret = register_framebuffer(fb_info);
if (ret != 0) {
dev_err(&pdev->dev,"Failed to register framebuffer\n");
- goto error_register;
+ goto fb_destroy;
}
if (!host->enabled) {
@@ -896,22 +963,12 @@ static int mxsfb_probe(struct platform_device *pdev)
return 0;
-error_register:
+fb_destroy:
if (host->enabled)
clk_disable_unprepare(host->clk);
fb_destroy_modelist(&fb_info->modelist);
-error_init_fb:
- kfree(fb_info->pseudo_palette);
-error_pseudo_pallette:
-error_panel_enable:
- clk_put(host->clk);
-error_getclock:
-error_getpin:
- iounmap(host->base);
-error_ioremap:
+fb_release:
framebuffer_release(fb_info);
-error_alloc_info:
- release_mem_region(res->start, resource_size(res));
return ret;
}
@@ -920,19 +977,14 @@ static int mxsfb_remove(struct platform_device *pdev)
{
struct fb_info *fb_info = platform_get_drvdata(pdev);
struct mxsfb_info *host = to_imxfb_host(fb_info);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (host->enabled)
mxsfb_disable_controller(fb_info);
unregister_framebuffer(fb_info);
- kfree(fb_info->pseudo_palette);
mxsfb_free_videomem(host);
- iounmap(host->base);
- clk_put(host->clk);
framebuffer_release(fb_info);
- release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL);
diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c
index 0ad61c6a65a5..055562c580b4 100644
--- a/fs/9p/vfs_addr.c
+++ b/fs/9p/vfs_addr.c
@@ -33,6 +33,7 @@
#include <linux/pagemap.h>
#include <linux/idr.h>
#include <linux/sched.h>
+#include <linux/aio.h>
#include <net/9p/9p.h>
#include <net/9p/client.h>
diff --git a/fs/afs/write.c b/fs/afs/write.c
index 7e03eadb40c0..a890db4b9898 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -14,6 +14,7 @@
#include <linux/pagemap.h>
#include <linux/writeback.h>
#include <linux/pagevec.h>
+#include <linux/aio.h>
#include "internal.h"
static int afs_write_back_from_locked_page(struct afs_writeback *wb,
diff --git a/fs/aio.c b/fs/aio.c
index 351afe7ac78e..c5b1a8c10411 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -8,6 +8,8 @@
*
* See ../COPYING for licensing terms.
*/
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
@@ -18,8 +20,6 @@
#include <linux/backing-dev.h>
#include <linux/uio.h>
-#define DEBUG 0
-
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/file.h>
@@ -39,11 +39,76 @@
#include <asm/kmap_types.h>
#include <asm/uaccess.h>
-#if DEBUG > 1
-#define dprintk printk
-#else
-#define dprintk(x...) do { ; } while (0)
-#endif
+#define AIO_RING_MAGIC 0xa10a10a1
+#define AIO_RING_COMPAT_FEATURES 1
+#define AIO_RING_INCOMPAT_FEATURES 0
+struct aio_ring {
+ unsigned id; /* kernel internal index number */
+ unsigned nr; /* number of io_events */
+ unsigned head;
+ unsigned tail;
+
+ unsigned magic;
+ unsigned compat_features;
+ unsigned incompat_features;
+ unsigned header_length; /* size of aio_ring */
+
+
+ struct io_event io_events[0];
+}; /* 128 bytes + ring size */
+
+#define AIO_RING_PAGES 8
+
+struct kioctx {
+ atomic_t users;
+ atomic_t dead;
+
+ /* This needs improving */
+ unsigned long user_id;
+ struct hlist_node list;
+
+ /*
+ * This is what userspace passed to io_setup(), it's not used for
+ * anything but counting against the global max_reqs quota.
+ *
+ * The real limit is nr_events - 1, which will be larger (see
+ * aio_setup_ring())
+ */
+ unsigned max_reqs;
+
+ /* Size of ringbuffer, in units of struct io_event */
+ unsigned nr_events;
+
+ unsigned long mmap_base;
+ unsigned long mmap_size;
+
+ struct page **ring_pages;
+ long nr_pages;
+
+ struct rcu_head rcu_head;
+ struct work_struct rcu_work;
+
+ struct {
+ atomic_t reqs_active;
+ } ____cacheline_aligned_in_smp;
+
+ struct {
+ spinlock_t ctx_lock;
+ struct list_head active_reqs; /* used for cancellation */
+ } ____cacheline_aligned_in_smp;
+
+ struct {
+ struct mutex ring_lock;
+ wait_queue_head_t wait;
+ } ____cacheline_aligned_in_smp;
+
+ struct {
+ unsigned tail;
+ spinlock_t completion_lock;
+ } ____cacheline_aligned_in_smp;
+
+ struct page *internal_pages[AIO_RING_PAGES];
+};
/*------ sysctl variables----*/
static DEFINE_SPINLOCK(aio_nr_lock);
@@ -54,11 +119,6 @@ unsigned long aio_max_nr = 0x10000; /* system wide maximum number of aio request
static struct kmem_cache *kiocb_cachep;
static struct kmem_cache *kioctx_cachep;
-static struct workqueue_struct *aio_wq;
-
-static void aio_kick_handler(struct work_struct *);
-static void aio_queue_work(struct kioctx *);
-
/* aio_setup
* Creates the slab caches used by the aio routines, panic on
* failure as this is done early during the boot sequence.
@@ -68,10 +128,7 @@ static int __init aio_setup(void)
kiocb_cachep = KMEM_CACHE(kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC);
kioctx_cachep = KMEM_CACHE(kioctx,SLAB_HWCACHE_ALIGN|SLAB_PANIC);
- aio_wq = alloc_workqueue("aio", 0, 1); /* used to limit concurrency */
- BUG_ON(!aio_wq);
-
- pr_debug("aio_setup: sizeof(struct page) = %d\n", (int)sizeof(struct page));
+ pr_debug("sizeof(struct page) = %zu\n", sizeof(struct page));
return 0;
}
@@ -79,28 +136,23 @@ __initcall(aio_setup);
static void aio_free_ring(struct kioctx *ctx)
{
- struct aio_ring_info *info = &ctx->ring_info;
long i;
- for (i=0; i<info->nr_pages; i++)
- put_page(info->ring_pages[i]);
+ for (i = 0; i < ctx->nr_pages; i++)
+ put_page(ctx->ring_pages[i]);
- if (info->mmap_size) {
- BUG_ON(ctx->mm != current->mm);
- vm_munmap(info->mmap_base, info->mmap_size);
- }
+ if (ctx->mmap_size)
+ vm_munmap(ctx->mmap_base, ctx->mmap_size);
- if (info->ring_pages && info->ring_pages != info->internal_pages)
- kfree(info->ring_pages);
- info->ring_pages = NULL;
- info->nr = 0;
+ if (ctx->ring_pages && ctx->ring_pages != ctx->internal_pages)
+ kfree(ctx->ring_pages);
}
static int aio_setup_ring(struct kioctx *ctx)
{
struct aio_ring *ring;
- struct aio_ring_info *info = &ctx->ring_info;
unsigned nr_events = ctx->max_reqs;
+ struct mm_struct *mm = current->mm;
unsigned long size, populate;
int nr_pages;
@@ -116,46 +168,44 @@ static int aio_setup_ring(struct kioctx *ctx)
nr_events = (PAGE_SIZE * nr_pages - sizeof(struct aio_ring)) / sizeof(struct io_event);
- info->nr = 0;
- info->ring_pages = info->internal_pages;
+ ctx->nr_events = 0;
+ ctx->ring_pages = ctx->internal_pages;
if (nr_pages > AIO_RING_PAGES) {
- info->ring_pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);
- if (!info->ring_pages)
+ ctx->ring_pages = kcalloc(nr_pages, sizeof(struct page *),
+ GFP_KERNEL);
+ if (!ctx->ring_pages)
return -ENOMEM;
}
- info->mmap_size = nr_pages * PAGE_SIZE;
- dprintk("attempting mmap of %lu bytes\n", info->mmap_size);
- down_write(&ctx->mm->mmap_sem);
- info->mmap_base = do_mmap_pgoff(NULL, 0, info->mmap_size,
- PROT_READ|PROT_WRITE,
- MAP_ANONYMOUS|MAP_PRIVATE, 0,
- &populate);
- if (IS_ERR((void *)info->mmap_base)) {
- up_write(&ctx->mm->mmap_sem);
- info->mmap_size = 0;
+ ctx->mmap_size = nr_pages * PAGE_SIZE;
+ pr_debug("attempting mmap of %lu bytes\n", ctx->mmap_size);
+ down_write(&mm->mmap_sem);
+ ctx->mmap_base = do_mmap_pgoff(NULL, 0, ctx->mmap_size,
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, 0, &populate);
+ if (IS_ERR((void *)ctx->mmap_base)) {
+ up_write(&mm->mmap_sem);
+ ctx->mmap_size = 0;
aio_free_ring(ctx);
return -EAGAIN;
}
- dprintk("mmap address: 0x%08lx\n", info->mmap_base);
- info->nr_pages = get_user_pages(current, ctx->mm,
- info->mmap_base, nr_pages,
- 1, 0, info->ring_pages, NULL);
- up_write(&ctx->mm->mmap_sem);
+ pr_debug("mmap address: 0x%08lx\n", ctx->mmap_base);
+ ctx->nr_pages = get_user_pages(current, mm, ctx->mmap_base, nr_pages,
+ 1, 0, ctx->ring_pages, NULL);
+ up_write(&mm->mmap_sem);
- if (unlikely(info->nr_pages != nr_pages)) {
+ if (unlikely(ctx->nr_pages != nr_pages)) {
aio_free_ring(ctx);
return -EAGAIN;
}
if (populate)
- mm_populate(info->mmap_base, populate);
+ mm_populate(ctx->mmap_base, populate);
- ctx->user_id = info->mmap_base;
+ ctx->user_id = ctx->mmap_base;
+ ctx->nr_events = nr_events; /* trusted copy */
- info->nr = nr_events; /* trusted copy */
-
- ring = kmap_atomic(info->ring_pages[0]);
+ ring = kmap_atomic(ctx->ring_pages[0]);
ring->nr = nr_events; /* user copy */
ring->id = ctx->user_id;
ring->head = ring->tail = 0;
@@ -164,72 +214,133 @@ static int aio_setup_ring(struct kioctx *ctx)
ring->incompat_features = AIO_RING_INCOMPAT_FEATURES;
ring->header_length = sizeof(struct aio_ring);
kunmap_atomic(ring);
+ flush_dcache_page(ctx->ring_pages[0]);
return 0;
}
-
-/* aio_ring_event: returns a pointer to the event at the given index from
- * kmap_atomic(). Release the pointer with put_aio_ring_event();
- */
#define AIO_EVENTS_PER_PAGE (PAGE_SIZE / sizeof(struct io_event))
#define AIO_EVENTS_FIRST_PAGE ((PAGE_SIZE - sizeof(struct aio_ring)) / sizeof(struct io_event))
#define AIO_EVENTS_OFFSET (AIO_EVENTS_PER_PAGE - AIO_EVENTS_FIRST_PAGE)
-#define aio_ring_event(info, nr) ({ \
- unsigned pos = (nr) + AIO_EVENTS_OFFSET; \
- struct io_event *__event; \
- __event = kmap_atomic( \
- (info)->ring_pages[pos / AIO_EVENTS_PER_PAGE]); \
- __event += pos % AIO_EVENTS_PER_PAGE; \
- __event; \
-})
-
-#define put_aio_ring_event(event) do { \
- struct io_event *__event = (event); \
- (void)__event; \
- kunmap_atomic((void *)((unsigned long)__event & PAGE_MASK)); \
-} while(0)
-
-static void ctx_rcu_free(struct rcu_head *head)
+void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel)
+{
+ struct kioctx *ctx = req->ki_ctx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->ctx_lock, flags);
+
+ if (!req->ki_list.next)
+ list_add(&req->ki_list, &ctx->active_reqs);
+
+ req->ki_cancel = cancel;
+
+ spin_unlock_irqrestore(&ctx->ctx_lock, flags);
+}
+EXPORT_SYMBOL(kiocb_set_cancel_fn);
+
+static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb,
+ struct io_event *res)
+{
+ kiocb_cancel_fn *old, *cancel;
+ int ret = -EINVAL;
+
+ /*
+ * Don't want to set kiocb->ki_cancel = KIOCB_CANCELLED unless it
+ * actually has a cancel function, hence the cmpxchg()
+ */
+
+ cancel = ACCESS_ONCE(kiocb->ki_cancel);
+ do {
+ if (!cancel || cancel == KIOCB_CANCELLED)
+ return ret;
+
+ old = cancel;
+ cancel = cmpxchg(&kiocb->ki_cancel, old, KIOCB_CANCELLED);
+ } while (cancel != old);
+
+ atomic_inc(&kiocb->ki_users);
+ spin_unlock_irq(&ctx->ctx_lock);
+
+ memset(res, 0, sizeof(*res));
+ res->obj = (u64)(unsigned long)kiocb->ki_obj.user;
+ res->data = kiocb->ki_user_data;
+ ret = cancel(kiocb, res);
+
+ spin_lock_irq(&ctx->ctx_lock);
+
+ return ret;
+}
+
+static void free_ioctx_rcu(struct rcu_head *head)
{
struct kioctx *ctx = container_of(head, struct kioctx, rcu_head);
kmem_cache_free(kioctx_cachep, ctx);
}
-/* __put_ioctx
- * Called when the last user of an aio context has gone away,
- * and the struct needs to be freed.
+/*
+ * When this function runs, the kioctx has been removed from the "hash table"
+ * and ctx->users has dropped to 0, so we know no more kiocbs can be submitted -
+ * now it's safe to cancel any that need to be.
*/
-static void __put_ioctx(struct kioctx *ctx)
+static void free_ioctx(struct kioctx *ctx)
{
- unsigned nr_events = ctx->max_reqs;
- BUG_ON(ctx->reqs_active);
+ struct aio_ring *ring;
+ struct io_event res;
+ struct kiocb *req;
+ unsigned head, avail;
- cancel_delayed_work_sync(&ctx->wq);
- aio_free_ring(ctx);
- mmdrop(ctx->mm);
- ctx->mm = NULL;
- if (nr_events) {
- spin_lock(&aio_nr_lock);
- BUG_ON(aio_nr - nr_events > aio_nr);
- aio_nr -= nr_events;
- spin_unlock(&aio_nr_lock);
+ spin_lock_irq(&ctx->ctx_lock);
+
+ while (!list_empty(&ctx->active_reqs)) {
+ req = list_first_entry(&ctx->active_reqs,
+ struct kiocb, ki_list);
+
+ list_del_init(&req->ki_list);
+ kiocb_cancel(ctx, req, &res);
}
- pr_debug("__put_ioctx: freeing %p\n", ctx);
- call_rcu(&ctx->rcu_head, ctx_rcu_free);
-}
-static inline int try_get_ioctx(struct kioctx *kioctx)
-{
- return atomic_inc_not_zero(&kioctx->users);
+ spin_unlock_irq(&ctx->ctx_lock);
+
+ ring = kmap_atomic(ctx->ring_pages[0]);
+ head = ring->head;
+ kunmap_atomic(ring);
+
+ while (atomic_read(&ctx->reqs_active) > 0) {
+ wait_event(ctx->wait, head != ctx->tail);
+
+ avail = (head <= ctx->tail ? ctx->tail : ctx->nr_events) - head;
+
+ atomic_sub(avail, &ctx->reqs_active);
+ head += avail;
+ head %= ctx->nr_events;
+ }
+
+ WARN_ON(atomic_read(&ctx->reqs_active) < 0);
+
+ aio_free_ring(ctx);
+
+ spin_lock(&aio_nr_lock);
+ BUG_ON(aio_nr - ctx->max_reqs > aio_nr);
+ aio_nr -= ctx->max_reqs;
+ spin_unlock(&aio_nr_lock);
+
+ pr_debug("freeing %p\n", ctx);
+
+ /*
+ * Here the call_rcu() is between the wait_event() for reqs_active to
+ * hit 0, and freeing the ioctx.
+ *
+ * aio_complete() decrements reqs_active, but it has to touch the ioctx
+ * after to issue a wakeup so we use rcu.
+ */
+ call_rcu(&ctx->rcu_head, free_ioctx_rcu);
}
-static inline void put_ioctx(struct kioctx *kioctx)
+static void put_ioctx(struct kioctx *ctx)
{
- BUG_ON(atomic_read(&kioctx->users) <= 0);
- if (unlikely(atomic_dec_and_test(&kioctx->users)))
- __put_ioctx(kioctx);
+ if (unlikely(atomic_dec_and_test(&ctx->users)))
+ free_ioctx(ctx);
}
/* ioctx_alloc
@@ -237,7 +348,7 @@ static inline void put_ioctx(struct kioctx *kioctx)
*/
static struct kioctx *ioctx_alloc(unsigned nr_events)
{
- struct mm_struct *mm;
+ struct mm_struct *mm = current->mm;
struct kioctx *ctx;
int err = -ENOMEM;
@@ -256,17 +367,15 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
return ERR_PTR(-ENOMEM);
ctx->max_reqs = nr_events;
- mm = ctx->mm = current->mm;
- atomic_inc(&mm->mm_count);
atomic_set(&ctx->users, 2);
+ atomic_set(&ctx->dead, 0);
spin_lock_init(&ctx->ctx_lock);
- spin_lock_init(&ctx->ring_info.ring_lock);
+ spin_lock_init(&ctx->completion_lock);
+ mutex_init(&ctx->ring_lock);
init_waitqueue_head(&ctx->wait);
INIT_LIST_HEAD(&ctx->active_reqs);
- INIT_LIST_HEAD(&ctx->run_list);
- INIT_DELAYED_WORK(&ctx->wq, aio_kick_handler);
if (aio_setup_ring(ctx) < 0)
goto out_freectx;
@@ -286,64 +395,56 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
hlist_add_head_rcu(&ctx->list, &mm->ioctx_list);
spin_unlock(&mm->ioctx_lock);
- dprintk("aio: allocated ioctx %p[%ld]: mm=%p mask=0x%x\n",
- ctx, ctx->user_id, current->mm, ctx->ring_info.nr);
+ pr_debug("allocated ioctx %p[%ld]: mm=%p mask=0x%x\n",
+ ctx, ctx->user_id, mm, ctx->nr_events);
return ctx;
out_cleanup:
err = -EAGAIN;
aio_free_ring(ctx);
out_freectx:
- mmdrop(mm);
kmem_cache_free(kioctx_cachep, ctx);
- dprintk("aio: error allocating ioctx %d\n", err);
+ pr_debug("error allocating ioctx %d\n", err);
return ERR_PTR(err);
}
-/* kill_ctx
- * Cancels all outstanding aio requests on an aio context. Used
- * when the processes owning a context have all exited to encourage
- * the rapid destruction of the kioctx.
- */
-static void kill_ctx(struct kioctx *ctx)
+static void kill_ioctx_work(struct work_struct *work)
{
- int (*cancel)(struct kiocb *, struct io_event *);
- struct task_struct *tsk = current;
- DECLARE_WAITQUEUE(wait, tsk);
- struct io_event res;
+ struct kioctx *ctx = container_of(work, struct kioctx, rcu_work);
- spin_lock_irq(&ctx->ctx_lock);
- ctx->dead = 1;
- while (!list_empty(&ctx->active_reqs)) {
- struct list_head *pos = ctx->active_reqs.next;
- struct kiocb *iocb = list_kiocb(pos);
- list_del_init(&iocb->ki_list);
- cancel = iocb->ki_cancel;
- kiocbSetCancelled(iocb);
- if (cancel) {
- iocb->ki_users++;
- spin_unlock_irq(&ctx->ctx_lock);
- cancel(iocb, &res);
- spin_lock_irq(&ctx->ctx_lock);
- }
- }
+ wake_up_all(&ctx->wait);
+ put_ioctx(ctx);
+}
- if (!ctx->reqs_active)
- goto out;
+static void kill_ioctx_rcu(struct rcu_head *head)
+{
+ struct kioctx *ctx = container_of(head, struct kioctx, rcu_head);
- add_wait_queue(&ctx->wait, &wait);
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
- while (ctx->reqs_active) {
- spin_unlock_irq(&ctx->ctx_lock);
- io_schedule();
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
- spin_lock_irq(&ctx->ctx_lock);
- }
- __set_task_state(tsk, TASK_RUNNING);
- remove_wait_queue(&ctx->wait, &wait);
+ INIT_WORK(&ctx->rcu_work, kill_ioctx_work);
+ schedule_work(&ctx->rcu_work);
+}
-out:
- spin_unlock_irq(&ctx->ctx_lock);
+/* kill_ioctx
+ * Cancels all outstanding aio requests on an aio context. Used
+ * when the processes owning a context have all exited to encourage
+ * the rapid destruction of the kioctx.
+ */
+static void kill_ioctx(struct kioctx *ctx)
+{
+ if (!atomic_xchg(&ctx->dead, 1)) {
+ hlist_del_rcu(&ctx->list);
+ /* Between hlist_del_rcu() and dropping the initial ref */
+ synchronize_rcu();
+
+ /*
+ * We can't punt to workqueue here because put_ioctx() ->
+ * free_ioctx() will unmap the ringbuffer, and that has to be
+ * done in the original process's context. kill_ioctx_rcu/work()
+ * exist for exit_aio(), as in that path free_ioctx() won't do
+ * the unmap.
+ */
+ kill_ioctx_work(&ctx->rcu_work);
+ }
}
/* wait_on_sync_kiocb:
@@ -351,9 +452,9 @@ out:
*/
ssize_t wait_on_sync_kiocb(struct kiocb *iocb)
{
- while (iocb->ki_users) {
+ while (atomic_read(&iocb->ki_users)) {
set_current_state(TASK_UNINTERRUPTIBLE);
- if (!iocb->ki_users)
+ if (!atomic_read(&iocb->ki_users))
break;
io_schedule();
}
@@ -362,28 +463,26 @@ ssize_t wait_on_sync_kiocb(struct kiocb *iocb)
}
EXPORT_SYMBOL(wait_on_sync_kiocb);
-/* exit_aio: called when the last user of mm goes away. At this point,
- * there is no way for any new requests to be submited or any of the
- * io_* syscalls to be called on the context. However, there may be
- * outstanding requests which hold references to the context; as they
- * go away, they will call put_ioctx and release any pinned memory
- * associated with the request (held via struct page * references).
+/*
+ * exit_aio: called when the last user of mm goes away. At this point, there is
+ * no way for any new requests to be submited or any of the io_* syscalls to be
+ * called on the context.
+ *
+ * There may be outstanding kiocbs, but free_ioctx() will explicitly wait on
+ * them.
*/
void exit_aio(struct mm_struct *mm)
{
struct kioctx *ctx;
+ struct hlist_node *n;
- while (!hlist_empty(&mm->ioctx_list)) {
- ctx = hlist_entry(mm->ioctx_list.first, struct kioctx, list);
- hlist_del_rcu(&ctx->list);
-
- kill_ctx(ctx);
-
+ hlist_for_each_entry_safe(ctx, n, &mm->ioctx_list, list) {
if (1 != atomic_read(&ctx->users))
printk(KERN_DEBUG
"exit_aio:ioctx still alive: %d %d %d\n",
- atomic_read(&ctx->users), ctx->dead,
- ctx->reqs_active);
+ atomic_read(&ctx->users),
+ atomic_read(&ctx->dead),
+ atomic_read(&ctx->reqs_active));
/*
* We don't need to bother with munmap() here -
* exit_mmap(mm) is coming and it'll unmap everything.
@@ -391,150 +490,53 @@ void exit_aio(struct mm_struct *mm)
* as indicator that it needs to unmap the area,
* just set it to 0; aio_free_ring() is the only
* place that uses ->mmap_size, so it's safe.
- * That way we get all munmap done to current->mm -
- * all other callers have ctx->mm == current->mm.
*/
- ctx->ring_info.mmap_size = 0;
- put_ioctx(ctx);
+ ctx->mmap_size = 0;
+
+ if (!atomic_xchg(&ctx->dead, 1)) {
+ hlist_del_rcu(&ctx->list);
+ call_rcu(&ctx->rcu_head, kill_ioctx_rcu);
+ }
}
}
/* aio_get_req
- * Allocate a slot for an aio request. Increments the users count
+ * Allocate a slot for an aio request. Increments the ki_users count
* of the kioctx so that the kioctx stays around until all requests are
* complete. Returns NULL if no requests are free.
*
- * Returns with kiocb->users set to 2. The io submit code path holds
+ * Returns with kiocb->ki_users set to 2. The io submit code path holds
* an extra reference while submitting the i/o.
* This prevents races between the aio code path referencing the
* req (after submitting it) and aio_complete() freeing the req.
*/
-static struct kiocb *__aio_get_req(struct kioctx *ctx)
+static inline struct kiocb *aio_get_req(struct kioctx *ctx)
{
- struct kiocb *req = NULL;
+ struct kiocb *req;
- req = kmem_cache_alloc(kiocb_cachep, GFP_KERNEL);
- if (unlikely(!req))
+ if (atomic_read(&ctx->reqs_active) >= ctx->nr_events)
return NULL;
- req->ki_flags = 0;
- req->ki_users = 2;
- req->ki_key = 0;
- req->ki_ctx = ctx;
- req->ki_cancel = NULL;
- req->ki_retry = NULL;
- req->ki_dtor = NULL;
- req->private = NULL;
- req->ki_iovec = NULL;
- INIT_LIST_HEAD(&req->ki_run_list);
- req->ki_eventfd = NULL;
-
- return req;
-}
-
-/*
- * struct kiocb's are allocated in batches to reduce the number of
- * times the ctx lock is acquired and released.
- */
-#define KIOCB_BATCH_SIZE 32L
-struct kiocb_batch {
- struct list_head head;
- long count; /* number of requests left to allocate */
-};
-
-static void kiocb_batch_init(struct kiocb_batch *batch, long total)
-{
- INIT_LIST_HEAD(&batch->head);
- batch->count = total;
-}
-
-static void kiocb_batch_free(struct kioctx *ctx, struct kiocb_batch *batch)
-{
- struct kiocb *req, *n;
-
- if (list_empty(&batch->head))
- return;
-
- spin_lock_irq(&ctx->ctx_lock);
- list_for_each_entry_safe(req, n, &batch->head, ki_batch) {
- list_del(&req->ki_batch);
- list_del(&req->ki_list);
- kmem_cache_free(kiocb_cachep, req);
- ctx->reqs_active--;
- }
- if (unlikely(!ctx->reqs_active && ctx->dead))
- wake_up_all(&ctx->wait);
- spin_unlock_irq(&ctx->ctx_lock);
-}
-
-/*
- * Allocate a batch of kiocbs. This avoids taking and dropping the
- * context lock a lot during setup.
- */
-static int kiocb_batch_refill(struct kioctx *ctx, struct kiocb_batch *batch)
-{
- unsigned short allocated, to_alloc;
- long avail;
- struct kiocb *req, *n;
- struct aio_ring *ring;
-
- to_alloc = min(batch->count, KIOCB_BATCH_SIZE);
- for (allocated = 0; allocated < to_alloc; allocated++) {
- req = __aio_get_req(ctx);
- if (!req)
- /* allocation failed, go with what we've got */
- break;
- list_add(&req->ki_batch, &batch->head);
- }
-
- if (allocated == 0)
- goto out;
-
- spin_lock_irq(&ctx->ctx_lock);
- ring = kmap_atomic(ctx->ring_info.ring_pages[0]);
-
- avail = aio_ring_avail(&ctx->ring_info, ring) - ctx->reqs_active;
- BUG_ON(avail < 0);
- if (avail < allocated) {
- /* Trim back the number of requests. */
- list_for_each_entry_safe(req, n, &batch->head, ki_batch) {
- list_del(&req->ki_batch);
- kmem_cache_free(kiocb_cachep, req);
- if (--allocated <= avail)
- break;
- }
- }
-
- batch->count -= allocated;
- list_for_each_entry(req, &batch->head, ki_batch) {
- list_add(&req->ki_list, &ctx->active_reqs);
- ctx->reqs_active++;
- }
+ if (atomic_inc_return(&ctx->reqs_active) > ctx->nr_events - 1)
+ goto out_put;
- kunmap_atomic(ring);
- spin_unlock_irq(&ctx->ctx_lock);
-
-out:
- return allocated;
-}
+ req = kmem_cache_alloc(kiocb_cachep, GFP_KERNEL|__GFP_ZERO);
+ if (unlikely(!req))
+ goto out_put;
-static inline struct kiocb *aio_get_req(struct kioctx *ctx,
- struct kiocb_batch *batch)
-{
- struct kiocb *req;
+ atomic_set(&req->ki_users, 2);
+ req->ki_ctx = ctx;
- if (list_empty(&batch->head))
- if (kiocb_batch_refill(ctx, batch) == 0)
- return NULL;
- req = list_first_entry(&batch->head, struct kiocb, ki_batch);
- list_del(&req->ki_batch);
return req;
+out_put:
+ atomic_dec(&ctx->reqs_active);
+ return NULL;
}
-static inline void really_put_req(struct kioctx *ctx, struct kiocb *req)
+static void kiocb_free(struct kiocb *req)
{
- assert_spin_locked(&ctx->ctx_lock);
-
+ if (req->ki_filp)
+ fput(req->ki_filp);
if (req->ki_eventfd != NULL)
eventfd_ctx_put(req->ki_eventfd);
if (req->ki_dtor)
@@ -542,48 +544,12 @@ static inline void really_put_req(struct kioctx *ctx, struct kiocb *req)
if (req->ki_iovec != &req->ki_inline_vec)
kfree(req->ki_iovec);
kmem_cache_free(kiocb_cachep, req);
- ctx->reqs_active--;
-
- if (unlikely(!ctx->reqs_active && ctx->dead))
- wake_up_all(&ctx->wait);
}
-/* __aio_put_req
- * Returns true if this put was the last user of the request.
- */
-static int __aio_put_req(struct kioctx *ctx, struct kiocb *req)
+void aio_put_req(struct kiocb *req)
{
- dprintk(KERN_DEBUG "aio_put(%p): f_count=%ld\n",
- req, atomic_long_read(&req->ki_filp->f_count));
-
- assert_spin_locked(&ctx->ctx_lock);
-
- req->ki_users--;
- BUG_ON(req->ki_users < 0);
- if (likely(req->ki_users))
- return 0;
- list_del(&req->ki_list); /* remove from active_reqs */
- req->ki_cancel = NULL;
- req->ki_retry = NULL;
-
- fput(req->ki_filp);
- req->ki_filp = NULL;
- really_put_req(ctx, req);
- return 1;
-}
-
-/* aio_put_req
- * Returns true if this put was the last user of the kiocb,
- * false if the request is still in use.
- */
-int aio_put_req(struct kiocb *req)
-{
- struct kioctx *ctx = req->ki_ctx;
- int ret;
- spin_lock_irq(&ctx->ctx_lock);
- ret = __aio_put_req(ctx, req);
- spin_unlock_irq(&ctx->ctx_lock);
- return ret;
+ if (atomic_dec_and_test(&req->ki_users))
+ kiocb_free(req);
}
EXPORT_SYMBOL(aio_put_req);
@@ -595,13 +561,8 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id)
rcu_read_lock();
hlist_for_each_entry_rcu(ctx, &mm->ioctx_list, list) {
- /*
- * RCU protects us against accessing freed memory but
- * we have to be careful not to get a reference when the
- * reference count already dropped to 0 (ctx->dead test
- * is unreliable because of races).
- */
- if (ctx->user_id == ctx_id && !ctx->dead && try_get_ioctx(ctx)){
+ if (ctx->user_id == ctx_id) {
+ atomic_inc(&ctx->users);
ret = ctx;
break;
}
@@ -611,295 +572,16 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id)
return ret;
}
-/*
- * Queue up a kiocb to be retried. Assumes that the kiocb
- * has already been marked as kicked, and places it on
- * the retry run list for the corresponding ioctx, if it
- * isn't already queued. Returns 1 if it actually queued
- * the kiocb (to tell the caller to activate the work
- * queue to process it), or 0, if it found that it was
- * already queued.
- */
-static inline int __queue_kicked_iocb(struct kiocb *iocb)
-{
- struct kioctx *ctx = iocb->ki_ctx;
-
- assert_spin_locked(&ctx->ctx_lock);
-
- if (list_empty(&iocb->ki_run_list)) {
- list_add_tail(&iocb->ki_run_list,
- &ctx->run_list);
- return 1;
- }
- return 0;
-}
-
-/* aio_run_iocb
- * This is the core aio execution routine. It is
- * invoked both for initial i/o submission and
- * subsequent retries via the aio_kick_handler.
- * Expects to be invoked with iocb->ki_ctx->lock
- * already held. The lock is released and reacquired
- * as needed during processing.
- *
- * Calls the iocb retry method (already setup for the
- * iocb on initial submission) for operation specific
- * handling, but takes care of most of common retry
- * execution details for a given iocb. The retry method
- * needs to be non-blocking as far as possible, to avoid
- * holding up other iocbs waiting to be serviced by the
- * retry kernel thread.
- *
- * The trickier parts in this code have to do with
- * ensuring that only one retry instance is in progress
- * for a given iocb at any time. Providing that guarantee
- * simplifies the coding of individual aio operations as
- * it avoids various potential races.
- */
-static ssize_t aio_run_iocb(struct kiocb *iocb)
-{
- struct kioctx *ctx = iocb->ki_ctx;
- ssize_t (*retry)(struct kiocb *);
- ssize_t ret;
-
- if (!(retry = iocb->ki_retry)) {
- printk("aio_run_iocb: iocb->ki_retry = NULL\n");
- return 0;
- }
-
- /*
- * We don't want the next retry iteration for this
- * operation to start until this one has returned and
- * updated the iocb state. However, wait_queue functions
- * can trigger a kick_iocb from interrupt context in the
- * meantime, indicating that data is available for the next
- * iteration. We want to remember that and enable the
- * next retry iteration _after_ we are through with
- * this one.
- *
- * So, in order to be able to register a "kick", but
- * prevent it from being queued now, we clear the kick
- * flag, but make the kick code *think* that the iocb is
- * still on the run list until we are actually done.
- * When we are done with this iteration, we check if
- * the iocb was kicked in the meantime and if so, queue
- * it up afresh.
- */
-
- kiocbClearKicked(iocb);
-
- /*
- * This is so that aio_complete knows it doesn't need to
- * pull the iocb off the run list (We can't just call
- * INIT_LIST_HEAD because we don't want a kick_iocb to
- * queue this on the run list yet)
- */
- iocb->ki_run_list.next = iocb->ki_run_list.prev = NULL;
- spin_unlock_irq(&ctx->ctx_lock);
-
- /* Quit retrying if the i/o has been cancelled */
- if (kiocbIsCancelled(iocb)) {
- ret = -EINTR;
- aio_complete(iocb, ret, 0);
- /* must not access the iocb after this */
- goto out;
- }
-
- /*
- * Now we are all set to call the retry method in async
- * context.
- */
- ret = retry(iocb);
-
- if (ret != -EIOCBRETRY && ret != -EIOCBQUEUED) {
- /*
- * There's no easy way to restart the syscall since other AIO's
- * may be already running. Just fail this IO with EINTR.
- */
- if (unlikely(ret == -ERESTARTSYS || ret == -ERESTARTNOINTR ||
- ret == -ERESTARTNOHAND || ret == -ERESTART_RESTARTBLOCK))
- ret = -EINTR;
- aio_complete(iocb, ret, 0);
- }
-out:
- spin_lock_irq(&ctx->ctx_lock);
-
- if (-EIOCBRETRY == ret) {
- /*
- * OK, now that we are done with this iteration
- * and know that there is more left to go,
- * this is where we let go so that a subsequent
- * "kick" can start the next iteration
- */
-
- /* will make __queue_kicked_iocb succeed from here on */
- INIT_LIST_HEAD(&iocb->ki_run_list);
- /* we must queue the next iteration ourselves, if it
- * has already been kicked */
- if (kiocbIsKicked(iocb)) {
- __queue_kicked_iocb(iocb);
-
- /*
- * __queue_kicked_iocb will always return 1 here, because
- * iocb->ki_run_list is empty at this point so it should
- * be safe to unconditionally queue the context into the
- * work queue.
- */
- aio_queue_work(ctx);
- }
- }
- return ret;
-}
-
-/*
- * __aio_run_iocbs:
- * Process all pending retries queued on the ioctx
- * run list.
- * Assumes it is operating within the aio issuer's mm
- * context.
- */
-static int __aio_run_iocbs(struct kioctx *ctx)
-{
- struct kiocb *iocb;
- struct list_head run_list;
-
- assert_spin_locked(&ctx->ctx_lock);
-
- list_replace_init(&ctx->run_list, &run_list);
- while (!list_empty(&run_list)) {
- iocb = list_entry(run_list.next, struct kiocb,
- ki_run_list);
- list_del(&iocb->ki_run_list);
- /*
- * Hold an extra reference while retrying i/o.
- */
- iocb->ki_users++; /* grab extra reference */
- aio_run_iocb(iocb);
- __aio_put_req(ctx, iocb);
- }
- if (!list_empty(&ctx->run_list))
- return 1;
- return 0;
-}
-
-static void aio_queue_work(struct kioctx * ctx)
-{
- unsigned long timeout;
- /*
- * if someone is waiting, get the work started right
- * away, otherwise, use a longer delay
- */
- smp_mb();
- if (waitqueue_active(&ctx->wait))
- timeout = 1;
- else
- timeout = HZ/10;
- queue_delayed_work(aio_wq, &ctx->wq, timeout);
-}
-
-/*
- * aio_run_all_iocbs:
- * Process all pending retries queued on the ioctx
- * run list, and keep running them until the list
- * stays empty.
- * Assumes it is operating within the aio issuer's mm context.
- */
-static inline void aio_run_all_iocbs(struct kioctx *ctx)
-{
- spin_lock_irq(&ctx->ctx_lock);
- while (__aio_run_iocbs(ctx))
- ;
- spin_unlock_irq(&ctx->ctx_lock);
-}
-
-/*
- * aio_kick_handler:
- * Work queue handler triggered to process pending
- * retries on an ioctx. Takes on the aio issuer's
- * mm context before running the iocbs, so that
- * copy_xxx_user operates on the issuer's address
- * space.
- * Run on aiod's context.
- */
-static void aio_kick_handler(struct work_struct *work)
-{
- struct kioctx *ctx = container_of(work, struct kioctx, wq.work);
- mm_segment_t oldfs = get_fs();
- struct mm_struct *mm;
- int requeue;
-
- set_fs(USER_DS);
- use_mm(ctx->mm);
- spin_lock_irq(&ctx->ctx_lock);
- requeue =__aio_run_iocbs(ctx);
- mm = ctx->mm;
- spin_unlock_irq(&ctx->ctx_lock);
- unuse_mm(mm);
- set_fs(oldfs);
- /*
- * we're in a worker thread already; no point using non-zero delay
- */
- if (requeue)
- queue_delayed_work(aio_wq, &ctx->wq, 0);
-}
-
-
-/*
- * Called by kick_iocb to queue the kiocb for retry
- * and if required activate the aio work queue to process
- * it
- */
-static void try_queue_kicked_iocb(struct kiocb *iocb)
-{
- struct kioctx *ctx = iocb->ki_ctx;
- unsigned long flags;
- int run = 0;
-
- spin_lock_irqsave(&ctx->ctx_lock, flags);
- /* set this inside the lock so that we can't race with aio_run_iocb()
- * testing it and putting the iocb on the run list under the lock */
- if (!kiocbTryKick(iocb))
- run = __queue_kicked_iocb(iocb);
- spin_unlock_irqrestore(&ctx->ctx_lock, flags);
- if (run)
- aio_queue_work(ctx);
-}
-
-/*
- * kick_iocb:
- * Called typically from a wait queue callback context
- * to trigger a retry of the iocb.
- * The retry is usually executed by aio workqueue
- * threads (See aio_kick_handler).
- */
-void kick_iocb(struct kiocb *iocb)
-{
- /* sync iocbs are easy: they can only ever be executing from a
- * single context. */
- if (is_sync_kiocb(iocb)) {
- kiocbSetKicked(iocb);
- wake_up_process(iocb->ki_obj.tsk);
- return;
- }
-
- try_queue_kicked_iocb(iocb);
-}
-EXPORT_SYMBOL(kick_iocb);
-
/* aio_complete
* Called when the io request on the given iocb is complete.
- * Returns true if this is the last user of the request. The
- * only other user of the request can be the cancellation code.
*/
-int aio_complete(struct kiocb *iocb, long res, long res2)
+void aio_complete(struct kiocb *iocb, long res, long res2)
{
struct kioctx *ctx = iocb->ki_ctx;
- struct aio_ring_info *info;
struct aio_ring *ring;
- struct io_event *event;
+ struct io_event *ev_page, *event;
unsigned long flags;
- unsigned long tail;
- int ret;
+ unsigned tail, pos;
/*
* Special case handling for sync iocbs:
@@ -909,61 +591,81 @@ int aio_complete(struct kiocb *iocb, long res, long res2)
* - the sync task helpfully left a reference to itself in the iocb
*/
if (is_sync_kiocb(iocb)) {
- BUG_ON(iocb->ki_users != 1);
+ BUG_ON(atomic_read(&iocb->ki_users) != 1);
iocb->ki_user_data = res;
- iocb->ki_users = 0;
+ atomic_set(&iocb->ki_users, 0);
wake_up_process(iocb->ki_obj.tsk);
- return 1;
+ return;
}
- info = &ctx->ring_info;
-
- /* add a completion event to the ring buffer.
- * must be done holding ctx->ctx_lock to prevent
- * other code from messing with the tail
- * pointer since we might be called from irq
- * context.
+ /*
+ * Take rcu_read_lock() in case the kioctx is being destroyed, as we
+ * need to issue a wakeup after decrementing reqs_active.
*/
- spin_lock_irqsave(&ctx->ctx_lock, flags);
+ rcu_read_lock();
- if (iocb->ki_run_list.prev && !list_empty(&iocb->ki_run_list))
- list_del_init(&iocb->ki_run_list);
+ if (iocb->ki_list.next) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->ctx_lock, flags);
+ list_del(&iocb->ki_list);
+ spin_unlock_irqrestore(&ctx->ctx_lock, flags);
+ }
/*
* cancelled requests don't get events, userland was given one
* when the event got cancelled.
*/
- if (kiocbIsCancelled(iocb))
+ if (unlikely(xchg(&iocb->ki_cancel,
+ KIOCB_CANCELLED) == KIOCB_CANCELLED)) {
+ atomic_dec(&ctx->reqs_active);
+ /* Still need the wake_up in case free_ioctx is waiting */
goto put_rq;
+ }
- ring = kmap_atomic(info->ring_pages[0]);
+ /*
+ * Add a completion event to the ring buffer. Must be done holding
+ * ctx->ctx_lock to prevent other code from messing with the tail
+ * pointer since we might be called from irq context.
+ */
+ spin_lock_irqsave(&ctx->completion_lock, flags);
- tail = info->tail;
- event = aio_ring_event(info, tail);
- if (++tail >= info->nr)
+ tail = ctx->tail;
+ pos = tail + AIO_EVENTS_OFFSET;
+
+ if (++tail >= ctx->nr_events)
tail = 0;
+ ev_page = kmap_atomic(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]);
+ event = ev_page + pos % AIO_EVENTS_PER_PAGE;
+
event->obj = (u64)(unsigned long)iocb->ki_obj.user;
event->data = iocb->ki_user_data;
event->res = res;
event->res2 = res2;
- dprintk("aio_complete: %p[%lu]: %p: %p %Lx %lx %lx\n",
- ctx, tail, iocb, iocb->ki_obj.user, iocb->ki_user_data,
- res, res2);
+ kunmap_atomic(ev_page);
+ flush_dcache_page(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]);
+
+ pr_debug("%p[%u]: %p: %p %Lx %lx %lx\n",
+ ctx, tail, iocb, iocb->ki_obj.user, iocb->ki_user_data,
+ res, res2);
/* after flagging the request as done, we
* must never even look at it again
*/
smp_wmb(); /* make event visible before updating tail */
- info->tail = tail;
- ring->tail = tail;
+ ctx->tail = tail;
- put_aio_ring_event(event);
+ ring = kmap_atomic(ctx->ring_pages[0]);
+ ring->tail = tail;
kunmap_atomic(ring);
+ flush_dcache_page(ctx->ring_pages[0]);
+
+ spin_unlock_irqrestore(&ctx->completion_lock, flags);
- pr_debug("added to ring %p at [%lu]\n", iocb, tail);
+ pr_debug("added to ring %p at [%u]\n", iocb, tail);
/*
* Check if the user asked us to deliver the result through an
@@ -975,7 +677,7 @@ int aio_complete(struct kiocb *iocb, long res, long res2)
put_rq:
/* everything turned out well, dispose of the aiocb. */
- ret = __aio_put_req(ctx, iocb);
+ aio_put_req(iocb);
/*
* We have to order our ring_info tail store above and test
@@ -988,233 +690,133 @@ put_rq:
if (waitqueue_active(&ctx->wait))
wake_up(&ctx->wait);
- spin_unlock_irqrestore(&ctx->ctx_lock, flags);
- return ret;
+ rcu_read_unlock();
}
EXPORT_SYMBOL(aio_complete);
-/* aio_read_evt
- * Pull an event off of the ioctx's event ring. Returns the number of
- * events fetched (0 or 1 ;-)
- * FIXME: make this use cmpxchg.
- * TODO: make the ringbuffer user mmap()able (requires FIXME).
+/* aio_read_events
+ * Pull an event off of the ioctx's event ring. Returns the number of
+ * events fetched
*/
-static int aio_read_evt(struct kioctx *ioctx, struct io_event *ent)
+static long aio_read_events_ring(struct kioctx *ctx,
+ struct io_event __user *event, long nr)
{
- struct aio_ring_info *info = &ioctx->ring_info;
struct aio_ring *ring;
- unsigned long head;
- int ret = 0;
-
- ring = kmap_atomic(info->ring_pages[0]);
- dprintk("in aio_read_evt h%lu t%lu m%lu\n",
- (unsigned long)ring->head, (unsigned long)ring->tail,
- (unsigned long)ring->nr);
-
- if (ring->head == ring->tail)
- goto out;
+ unsigned head, pos;
+ long ret = 0;
+ int copy_ret;
- spin_lock(&info->ring_lock);
-
- head = ring->head % info->nr;
- if (head != ring->tail) {
- struct io_event *evp = aio_ring_event(info, head);
- *ent = *evp;
- head = (head + 1) % info->nr;
- smp_mb(); /* finish reading the event before updatng the head */
- ring->head = head;
- ret = 1;
- put_aio_ring_event(evp);
- }
- spin_unlock(&info->ring_lock);
+ mutex_lock(&ctx->ring_lock);
-out:
- dprintk("leaving aio_read_evt: %d h%lu t%lu\n", ret,
- (unsigned long)ring->head, (unsigned long)ring->tail);
+ ring = kmap_atomic(ctx->ring_pages[0]);
+ head = ring->head;
kunmap_atomic(ring);
- return ret;
-}
-struct aio_timeout {
- struct timer_list timer;
- int timed_out;
- struct task_struct *p;
-};
+ pr_debug("h%u t%u m%u\n", head, ctx->tail, ctx->nr_events);
-static void timeout_func(unsigned long data)
-{
- struct aio_timeout *to = (struct aio_timeout *)data;
+ if (head == ctx->tail)
+ goto out;
- to->timed_out = 1;
- wake_up_process(to->p);
-}
+ while (ret < nr) {
+ long avail;
+ struct io_event *ev;
+ struct page *page;
-static inline void init_timeout(struct aio_timeout *to)
-{
- setup_timer_on_stack(&to->timer, timeout_func, (unsigned long) to);
- to->timed_out = 0;
- to->p = current;
-}
+ avail = (head <= ctx->tail ? ctx->tail : ctx->nr_events) - head;
+ if (head == ctx->tail)
+ break;
-static inline void set_timeout(long start_jiffies, struct aio_timeout *to,
- const struct timespec *ts)
-{
- to->timer.expires = start_jiffies + timespec_to_jiffies(ts);
- if (time_after(to->timer.expires, jiffies))
- add_timer(&to->timer);
- else
- to->timed_out = 1;
-}
+ avail = min(avail, nr - ret);
+ avail = min_t(long, avail, AIO_EVENTS_PER_PAGE -
+ ((head + AIO_EVENTS_OFFSET) % AIO_EVENTS_PER_PAGE));
-static inline void clear_timeout(struct aio_timeout *to)
-{
- del_singleshot_timer_sync(&to->timer);
-}
+ pos = head + AIO_EVENTS_OFFSET;
+ page = ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE];
+ pos %= AIO_EVENTS_PER_PAGE;
-static int read_events(struct kioctx *ctx,
- long min_nr, long nr,
- struct io_event __user *event,
- struct timespec __user *timeout)
-{
- long start_jiffies = jiffies;
- struct task_struct *tsk = current;
- DECLARE_WAITQUEUE(wait, tsk);
- int ret;
- int i = 0;
- struct io_event ent;
- struct aio_timeout to;
- int retry = 0;
-
- /* needed to zero any padding within an entry (there shouldn't be
- * any, but C is fun!
- */
- memset(&ent, 0, sizeof(ent));
-retry:
- ret = 0;
- while (likely(i < nr)) {
- ret = aio_read_evt(ctx, &ent);
- if (unlikely(ret <= 0))
- break;
-
- dprintk("read event: %Lx %Lx %Lx %Lx\n",
- ent.data, ent.obj, ent.res, ent.res2);
+ ev = kmap(page);
+ copy_ret = copy_to_user(event + ret, ev + pos,
+ sizeof(*ev) * avail);
+ kunmap(page);
- /* Could we split the check in two? */
- ret = -EFAULT;
- if (unlikely(copy_to_user(event, &ent, sizeof(ent)))) {
- dprintk("aio: lost an event due to EFAULT.\n");
- break;
+ if (unlikely(copy_ret)) {
+ ret = -EFAULT;
+ goto out;
}
- ret = 0;
- /* Good, event copied to userland, update counts. */
- event ++;
- i ++;
+ ret += avail;
+ head += avail;
+ head %= ctx->nr_events;
}
- if (min_nr <= i)
- return i;
- if (ret)
- return ret;
+ ring = kmap_atomic(ctx->ring_pages[0]);
+ ring->head = head;
+ kunmap_atomic(ring);
+ flush_dcache_page(ctx->ring_pages[0]);
- /* End fast path */
+ pr_debug("%li h%u t%u\n", ret, head, ctx->tail);
- /* racey check, but it gets redone */
- if (!retry && unlikely(!list_empty(&ctx->run_list))) {
- retry = 1;
- aio_run_all_iocbs(ctx);
- goto retry;
- }
+ atomic_sub(ret, &ctx->reqs_active);
+out:
+ mutex_unlock(&ctx->ring_lock);
- init_timeout(&to);
- if (timeout) {
- struct timespec ts;
- ret = -EFAULT;
- if (unlikely(copy_from_user(&ts, timeout, sizeof(ts))))
- goto out;
+ return ret;
+}
- set_timeout(start_jiffies, &to, &ts);
- }
+static bool aio_read_events(struct kioctx *ctx, long min_nr, long nr,
+ struct io_event __user *event, long *i)
+{
+ long ret = aio_read_events_ring(ctx, event + *i, nr - *i);
- while (likely(i < nr)) {
- add_wait_queue_exclusive(&ctx->wait, &wait);
- do {
- set_task_state(tsk, TASK_INTERRUPTIBLE);
- ret = aio_read_evt(ctx, &ent);
- if (ret)
- break;
- if (min_nr <= i)
- break;
- if (unlikely(ctx->dead)) {
- ret = -EINVAL;
- break;
- }
- if (to.timed_out) /* Only check after read evt */
- break;
- /* Try to only show up in io wait if there are ops
- * in flight */
- if (ctx->reqs_active)
- io_schedule();
- else
- schedule();
- if (signal_pending(tsk)) {
- ret = -EINTR;
- break;
- }
- /*ret = aio_read_evt(ctx, &ent);*/
- } while (1) ;
-
- set_task_state(tsk, TASK_RUNNING);
- remove_wait_queue(&ctx->wait, &wait);
-
- if (unlikely(ret <= 0))
- break;
+ if (ret > 0)
+ *i += ret;
- ret = -EFAULT;
- if (unlikely(copy_to_user(event, &ent, sizeof(ent)))) {
- dprintk("aio: lost an event due to EFAULT.\n");
- break;
- }
+ if (unlikely(atomic_read(&ctx->dead)))
+ ret = -EINVAL;
- /* Good, event copied to userland, update counts. */
- event ++;
- i ++;
- }
+ if (!*i)
+ *i = ret;
- if (timeout)
- clear_timeout(&to);
-out:
- destroy_timer_on_stack(&to.timer);
- return i ? i : ret;
+ return ret < 0 || *i >= min_nr;
}
-/* Take an ioctx and remove it from the list of ioctx's. Protects
- * against races with itself via ->dead.
- */
-static void io_destroy(struct kioctx *ioctx)
+static long read_events(struct kioctx *ctx, long min_nr, long nr,
+ struct io_event __user *event,
+ struct timespec __user *timeout)
{
- struct mm_struct *mm = current->mm;
- int was_dead;
+ ktime_t until = { .tv64 = KTIME_MAX };
+ long ret = 0;
- /* delete the entry from the list is someone else hasn't already */
- spin_lock(&mm->ioctx_lock);
- was_dead = ioctx->dead;
- ioctx->dead = 1;
- hlist_del_rcu(&ioctx->list);
- spin_unlock(&mm->ioctx_lock);
+ if (timeout) {
+ struct timespec ts;
- dprintk("aio_release(%p)\n", ioctx);
- if (likely(!was_dead))
- put_ioctx(ioctx); /* twice for the list */
+ if (unlikely(copy_from_user(&ts, timeout, sizeof(ts))))
+ return -EFAULT;
- kill_ctx(ioctx);
+ until = timespec_to_ktime(ts);
+ }
/*
- * Wake up any waiters. The setting of ctx->dead must be seen
- * by other CPUs at this point. Right now, we rely on the
- * locking done by the above calls to ensure this consistency.
+ * Note that aio_read_events() is being called as the conditional - i.e.
+ * we're calling it after prepare_to_wait() has set task state to
+ * TASK_INTERRUPTIBLE.
+ *
+ * But aio_read_events() can block, and if it blocks it's going to flip
+ * the task state back to TASK_RUNNING.
+ *
+ * This should be ok, provided it doesn't flip the state back to
+ * TASK_RUNNING and return 0 too much - that causes us to spin. That
+ * will only happen if the mutex_lock() call blocks, and we then find
+ * the ringbuffer empty. So in practice we should be ok, but it's
+ * something to be aware of when touching this code.
*/
- wake_up_all(&ioctx->wait);
+ wait_event_interruptible_hrtimeout(ctx->wait,
+ aio_read_events(ctx, min_nr, nr, event, &ret), until);
+
+ if (!ret && signal_pending(current))
+ ret = -EINTR;
+
+ return ret;
}
/* sys_io_setup:
@@ -1252,7 +854,7 @@ SYSCALL_DEFINE2(io_setup, unsigned, nr_events, aio_context_t __user *, ctxp)
if (!IS_ERR(ioctx)) {
ret = put_user(ioctx->user_id, ctxp);
if (ret)
- io_destroy(ioctx);
+ kill_ioctx(ioctx);
put_ioctx(ioctx);
}
@@ -1270,7 +872,7 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx)
{
struct kioctx *ioctx = lookup_ioctx(ctx);
if (likely(NULL != ioctx)) {
- io_destroy(ioctx);
+ kill_ioctx(ioctx);
put_ioctx(ioctx);
return 0;
}
@@ -1301,30 +903,21 @@ static void aio_advance_iovec(struct kiocb *iocb, ssize_t ret)
BUG_ON(ret > 0 && iocb->ki_left == 0);
}
-static ssize_t aio_rw_vect_retry(struct kiocb *iocb)
+typedef ssize_t (aio_rw_op)(struct kiocb *, const struct iovec *,
+ unsigned long, loff_t);
+
+static ssize_t aio_rw_vect_retry(struct kiocb *iocb, int rw, aio_rw_op *rw_op)
{
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host;
- ssize_t (*rw_op)(struct kiocb *, const struct iovec *,
- unsigned long, loff_t);
ssize_t ret = 0;
- unsigned short opcode;
-
- if ((iocb->ki_opcode == IOCB_CMD_PREADV) ||
- (iocb->ki_opcode == IOCB_CMD_PREAD)) {
- rw_op = file->f_op->aio_read;
- opcode = IOCB_CMD_PREADV;
- } else {
- rw_op = file->f_op->aio_write;
- opcode = IOCB_CMD_PWRITEV;
- }
/* This matches the pread()/pwrite() logic */
if (iocb->ki_pos < 0)
return -EINVAL;
- if (opcode == IOCB_CMD_PWRITEV)
+ if (rw == WRITE)
file_start_write(file);
do {
ret = rw_op(iocb, &iocb->ki_iovec[iocb->ki_cur_seg],
@@ -1336,9 +929,9 @@ static ssize_t aio_rw_vect_retry(struct kiocb *iocb)
/* retry all partial writes. retry partial reads as long as its a
* regular file. */
} while (ret > 0 && iocb->ki_left > 0 &&
- (opcode == IOCB_CMD_PWRITEV ||
+ (rw == WRITE ||
(!S_ISFIFO(inode->i_mode) && !S_ISSOCK(inode->i_mode))));
- if (opcode == IOCB_CMD_PWRITEV)
+ if (rw == WRITE)
file_end_write(file);
/* This means we must have transferred all that we could */
@@ -1348,81 +941,49 @@ static ssize_t aio_rw_vect_retry(struct kiocb *iocb)
/* If we managed to write some out we return that, rather than
* the eventual error. */
- if (opcode == IOCB_CMD_PWRITEV
- && ret < 0 && ret != -EIOCBQUEUED && ret != -EIOCBRETRY
+ if (rw == WRITE
+ && ret < 0 && ret != -EIOCBQUEUED
&& iocb->ki_nbytes - iocb->ki_left)
ret = iocb->ki_nbytes - iocb->ki_left;
return ret;
}
-static ssize_t aio_fdsync(struct kiocb *iocb)
-{
- struct file *file = iocb->ki_filp;
- ssize_t ret = -EINVAL;
-
- if (file->f_op->aio_fsync)
- ret = file->f_op->aio_fsync(iocb, 1);
- return ret;
-}
-
-static ssize_t aio_fsync(struct kiocb *iocb)
-{
- struct file *file = iocb->ki_filp;
- ssize_t ret = -EINVAL;
-
- if (file->f_op->aio_fsync)
- ret = file->f_op->aio_fsync(iocb, 0);
- return ret;
-}
-
-static ssize_t aio_setup_vectored_rw(int type, struct kiocb *kiocb, bool compat)
+static ssize_t aio_setup_vectored_rw(int rw, struct kiocb *kiocb, bool compat)
{
ssize_t ret;
+ kiocb->ki_nr_segs = kiocb->ki_nbytes;
+
#ifdef CONFIG_COMPAT
if (compat)
- ret = compat_rw_copy_check_uvector(type,
+ ret = compat_rw_copy_check_uvector(rw,
(struct compat_iovec __user *)kiocb->ki_buf,
- kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec,
+ kiocb->ki_nr_segs, 1, &kiocb->ki_inline_vec,
&kiocb->ki_iovec);
else
#endif
- ret = rw_copy_check_uvector(type,
+ ret = rw_copy_check_uvector(rw,
(struct iovec __user *)kiocb->ki_buf,
- kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec,
+ kiocb->ki_nr_segs, 1, &kiocb->ki_inline_vec,
&kiocb->ki_iovec);
if (ret < 0)
- goto out;
-
- ret = rw_verify_area(type, kiocb->ki_filp, &kiocb->ki_pos, ret);
- if (ret < 0)
- goto out;
+ return ret;
- kiocb->ki_nr_segs = kiocb->ki_nbytes;
- kiocb->ki_cur_seg = 0;
- /* ki_nbytes/left now reflect bytes instead of segs */
+ /* ki_nbytes now reflect bytes instead of segs */
kiocb->ki_nbytes = ret;
- kiocb->ki_left = ret;
-
- ret = 0;
-out:
- return ret;
+ return 0;
}
-static ssize_t aio_setup_single_vector(int type, struct file * file, struct kiocb *kiocb)
+static ssize_t aio_setup_single_vector(int rw, struct kiocb *kiocb)
{
- int bytes;
-
- bytes = rw_verify_area(type, file, &kiocb->ki_pos, kiocb->ki_left);
- if (bytes < 0)
- return bytes;
+ if (unlikely(!access_ok(!rw, kiocb->ki_buf, kiocb->ki_nbytes)))
+ return -EFAULT;
kiocb->ki_iovec = &kiocb->ki_inline_vec;
kiocb->ki_iovec->iov_base = kiocb->ki_buf;
- kiocb->ki_iovec->iov_len = bytes;
+ kiocb->ki_iovec->iov_len = kiocb->ki_nbytes;
kiocb->ki_nr_segs = 1;
- kiocb->ki_cur_seg = 0;
return 0;
}
@@ -1431,96 +992,95 @@ static ssize_t aio_setup_single_vector(int type, struct file * file, struct kioc
* Performs the initial checks and aio retry method
* setup for the kiocb at the time of io submission.
*/
-static ssize_t aio_setup_iocb(struct kiocb *kiocb, bool compat)
+static ssize_t aio_run_iocb(struct kiocb *req, bool compat)
{
- struct file *file = kiocb->ki_filp;
- ssize_t ret = 0;
+ struct file *file = req->ki_filp;
+ ssize_t ret;
+ int rw;
+ fmode_t mode;
+ aio_rw_op *rw_op;
- switch (kiocb->ki_opcode) {
+ switch (req->ki_opcode) {
case IOCB_CMD_PREAD:
- ret = -EBADF;
- if (unlikely(!(file->f_mode & FMODE_READ)))
- break;
- ret = -EFAULT;
- if (unlikely(!access_ok(VERIFY_WRITE, kiocb->ki_buf,
- kiocb->ki_left)))
- break;
- ret = aio_setup_single_vector(READ, file, kiocb);
- if (ret)
- break;
- ret = -EINVAL;
- if (file->f_op->aio_read)
- kiocb->ki_retry = aio_rw_vect_retry;
- break;
- case IOCB_CMD_PWRITE:
- ret = -EBADF;
- if (unlikely(!(file->f_mode & FMODE_WRITE)))
- break;
- ret = -EFAULT;
- if (unlikely(!access_ok(VERIFY_READ, kiocb->ki_buf,
- kiocb->ki_left)))
- break;
- ret = aio_setup_single_vector(WRITE, file, kiocb);
- if (ret)
- break;
- ret = -EINVAL;
- if (file->f_op->aio_write)
- kiocb->ki_retry = aio_rw_vect_retry;
- break;
case IOCB_CMD_PREADV:
- ret = -EBADF;
- if (unlikely(!(file->f_mode & FMODE_READ)))
- break;
- ret = aio_setup_vectored_rw(READ, kiocb, compat);
- if (ret)
- break;
- ret = -EINVAL;
- if (file->f_op->aio_read)
- kiocb->ki_retry = aio_rw_vect_retry;
- break;
+ mode = FMODE_READ;
+ rw = READ;
+ rw_op = file->f_op->aio_read;
+ goto rw_common;
+
+ case IOCB_CMD_PWRITE:
case IOCB_CMD_PWRITEV:
- ret = -EBADF;
- if (unlikely(!(file->f_mode & FMODE_WRITE)))
- break;
- ret = aio_setup_vectored_rw(WRITE, kiocb, compat);
+ mode = FMODE_WRITE;
+ rw = WRITE;
+ rw_op = file->f_op->aio_write;
+ goto rw_common;
+rw_common:
+ if (unlikely(!(file->f_mode & mode)))
+ return -EBADF;
+
+ if (!rw_op)
+ return -EINVAL;
+
+ ret = (req->ki_opcode == IOCB_CMD_PREADV ||
+ req->ki_opcode == IOCB_CMD_PWRITEV)
+ ? aio_setup_vectored_rw(rw, req, compat)
+ : aio_setup_single_vector(rw, req);
if (ret)
- break;
- ret = -EINVAL;
- if (file->f_op->aio_write)
- kiocb->ki_retry = aio_rw_vect_retry;
+ return ret;
+
+ ret = rw_verify_area(rw, file, &req->ki_pos, req->ki_nbytes);
+ if (ret < 0)
+ return ret;
+
+ req->ki_nbytes = ret;
+ req->ki_left = ret;
+
+ ret = aio_rw_vect_retry(req, rw, rw_op);
break;
+
case IOCB_CMD_FDSYNC:
- ret = -EINVAL;
- if (file->f_op->aio_fsync)
- kiocb->ki_retry = aio_fdsync;
+ if (!file->f_op->aio_fsync)
+ return -EINVAL;
+
+ ret = file->f_op->aio_fsync(req, 1);
break;
+
case IOCB_CMD_FSYNC:
- ret = -EINVAL;
- if (file->f_op->aio_fsync)
- kiocb->ki_retry = aio_fsync;
+ if (!file->f_op->aio_fsync)
+ return -EINVAL;
+
+ ret = file->f_op->aio_fsync(req, 0);
break;
+
default:
- dprintk("EINVAL: io_submit: no operation provided\n");
- ret = -EINVAL;
+ pr_debug("EINVAL: no operation provided\n");
+ return -EINVAL;
}
- if (!kiocb->ki_retry)
- return ret;
+ if (ret != -EIOCBQUEUED) {
+ /*
+ * There's no easy way to restart the syscall since other AIO's
+ * may be already running. Just fail this IO with EINTR.
+ */
+ if (unlikely(ret == -ERESTARTSYS || ret == -ERESTARTNOINTR ||
+ ret == -ERESTARTNOHAND ||
+ ret == -ERESTART_RESTARTBLOCK))
+ ret = -EINTR;
+ aio_complete(req, ret, 0);
+ }
return 0;
}
static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
- struct iocb *iocb, struct kiocb_batch *batch,
- bool compat)
+ struct iocb *iocb, bool compat)
{
struct kiocb *req;
- struct file *file;
ssize_t ret;
/* enforce forwards compatibility on users */
if (unlikely(iocb->aio_reserved1 || iocb->aio_reserved2)) {
- pr_debug("EINVAL: io_submit: reserve field set\n");
+ pr_debug("EINVAL: reserve field set\n");
return -EINVAL;
}
@@ -1534,16 +1094,16 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
return -EINVAL;
}
- file = fget(iocb->aio_fildes);
- if (unlikely(!file))
- return -EBADF;
-
- req = aio_get_req(ctx, batch); /* returns with 2 references to req */
- if (unlikely(!req)) {
- fput(file);
+ req = aio_get_req(ctx);
+ if (unlikely(!req))
return -EAGAIN;
+
+ req->ki_filp = fget(iocb->aio_fildes);
+ if (unlikely(!req->ki_filp)) {
+ ret = -EBADF;
+ goto out_put_req;
}
- req->ki_filp = file;
+
if (iocb->aio_flags & IOCB_FLAG_RESFD) {
/*
* If the IOCB_FLAG_RESFD flag of aio_flags is set, get an
@@ -1559,9 +1119,9 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
}
}
- ret = put_user(req->ki_key, &user_iocb->aio_key);
+ ret = put_user(KIOCB_KEY, &user_iocb->aio_key);
if (unlikely(ret)) {
- dprintk("EFAULT: aio_key\n");
+ pr_debug("EFAULT: aio_key\n");
goto out_put_req;
}
@@ -1573,41 +1133,14 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
req->ki_left = req->ki_nbytes = iocb->aio_nbytes;
req->ki_opcode = iocb->aio_lio_opcode;
- ret = aio_setup_iocb(req, compat);
-
+ ret = aio_run_iocb(req, compat);
if (ret)
goto out_put_req;
- spin_lock_irq(&ctx->ctx_lock);
- /*
- * We could have raced with io_destroy() and are currently holding a
- * reference to ctx which should be destroyed. We cannot submit IO
- * since ctx gets freed as soon as io_submit() puts its reference. The
- * check here is reliable: io_destroy() sets ctx->dead before waiting
- * for outstanding IO and the barrier between these two is realized by
- * unlock of mm->ioctx_lock and lock of ctx->ctx_lock. Analogously we
- * increment ctx->reqs_active before checking for ctx->dead and the
- * barrier is realized by unlock and lock of ctx->ctx_lock. Thus if we
- * don't see ctx->dead set here, io_destroy() waits for our IO to
- * finish.
- */
- if (ctx->dead) {
- spin_unlock_irq(&ctx->ctx_lock);
- ret = -EINVAL;
- goto out_put_req;
- }
- aio_run_iocb(req);
- if (!list_empty(&ctx->run_list)) {
- /* drain the run list */
- while (__aio_run_iocbs(ctx))
- ;
- }
- spin_unlock_irq(&ctx->ctx_lock);
-
aio_put_req(req); /* drop extra ref to req */
return 0;
-
out_put_req:
+ atomic_dec(&ctx->reqs_active);
aio_put_req(req); /* drop extra ref to req */
aio_put_req(req); /* drop i/o ref to req */
return ret;
@@ -1620,7 +1153,6 @@ long do_io_submit(aio_context_t ctx_id, long nr,
long ret = 0;
int i = 0;
struct blk_plug plug;
- struct kiocb_batch batch;
if (unlikely(nr < 0))
return -EINVAL;
@@ -1633,12 +1165,10 @@ long do_io_submit(aio_context_t ctx_id, long nr,
ctx = lookup_ioctx(ctx_id);
if (unlikely(!ctx)) {
- pr_debug("EINVAL: io_submit: invalid context id\n");
+ pr_debug("EINVAL: invalid context id\n");
return -EINVAL;
}
- kiocb_batch_init(&batch, nr);
-
blk_start_plug(&plug);
/*
@@ -1659,13 +1189,12 @@ long do_io_submit(aio_context_t ctx_id, long nr,
break;
}
- ret = io_submit_one(ctx, user_iocb, &tmp, &batch, compat);
+ ret = io_submit_one(ctx, user_iocb, &tmp, compat);
if (ret)
break;
}
blk_finish_plug(&plug);
- kiocb_batch_free(ctx, &batch);
put_ioctx(ctx);
return i ? i : ret;
}
@@ -1698,10 +1227,13 @@ static struct kiocb *lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb,
assert_spin_locked(&ctx->ctx_lock);
+ if (key != KIOCB_KEY)
+ return NULL;
+
/* TODO: use a hash or array, this sucks. */
list_for_each(pos, &ctx->active_reqs) {
struct kiocb *kiocb = list_kiocb(pos);
- if (kiocb->ki_obj.user == iocb && kiocb->ki_key == key)
+ if (kiocb->ki_obj.user == iocb)
return kiocb;
}
return NULL;
@@ -1720,7 +1252,7 @@ static struct kiocb *lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb,
SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb,
struct io_event __user *, result)
{
- int (*cancel)(struct kiocb *iocb, struct io_event *res);
+ struct io_event res;
struct kioctx *ctx;
struct kiocb *kiocb;
u32 key;
@@ -1735,32 +1267,22 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb,
return -EINVAL;
spin_lock_irq(&ctx->ctx_lock);
- ret = -EAGAIN;
+
kiocb = lookup_kiocb(ctx, iocb, key);
- if (kiocb && kiocb->ki_cancel) {
- cancel = kiocb->ki_cancel;
- kiocb->ki_users ++;
- kiocbSetCancelled(kiocb);
- } else
- cancel = NULL;
+ if (kiocb)
+ ret = kiocb_cancel(ctx, kiocb, &res);
+ else
+ ret = -EINVAL;
+
spin_unlock_irq(&ctx->ctx_lock);
- if (NULL != cancel) {
- struct io_event tmp;
- pr_debug("calling cancel\n");
- memset(&tmp, 0, sizeof(tmp));
- tmp.obj = (u64)(unsigned long)kiocb->ki_obj.user;
- tmp.data = kiocb->ki_user_data;
- ret = cancel(kiocb, &tmp);
- if (!ret) {
- /* Cancellation succeeded -- copy the result
- * into the user's buffer.
- */
- if (copy_to_user(result, &tmp, sizeof(tmp)))
- ret = -EFAULT;
- }
- } else
- ret = -EINVAL;
+ if (!ret) {
+ /* Cancellation succeeded -- copy the result
+ * into the user's buffer.
+ */
+ if (copy_to_user(result, &res, sizeof(res)))
+ ret = -EFAULT;
+ }
put_ioctx(ctx);
diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c
index a3f28f331b2b..8fb42916d8a2 100644
--- a/fs/bio-integrity.c
+++ b/fs/bio-integrity.c
@@ -27,48 +27,11 @@
#include <linux/workqueue.h>
#include <linux/slab.h>
-struct integrity_slab {
- struct kmem_cache *slab;
- unsigned short nr_vecs;
- char name[8];
-};
-
-#define IS(x) { .nr_vecs = x, .name = "bip-"__stringify(x) }
-struct integrity_slab bip_slab[BIOVEC_NR_POOLS] __read_mostly = {
- IS(1), IS(4), IS(16), IS(64), IS(128), IS(BIO_MAX_PAGES),
-};
-#undef IS
+#define BIP_INLINE_VECS 4
+static struct kmem_cache *bip_slab;
static struct workqueue_struct *kintegrityd_wq;
-static inline unsigned int vecs_to_idx(unsigned int nr)
-{
- switch (nr) {
- case 1:
- return 0;
- case 2 ... 4:
- return 1;
- case 5 ... 16:
- return 2;
- case 17 ... 64:
- return 3;
- case 65 ... 128:
- return 4;
- case 129 ... BIO_MAX_PAGES:
- return 5;
- default:
- BUG();
- }
-}
-
-static inline int use_bip_pool(unsigned int idx)
-{
- if (idx == BIOVEC_MAX_IDX)
- return 1;
-
- return 0;
-}
-
/**
* bio_integrity_alloc - Allocate integrity payload and attach it to bio
* @bio: bio to attach integrity metadata to
@@ -84,37 +47,41 @@ struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio,
unsigned int nr_vecs)
{
struct bio_integrity_payload *bip;
- unsigned int idx = vecs_to_idx(nr_vecs);
struct bio_set *bs = bio->bi_pool;
-
- if (!bs)
- bs = fs_bio_set;
-
- BUG_ON(bio == NULL);
- bip = NULL;
-
- /* Lower order allocations come straight from slab */
- if (!use_bip_pool(idx))
- bip = kmem_cache_alloc(bip_slab[idx].slab, gfp_mask);
-
- /* Use mempool if lower order alloc failed or max vecs were requested */
- if (bip == NULL) {
- idx = BIOVEC_MAX_IDX; /* so we free the payload properly later */
+ unsigned long idx = BIO_POOL_NONE;
+ unsigned inline_vecs;
+
+ if (!bs) {
+ bip = kmalloc(sizeof(struct bio_integrity_payload) +
+ sizeof(struct bio_vec) * nr_vecs, gfp_mask);
+ inline_vecs = nr_vecs;
+ } else {
bip = mempool_alloc(bs->bio_integrity_pool, gfp_mask);
-
- if (unlikely(bip == NULL)) {
- printk(KERN_ERR "%s: could not alloc bip\n", __func__);
- return NULL;
- }
+ inline_vecs = BIP_INLINE_VECS;
}
+ if (unlikely(!bip))
+ return NULL;
+
memset(bip, 0, sizeof(*bip));
+ if (nr_vecs > inline_vecs) {
+ bip->bip_vec = bvec_alloc(gfp_mask, nr_vecs, &idx,
+ bs->bvec_integrity_pool);
+ if (!bip->bip_vec)
+ goto err;
+ } else {
+ bip->bip_vec = bip->bip_inline_vecs;
+ }
+
bip->bip_slab = idx;
bip->bip_bio = bio;
bio->bi_integrity = bip;
return bip;
+err:
+ mempool_free(bip, bs->bio_integrity_pool);
+ return NULL;
}
EXPORT_SYMBOL(bio_integrity_alloc);
@@ -130,20 +97,18 @@ void bio_integrity_free(struct bio *bio)
struct bio_integrity_payload *bip = bio->bi_integrity;
struct bio_set *bs = bio->bi_pool;
- if (!bs)
- bs = fs_bio_set;
-
- BUG_ON(bip == NULL);
-
- /* A cloned bio doesn't own the integrity metadata */
- if (!bio_flagged(bio, BIO_CLONED) && !bio_flagged(bio, BIO_FS_INTEGRITY)
- && bip->bip_buf != NULL)
+ if (bip->bip_owns_buf)
kfree(bip->bip_buf);
- if (use_bip_pool(bip->bip_slab))
+ if (bs) {
+ if (bip->bip_slab != BIO_POOL_NONE)
+ bvec_free(bs->bvec_integrity_pool, bip->bip_vec,
+ bip->bip_slab);
+
mempool_free(bip, bs->bio_integrity_pool);
- else
- kmem_cache_free(bip_slab[bip->bip_slab].slab, bip);
+ } else {
+ kfree(bip);
+ }
bio->bi_integrity = NULL;
}
@@ -419,6 +384,7 @@ int bio_integrity_prep(struct bio *bio)
return -EIO;
}
+ bip->bip_owns_buf = 1;
bip->bip_buf = buf;
bip->bip_size = len;
bip->bip_sector = bio->bi_sector;
@@ -694,11 +660,11 @@ void bio_integrity_split(struct bio *bio, struct bio_pair *bp, int sectors)
bp->bio1.bi_integrity = &bp->bip1;
bp->bio2.bi_integrity = &bp->bip2;
- bp->iv1 = bip->bip_vec[0];
- bp->iv2 = bip->bip_vec[0];
+ bp->iv1 = bip->bip_vec[bip->bip_idx];
+ bp->iv2 = bip->bip_vec[bip->bip_idx];
- bp->bip1.bip_vec[0] = bp->iv1;
- bp->bip2.bip_vec[0] = bp->iv2;
+ bp->bip1.bip_vec = &bp->iv1;
+ bp->bip2.bip_vec = &bp->iv2;
bp->iv1.bv_len = sectors * bi->tuple_size;
bp->iv2.bv_offset += sectors * bi->tuple_size;
@@ -746,13 +712,14 @@ EXPORT_SYMBOL(bio_integrity_clone);
int bioset_integrity_create(struct bio_set *bs, int pool_size)
{
- unsigned int max_slab = vecs_to_idx(BIO_MAX_PAGES);
-
if (bs->bio_integrity_pool)
return 0;
- bs->bio_integrity_pool =
- mempool_create_slab_pool(pool_size, bip_slab[max_slab].slab);
+ bs->bio_integrity_pool = mempool_create_slab_pool(pool_size, bip_slab);
+
+ bs->bvec_integrity_pool = biovec_create_pool(bs, pool_size);
+ if (!bs->bvec_integrity_pool)
+ return -1;
if (!bs->bio_integrity_pool)
return -1;
@@ -765,13 +732,14 @@ void bioset_integrity_free(struct bio_set *bs)
{
if (bs->bio_integrity_pool)
mempool_destroy(bs->bio_integrity_pool);
+
+ if (bs->bvec_integrity_pool)
+ mempool_destroy(bs->bio_integrity_pool);
}
EXPORT_SYMBOL(bioset_integrity_free);
void __init bio_integrity_init(void)
{
- unsigned int i;
-
/*
* kintegrityd won't block much but may burn a lot of CPU cycles.
* Make it highpri CPU intensive wq with max concurrency of 1.
@@ -781,14 +749,10 @@ void __init bio_integrity_init(void)
if (!kintegrityd_wq)
panic("Failed to create kintegrityd\n");
- for (i = 0 ; i < BIOVEC_NR_POOLS ; i++) {
- unsigned int size;
-
- size = sizeof(struct bio_integrity_payload)
- + bip_slab[i].nr_vecs * sizeof(struct bio_vec);
-
- bip_slab[i].slab =
- kmem_cache_create(bip_slab[i].name, size, 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
- }
+ bip_slab = kmem_cache_create("bio_integrity_payload",
+ sizeof(struct bio_integrity_payload) +
+ sizeof(struct bio_vec) * BIP_INLINE_VECS,
+ 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+ if (!bip_slab)
+ panic("Failed to create slab\n");
}
diff --git a/fs/bio.c b/fs/bio.c
index b96fc6ce4855..94bbc04dba77 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -19,6 +19,7 @@
#include <linux/swap.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/uio.h>
#include <linux/iocontext.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -160,12 +161,12 @@ unsigned int bvec_nr_vecs(unsigned short idx)
return bvec_slabs[idx].nr_vecs;
}
-void bvec_free_bs(struct bio_set *bs, struct bio_vec *bv, unsigned int idx)
+void bvec_free(mempool_t *pool, struct bio_vec *bv, unsigned int idx)
{
BIO_BUG_ON(idx >= BIOVEC_NR_POOLS);
if (idx == BIOVEC_MAX_IDX)
- mempool_free(bv, bs->bvec_pool);
+ mempool_free(bv, pool);
else {
struct biovec_slab *bvs = bvec_slabs + idx;
@@ -173,8 +174,8 @@ void bvec_free_bs(struct bio_set *bs, struct bio_vec *bv, unsigned int idx)
}
}
-struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx,
- struct bio_set *bs)
+struct bio_vec *bvec_alloc(gfp_t gfp_mask, int nr, unsigned long *idx,
+ mempool_t *pool)
{
struct bio_vec *bvl;
@@ -210,7 +211,7 @@ struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx,
*/
if (*idx == BIOVEC_MAX_IDX) {
fallback:
- bvl = mempool_alloc(bs->bvec_pool, gfp_mask);
+ bvl = mempool_alloc(pool, gfp_mask);
} else {
struct biovec_slab *bvs = bvec_slabs + *idx;
gfp_t __gfp_mask = gfp_mask & ~(__GFP_WAIT | __GFP_IO);
@@ -252,8 +253,8 @@ static void bio_free(struct bio *bio)
__bio_free(bio);
if (bs) {
- if (bio_has_allocated_vec(bio))
- bvec_free_bs(bs, bio->bi_io_vec, BIO_POOL_IDX(bio));
+ if (bio_flagged(bio, BIO_OWNS_VEC))
+ bvec_free(bs->bvec_pool, bio->bi_io_vec, BIO_POOL_IDX(bio));
/*
* If we have front padding, adjust the bio pointer before freeing
@@ -297,6 +298,54 @@ void bio_reset(struct bio *bio)
}
EXPORT_SYMBOL(bio_reset);
+static void bio_alloc_rescue(struct work_struct *work)
+{
+ struct bio_set *bs = container_of(work, struct bio_set, rescue_work);
+ struct bio *bio;
+
+ while (1) {
+ spin_lock(&bs->rescue_lock);
+ bio = bio_list_pop(&bs->rescue_list);
+ spin_unlock(&bs->rescue_lock);
+
+ if (!bio)
+ break;
+
+ generic_make_request(bio);
+ }
+}
+
+static void punt_bios_to_rescuer(struct bio_set *bs)
+{
+ struct bio_list punt, nopunt;
+ struct bio *bio;
+
+ /*
+ * In order to guarantee forward progress we must punt only bios that
+ * were allocated from this bio_set; otherwise, if there was a bio on
+ * there for a stacking driver higher up in the stack, processing it
+ * could require allocating bios from this bio_set, and doing that from
+ * our own rescuer would be bad.
+ *
+ * Since bio lists are singly linked, pop them all instead of trying to
+ * remove from the middle of the list:
+ */
+
+ bio_list_init(&punt);
+ bio_list_init(&nopunt);
+
+ while ((bio = bio_list_pop(current->bio_list)))
+ bio_list_add(bio->bi_pool == bs ? &punt : &nopunt, bio);
+
+ *current->bio_list = nopunt;
+
+ spin_lock(&bs->rescue_lock);
+ bio_list_merge(&bs->rescue_list, &punt);
+ spin_unlock(&bs->rescue_lock);
+
+ queue_work(bs->rescue_workqueue, &bs->rescue_work);
+}
+
/**
* bio_alloc_bioset - allocate a bio for I/O
* @gfp_mask: the GFP_ mask given to the slab allocator
@@ -314,11 +363,27 @@ EXPORT_SYMBOL(bio_reset);
* previously allocated bio for IO before attempting to allocate a new one.
* Failure to do so can cause deadlocks under memory pressure.
*
+ * Note that when running under generic_make_request() (i.e. any block
+ * driver), bios are not submitted until after you return - see the code in
+ * generic_make_request() that converts recursion into iteration, to prevent
+ * stack overflows.
+ *
+ * This would normally mean allocating multiple bios under
+ * generic_make_request() would be susceptible to deadlocks, but we have
+ * deadlock avoidance code that resubmits any blocked bios from a rescuer
+ * thread.
+ *
+ * However, we do not guarantee forward progress for allocations from other
+ * mempools. Doing multiple allocations from the same mempool under
+ * generic_make_request() should be avoided - instead, use bio_set's front_pad
+ * for per bio allocations.
+ *
* RETURNS:
* Pointer to new bio on success, NULL on failure.
*/
struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
{
+ gfp_t saved_gfp = gfp_mask;
unsigned front_pad;
unsigned inline_vecs;
unsigned long idx = BIO_POOL_NONE;
@@ -336,7 +401,37 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
front_pad = 0;
inline_vecs = nr_iovecs;
} else {
+ /*
+ * generic_make_request() converts recursion to iteration; this
+ * means if we're running beneath it, any bios we allocate and
+ * submit will not be submitted (and thus freed) until after we
+ * return.
+ *
+ * This exposes us to a potential deadlock if we allocate
+ * multiple bios from the same bio_set() while running
+ * underneath generic_make_request(). If we were to allocate
+ * multiple bios (say a stacking block driver that was splitting
+ * bios), we would deadlock if we exhausted the mempool's
+ * reserve.
+ *
+ * We solve this, and guarantee forward progress, with a rescuer
+ * workqueue per bio_set. If we go to allocate and there are
+ * bios on current->bio_list, we first try the allocation
+ * without __GFP_WAIT; if that fails, we punt those bios we
+ * would be blocking to the rescuer workqueue before we retry
+ * with the original gfp_flags.
+ */
+
+ if (current->bio_list && !bio_list_empty(current->bio_list))
+ gfp_mask &= ~__GFP_WAIT;
+
p = mempool_alloc(bs->bio_pool, gfp_mask);
+ if (!p && gfp_mask != saved_gfp) {
+ punt_bios_to_rescuer(bs);
+ gfp_mask = saved_gfp;
+ p = mempool_alloc(bs->bio_pool, gfp_mask);
+ }
+
front_pad = bs->front_pad;
inline_vecs = BIO_INLINE_VECS;
}
@@ -348,9 +443,17 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
bio_init(bio);
if (nr_iovecs > inline_vecs) {
- bvl = bvec_alloc_bs(gfp_mask, nr_iovecs, &idx, bs);
+ bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, bs->bvec_pool);
+ if (!bvl && gfp_mask != saved_gfp) {
+ punt_bios_to_rescuer(bs);
+ gfp_mask = saved_gfp;
+ bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, bs->bvec_pool);
+ }
+
if (unlikely(!bvl))
goto err_free;
+
+ bio->bi_flags |= 1 << BIO_OWNS_VEC;
} else if (nr_iovecs) {
bvl = bio->bi_inline_vecs;
}
@@ -652,6 +755,181 @@ int bio_add_page(struct bio *bio, struct page *page, unsigned int len,
}
EXPORT_SYMBOL(bio_add_page);
+struct submit_bio_ret {
+ struct completion event;
+ int error;
+};
+
+static void submit_bio_wait_endio(struct bio *bio, int error)
+{
+ struct submit_bio_ret *ret = bio->bi_private;
+
+ ret->error = error;
+ complete(&ret->event);
+}
+
+/**
+ * submit_bio_wait - submit a bio, and wait until it completes
+ * @rw: whether to %READ or %WRITE, or maybe to %READA (read ahead)
+ * @bio: The &struct bio which describes the I/O
+ *
+ * Simple wrapper around submit_bio(). Returns 0 on success, or the error from
+ * bio_endio() on failure.
+ */
+int submit_bio_wait(int rw, struct bio *bio)
+{
+ struct submit_bio_ret ret;
+
+ rw |= REQ_SYNC;
+ init_completion(&ret.event);
+ bio->bi_private = &ret;
+ bio->bi_end_io = submit_bio_wait_endio;
+ submit_bio(rw, bio);
+ wait_for_completion(&ret.event);
+
+ return ret.error;
+}
+EXPORT_SYMBOL(submit_bio_wait);
+
+/**
+ * bio_advance - increment/complete a bio by some number of bytes
+ * @bio: bio to advance
+ * @bytes: number of bytes to complete
+ *
+ * This updates bi_sector, bi_size and bi_idx; if the number of bytes to
+ * complete doesn't align with a bvec boundary, then bv_len and bv_offset will
+ * be updated on the last bvec as well.
+ *
+ * @bio will then represent the remaining, uncompleted portion of the io.
+ */
+void bio_advance(struct bio *bio, unsigned bytes)
+{
+ if (bio_integrity(bio))
+ bio_integrity_advance(bio, bytes);
+
+ bio->bi_sector += bytes >> 9;
+ bio->bi_size -= bytes;
+
+ if (bio->bi_rw & BIO_NO_ADVANCE_ITER_MASK)
+ return;
+
+ while (bytes) {
+ if (unlikely(bio->bi_idx >= bio->bi_vcnt)) {
+ WARN_ONCE(1, "bio idx %d >= vcnt %d\n",
+ bio->bi_idx, bio->bi_vcnt);
+ break;
+ }
+
+ if (bytes >= bio_iovec(bio)->bv_len) {
+ bytes -= bio_iovec(bio)->bv_len;
+ bio->bi_idx++;
+ } else {
+ bio_iovec(bio)->bv_len -= bytes;
+ bio_iovec(bio)->bv_offset += bytes;
+ bytes = 0;
+ }
+ }
+}
+EXPORT_SYMBOL(bio_advance);
+
+/**
+ * bio_alloc_pages - allocates a single page for each bvec in a bio
+ * @bio: bio to allocate pages for
+ * @gfp_mask: flags for allocation
+ *
+ * Allocates pages up to @bio->bi_vcnt.
+ *
+ * Returns 0 on success, -ENOMEM on failure. On failure, any allocated pages are
+ * freed.
+ */
+int bio_alloc_pages(struct bio *bio, gfp_t gfp_mask)
+{
+ int i;
+ struct bio_vec *bv;
+
+ bio_for_each_segment_all(bv, bio, i) {
+ bv->bv_page = alloc_page(gfp_mask);
+ if (!bv->bv_page) {
+ while (--bv >= bio->bi_io_vec)
+ __free_page(bv->bv_page);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(bio_alloc_pages);
+
+/**
+ * bio_copy_data - copy contents of data buffers from one chain of bios to
+ * another
+ * @src: source bio list
+ * @dst: destination bio list
+ *
+ * If @src and @dst are single bios, bi_next must be NULL - otherwise, treats
+ * @src and @dst as linked lists of bios.
+ *
+ * Stops when it reaches the end of either @src or @dst - that is, copies
+ * min(src->bi_size, dst->bi_size) bytes (or the equivalent for lists of bios).
+ */
+void bio_copy_data(struct bio *dst, struct bio *src)
+{
+ struct bio_vec *src_bv, *dst_bv;
+ unsigned src_offset, dst_offset, bytes;
+ void *src_p, *dst_p;
+
+ src_bv = bio_iovec(src);
+ dst_bv = bio_iovec(dst);
+
+ src_offset = src_bv->bv_offset;
+ dst_offset = dst_bv->bv_offset;
+
+ while (1) {
+ if (src_offset == src_bv->bv_offset + src_bv->bv_len) {
+ src_bv++;
+ if (src_bv == bio_iovec_idx(src, src->bi_vcnt)) {
+ src = src->bi_next;
+ if (!src)
+ break;
+
+ src_bv = bio_iovec(src);
+ }
+
+ src_offset = src_bv->bv_offset;
+ }
+
+ if (dst_offset == dst_bv->bv_offset + dst_bv->bv_len) {
+ dst_bv++;
+ if (dst_bv == bio_iovec_idx(dst, dst->bi_vcnt)) {
+ dst = dst->bi_next;
+ if (!dst)
+ break;
+
+ dst_bv = bio_iovec(dst);
+ }
+
+ dst_offset = dst_bv->bv_offset;
+ }
+
+ bytes = min(dst_bv->bv_offset + dst_bv->bv_len - dst_offset,
+ src_bv->bv_offset + src_bv->bv_len - src_offset);
+
+ src_p = kmap_atomic(src_bv->bv_page);
+ dst_p = kmap_atomic(dst_bv->bv_page);
+
+ memcpy(dst_p + dst_bv->bv_offset,
+ src_p + src_bv->bv_offset,
+ bytes);
+
+ kunmap_atomic(dst_p);
+ kunmap_atomic(src_p);
+
+ src_offset += bytes;
+ dst_offset += bytes;
+ }
+}
+EXPORT_SYMBOL(bio_copy_data);
+
struct bio_map_data {
struct bio_vec *iovecs;
struct sg_iovec *sgvecs;
@@ -714,7 +992,7 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs,
int iov_idx = 0;
unsigned int iov_off = 0;
- __bio_for_each_segment(bvec, bio, i, 0) {
+ bio_for_each_segment_all(bvec, bio, i) {
char *bv_addr = page_address(bvec->bv_page);
unsigned int bv_len = iovecs[i].bv_len;
@@ -896,7 +1174,7 @@ struct bio *bio_copy_user_iov(struct request_queue *q,
return bio;
cleanup:
if (!map_data)
- bio_for_each_segment(bvec, bio, i)
+ bio_for_each_segment_all(bvec, bio, i)
__free_page(bvec->bv_page);
bio_put(bio);
@@ -1110,7 +1388,7 @@ static void __bio_unmap_user(struct bio *bio)
/*
* make sure we dirty pages we wrote to
*/
- __bio_for_each_segment(bvec, bio, i, 0) {
+ bio_for_each_segment_all(bvec, bio, i) {
if (bio_data_dir(bio) == READ)
set_page_dirty_lock(bvec->bv_page);
@@ -1216,7 +1494,7 @@ static void bio_copy_kern_endio(struct bio *bio, int err)
int i;
char *p = bmd->sgvecs[0].iov_base;
- __bio_for_each_segment(bvec, bio, i, 0) {
+ bio_for_each_segment_all(bvec, bio, i) {
char *addr = page_address(bvec->bv_page);
int len = bmd->iovecs[i].bv_len;
@@ -1256,7 +1534,7 @@ struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len,
if (!reading) {
void *p = data;
- bio_for_each_segment(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i) {
char *addr = page_address(bvec->bv_page);
memcpy(addr, p, bvec->bv_len);
@@ -1301,11 +1579,11 @@ EXPORT_SYMBOL(bio_copy_kern);
*/
void bio_set_pages_dirty(struct bio *bio)
{
- struct bio_vec *bvec = bio->bi_io_vec;
+ struct bio_vec *bvec;
int i;
- for (i = 0; i < bio->bi_vcnt; i++) {
- struct page *page = bvec[i].bv_page;
+ bio_for_each_segment_all(bvec, bio, i) {
+ struct page *page = bvec->bv_page;
if (page && !PageCompound(page))
set_page_dirty_lock(page);
@@ -1314,11 +1592,11 @@ void bio_set_pages_dirty(struct bio *bio)
static void bio_release_pages(struct bio *bio)
{
- struct bio_vec *bvec = bio->bi_io_vec;
+ struct bio_vec *bvec;
int i;
- for (i = 0; i < bio->bi_vcnt; i++) {
- struct page *page = bvec[i].bv_page;
+ bio_for_each_segment_all(bvec, bio, i) {
+ struct page *page = bvec->bv_page;
if (page)
put_page(page);
@@ -1367,16 +1645,16 @@ static void bio_dirty_fn(struct work_struct *work)
void bio_check_pages_dirty(struct bio *bio)
{
- struct bio_vec *bvec = bio->bi_io_vec;
+ struct bio_vec *bvec;
int nr_clean_pages = 0;
int i;
- for (i = 0; i < bio->bi_vcnt; i++) {
- struct page *page = bvec[i].bv_page;
+ bio_for_each_segment_all(bvec, bio, i) {
+ struct page *page = bvec->bv_page;
if (PageDirty(page) || PageCompound(page)) {
page_cache_release(page);
- bvec[i].bv_page = NULL;
+ bvec->bv_page = NULL;
} else {
nr_clean_pages++;
}
@@ -1477,8 +1755,7 @@ struct bio_pair *bio_split(struct bio *bi, int first_sectors)
trace_block_split(bdev_get_queue(bi->bi_bdev), bi,
bi->bi_sector + first_sectors);
- BUG_ON(bi->bi_vcnt != 1 && bi->bi_vcnt != 0);
- BUG_ON(bi->bi_idx != 0);
+ BUG_ON(bio_segments(bi) > 1);
atomic_set(&bp->cnt, 3);
bp->error = 0;
bp->bio1 = *bi;
@@ -1488,8 +1765,8 @@ struct bio_pair *bio_split(struct bio *bi, int first_sectors)
bp->bio1.bi_size = first_sectors << 9;
if (bi->bi_vcnt != 0) {
- bp->bv1 = bi->bi_io_vec[0];
- bp->bv2 = bi->bi_io_vec[0];
+ bp->bv1 = *bio_iovec(bi);
+ bp->bv2 = *bio_iovec(bi);
if (bio_is_rw(bi)) {
bp->bv2.bv_offset += first_sectors << 9;
@@ -1541,7 +1818,7 @@ sector_t bio_sector_offset(struct bio *bio, unsigned short index,
if (index >= bio->bi_idx)
index = bio->bi_vcnt - 1;
- __bio_for_each_segment(bv, bio, i, 0) {
+ bio_for_each_segment_all(bv, bio, i) {
if (i == index) {
if (offset > bv->bv_offset)
sectors += (offset - bv->bv_offset) / sector_sz;
@@ -1559,29 +1836,25 @@ EXPORT_SYMBOL(bio_sector_offset);
* create memory pools for biovec's in a bio_set.
* use the global biovec slabs created for general use.
*/
-static int biovec_create_pools(struct bio_set *bs, int pool_entries)
+mempool_t *biovec_create_pool(struct bio_set *bs, int pool_entries)
{
struct biovec_slab *bp = bvec_slabs + BIOVEC_MAX_IDX;
- bs->bvec_pool = mempool_create_slab_pool(pool_entries, bp->slab);
- if (!bs->bvec_pool)
- return -ENOMEM;
-
- return 0;
-}
-
-static void biovec_free_pools(struct bio_set *bs)
-{
- mempool_destroy(bs->bvec_pool);
+ return mempool_create_slab_pool(pool_entries, bp->slab);
}
void bioset_free(struct bio_set *bs)
{
+ if (bs->rescue_workqueue)
+ destroy_workqueue(bs->rescue_workqueue);
+
if (bs->bio_pool)
mempool_destroy(bs->bio_pool);
+ if (bs->bvec_pool)
+ mempool_destroy(bs->bvec_pool);
+
bioset_integrity_free(bs);
- biovec_free_pools(bs);
bio_put_slab(bs);
kfree(bs);
@@ -1612,6 +1885,10 @@ struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad)
bs->front_pad = front_pad;
+ spin_lock_init(&bs->rescue_lock);
+ bio_list_init(&bs->rescue_list);
+ INIT_WORK(&bs->rescue_work, bio_alloc_rescue);
+
bs->bio_slab = bio_find_or_create_slab(front_pad + back_pad);
if (!bs->bio_slab) {
kfree(bs);
@@ -1622,9 +1899,15 @@ struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad)
if (!bs->bio_pool)
goto bad;
- if (!biovec_create_pools(bs, pool_size))
- return bs;
+ bs->bvec_pool = biovec_create_pool(bs, pool_size);
+ if (!bs->bvec_pool)
+ goto bad;
+
+ bs->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0);
+ if (!bs->rescue_workqueue)
+ goto bad;
+ return bs;
bad:
bioset_free(bs);
return NULL;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index ce08de7467a3..2091db8cdd78 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -27,6 +27,7 @@
#include <linux/namei.h>
#include <linux/log2.h>
#include <linux/cleancache.h>
+#include <linux/aio.h>
#include <asm/uaccess.h>
#include "internal.h"
@@ -1045,7 +1046,7 @@ void bd_set_size(struct block_device *bdev, loff_t size)
}
EXPORT_SYMBOL(bd_set_size);
-static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part);
+static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part);
/*
* bd_mutex locking:
@@ -1400,9 +1401,8 @@ static int blkdev_open(struct inode * inode, struct file * filp)
return blkdev_get(bdev, filp->f_mode, filp);
}
-static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
+static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
{
- int ret = 0;
struct gendisk *disk = bdev->bd_disk;
struct block_device *victim = NULL;
@@ -1422,7 +1422,7 @@ static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
}
if (bdev->bd_contains == bdev) {
if (disk->fops->release)
- ret = disk->fops->release(disk, mode);
+ disk->fops->release(disk, mode);
}
if (!bdev->bd_openers) {
struct module *owner = disk->fops->owner;
@@ -1441,10 +1441,9 @@ static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
bdput(bdev);
if (victim)
__blkdev_put(victim, mode, 1);
- return ret;
}
-int blkdev_put(struct block_device *bdev, fmode_t mode)
+void blkdev_put(struct block_device *bdev, fmode_t mode)
{
mutex_lock(&bdev->bd_mutex);
@@ -1488,15 +1487,15 @@ int blkdev_put(struct block_device *bdev, fmode_t mode)
mutex_unlock(&bdev->bd_mutex);
- return __blkdev_put(bdev, mode, 0);
+ __blkdev_put(bdev, mode, 0);
}
EXPORT_SYMBOL(blkdev_put);
static int blkdev_close(struct inode * inode, struct file * filp)
{
struct block_device *bdev = I_BDEV(filp->f_mapping->host);
-
- return blkdev_put(bdev, filp->f_mode);
+ blkdev_put(bdev, filp->f_mode);
+ return 0;
}
static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
@@ -1557,7 +1556,7 @@ static ssize_t blkdev_aio_read(struct kiocb *iocb, const struct iovec *iov,
return 0;
size -= pos;
- if (size < INT_MAX)
+ if (size < iocb->ki_left)
nr_segs = iov_shorten((struct iovec *)iov, nr_segs, size);
return generic_file_aio_read(iocb, iov, nr_segs, pos);
}
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index cdee391fc7bf..73f2bfe3ac93 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2560,8 +2560,7 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree,
if (old_compressed)
contig = bio->bi_sector == sector;
else
- contig = bio->bi_sector + (bio->bi_size >> 9) ==
- sector;
+ contig = bio_end_sector(bio) == sector;
if (prev_bio_flags != bio_flags || !contig ||
merge_bio(rw, tree, page, offset, page_size, bio, bio_flags) ||
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index bb8b7a0e28a6..bc4d54c465a0 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -24,6 +24,7 @@
#include <linux/string.h>
#include <linux/backing-dev.h>
#include <linux/mpage.h>
+#include <linux/aio.h>
#include <linux/falloc.h>
#include <linux/swap.h>
#include <linux/writeback.h>
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 09c58a35b429..898da0a01e04 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -32,6 +32,7 @@
#include <linux/writeback.h>
#include <linux/statfs.h>
#include <linux/compat.h>
+#include <linux/aio.h>
#include <linux/bit_spinlock.h>
#include <linux/xattr.h>
#include <linux/posix_acl.h>
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 2854c824ab64..678977226570 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -5177,7 +5177,7 @@ static int bio_size_ok(struct block_device *bdev, struct bio *bio,
}
prev = &bio->bi_io_vec[bio->bi_vcnt - 1];
- if ((bio->bi_size >> 9) > max_sectors)
+ if (bio_sectors(bio) > max_sectors)
return 0;
if (!q->merge_bvec_fn)
diff --git a/fs/buffer.c b/fs/buffer.c
index bc1fe14aaa3e..d2a4d1bb2d57 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2977,7 +2977,6 @@ int _submit_bh(int rw, struct buffer_head *bh, unsigned long bio_flags)
bio->bi_io_vec[0].bv_offset = bh_offset(bh);
bio->bi_vcnt = 1;
- bio->bi_idx = 0;
bio->bi_size = bh->b_size;
bio->bi_end_io = end_bio_bh_io_sync;
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index d70830c66833..656e16907430 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -7,6 +7,7 @@
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/writeback.h>
+#include <linux/aio.h>
#include "super.h"
#include "mds_client.h"
diff --git a/fs/compat.c b/fs/compat.c
index 93f7d021b716..fc3b55dce184 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -47,6 +47,7 @@
#include <linux/fs_struct.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
+#include <linux/aio.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
diff --git a/fs/direct-io.c b/fs/direct-io.c
index cfb816dc6d9f..7ab90f5081ee 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -37,6 +37,7 @@
#include <linux/uio.h>
#include <linux/atomic.h>
#include <linux/prefetch.h>
+#include <linux/aio.h>
/*
* How many user pages to map in one call to get_user_pages(). This determines
@@ -441,8 +442,8 @@ static struct bio *dio_await_one(struct dio *dio)
static int dio_bio_complete(struct dio *dio, struct bio *bio)
{
const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
- struct bio_vec *bvec = bio->bi_io_vec;
- int page_no;
+ struct bio_vec *bvec;
+ unsigned i;
if (!uptodate)
dio->io_error = -EIO;
@@ -450,8 +451,8 @@ static int dio_bio_complete(struct dio *dio, struct bio *bio)
if (dio->is_async && dio->rw == READ) {
bio_check_pages_dirty(bio); /* transfers ownership */
} else {
- for (page_no = 0; page_no < bio->bi_vcnt; page_no++) {
- struct page *page = bvec[page_no].bv_page;
+ bio_for_each_segment_all(bvec, bio, i) {
+ struct page *page = bvec->bv_page;
if (dio->rw == READ && !PageCompound(page))
set_page_dirty_lock(page);
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index 63b1f54b6a1f..201f0a0d6b0a 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -31,6 +31,7 @@
#include <linux/security.h>
#include <linux/compat.h>
#include <linux/fs_stack.h>
+#include <linux/aio.h>
#include "ecryptfs_kernel.h"
/**
diff --git a/fs/exofs/ore.c b/fs/exofs/ore.c
index f936cb50dc0d..b74422888604 100644
--- a/fs/exofs/ore.c
+++ b/fs/exofs/ore.c
@@ -401,7 +401,7 @@ static void _clear_bio(struct bio *bio)
struct bio_vec *bv;
unsigned i;
- __bio_for_each_segment(bv, bio, i, 0) {
+ bio_for_each_segment_all(bv, bio, i) {
unsigned this_count = bv->bv_len;
if (likely(PAGE_SIZE == this_count))
diff --git a/fs/exofs/ore_raid.c b/fs/exofs/ore_raid.c
index b963f38ac298..7682b970d0f1 100644
--- a/fs/exofs/ore_raid.c
+++ b/fs/exofs/ore_raid.c
@@ -432,7 +432,7 @@ static void _mark_read4write_pages_uptodate(struct ore_io_state *ios, int ret)
if (!bio)
continue;
- __bio_for_each_segment(bv, bio, i, 0) {
+ bio_for_each_segment_all(bv, bio, i) {
struct page *page = bv->bv_page;
SetPageUptodate(page);
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index fe60cc1117d8..0a87bb10998d 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -31,6 +31,7 @@
#include <linux/mpage.h>
#include <linux/fiemap.h>
#include <linux/namei.h>
+#include <linux/aio.h>
#include "ext2.h"
#include "acl.h"
#include "xip.h"
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index d706dbfa6220..23c712825640 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -27,6 +27,7 @@
#include <linux/writeback.h>
#include <linux/mpage.h>
#include <linux/namei.h>
+#include <linux/aio.h>
#include "ext3.h"
#include "xattr.h"
#include "acl.h"
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 3dc48cc8b6eb..6356665a74bb 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -362,22 +362,19 @@ fail:
/*
* Release the journal device
*/
-static int ext3_blkdev_put(struct block_device *bdev)
+static void ext3_blkdev_put(struct block_device *bdev)
{
- return blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
+ blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
}
-static int ext3_blkdev_remove(struct ext3_sb_info *sbi)
+static void ext3_blkdev_remove(struct ext3_sb_info *sbi)
{
struct block_device *bdev;
- int ret = -ENODEV;
-
bdev = sbi->journal_bdev;
if (bdev) {
- ret = ext3_blkdev_put(bdev);
+ ext3_blkdev_put(bdev);
sbi->journal_bdev = NULL;
}
- return ret;
}
static inline struct inode *orphan_list_entry(struct list_head *l)
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 64848b595b24..4959e29573b6 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -23,6 +23,7 @@
#include <linux/jbd2.h>
#include <linux/mount.h>
#include <linux/path.h>
+#include <linux/aio.h>
#include <linux/quotaops.h>
#include <linux/pagevec.h>
#include "ext4.h"
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
index 98be6f697463..b8d5d351e24f 100644
--- a/fs/ext4/indirect.c
+++ b/fs/ext4/indirect.c
@@ -20,6 +20,7 @@
* (sct@redhat.com), 1993, 1998
*/
+#include <linux/aio.h>
#include "ext4_jbd2.h"
#include "truncate.h"
#include "ext4_extents.h" /* Needed for EXT_MAX_BLOCKS */
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 793d44b84d7f..0723774bdfb5 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -37,6 +37,7 @@
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/ratelimit.h>
+#include <linux/aio.h>
#include "ext4_jbd2.h"
#include "xattr.h"
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 5929cd0baa20..19599bded62a 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -18,6 +18,7 @@
#include <linux/pagevec.h>
#include <linux/mpage.h>
#include <linux/namei.h>
+#include <linux/aio.h>
#include <linux/uio.h>
#include <linux/bio.h>
#include <linux/workqueue.h>
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 24a146bde742..94cc84db7c9a 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -703,22 +703,19 @@ fail:
/*
* Release the journal device
*/
-static int ext4_blkdev_put(struct block_device *bdev)
+static void ext4_blkdev_put(struct block_device *bdev)
{
- return blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
+ blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
}
-static int ext4_blkdev_remove(struct ext4_sb_info *sbi)
+static void ext4_blkdev_remove(struct ext4_sb_info *sbi)
{
struct block_device *bdev;
- int ret = -ENODEV;
-
bdev = sbi->journal_bdev;
if (bdev) {
- ret = ext4_blkdev_put(bdev);
+ ext4_blkdev_put(bdev);
sbi->journal_bdev = NULL;
}
- return ret;
}
static inline struct inode *orphan_list_entry(struct list_head *l)
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 2b6fc131e2ce..b1de01da1a40 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -20,6 +20,7 @@
#include "f2fs.h"
#include "node.h"
#include "segment.h"
+#include <trace/events/f2fs.h>
static struct kmem_cache *orphan_entry_slab;
static struct kmem_cache *inode_entry_slab;
@@ -57,13 +58,19 @@ repeat:
cond_resched();
goto repeat;
}
- if (f2fs_readpage(sbi, page, index, READ_SYNC)) {
+ if (PageUptodate(page))
+ goto out;
+
+ if (f2fs_readpage(sbi, page, index, READ_SYNC))
+ goto repeat;
+
+ lock_page(page);
+ if (page->mapping != mapping) {
f2fs_put_page(page, 1);
goto repeat;
}
+out:
mark_page_accessed(page);
-
- /* We do not allow returning an errorneous page */
return page;
}
@@ -541,54 +548,44 @@ retry:
*/
static void block_operations(struct f2fs_sb_info *sbi)
{
- int t;
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL,
.nr_to_write = LONG_MAX,
.for_reclaim = 0,
};
+ struct blk_plug plug;
- /* Stop renaming operation */
- mutex_lock_op(sbi, RENAME);
- mutex_lock_op(sbi, DENTRY_OPS);
+ blk_start_plug(&plug);
-retry_dents:
- /* write all the dirty dentry pages */
- sync_dirty_dir_inodes(sbi);
+retry_flush_dents:
+ mutex_lock_all(sbi);
- mutex_lock_op(sbi, DATA_WRITE);
+ /* write all the dirty dentry pages */
if (get_pages(sbi, F2FS_DIRTY_DENTS)) {
- mutex_unlock_op(sbi, DATA_WRITE);
- goto retry_dents;
+ mutex_unlock_all(sbi);
+ sync_dirty_dir_inodes(sbi);
+ goto retry_flush_dents;
}
- /* block all the operations */
- for (t = DATA_NEW; t <= NODE_TRUNC; t++)
- mutex_lock_op(sbi, t);
-
- mutex_lock(&sbi->write_inode);
-
/*
* POR: we should ensure that there is no dirty node pages
* until finishing nat/sit flush.
*/
-retry:
- sync_node_pages(sbi, 0, &wbc);
-
- mutex_lock_op(sbi, NODE_WRITE);
+retry_flush_nodes:
+ mutex_lock(&sbi->node_write);
if (get_pages(sbi, F2FS_DIRTY_NODES)) {
- mutex_unlock_op(sbi, NODE_WRITE);
- goto retry;
+ mutex_unlock(&sbi->node_write);
+ sync_node_pages(sbi, 0, &wbc);
+ goto retry_flush_nodes;
}
- mutex_unlock(&sbi->write_inode);
+ blk_finish_plug(&plug);
}
static void unblock_operations(struct f2fs_sb_info *sbi)
{
- int t;
- for (t = NODE_WRITE; t >= RENAME; t--)
- mutex_unlock_op(sbi, t);
+ mutex_unlock(&sbi->node_write);
+ mutex_unlock_all(sbi);
}
static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
@@ -727,9 +724,13 @@ void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
unsigned long long ckpt_ver;
+ trace_f2fs_write_checkpoint(sbi->sb, is_umount, "start block_ops");
+
mutex_lock(&sbi->cp_mutex);
block_operations(sbi);
+ trace_f2fs_write_checkpoint(sbi->sb, is_umount, "finish block_ops");
+
f2fs_submit_bio(sbi, DATA, true);
f2fs_submit_bio(sbi, NODE, true);
f2fs_submit_bio(sbi, META, true);
@@ -746,13 +747,13 @@ void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
flush_nat_entries(sbi);
flush_sit_entries(sbi);
- reset_victim_segmap(sbi);
-
/* unlock all the fs_lock[] in do_checkpoint() */
do_checkpoint(sbi, is_umount);
unblock_operations(sbi);
mutex_unlock(&sbi->cp_mutex);
+
+ trace_f2fs_write_checkpoint(sbi->sb, is_umount, "finish checkpoint");
}
void init_orphan_info(struct f2fs_sb_info *sbi)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 7bd22a201125..91ff93b0b0f4 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -12,6 +12,7 @@
#include <linux/f2fs_fs.h>
#include <linux/buffer_head.h>
#include <linux/mpage.h>
+#include <linux/aio.h>
#include <linux/writeback.h>
#include <linux/backing-dev.h>
#include <linux/blkdev.h>
@@ -21,6 +22,7 @@
#include "f2fs.h"
#include "node.h"
#include "segment.h"
+#include <trace/events/f2fs.h>
/*
* Lock ordering for the change of data block address:
@@ -54,6 +56,8 @@ int reserve_new_block(struct dnode_of_data *dn)
if (!inc_valid_block_count(sbi, dn->inode, 1))
return -ENOSPC;
+ trace_f2fs_reserve_new_block(dn->inode, dn->nid, dn->ofs_in_node);
+
__set_data_blkaddr(dn, NEW_ADDR);
dn->data_blkaddr = NEW_ADDR;
sync_inode_page(dn);
@@ -133,7 +137,7 @@ void update_extent_cache(block_t blk_addr, struct dnode_of_data *dn)
goto end_update;
}
- /* Frone merge */
+ /* Front merge */
if (fofs == start_fofs - 1 && blk_addr == start_blkaddr - 1) {
fi->ext.fofs--;
fi->ext.blk_addr--;
@@ -169,7 +173,7 @@ end_update:
return;
}
-struct page *find_data_page(struct inode *inode, pgoff_t index)
+struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct address_space *mapping = inode->i_mapping;
@@ -183,7 +187,7 @@ struct page *find_data_page(struct inode *inode, pgoff_t index)
f2fs_put_page(page, 0);
set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, index, RDONLY_NODE);
+ err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (err)
return ERR_PTR(err);
f2fs_put_dnode(&dn);
@@ -199,12 +203,20 @@ struct page *find_data_page(struct inode *inode, pgoff_t index)
if (!page)
return ERR_PTR(-ENOMEM);
- err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
- if (err) {
- f2fs_put_page(page, 1);
- return ERR_PTR(err);
+ if (PageUptodate(page)) {
+ unlock_page(page);
+ return page;
+ }
+
+ err = f2fs_readpage(sbi, page, dn.data_blkaddr,
+ sync ? READ_SYNC : READA);
+ if (sync) {
+ wait_on_page_locked(page);
+ if (!PageUptodate(page)) {
+ f2fs_put_page(page, 0);
+ return ERR_PTR(-EIO);
+ }
}
- unlock_page(page);
return page;
}
@@ -222,14 +234,14 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
int err;
set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, index, RDONLY_NODE);
+ err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (err)
return ERR_PTR(err);
f2fs_put_dnode(&dn);
if (dn.data_blkaddr == NULL_ADDR)
return ERR_PTR(-ENOENT);
-
+repeat:
page = grab_cache_page(mapping, index);
if (!page)
return ERR_PTR(-ENOMEM);
@@ -241,9 +253,17 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
BUG_ON(dn.data_blkaddr == NULL_ADDR);
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
- if (err) {
- f2fs_put_page(page, 1);
+ if (err)
return ERR_PTR(err);
+
+ lock_page(page);
+ if (!PageUptodate(page)) {
+ f2fs_put_page(page, 1);
+ return ERR_PTR(-EIO);
+ }
+ if (page->mapping != mapping) {
+ f2fs_put_page(page, 1);
+ goto repeat;
}
return page;
}
@@ -251,6 +271,9 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
/*
* Caller ensures that this data page is never allocated.
* A new zero-filled data page is allocated in the page cache.
+ *
+ * Also, caller should grab and release a mutex by calling mutex_lock_op() and
+ * mutex_unlock_op().
*/
struct page *get_new_data_page(struct inode *inode, pgoff_t index,
bool new_i_size)
@@ -262,7 +285,7 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
int err;
set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, index, 0);
+ err = get_dnode_of_data(&dn, index, ALLOC_NODE);
if (err)
return ERR_PTR(err);
@@ -273,7 +296,7 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
}
}
f2fs_put_dnode(&dn);
-
+repeat:
page = grab_cache_page(mapping, index);
if (!page)
return ERR_PTR(-ENOMEM);
@@ -283,14 +306,21 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
+ SetPageUptodate(page);
} else {
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
- if (err) {
- f2fs_put_page(page, 1);
+ if (err)
return ERR_PTR(err);
+ lock_page(page);
+ if (!PageUptodate(page)) {
+ f2fs_put_page(page, 1);
+ return ERR_PTR(-EIO);
+ }
+ if (page->mapping != mapping) {
+ f2fs_put_page(page, 1);
+ goto repeat;
}
}
- SetPageUptodate(page);
if (new_i_size &&
i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
@@ -325,21 +355,15 @@ static void read_end_io(struct bio *bio, int err)
/*
* Fill the locked page with data located in the block address.
- * Read operation is synchronous, and caller must unlock the page.
+ * Return unlocked page.
*/
int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
block_t blk_addr, int type)
{
struct block_device *bdev = sbi->sb->s_bdev;
- bool sync = (type == READ_SYNC);
struct bio *bio;
- /* This page can be already read by other threads */
- if (PageUptodate(page)) {
- if (!sync)
- unlock_page(page);
- return 0;
- }
+ trace_f2fs_readpage(page, blk_addr, type);
down_read(&sbi->bio_sem);
@@ -354,18 +378,12 @@ int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
kfree(bio->bi_private);
bio_put(bio);
up_read(&sbi->bio_sem);
+ f2fs_put_page(page, 1);
return -EFAULT;
}
submit_bio(type, bio);
up_read(&sbi->bio_sem);
-
- /* wait for read completion if sync */
- if (sync) {
- lock_page(page);
- if (PageError(page))
- return -EIO;
- }
return 0;
}
@@ -387,14 +405,18 @@ static int get_data_block_ro(struct inode *inode, sector_t iblock,
/* Get the page offset from the block offset(iblock) */
pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits));
- if (check_extent_cache(inode, pgofs, bh_result))
+ if (check_extent_cache(inode, pgofs, bh_result)) {
+ trace_f2fs_get_data_block(inode, iblock, bh_result, 0);
return 0;
+ }
/* When reading holes, we need its node page */
set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, pgofs, RDONLY_NODE);
- if (err)
+ err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA);
+ if (err) {
+ trace_f2fs_get_data_block(inode, iblock, bh_result, err);
return (err == -ENOENT) ? 0 : err;
+ }
/* It does not support data allocation */
BUG_ON(create);
@@ -419,6 +441,7 @@ static int get_data_block_ro(struct inode *inode, sector_t iblock,
bh_result->b_size = (i << blkbits);
}
f2fs_put_dnode(&dn);
+ trace_f2fs_get_data_block(inode, iblock, bh_result, 0);
return 0;
}
@@ -437,13 +460,12 @@ static int f2fs_read_data_pages(struct file *file,
int do_write_data_page(struct page *page)
{
struct inode *inode = page->mapping->host;
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
block_t old_blk_addr, new_blk_addr;
struct dnode_of_data dn;
int err = 0;
set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, page->index, RDONLY_NODE);
+ err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
if (err)
return err;
@@ -467,8 +489,6 @@ int do_write_data_page(struct page *page)
write_data_page(inode, page, &dn,
old_blk_addr, &new_blk_addr);
update_extent_cache(new_blk_addr, &dn);
- F2FS_I(inode)->data_version =
- le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver);
}
out_writepage:
f2fs_put_dnode(&dn);
@@ -484,10 +504,11 @@ static int f2fs_write_data_page(struct page *page,
const pgoff_t end_index = ((unsigned long long) i_size)
>> PAGE_CACHE_SHIFT;
unsigned offset;
+ bool need_balance_fs = false;
int err = 0;
if (page->index < end_index)
- goto out;
+ goto write;
/*
* If the offset is out-of-range of file size,
@@ -499,50 +520,46 @@ static int f2fs_write_data_page(struct page *page,
dec_page_count(sbi, F2FS_DIRTY_DENTS);
inode_dec_dirty_dents(inode);
}
- goto unlock_out;
+ goto out;
}
zero_user_segment(page, offset, PAGE_CACHE_SIZE);
-out:
- if (sbi->por_doing)
- goto redirty_out;
-
- if (wbc->for_reclaim && !S_ISDIR(inode->i_mode) && !is_cold_data(page))
+write:
+ if (sbi->por_doing) {
+ err = AOP_WRITEPAGE_ACTIVATE;
goto redirty_out;
+ }
- mutex_lock_op(sbi, DATA_WRITE);
+ /* Dentry blocks are controlled by checkpoint */
if (S_ISDIR(inode->i_mode)) {
dec_page_count(sbi, F2FS_DIRTY_DENTS);
inode_dec_dirty_dents(inode);
+ err = do_write_data_page(page);
+ } else {
+ int ilock = mutex_lock_op(sbi);
+ err = do_write_data_page(page);
+ mutex_unlock_op(sbi, ilock);
+ need_balance_fs = true;
}
- err = do_write_data_page(page);
- if (err && err != -ENOENT) {
- wbc->pages_skipped++;
- set_page_dirty(page);
- }
- mutex_unlock_op(sbi, DATA_WRITE);
+ if (err == -ENOENT)
+ goto out;
+ else if (err)
+ goto redirty_out;
if (wbc->for_reclaim)
f2fs_submit_bio(sbi, DATA, true);
- if (err == -ENOENT)
- goto unlock_out;
-
clear_cold_data(page);
+out:
unlock_page(page);
-
- if (!wbc->for_reclaim && !S_ISDIR(inode->i_mode))
+ if (need_balance_fs)
f2fs_balance_fs(sbi);
return 0;
-unlock_out:
- unlock_page(page);
- return (err == -ENOENT) ? 0 : err;
-
redirty_out:
wbc->pages_skipped++;
set_page_dirty(page);
- return AOP_WRITEPAGE_ACTIVATE;
+ return err;
}
#define MAX_DESIRED_PAGES_WP 4096
@@ -561,19 +578,26 @@ static int f2fs_write_data_pages(struct address_space *mapping,
{
struct inode *inode = mapping->host;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+ bool locked = false;
int ret;
long excess_nrtw = 0, desired_nrtw;
+ /* deal with chardevs and other special file */
+ if (!mapping->a_ops->writepage)
+ return 0;
+
if (wbc->nr_to_write < MAX_DESIRED_PAGES_WP) {
desired_nrtw = MAX_DESIRED_PAGES_WP;
excess_nrtw = desired_nrtw - wbc->nr_to_write;
wbc->nr_to_write = desired_nrtw;
}
- if (!S_ISDIR(inode->i_mode))
+ if (!S_ISDIR(inode->i_mode)) {
mutex_lock(&sbi->writepages);
+ locked = true;
+ }
ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
- if (!S_ISDIR(inode->i_mode))
+ if (locked)
mutex_unlock(&sbi->writepages);
f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL));
@@ -593,39 +617,33 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT;
struct dnode_of_data dn;
int err = 0;
+ int ilock;
/* for nobh_write_end */
*fsdata = NULL;
f2fs_balance_fs(sbi);
-
+repeat:
page = grab_cache_page_write_begin(mapping, index, flags);
if (!page)
return -ENOMEM;
*pagep = page;
- mutex_lock_op(sbi, DATA_NEW);
+ ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, index, 0);
- if (err) {
- mutex_unlock_op(sbi, DATA_NEW);
- f2fs_put_page(page, 1);
- return err;
- }
+ err = get_dnode_of_data(&dn, index, ALLOC_NODE);
+ if (err)
+ goto err;
- if (dn.data_blkaddr == NULL_ADDR) {
+ if (dn.data_blkaddr == NULL_ADDR)
err = reserve_new_block(&dn);
- if (err) {
- f2fs_put_dnode(&dn);
- mutex_unlock_op(sbi, DATA_NEW);
- f2fs_put_page(page, 1);
- return err;
- }
- }
+
f2fs_put_dnode(&dn);
+ if (err)
+ goto err;
- mutex_unlock_op(sbi, DATA_NEW);
+ mutex_unlock_op(sbi, ilock);
if ((len == PAGE_CACHE_SIZE) || PageUptodate(page))
return 0;
@@ -636,21 +654,34 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
/* Reading beyond i_size is simple: memset to zero */
zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE);
- return 0;
+ goto out;
}
if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
} else {
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
- if (err) {
- f2fs_put_page(page, 1);
+ if (err)
return err;
+ lock_page(page);
+ if (!PageUptodate(page)) {
+ f2fs_put_page(page, 1);
+ return -EIO;
+ }
+ if (page->mapping != mapping) {
+ f2fs_put_page(page, 1);
+ goto repeat;
}
}
+out:
SetPageUptodate(page);
clear_cold_data(page);
return 0;
+
+err:
+ mutex_unlock_op(sbi, ilock);
+ f2fs_put_page(page, 1);
+ return err;
}
static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
@@ -681,7 +712,7 @@ static void f2fs_invalidate_data_page(struct page *page, unsigned long offset)
static int f2fs_release_data_page(struct page *page, gfp_t wait)
{
ClearPagePrivate(page);
- return 0;
+ return 1;
}
static int f2fs_set_data_page_dirty(struct page *page)
diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c
index 025b9e2f935d..8d9943786c31 100644
--- a/fs/f2fs/debug.c
+++ b/fs/f2fs/debug.c
@@ -13,7 +13,6 @@
#include <linux/fs.h>
#include <linux/backing-dev.h>
-#include <linux/proc_fs.h>
#include <linux/f2fs_fs.h>
#include <linux/blkdev.h>
#include <linux/debugfs.h>
@@ -106,7 +105,7 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
}
}
mutex_unlock(&sit_i->sentry_lock);
- dist = sbi->total_sections * hblks_per_sec * hblks_per_sec / 100;
+ dist = TOTAL_SECS(sbi) * hblks_per_sec * hblks_per_sec / 100;
si->bimodal = bimodal / dist;
if (si->dirty_count)
si->avg_vblocks = total_vblocks / ndirty;
@@ -138,14 +137,13 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
si->base_mem += f2fs_bitmap_size(TOTAL_SEGS(sbi));
si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * TOTAL_SEGS(sbi);
if (sbi->segs_per_sec > 1)
- si->base_mem += sbi->total_sections *
- sizeof(struct sec_entry);
+ si->base_mem += TOTAL_SECS(sbi) * sizeof(struct sec_entry);
si->base_mem += __bitmap_size(sbi, SIT_BITMAP);
/* build free segmap */
si->base_mem += sizeof(struct free_segmap_info);
si->base_mem += f2fs_bitmap_size(TOTAL_SEGS(sbi));
- si->base_mem += f2fs_bitmap_size(sbi->total_sections);
+ si->base_mem += f2fs_bitmap_size(TOTAL_SECS(sbi));
/* build curseg */
si->base_mem += sizeof(struct curseg_info) * NR_CURSEG_TYPE;
@@ -154,7 +152,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
/* build dirty segmap */
si->base_mem += sizeof(struct dirty_seglist_info);
si->base_mem += NR_DIRTY_TYPE * f2fs_bitmap_size(TOTAL_SEGS(sbi));
- si->base_mem += 2 * f2fs_bitmap_size(TOTAL_SEGS(sbi));
+ si->base_mem += f2fs_bitmap_size(TOTAL_SECS(sbi));
/* buld nm */
si->base_mem += sizeof(struct f2fs_nm_info);
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 1be948768e2f..1ac6b93036b7 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -148,7 +148,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
for (; bidx < end_block; bidx++) {
/* no need to allocate new dentry pages to all the indices */
- dentry_page = find_data_page(dir, bidx);
+ dentry_page = find_data_page(dir, bidx, true);
if (IS_ERR(dentry_page)) {
room = true;
continue;
@@ -189,6 +189,9 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
unsigned int max_depth;
unsigned int level;
+ if (namelen > F2FS_NAME_LEN)
+ return NULL;
+
if (npages == 0)
return NULL;
@@ -246,9 +249,6 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr)
void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
struct page *page, struct inode *inode)
{
- struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
-
- mutex_lock_op(sbi, DENTRY_OPS);
lock_page(page);
wait_on_page_writeback(page);
de->ino = cpu_to_le32(inode->i_ino);
@@ -262,7 +262,6 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
F2FS_I(inode)->i_pino = dir->i_ino;
f2fs_put_page(page, 1);
- mutex_unlock_op(sbi, DENTRY_OPS);
}
void init_dent_inode(const struct qstr *name, struct page *ipage)
@@ -281,6 +280,43 @@ void init_dent_inode(const struct qstr *name, struct page *ipage)
set_page_dirty(ipage);
}
+static int make_empty_dir(struct inode *inode, struct inode *parent)
+{
+ struct page *dentry_page;
+ struct f2fs_dentry_block *dentry_blk;
+ struct f2fs_dir_entry *de;
+ void *kaddr;
+
+ dentry_page = get_new_data_page(inode, 0, true);
+ if (IS_ERR(dentry_page))
+ return PTR_ERR(dentry_page);
+
+ kaddr = kmap_atomic(dentry_page);
+ dentry_blk = (struct f2fs_dentry_block *)kaddr;
+
+ de = &dentry_blk->dentry[0];
+ de->name_len = cpu_to_le16(1);
+ de->hash_code = 0;
+ de->ino = cpu_to_le32(inode->i_ino);
+ memcpy(dentry_blk->filename[0], ".", 1);
+ set_de_type(de, inode);
+
+ de = &dentry_blk->dentry[1];
+ de->hash_code = 0;
+ de->name_len = cpu_to_le16(2);
+ de->ino = cpu_to_le32(parent->i_ino);
+ memcpy(dentry_blk->filename[1], "..", 2);
+ set_de_type(de, inode);
+
+ test_and_set_bit_le(0, &dentry_blk->dentry_bitmap);
+ test_and_set_bit_le(1, &dentry_blk->dentry_bitmap);
+ kunmap_atomic(kaddr);
+
+ set_page_dirty(dentry_page);
+ f2fs_put_page(dentry_page, 1);
+ return 0;
+}
+
static int init_inode_metadata(struct inode *inode,
struct inode *dir, const struct qstr *name)
{
@@ -291,7 +327,7 @@ static int init_inode_metadata(struct inode *inode,
return err;
if (S_ISDIR(inode->i_mode)) {
- err = f2fs_make_empty(inode, dir);
+ err = make_empty_dir(inode, dir);
if (err) {
remove_inode_page(inode);
return err;
@@ -314,7 +350,7 @@ static int init_inode_metadata(struct inode *inode,
}
if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) {
inc_nlink(inode);
- f2fs_write_inode(inode, NULL);
+ update_inode_page(inode);
}
return 0;
}
@@ -338,7 +374,7 @@ static void update_parent_metadata(struct inode *dir, struct inode *inode,
}
if (need_dir_update)
- f2fs_write_inode(dir, NULL);
+ update_inode_page(dir);
else
mark_inode_dirty(dir);
@@ -370,6 +406,10 @@ next:
goto next;
}
+/*
+ * Caller should grab and release a mutex by calling mutex_lock_op() and
+ * mutex_unlock_op().
+ */
int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *inode)
{
unsigned int bit_pos;
@@ -379,7 +419,6 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in
f2fs_hash_t dentry_hash;
struct f2fs_dir_entry *de;
unsigned int nbucket, nblock;
- struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
size_t namelen = name->len;
struct page *dentry_page = NULL;
struct f2fs_dentry_block *dentry_blk = NULL;
@@ -409,12 +448,9 @@ start:
bidx = dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket));
for (block = bidx; block <= (bidx + nblock - 1); block++) {
- mutex_lock_op(sbi, DENTRY_OPS);
dentry_page = get_new_data_page(dir, block, true);
- if (IS_ERR(dentry_page)) {
- mutex_unlock_op(sbi, DENTRY_OPS);
+ if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page);
- }
dentry_blk = kmap(dentry_page);
bit_pos = room_for_filename(dentry_blk, slots);
@@ -423,7 +459,6 @@ start:
kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
- mutex_unlock_op(sbi, DENTRY_OPS);
}
/* Move to next level to find the empty slot for new dentry */
@@ -453,7 +488,6 @@ add_dentry:
fail:
kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
- mutex_unlock_op(sbi, DENTRY_OPS);
return err;
}
@@ -473,8 +507,6 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
void *kaddr = page_address(page);
int i;
- mutex_lock_op(sbi, DENTRY_OPS);
-
lock_page(page);
wait_on_page_writeback(page);
@@ -494,7 +526,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
if (inode && S_ISDIR(inode->i_mode)) {
drop_nlink(dir);
- f2fs_write_inode(dir, NULL);
+ update_inode_page(dir);
} else {
mark_inode_dirty(dir);
}
@@ -506,7 +538,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
drop_nlink(inode);
i_size_write(inode, 0);
}
- f2fs_write_inode(inode, NULL);
+ update_inode_page(inode);
+
if (inode->i_nlink == 0)
add_orphan_inode(sbi, inode->i_ino);
}
@@ -519,45 +552,6 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
inode_dec_dirty_dents(dir);
}
f2fs_put_page(page, 1);
-
- mutex_unlock_op(sbi, DENTRY_OPS);
-}
-
-int f2fs_make_empty(struct inode *inode, struct inode *parent)
-{
- struct page *dentry_page;
- struct f2fs_dentry_block *dentry_blk;
- struct f2fs_dir_entry *de;
- void *kaddr;
-
- dentry_page = get_new_data_page(inode, 0, true);
- if (IS_ERR(dentry_page))
- return PTR_ERR(dentry_page);
-
- kaddr = kmap_atomic(dentry_page);
- dentry_blk = (struct f2fs_dentry_block *)kaddr;
-
- de = &dentry_blk->dentry[0];
- de->name_len = cpu_to_le16(1);
- de->hash_code = f2fs_dentry_hash(".", 1);
- de->ino = cpu_to_le32(inode->i_ino);
- memcpy(dentry_blk->filename[0], ".", 1);
- set_de_type(de, inode);
-
- de = &dentry_blk->dentry[1];
- de->hash_code = f2fs_dentry_hash("..", 2);
- de->name_len = cpu_to_le16(2);
- de->ino = cpu_to_le32(parent->i_ino);
- memcpy(dentry_blk->filename[1], "..", 2);
- set_de_type(de, inode);
-
- test_and_set_bit_le(0, &dentry_blk->dentry_bitmap);
- test_and_set_bit_le(1, &dentry_blk->dentry_bitmap);
- kunmap_atomic(kaddr);
-
- set_page_dirty(dentry_page);
- f2fs_put_page(dentry_page, 1);
- return 0;
}
bool f2fs_empty_dir(struct inode *dir)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 201c8d3b0f86..20aab02f2a42 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -125,11 +125,15 @@ static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i)
* file keeping -1 as its node offset to
* distinguish from index node blocks.
*/
-#define RDONLY_NODE 1 /*
- * specify a read-only mode when getting
- * a node block. 0 is read-write mode.
- * used by get_dnode_of_data().
+enum {
+ ALLOC_NODE, /* allocate a new node page if needed */
+ LOOKUP_NODE, /* look up a node without readahead */
+ LOOKUP_NODE_RA, /*
+ * look up a node with readahead called
+ * by get_datablock_ro.
*/
+};
+
#define F2FS_LINK_MAX 32000 /* maximum link count per file */
/* for in-memory extent cache entry */
@@ -144,6 +148,7 @@ struct extent_info {
* i_advise uses FADVISE_XXX_BIT. We can add additional hints later.
*/
#define FADVISE_COLD_BIT 0x01
+#define FADVISE_CP_BIT 0x02
struct f2fs_inode_info {
struct inode vfs_inode; /* serve a vfs inode */
@@ -155,7 +160,6 @@ struct f2fs_inode_info {
/* Use below internally in f2fs*/
unsigned long flags; /* use to pass per-file flags */
- unsigned long long data_version;/* latest version of data for fsync */
atomic_t dirty_dents; /* # of dirty dentry pages */
f2fs_hash_t chash; /* hash value of given file name */
unsigned int clevel; /* maximum level of given file name */
@@ -186,7 +190,6 @@ static inline void set_raw_extent(struct extent_info *ext,
struct f2fs_nm_info {
block_t nat_blkaddr; /* base disk address of NAT */
nid_t max_nid; /* maximum possible node ids */
- nid_t init_scan_nid; /* the first nid to be scanned */
nid_t next_scan_nid; /* the next nid to be scanned */
/* NAT cache management */
@@ -305,23 +308,12 @@ enum count_type {
};
/*
- * FS_LOCK nesting subclasses for the lock validator:
- *
- * The locking order between these classes is
- * RENAME -> DENTRY_OPS -> DATA_WRITE -> DATA_NEW
- * -> DATA_TRUNC -> NODE_WRITE -> NODE_NEW -> NODE_TRUNC
+ * Uses as sbi->fs_lock[NR_GLOBAL_LOCKS].
+ * The checkpoint procedure blocks all the locks in this fs_lock array.
+ * Some FS operations grab free locks, and if there is no free lock,
+ * then wait to grab a lock in a round-robin manner.
*/
-enum lock_type {
- RENAME, /* for renaming operations */
- DENTRY_OPS, /* for directory operations */
- DATA_WRITE, /* for data write */
- DATA_NEW, /* for data allocation */
- DATA_TRUNC, /* for data truncate */
- NODE_NEW, /* for node allocation */
- NODE_TRUNC, /* for node truncate */
- NODE_WRITE, /* for node write */
- NR_LOCK_TYPE,
-};
+#define NR_GLOBAL_LOCKS 8
/*
* The below are the page types of bios used in submti_bio().
@@ -361,11 +353,13 @@ struct f2fs_sb_info {
/* for checkpoint */
struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */
struct inode *meta_inode; /* cache meta blocks */
- struct mutex cp_mutex; /* for checkpoint procedure */
- struct mutex fs_lock[NR_LOCK_TYPE]; /* for blocking FS operations */
- struct mutex write_inode; /* mutex for write inode */
+ struct mutex cp_mutex; /* checkpoint procedure lock */
+ struct mutex fs_lock[NR_GLOBAL_LOCKS]; /* blocking FS operations */
+ struct mutex node_write; /* locking node writes */
struct mutex writepages; /* mutex for writepages() */
+ unsigned char next_lock_num; /* round-robin global locks */
int por_doing; /* recovery is doing or not */
+ int on_build_free_nids; /* build_free_nids is doing */
/* for orphan inode management */
struct list_head orphan_inode_list; /* orphan inode list */
@@ -406,6 +400,7 @@ struct f2fs_sb_info {
/* for cleaning operations */
struct mutex gc_mutex; /* mutex for GC */
struct f2fs_gc_kthread *gc_thread; /* GC thread */
+ unsigned int cur_victim_sec; /* current victim section num */
/*
* for stat information.
@@ -498,22 +493,51 @@ static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f)
cp->ckpt_flags = cpu_to_le32(ckpt_flags);
}
-static inline void mutex_lock_op(struct f2fs_sb_info *sbi, enum lock_type t)
+static inline void mutex_lock_all(struct f2fs_sb_info *sbi)
{
- mutex_lock_nested(&sbi->fs_lock[t], t);
+ int i = 0;
+ for (; i < NR_GLOBAL_LOCKS; i++)
+ mutex_lock(&sbi->fs_lock[i]);
}
-static inline void mutex_unlock_op(struct f2fs_sb_info *sbi, enum lock_type t)
+static inline void mutex_unlock_all(struct f2fs_sb_info *sbi)
{
- mutex_unlock(&sbi->fs_lock[t]);
+ int i = 0;
+ for (; i < NR_GLOBAL_LOCKS; i++)
+ mutex_unlock(&sbi->fs_lock[i]);
+}
+
+static inline int mutex_lock_op(struct f2fs_sb_info *sbi)
+{
+ unsigned char next_lock = sbi->next_lock_num % NR_GLOBAL_LOCKS;
+ int i = 0;
+
+ for (; i < NR_GLOBAL_LOCKS; i++)
+ if (mutex_trylock(&sbi->fs_lock[i]))
+ return i;
+
+ mutex_lock(&sbi->fs_lock[next_lock]);
+ sbi->next_lock_num++;
+ return next_lock;
+}
+
+static inline void mutex_unlock_op(struct f2fs_sb_info *sbi, int ilock)
+{
+ if (ilock < 0)
+ return;
+ BUG_ON(ilock >= NR_GLOBAL_LOCKS);
+ mutex_unlock(&sbi->fs_lock[ilock]);
}
/*
* Check whether the given nid is within node id range.
*/
-static inline void check_nid_range(struct f2fs_sb_info *sbi, nid_t nid)
+static inline int check_nid_range(struct f2fs_sb_info *sbi, nid_t nid)
{
- BUG_ON((nid >= NM_I(sbi)->max_nid));
+ WARN_ON((nid >= NM_I(sbi)->max_nid));
+ if (nid >= NM_I(sbi)->max_nid)
+ return -EINVAL;
+ return 0;
}
#define F2FS_DEFAULT_ALLOCATED_BLOCKS 1
@@ -819,7 +843,6 @@ static inline int f2fs_clear_bit(unsigned int nr, char *addr)
/* used for f2fs_inode_info->flags */
enum {
FI_NEW_INODE, /* indicate newly allocated inode */
- FI_NEED_CP, /* need to do checkpoint during fsync */
FI_INC_LINK, /* need to increment i_nlink */
FI_ACL_MODE, /* indicate acl mode */
FI_NO_ALLOC, /* should not allocate any blocks */
@@ -872,6 +895,7 @@ long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long);
void f2fs_set_inode_flags(struct inode *);
struct inode *f2fs_iget(struct super_block *, unsigned long);
void update_inode(struct inode *, struct page *);
+int update_inode_page(struct inode *);
int f2fs_write_inode(struct inode *, struct writeback_control *);
void f2fs_evict_inode(struct inode *);
@@ -973,7 +997,6 @@ int lookup_journal_in_cursum(struct f2fs_summary_block *,
int, unsigned int, int);
void flush_sit_entries(struct f2fs_sb_info *);
int build_segment_manager(struct f2fs_sb_info *);
-void reset_victim_segmap(struct f2fs_sb_info *);
void destroy_segment_manager(struct f2fs_sb_info *);
/*
@@ -1000,7 +1023,7 @@ void destroy_checkpoint_caches(void);
*/
int reserve_new_block(struct dnode_of_data *);
void update_extent_cache(block_t, struct dnode_of_data *);
-struct page *find_data_page(struct inode *, pgoff_t);
+struct page *find_data_page(struct inode *, pgoff_t, bool);
struct page *get_lock_data_page(struct inode *, pgoff_t);
struct page *get_new_data_page(struct inode *, pgoff_t, bool);
int f2fs_readpage(struct f2fs_sb_info *, struct page *, block_t, int);
@@ -1020,7 +1043,7 @@ void destroy_gc_caches(void);
/*
* recovery.c
*/
-void recover_fsync_data(struct f2fs_sb_info *);
+int recover_fsync_data(struct f2fs_sb_info *);
bool space_for_roll_forward(struct f2fs_sb_info *);
/*
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index db626282d424..1cae864f8dfc 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -13,6 +13,7 @@
#include <linux/stat.h>
#include <linux/buffer_head.h>
#include <linux/writeback.h>
+#include <linux/blkdev.h>
#include <linux/falloc.h>
#include <linux/types.h>
#include <linux/compat.h>
@@ -24,6 +25,7 @@
#include "segment.h"
#include "xattr.h"
#include "acl.h"
+#include <trace/events/f2fs.h>
static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
struct vm_fault *vmf)
@@ -33,19 +35,18 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
block_t old_blk_addr;
struct dnode_of_data dn;
- int err;
+ int err, ilock;
f2fs_balance_fs(sbi);
sb_start_pagefault(inode->i_sb);
- mutex_lock_op(sbi, DATA_NEW);
-
/* block allocation */
+ ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, page->index, 0);
+ err = get_dnode_of_data(&dn, page->index, ALLOC_NODE);
if (err) {
- mutex_unlock_op(sbi, DATA_NEW);
+ mutex_unlock_op(sbi, ilock);
goto out;
}
@@ -55,13 +56,12 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
err = reserve_new_block(&dn);
if (err) {
f2fs_put_dnode(&dn);
- mutex_unlock_op(sbi, DATA_NEW);
+ mutex_unlock_op(sbi, ilock);
goto out;
}
}
f2fs_put_dnode(&dn);
-
- mutex_unlock_op(sbi, DATA_NEW);
+ mutex_unlock_op(sbi, ilock);
lock_page(page);
if (page->mapping != inode->i_mapping ||
@@ -102,28 +102,10 @@ static const struct vm_operations_struct f2fs_file_vm_ops = {
.remap_pages = generic_file_remap_pages,
};
-static int need_to_sync_dir(struct f2fs_sb_info *sbi, struct inode *inode)
-{
- struct dentry *dentry;
- nid_t pino;
-
- inode = igrab(inode);
- dentry = d_find_any_alias(inode);
- if (!dentry) {
- iput(inode);
- return 0;
- }
- pino = dentry->d_parent->d_inode->i_ino;
- dput(dentry);
- iput(inode);
- return !is_checkpointed_node(sbi, pino);
-}
-
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
struct inode *inode = file->f_mapping->host;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- unsigned long long cur_version;
int ret = 0;
bool need_cp = false;
struct writeback_control wbc = {
@@ -135,9 +117,12 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
if (inode->i_sb->s_flags & MS_RDONLY)
return 0;
+ trace_f2fs_sync_file_enter(inode);
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
- if (ret)
+ if (ret) {
+ trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
return ret;
+ }
/* guarantee free sections for fsync */
f2fs_balance_fs(sbi);
@@ -147,28 +132,18 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
goto out;
- mutex_lock(&sbi->cp_mutex);
- cur_version = le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver);
- mutex_unlock(&sbi->cp_mutex);
-
- if (F2FS_I(inode)->data_version != cur_version &&
- !(inode->i_state & I_DIRTY))
- goto out;
- F2FS_I(inode)->data_version--;
-
if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
need_cp = true;
- else if (is_inode_flag_set(F2FS_I(inode), FI_NEED_CP))
+ else if (is_cp_file(inode))
need_cp = true;
else if (!space_for_roll_forward(sbi))
need_cp = true;
- else if (need_to_sync_dir(sbi, inode))
+ else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino))
need_cp = true;
if (need_cp) {
/* all the dirty node pages should be flushed for POR */
ret = f2fs_sync_fs(inode->i_sb, 1);
- clear_inode_flag(F2FS_I(inode), FI_NEED_CP);
} else {
/* if there is no written node page, write its inode page */
while (!sync_node_pages(sbi, inode->i_ino, &wbc)) {
@@ -178,9 +153,11 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
}
filemap_fdatawait_range(sbi->node_inode->i_mapping,
0, LONG_MAX);
+ ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
}
out:
mutex_unlock(&inode->i_mutex);
+ trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
return ret;
}
@@ -216,6 +193,9 @@ static int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
sync_inode_page(dn);
}
dn->ofs_in_node = ofs;
+
+ trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid,
+ dn->ofs_in_node, nr_free);
return nr_free;
}
@@ -232,11 +212,15 @@ static void truncate_partial_data_page(struct inode *inode, u64 from)
if (!offset)
return;
- page = find_data_page(inode, from >> PAGE_CACHE_SHIFT);
+ page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false);
if (IS_ERR(page))
return;
lock_page(page);
+ if (page->mapping != inode->i_mapping) {
+ f2fs_put_page(page, 1);
+ return;
+ }
wait_on_page_writeback(page);
zero_user(page, offset, PAGE_CACHE_SIZE - offset);
set_page_dirty(page);
@@ -249,20 +233,22 @@ static int truncate_blocks(struct inode *inode, u64 from)
unsigned int blocksize = inode->i_sb->s_blocksize;
struct dnode_of_data dn;
pgoff_t free_from;
- int count = 0;
+ int count = 0, ilock = -1;
int err;
+ trace_f2fs_truncate_blocks_enter(inode, from);
+
free_from = (pgoff_t)
((from + blocksize - 1) >> (sbi->log_blocksize));
- mutex_lock_op(sbi, DATA_TRUNC);
-
+ ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, free_from, RDONLY_NODE);
+ err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE);
if (err) {
if (err == -ENOENT)
goto free_next;
- mutex_unlock_op(sbi, DATA_TRUNC);
+ mutex_unlock_op(sbi, ilock);
+ trace_f2fs_truncate_blocks_exit(inode, err);
return err;
}
@@ -273,6 +259,7 @@ static int truncate_blocks(struct inode *inode, u64 from)
count -= dn.ofs_in_node;
BUG_ON(count < 0);
+
if (dn.ofs_in_node || IS_INODE(dn.node_page)) {
truncate_data_blocks_range(&dn, count);
free_from += count;
@@ -281,11 +268,12 @@ static int truncate_blocks(struct inode *inode, u64 from)
f2fs_put_dnode(&dn);
free_next:
err = truncate_inode_blocks(inode, free_from);
- mutex_unlock_op(sbi, DATA_TRUNC);
+ mutex_unlock_op(sbi, ilock);
/* lastly zero out the first data page */
truncate_partial_data_page(inode, from);
+ trace_f2fs_truncate_blocks_exit(inode, err);
return err;
}
@@ -295,6 +283,8 @@ void f2fs_truncate(struct inode *inode)
S_ISLNK(inode->i_mode)))
return;
+ trace_f2fs_truncate(inode);
+
if (!truncate_blocks(inode, i_size_read(inode))) {
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
@@ -389,15 +379,16 @@ static void fill_zero(struct inode *inode, pgoff_t index,
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct page *page;
+ int ilock;
if (!len)
return;
f2fs_balance_fs(sbi);
- mutex_lock_op(sbi, DATA_NEW);
+ ilock = mutex_lock_op(sbi);
page = get_new_data_page(inode, index, false);
- mutex_unlock_op(sbi, DATA_NEW);
+ mutex_unlock_op(sbi, ilock);
if (!IS_ERR(page)) {
wait_on_page_writeback(page);
@@ -414,15 +405,10 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
for (index = pg_start; index < pg_end; index++) {
struct dnode_of_data dn;
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
-
- f2fs_balance_fs(sbi);
- mutex_lock_op(sbi, DATA_TRUNC);
set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, index, RDONLY_NODE);
+ err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (err) {
- mutex_unlock_op(sbi, DATA_TRUNC);
if (err == -ENOENT)
continue;
return err;
@@ -431,7 +417,6 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
if (dn.data_blkaddr != NULL_ADDR)
truncate_data_blocks_range(&dn, 1);
f2fs_put_dnode(&dn);
- mutex_unlock_op(sbi, DATA_TRUNC);
}
return 0;
}
@@ -461,12 +446,19 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len, int mode)
if (pg_start < pg_end) {
struct address_space *mapping = inode->i_mapping;
loff_t blk_start, blk_end;
+ struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+ int ilock;
+
+ f2fs_balance_fs(sbi);
blk_start = pg_start << PAGE_CACHE_SHIFT;
blk_end = pg_end << PAGE_CACHE_SHIFT;
truncate_inode_pages_range(mapping, blk_start,
blk_end - 1);
+
+ ilock = mutex_lock_op(sbi);
ret = truncate_hole(inode, pg_start, pg_end);
+ mutex_unlock_op(sbi, ilock);
}
}
@@ -500,13 +492,13 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
for (index = pg_start; index <= pg_end; index++) {
struct dnode_of_data dn;
+ int ilock;
- mutex_lock_op(sbi, DATA_NEW);
-
+ ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0);
- ret = get_dnode_of_data(&dn, index, 0);
+ ret = get_dnode_of_data(&dn, index, ALLOC_NODE);
if (ret) {
- mutex_unlock_op(sbi, DATA_NEW);
+ mutex_unlock_op(sbi, ilock);
break;
}
@@ -514,13 +506,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
ret = reserve_new_block(&dn);
if (ret) {
f2fs_put_dnode(&dn);
- mutex_unlock_op(sbi, DATA_NEW);
+ mutex_unlock_op(sbi, ilock);
break;
}
}
f2fs_put_dnode(&dn);
-
- mutex_unlock_op(sbi, DATA_NEW);
+ mutex_unlock_op(sbi, ilock);
if (pg_start == pg_end)
new_size = offset + len;
@@ -559,6 +550,7 @@ static long f2fs_fallocate(struct file *file, int mode,
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
}
+ trace_f2fs_fallocate(inode, mode, offset, len, ret);
return ret;
}
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 2e3eb2d4fc30..14961593e93c 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -11,7 +11,6 @@
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/backing-dev.h>
-#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/f2fs_fs.h>
#include <linux/kthread.h>
@@ -23,6 +22,7 @@
#include "node.h"
#include "segment.h"
#include "gc.h"
+#include <trace/events/f2fs.h>
static struct kmem_cache *winode_slab;
@@ -81,9 +81,6 @@ static int gc_thread_func(void *data)
/* if return value is not zero, no victim was selected */
if (f2fs_gc(sbi))
wait_ms = GC_THREAD_NOGC_SLEEP_TIME;
- else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME)
- wait_ms = GC_THREAD_MAX_SLEEP_TIME;
-
} while (!kthread_should_stop());
return 0;
}
@@ -131,7 +128,7 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type,
{
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
- if (p->alloc_mode) {
+ if (p->alloc_mode == SSR) {
p->gc_mode = GC_GREEDY;
p->dirty_segmap = dirty_i->dirty_segmap[type];
p->ofs_unit = 1;
@@ -160,18 +157,21 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi,
static unsigned int check_bg_victims(struct f2fs_sb_info *sbi)
{
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
- unsigned int segno;
+ unsigned int hint = 0;
+ unsigned int secno;
/*
* If the gc_type is FG_GC, we can select victim segments
* selected by background GC before.
* Those segments guarantee they have small valid blocks.
*/
- segno = find_next_bit(dirty_i->victim_segmap[BG_GC],
- TOTAL_SEGS(sbi), 0);
- if (segno < TOTAL_SEGS(sbi)) {
- clear_bit(segno, dirty_i->victim_segmap[BG_GC]);
- return segno;
+next:
+ secno = find_next_bit(dirty_i->victim_secmap, TOTAL_SECS(sbi), hint++);
+ if (secno < TOTAL_SECS(sbi)) {
+ if (sec_usage_check(sbi, secno))
+ goto next;
+ clear_bit(secno, dirty_i->victim_secmap);
+ return secno * sbi->segs_per_sec;
}
return NULL_SEGNO;
}
@@ -234,7 +234,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
{
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
struct victim_sel_policy p;
- unsigned int segno;
+ unsigned int secno;
int nsearched = 0;
p.alloc_mode = alloc_mode;
@@ -253,6 +253,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
while (1) {
unsigned long cost;
+ unsigned int segno;
segno = find_next_bit(p.dirty_segmap,
TOTAL_SEGS(sbi), p.offset);
@@ -265,13 +266,11 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
break;
}
p.offset = ((segno / p.ofs_unit) * p.ofs_unit) + p.ofs_unit;
+ secno = GET_SECNO(sbi, segno);
- if (test_bit(segno, dirty_i->victim_segmap[FG_GC]))
- continue;
- if (gc_type == BG_GC &&
- test_bit(segno, dirty_i->victim_segmap[BG_GC]))
+ if (sec_usage_check(sbi, secno))
continue;
- if (IS_CURSEC(sbi, GET_SECNO(sbi, segno)))
+ if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
continue;
cost = get_gc_cost(sbi, segno, &p);
@@ -291,13 +290,18 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
}
got_it:
if (p.min_segno != NULL_SEGNO) {
- *result = (p.min_segno / p.ofs_unit) * p.ofs_unit;
if (p.alloc_mode == LFS) {
- int i;
- for (i = 0; i < p.ofs_unit; i++)
- set_bit(*result + i,
- dirty_i->victim_segmap[gc_type]);
+ secno = GET_SECNO(sbi, p.min_segno);
+ if (gc_type == FG_GC)
+ sbi->cur_victim_sec = secno;
+ else
+ set_bit(secno, dirty_i->victim_secmap);
}
+ *result = (p.min_segno / p.ofs_unit) * p.ofs_unit;
+
+ trace_f2fs_get_victim(sbi->sb, type, gc_type, &p,
+ sbi->cur_victim_sec,
+ prefree_segments(sbi), free_segments(sbi));
}
mutex_unlock(&dirty_i->seglist_lock);
@@ -381,6 +385,7 @@ static void gc_node_segment(struct f2fs_sb_info *sbi,
next_step:
entry = sum;
+
for (off = 0; off < sbi->blocks_per_seg; off++, entry++) {
nid_t nid = le32_to_cpu(entry->nid);
struct page *node_page;
@@ -401,11 +406,18 @@ next_step:
continue;
/* set page dirty and write it */
- if (!PageWriteback(node_page))
+ if (gc_type == FG_GC) {
+ f2fs_submit_bio(sbi, NODE, true);
+ wait_on_page_writeback(node_page);
set_page_dirty(node_page);
+ } else {
+ if (!PageWriteback(node_page))
+ set_page_dirty(node_page);
+ }
f2fs_put_page(node_page, 1);
stat_inc_node_blk_count(sbi, 1);
}
+
if (initial) {
initial = false;
goto next_step;
@@ -418,6 +430,13 @@ next_step:
.for_reclaim = 0,
};
sync_node_pages(sbi, 0, &wbc);
+
+ /*
+ * In the case of FG_GC, it'd be better to reclaim this victim
+ * completely.
+ */
+ if (get_valid_blocks(sbi, segno, 1) != 0)
+ goto next_step;
}
}
@@ -481,21 +500,19 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
static void move_data_page(struct inode *inode, struct page *page, int gc_type)
{
- if (page->mapping != inode->i_mapping)
- goto out;
-
- if (inode != page->mapping->host)
- goto out;
-
- if (PageWriteback(page))
- goto out;
-
if (gc_type == BG_GC) {
+ if (PageWriteback(page))
+ goto out;
set_page_dirty(page);
set_cold_data(page);
} else {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- mutex_lock_op(sbi, DATA_WRITE);
+
+ if (PageWriteback(page)) {
+ f2fs_submit_bio(sbi, DATA, true);
+ wait_on_page_writeback(page);
+ }
+
if (clear_page_dirty_for_io(page) &&
S_ISDIR(inode->i_mode)) {
dec_page_count(sbi, F2FS_DIRTY_DENTS);
@@ -503,7 +520,6 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type)
}
set_cold_data(page);
do_write_data_page(page);
- mutex_unlock_op(sbi, DATA_WRITE);
clear_cold_data(page);
}
out:
@@ -530,6 +546,7 @@ static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
next_step:
entry = sum;
+
for (off = 0; off < sbi->blocks_per_seg; off++, entry++) {
struct page *data_page;
struct inode *inode;
@@ -567,7 +584,7 @@ next_step:
continue;
data_page = find_data_page(inode,
- start_bidx + ofs_in_node);
+ start_bidx + ofs_in_node, false);
if (IS_ERR(data_page))
goto next_iput;
@@ -588,11 +605,22 @@ next_step:
next_iput:
iput(inode);
}
+
if (++phase < 4)
goto next_step;
- if (gc_type == FG_GC)
+ if (gc_type == FG_GC) {
f2fs_submit_bio(sbi, DATA, true);
+
+ /*
+ * In the case of FG_GC, it'd be better to reclaim this victim
+ * completely.
+ */
+ if (get_valid_blocks(sbi, segno, 1) != 0) {
+ phase = 2;
+ goto next_step;
+ }
+ }
}
static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
@@ -611,18 +639,15 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
{
struct page *sum_page;
struct f2fs_summary_block *sum;
+ struct blk_plug plug;
/* read segment summary of victim */
sum_page = get_sum_page(sbi, segno);
if (IS_ERR(sum_page))
return;
- /*
- * CP needs to lock sum_page. In this time, we don't need
- * to lock this page, because this summary page is not gone anywhere.
- * Also, this page is not gonna be updated before GC is done.
- */
- unlock_page(sum_page);
+ blk_start_plug(&plug);
+
sum = page_address(sum_page);
switch (GET_SUM_TYPE((&sum->footer))) {
@@ -633,10 +658,12 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
gc_data_segment(sbi, sum->entries, ilist, segno, gc_type);
break;
}
+ blk_finish_plug(&plug);
+
stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)));
stat_inc_call_count(sbi->stat_info);
- f2fs_put_page(sum_page, 0);
+ f2fs_put_page(sum_page, 1);
}
int f2fs_gc(struct f2fs_sb_info *sbi)
@@ -652,8 +679,10 @@ gc_more:
if (!(sbi->sb->s_flags & MS_ACTIVE))
goto stop;
- if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree))
+ if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) {
gc_type = FG_GC;
+ write_checkpoint(sbi, false);
+ }
if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE))
goto stop;
@@ -662,9 +691,11 @@ gc_more:
for (i = 0; i < sbi->segs_per_sec; i++)
do_garbage_collect(sbi, segno + i, &ilist, gc_type);
- if (gc_type == FG_GC &&
- get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0)
+ if (gc_type == FG_GC) {
+ sbi->cur_victim_sec = NULL_SEGNO;
nfree++;
+ WARN_ON(get_valid_blocks(sbi, segno, sbi->segs_per_sec));
+ }
if (has_not_enough_free_secs(sbi, nfree))
goto gc_more;
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index 30b2db003acd..2c6a6bd08322 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -13,9 +13,9 @@
* whether IO subsystem is idle
* or not
*/
-#define GC_THREAD_MIN_SLEEP_TIME 10000 /* milliseconds */
-#define GC_THREAD_MAX_SLEEP_TIME 30000
-#define GC_THREAD_NOGC_SLEEP_TIME 10000
+#define GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */
+#define GC_THREAD_MAX_SLEEP_TIME 60000
+#define GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */
#define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */
#define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */
@@ -58,6 +58,9 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi)
static inline long increase_sleep_time(long wait)
{
+ if (wait == GC_THREAD_NOGC_SLEEP_TIME)
+ return wait;
+
wait += GC_THREAD_MIN_SLEEP_TIME;
if (wait > GC_THREAD_MAX_SLEEP_TIME)
wait = GC_THREAD_MAX_SLEEP_TIME;
@@ -66,6 +69,9 @@ static inline long increase_sleep_time(long wait)
static inline long decrease_sleep_time(long wait)
{
+ if (wait == GC_THREAD_NOGC_SLEEP_TIME)
+ wait = GC_THREAD_MAX_SLEEP_TIME;
+
wait -= GC_THREAD_MIN_SLEEP_TIME;
if (wait <= GC_THREAD_MIN_SLEEP_TIME)
wait = GC_THREAD_MIN_SLEEP_TIME;
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index ddae412d30c8..91ac7f9d88ee 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -16,6 +16,8 @@
#include "f2fs.h"
#include "node.h"
+#include <trace/events/f2fs.h>
+
void f2fs_set_inode_flags(struct inode *inode)
{
unsigned int flags = F2FS_I(inode)->i_flags;
@@ -44,7 +46,11 @@ static int do_read_inode(struct inode *inode)
struct f2fs_inode *ri;
/* Check if ino is within scope */
- check_nid_range(sbi, inode->i_ino);
+ if (check_nid_range(sbi, inode->i_ino)) {
+ f2fs_msg(inode->i_sb, KERN_ERR, "bad inode number: %lu",
+ (unsigned long) inode->i_ino);
+ return -EINVAL;
+ }
node_page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(node_page))
@@ -76,7 +82,6 @@ static int do_read_inode(struct inode *inode)
fi->i_xattr_nid = le32_to_cpu(ri->i_xattr_nid);
fi->i_flags = le32_to_cpu(ri->i_flags);
fi->flags = 0;
- fi->data_version = le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver) - 1;
fi->i_advise = ri->i_advise;
fi->i_pino = le32_to_cpu(ri->i_pino);
get_extent_info(&fi->ext, ri->i_ext);
@@ -88,13 +93,16 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct inode *inode;
- int ret;
+ int ret = 0;
inode = iget_locked(sb, ino);
if (!inode)
return ERR_PTR(-ENOMEM);
- if (!(inode->i_state & I_NEW))
+
+ if (!(inode->i_state & I_NEW)) {
+ trace_f2fs_iget(inode);
return inode;
+ }
if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi))
goto make_now;
@@ -136,11 +144,12 @@ make_now:
goto bad_inode;
}
unlock_new_inode(inode);
-
+ trace_f2fs_iget(inode);
return inode;
bad_inode:
iget_failed(inode);
+ trace_f2fs_iget_exit(inode, ret);
return ERR_PTR(ret);
}
@@ -192,47 +201,51 @@ void update_inode(struct inode *inode, struct page *node_page)
set_page_dirty(node_page);
}
-int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
+int update_inode_page(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct page *node_page;
- bool need_lock = false;
-
- if (inode->i_ino == F2FS_NODE_INO(sbi) ||
- inode->i_ino == F2FS_META_INO(sbi))
- return 0;
-
- if (wbc)
- f2fs_balance_fs(sbi);
node_page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(node_page))
return PTR_ERR(node_page);
- if (!PageDirty(node_page)) {
- need_lock = true;
- f2fs_put_page(node_page, 1);
- mutex_lock(&sbi->write_inode);
- node_page = get_node_page(sbi, inode->i_ino);
- if (IS_ERR(node_page)) {
- mutex_unlock(&sbi->write_inode);
- return PTR_ERR(node_page);
- }
- }
update_inode(inode, node_page);
f2fs_put_page(node_page, 1);
- if (need_lock)
- mutex_unlock(&sbi->write_inode);
return 0;
}
+int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+ struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+ int ret, ilock;
+
+ if (inode->i_ino == F2FS_NODE_INO(sbi) ||
+ inode->i_ino == F2FS_META_INO(sbi))
+ return 0;
+
+ if (wbc)
+ f2fs_balance_fs(sbi);
+
+ /*
+ * We need to lock here to prevent from producing dirty node pages
+ * during the urgent cleaning time when runing out of free sections.
+ */
+ ilock = mutex_lock_op(sbi);
+ ret = update_inode_page(inode);
+ mutex_unlock_op(sbi, ilock);
+ return ret;
+}
+
/*
* Called at the last iput() if i_nlink is zero
*/
void f2fs_evict_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+ int ilock;
+ trace_f2fs_evict_inode(inode);
truncate_inode_pages(&inode->i_data, 0);
if (inode->i_ino == F2FS_NODE_INO(sbi) ||
@@ -252,7 +265,10 @@ void f2fs_evict_inode(struct inode *inode)
if (F2FS_HAS_BLOCKS(inode))
f2fs_truncate(inode);
+ ilock = mutex_lock_op(sbi);
remove_inode_page(inode);
+ mutex_unlock_op(sbi, ilock);
+
sb_end_intwrite(inode->i_sb);
no_delete:
clear_inode(inode);
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 1a49b881bac0..47abc9722b17 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -15,8 +15,10 @@
#include <linux/ctype.h>
#include "f2fs.h"
+#include "node.h"
#include "xattr.h"
#include "acl.h"
+#include <trace/events/f2fs.h>
static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
{
@@ -25,19 +27,19 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
nid_t ino;
struct inode *inode;
bool nid_free = false;
- int err;
+ int err, ilock;
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
- mutex_lock_op(sbi, NODE_NEW);
+ ilock = mutex_lock_op(sbi);
if (!alloc_nid(sbi, &ino)) {
- mutex_unlock_op(sbi, NODE_NEW);
+ mutex_unlock_op(sbi, ilock);
err = -ENOSPC;
goto fail;
}
- mutex_unlock_op(sbi, NODE_NEW);
+ mutex_unlock_op(sbi, ilock);
inode->i_uid = current_fsuid();
@@ -61,7 +63,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
nid_free = true;
goto out;
}
-
+ trace_f2fs_new_inode(inode, 0);
mark_inode_dirty(inode);
return inode;
@@ -69,6 +71,8 @@ out:
clear_nlink(inode);
unlock_new_inode(inode);
fail:
+ trace_f2fs_new_inode(inode, err);
+ make_bad_inode(inode);
iput(inode);
if (nid_free)
alloc_nid_failed(sbi, ino);
@@ -82,7 +86,7 @@ static int is_multimedia_file(const unsigned char *s, const char *sub)
int ret;
if (sublen > slen)
- return 1;
+ return 0;
ret = memcmp(s + slen - sublen, sub, sublen);
if (ret) { /* compare upper case */
@@ -90,16 +94,16 @@ static int is_multimedia_file(const unsigned char *s, const char *sub)
char upper_sub[8];
for (i = 0; i < sublen && i < sizeof(upper_sub); i++)
upper_sub[i] = toupper(sub[i]);
- return memcmp(s + slen - sublen, upper_sub, sublen);
+ return !memcmp(s + slen - sublen, upper_sub, sublen);
}
- return ret;
+ return !ret;
}
/*
* Set multimedia files as cold files for hot/cold data separation
*/
-static inline void set_cold_file(struct f2fs_sb_info *sbi, struct inode *inode,
+static inline void set_cold_files(struct f2fs_sb_info *sbi, struct inode *inode,
const unsigned char *name)
{
int i;
@@ -107,8 +111,8 @@ static inline void set_cold_file(struct f2fs_sb_info *sbi, struct inode *inode,
int count = le32_to_cpu(sbi->raw_super->extension_count);
for (i = 0; i < count; i++) {
- if (!is_multimedia_file(name, extlist[i])) {
- F2FS_I(inode)->i_advise |= FADVISE_COLD_BIT;
+ if (is_multimedia_file(name, extlist[i])) {
+ set_cold_file(inode);
break;
}
}
@@ -121,7 +125,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct inode *inode;
nid_t ino = 0;
- int err;
+ int err, ilock;
f2fs_balance_fs(sbi);
@@ -130,14 +134,16 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
return PTR_ERR(inode);
if (!test_opt(sbi, DISABLE_EXT_IDENTIFY))
- set_cold_file(sbi, inode, dentry->d_name.name);
+ set_cold_files(sbi, inode, dentry->d_name.name);
inode->i_op = &f2fs_file_inode_operations;
inode->i_fop = &f2fs_file_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops;
ino = inode->i_ino;
+ ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
+ mutex_unlock_op(sbi, ilock);
if (err)
goto out;
@@ -150,6 +156,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
out:
clear_nlink(inode);
unlock_new_inode(inode);
+ make_bad_inode(inode);
iput(inode);
alloc_nid_failed(sbi, ino);
return err;
@@ -161,7 +168,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
struct inode *inode = old_dentry->d_inode;
struct super_block *sb = dir->i_sb;
struct f2fs_sb_info *sbi = F2FS_SB(sb);
- int err;
+ int err, ilock;
f2fs_balance_fs(sbi);
@@ -169,14 +176,23 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
atomic_inc(&inode->i_count);
set_inode_flag(F2FS_I(inode), FI_INC_LINK);
+ ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
+ mutex_unlock_op(sbi, ilock);
if (err)
goto out;
+ /*
+ * This file should be checkpointed during fsync.
+ * We lost i_pino from now on.
+ */
+ set_cp_file(inode);
+
d_instantiate(dentry, inode);
return 0;
out:
clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
+ make_bad_inode(inode);
iput(inode);
return err;
}
@@ -197,7 +213,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
struct f2fs_dir_entry *de;
struct page *page;
- if (dentry->d_name.len > F2FS_MAX_NAME_LEN)
+ if (dentry->d_name.len > F2FS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
de = f2fs_find_entry(dir, &dentry->d_name, &page);
@@ -222,7 +238,9 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
struct f2fs_dir_entry *de;
struct page *page;
int err = -ENOENT;
+ int ilock;
+ trace_f2fs_unlink_enter(dir, dentry);
f2fs_balance_fs(sbi);
de = f2fs_find_entry(dir, &dentry->d_name, &page);
@@ -236,11 +254,14 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
goto fail;
}
+ ilock = mutex_lock_op(sbi);
f2fs_delete_entry(de, page, inode);
+ mutex_unlock_op(sbi, ilock);
/* In order to evict this inode, we set it dirty */
mark_inode_dirty(inode);
fail:
+ trace_f2fs_unlink_exit(inode, err);
return err;
}
@@ -251,7 +272,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct inode *inode;
size_t symlen = strlen(symname) + 1;
- int err;
+ int err, ilock;
f2fs_balance_fs(sbi);
@@ -262,7 +283,9 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
inode->i_op = &f2fs_symlink_inode_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops;
+ ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
+ mutex_unlock_op(sbi, ilock);
if (err)
goto out;
@@ -275,6 +298,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
out:
clear_nlink(inode);
unlock_new_inode(inode);
+ make_bad_inode(inode);
iput(inode);
alloc_nid_failed(sbi, inode->i_ino);
return err;
@@ -284,7 +308,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
struct inode *inode;
- int err;
+ int err, ilock;
f2fs_balance_fs(sbi);
@@ -298,7 +322,9 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
set_inode_flag(F2FS_I(inode), FI_INC_LINK);
+ ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
+ mutex_unlock_op(sbi, ilock);
if (err)
goto out_fail;
@@ -313,6 +339,7 @@ out_fail:
clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
clear_nlink(inode);
unlock_new_inode(inode);
+ make_bad_inode(inode);
iput(inode);
alloc_nid_failed(sbi, inode->i_ino);
return err;
@@ -333,6 +360,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct inode *inode;
int err = 0;
+ int ilock;
if (!new_valid_dev(rdev))
return -EINVAL;
@@ -346,7 +374,9 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
init_special_inode(inode, inode->i_mode, rdev);
inode->i_op = &f2fs_special_inode_operations;
+ ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
+ mutex_unlock_op(sbi, ilock);
if (err)
goto out;
@@ -357,6 +387,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
out:
clear_nlink(inode);
unlock_new_inode(inode);
+ make_bad_inode(inode);
iput(inode);
alloc_nid_failed(sbi, inode->i_ino);
return err;
@@ -374,7 +405,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct f2fs_dir_entry *old_dir_entry = NULL;
struct f2fs_dir_entry *old_entry;
struct f2fs_dir_entry *new_entry;
- int err = -ENOENT;
+ int err = -ENOENT, ilock = -1;
f2fs_balance_fs(sbi);
@@ -389,7 +420,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto out_old;
}
- mutex_lock_op(sbi, RENAME);
+ ilock = mutex_lock_op(sbi);
if (new_inode) {
struct page *new_page;
@@ -412,7 +443,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
drop_nlink(new_inode);
if (!new_inode->i_nlink)
add_orphan_inode(sbi, new_inode->i_ino);
- f2fs_write_inode(new_inode, NULL);
+ update_inode_page(new_inode);
} else {
err = f2fs_add_link(new_dentry, old_inode);
if (err)
@@ -420,12 +451,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (old_dir_entry) {
inc_nlink(new_dir);
- f2fs_write_inode(new_dir, NULL);
+ update_inode_page(new_dir);
}
}
old_inode->i_ctime = CURRENT_TIME;
- set_inode_flag(F2FS_I(old_inode), FI_NEED_CP);
mark_inode_dirty(old_inode);
f2fs_delete_entry(old_entry, old_page, NULL);
@@ -439,10 +469,10 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_put_page(old_dir_page, 0);
}
drop_nlink(old_dir);
- f2fs_write_inode(old_dir, NULL);
+ update_inode_page(old_dir);
}
- mutex_unlock_op(sbi, RENAME);
+ mutex_unlock_op(sbi, ilock);
return 0;
out_dir:
@@ -450,7 +480,7 @@ out_dir:
kunmap(old_dir_page);
f2fs_put_page(old_dir_page, 0);
}
- mutex_unlock_op(sbi, RENAME);
+ mutex_unlock_op(sbi, ilock);
out_old:
kunmap(old_page);
f2fs_put_page(old_page, 0);
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index e275218904ed..3df43b4efd89 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -19,6 +19,7 @@
#include "f2fs.h"
#include "node.h"
#include "segment.h"
+#include <trace/events/f2fs.h>
static struct kmem_cache *nat_entry_slab;
static struct kmem_cache *free_nid_slab;
@@ -88,10 +89,13 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid)
{
struct address_space *mapping = sbi->meta_inode->i_mapping;
struct f2fs_nm_info *nm_i = NM_I(sbi);
+ struct blk_plug plug;
struct page *page;
pgoff_t index;
int i;
+ blk_start_plug(&plug);
+
for (i = 0; i < FREE_NID_PAGES; i++, nid += NAT_ENTRY_PER_BLOCK) {
if (nid >= nm_i->max_nid)
nid = 0;
@@ -100,12 +104,16 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid)
page = grab_cache_page(mapping, index);
if (!page)
continue;
- if (f2fs_readpage(sbi, page, index, READ)) {
+ if (PageUptodate(page)) {
f2fs_put_page(page, 1);
continue;
}
+ if (f2fs_readpage(sbi, page, index, READ))
+ continue;
+
f2fs_put_page(page, 0);
}
+ blk_finish_plug(&plug);
}
static struct nat_entry *__lookup_nat_cache(struct f2fs_nm_info *nm_i, nid_t n)
@@ -236,7 +244,7 @@ static int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
- if (nm_i->nat_cnt < 2 * NM_WOUT_THRESHOLD)
+ if (nm_i->nat_cnt <= NM_WOUT_THRESHOLD)
return 0;
write_lock(&nm_i->nat_tree_lock);
@@ -320,15 +328,14 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
noffset[0] = 0;
if (block < direct_index) {
- offset[n++] = block;
- level = 0;
+ offset[n] = block;
goto got;
}
block -= direct_index;
if (block < direct_blks) {
offset[n++] = NODE_DIR1_BLOCK;
noffset[n] = 1;
- offset[n++] = block;
+ offset[n] = block;
level = 1;
goto got;
}
@@ -336,7 +343,7 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
if (block < direct_blks) {
offset[n++] = NODE_DIR2_BLOCK;
noffset[n] = 2;
- offset[n++] = block;
+ offset[n] = block;
level = 1;
goto got;
}
@@ -346,7 +353,7 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
noffset[n] = 3;
offset[n++] = block / direct_blks;
noffset[n] = 4 + offset[n - 1];
- offset[n++] = block % direct_blks;
+ offset[n] = block % direct_blks;
level = 2;
goto got;
}
@@ -356,7 +363,7 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
noffset[n] = 4 + dptrs_per_blk;
offset[n++] = block / direct_blks;
noffset[n] = 5 + dptrs_per_blk + offset[n - 1];
- offset[n++] = block % direct_blks;
+ offset[n] = block % direct_blks;
level = 2;
goto got;
}
@@ -371,7 +378,7 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
noffset[n] = 7 + (dptrs_per_blk * 2) +
offset[n - 2] * (dptrs_per_blk + 1) +
offset[n - 1];
- offset[n++] = block % direct_blks;
+ offset[n] = block % direct_blks;
level = 3;
goto got;
} else {
@@ -383,8 +390,11 @@ got:
/*
* Caller should call f2fs_put_dnode(dn).
+ * Also, it should grab and release a mutex by calling mutex_lock_op() and
+ * mutex_unlock_op() only if ro is not set RDONLY_NODE.
+ * In the case of RDONLY_NODE, we don't need to care about mutex.
*/
-int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro)
+int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
{
struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb);
struct page *npage[4];
@@ -403,7 +413,8 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro)
return PTR_ERR(npage[0]);
parent = npage[0];
- nids[1] = get_nid(parent, offset[0], true);
+ if (level != 0)
+ nids[1] = get_nid(parent, offset[0], true);
dn->inode_page = npage[0];
dn->inode_page_locked = true;
@@ -411,12 +422,9 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro)
for (i = 1; i <= level; i++) {
bool done = false;
- if (!nids[i] && !ro) {
- mutex_lock_op(sbi, NODE_NEW);
-
+ if (!nids[i] && mode == ALLOC_NODE) {
/* alloc new node */
if (!alloc_nid(sbi, &(nids[i]))) {
- mutex_unlock_op(sbi, NODE_NEW);
err = -ENOSPC;
goto release_pages;
}
@@ -425,16 +433,14 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro)
npage[i] = new_node_page(dn, noffset[i]);
if (IS_ERR(npage[i])) {
alloc_nid_failed(sbi, nids[i]);
- mutex_unlock_op(sbi, NODE_NEW);
err = PTR_ERR(npage[i]);
goto release_pages;
}
set_nid(parent, offset[i - 1], nids[i], i == 1);
alloc_nid_done(sbi, nids[i]);
- mutex_unlock_op(sbi, NODE_NEW);
done = true;
- } else if (ro && i == level && level > 1) {
+ } else if (mode == LOOKUP_NODE_RA && i == level && level > 1) {
npage[i] = get_node_page_ra(parent, offset[i - 1]);
if (IS_ERR(npage[i])) {
err = PTR_ERR(npage[i]);
@@ -507,6 +513,7 @@ invalidate:
f2fs_put_page(dn->node_page, 1);
dn->node_page = NULL;
+ trace_f2fs_truncate_node(dn->inode, dn->nid, ni.blk_addr);
}
static int truncate_dnode(struct dnode_of_data *dn)
@@ -547,9 +554,13 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
if (dn->nid == 0)
return NIDS_PER_BLOCK + 1;
+ trace_f2fs_truncate_nodes_enter(dn->inode, dn->nid, dn->data_blkaddr);
+
page = get_node_page(sbi, dn->nid);
- if (IS_ERR(page))
+ if (IS_ERR(page)) {
+ trace_f2fs_truncate_nodes_exit(dn->inode, PTR_ERR(page));
return PTR_ERR(page);
+ }
rn = (struct f2fs_node *)page_address(page);
if (depth < 3) {
@@ -591,10 +602,12 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
} else {
f2fs_put_page(page, 1);
}
+ trace_f2fs_truncate_nodes_exit(dn->inode, freed);
return freed;
out_err:
f2fs_put_page(page, 1);
+ trace_f2fs_truncate_nodes_exit(dn->inode, ret);
return ret;
}
@@ -649,6 +662,9 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,
fail:
for (i = depth - 3; i >= 0; i--)
f2fs_put_page(pages[i], 1);
+
+ trace_f2fs_truncate_partial_nodes(dn->inode, nid, depth, err);
+
return err;
}
@@ -658,6 +674,7 @@ fail:
int truncate_inode_blocks(struct inode *inode, pgoff_t from)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+ struct address_space *node_mapping = sbi->node_inode->i_mapping;
int err = 0, cont = 1;
int level, offset[4], noffset[4];
unsigned int nofs = 0;
@@ -665,11 +682,15 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from)
struct dnode_of_data dn;
struct page *page;
- level = get_node_path(from, offset, noffset);
+ trace_f2fs_truncate_inode_blocks_enter(inode, from);
+ level = get_node_path(from, offset, noffset);
+restart:
page = get_node_page(sbi, inode->i_ino);
- if (IS_ERR(page))
+ if (IS_ERR(page)) {
+ trace_f2fs_truncate_inode_blocks_exit(inode, PTR_ERR(page));
return PTR_ERR(page);
+ }
set_new_dnode(&dn, inode, page, NULL, 0);
unlock_page(page);
@@ -728,6 +749,10 @@ skip_partial:
if (offset[1] == 0 &&
rn->i.i_nid[offset[0] - NODE_DIR1_BLOCK]) {
lock_page(page);
+ if (page->mapping != node_mapping) {
+ f2fs_put_page(page, 1);
+ goto restart;
+ }
wait_on_page_writeback(page);
rn->i.i_nid[offset[0] - NODE_DIR1_BLOCK] = 0;
set_page_dirty(page);
@@ -739,9 +764,14 @@ skip_partial:
}
fail:
f2fs_put_page(page, 0);
+ trace_f2fs_truncate_inode_blocks_exit(inode, err);
return err > 0 ? 0 : err;
}
+/*
+ * Caller should grab and release a mutex by calling mutex_lock_op() and
+ * mutex_unlock_op().
+ */
int remove_inode_page(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
@@ -749,21 +779,16 @@ int remove_inode_page(struct inode *inode)
nid_t ino = inode->i_ino;
struct dnode_of_data dn;
- mutex_lock_op(sbi, NODE_TRUNC);
page = get_node_page(sbi, ino);
- if (IS_ERR(page)) {
- mutex_unlock_op(sbi, NODE_TRUNC);
+ if (IS_ERR(page))
return PTR_ERR(page);
- }
if (F2FS_I(inode)->i_xattr_nid) {
nid_t nid = F2FS_I(inode)->i_xattr_nid;
struct page *npage = get_node_page(sbi, nid);
- if (IS_ERR(npage)) {
- mutex_unlock_op(sbi, NODE_TRUNC);
+ if (IS_ERR(npage))
return PTR_ERR(npage);
- }
F2FS_I(inode)->i_xattr_nid = 0;
set_new_dnode(&dn, inode, page, npage, nid);
@@ -775,23 +800,18 @@ int remove_inode_page(struct inode *inode)
BUG_ON(inode->i_blocks != 0 && inode->i_blocks != 1);
set_new_dnode(&dn, inode, page, page, ino);
truncate_node(&dn);
-
- mutex_unlock_op(sbi, NODE_TRUNC);
return 0;
}
int new_inode_page(struct inode *inode, const struct qstr *name)
{
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct page *page;
struct dnode_of_data dn;
/* allocate inode page for new inode */
set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino);
- mutex_lock_op(sbi, NODE_NEW);
page = new_node_page(&dn, 0);
init_dent_inode(name, page);
- mutex_unlock_op(sbi, NODE_NEW);
if (IS_ERR(page))
return PTR_ERR(page);
f2fs_put_page(page, 1);
@@ -844,6 +864,12 @@ fail:
return ERR_PTR(err);
}
+/*
+ * Caller should do after getting the following values.
+ * 0: f2fs_put_page(page, 0)
+ * LOCKED_PAGE: f2fs_put_page(page, 1)
+ * error: nothing
+ */
static int read_node_page(struct page *page, int type)
{
struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb);
@@ -851,8 +877,14 @@ static int read_node_page(struct page *page, int type)
get_node_info(sbi, page->index, &ni);
- if (ni.blk_addr == NULL_ADDR)
+ if (ni.blk_addr == NULL_ADDR) {
+ f2fs_put_page(page, 1);
return -ENOENT;
+ }
+
+ if (PageUptodate(page))
+ return LOCKED_PAGE;
+
return f2fs_readpage(sbi, page, ni.blk_addr, type);
}
@@ -863,40 +895,53 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
{
struct address_space *mapping = sbi->node_inode->i_mapping;
struct page *apage;
+ int err;
apage = find_get_page(mapping, nid);
- if (apage && PageUptodate(apage))
- goto release_out;
+ if (apage && PageUptodate(apage)) {
+ f2fs_put_page(apage, 0);
+ return;
+ }
f2fs_put_page(apage, 0);
apage = grab_cache_page(mapping, nid);
if (!apage)
return;
- if (read_node_page(apage, READA))
- unlock_page(apage);
-
-release_out:
- f2fs_put_page(apage, 0);
+ err = read_node_page(apage, READA);
+ if (err == 0)
+ f2fs_put_page(apage, 0);
+ else if (err == LOCKED_PAGE)
+ f2fs_put_page(apage, 1);
return;
}
struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
{
- int err;
- struct page *page;
struct address_space *mapping = sbi->node_inode->i_mapping;
-
+ struct page *page;
+ int err;
+repeat:
page = grab_cache_page(mapping, nid);
if (!page)
return ERR_PTR(-ENOMEM);
err = read_node_page(page, READ_SYNC);
- if (err) {
- f2fs_put_page(page, 1);
+ if (err < 0)
return ERR_PTR(err);
- }
+ else if (err == LOCKED_PAGE)
+ goto got_it;
+ lock_page(page);
+ if (!PageUptodate(page)) {
+ f2fs_put_page(page, 1);
+ return ERR_PTR(-EIO);
+ }
+ if (page->mapping != mapping) {
+ f2fs_put_page(page, 1);
+ goto repeat;
+ }
+got_it:
BUG_ON(nid != nid_of_node(page));
mark_page_accessed(page);
return page;
@@ -910,31 +955,27 @@ struct page *get_node_page_ra(struct page *parent, int start)
{
struct f2fs_sb_info *sbi = F2FS_SB(parent->mapping->host->i_sb);
struct address_space *mapping = sbi->node_inode->i_mapping;
- int i, end;
- int err = 0;
- nid_t nid;
+ struct blk_plug plug;
struct page *page;
+ int err, i, end;
+ nid_t nid;
/* First, try getting the desired direct node. */
nid = get_nid(parent, start, false);
if (!nid)
return ERR_PTR(-ENOENT);
-
- page = find_get_page(mapping, nid);
- if (page && PageUptodate(page))
- goto page_hit;
- f2fs_put_page(page, 0);
-
repeat:
page = grab_cache_page(mapping, nid);
if (!page)
return ERR_PTR(-ENOMEM);
- err = read_node_page(page, READA);
- if (err) {
- f2fs_put_page(page, 1);
+ err = read_node_page(page, READ_SYNC);
+ if (err < 0)
return ERR_PTR(err);
- }
+ else if (err == LOCKED_PAGE)
+ goto page_hit;
+
+ blk_start_plug(&plug);
/* Then, try readahead for siblings of the desired node */
end = start + MAX_RA_NODE;
@@ -946,18 +987,19 @@ repeat:
ra_node_page(sbi, nid);
}
-page_hit:
- lock_page(page);
- if (PageError(page)) {
- f2fs_put_page(page, 1);
- return ERR_PTR(-EIO);
- }
+ blk_finish_plug(&plug);
- /* Has the page been truncated? */
+ lock_page(page);
if (page->mapping != mapping) {
f2fs_put_page(page, 1);
goto repeat;
}
+page_hit:
+ if (!PageUptodate(page)) {
+ f2fs_put_page(page, 1);
+ return ERR_PTR(-EIO);
+ }
+ mark_page_accessed(page);
return page;
}
@@ -972,7 +1014,7 @@ void sync_inode_page(struct dnode_of_data *dn)
if (!dn->inode_page_locked)
unlock_page(dn->inode_page);
} else {
- f2fs_write_inode(dn->inode, NULL);
+ update_inode_page(dn->inode);
}
}
@@ -1087,17 +1129,8 @@ static int f2fs_write_node_page(struct page *page,
block_t new_addr;
struct node_info ni;
- if (wbc->for_reclaim) {
- dec_page_count(sbi, F2FS_DIRTY_NODES);
- wbc->pages_skipped++;
- set_page_dirty(page);
- return AOP_WRITEPAGE_ACTIVATE;
- }
-
wait_on_page_writeback(page);
- mutex_lock_op(sbi, NODE_WRITE);
-
/* get old block addr of this node page */
nid = nid_of_node(page);
BUG_ON(page->index != nid);
@@ -1105,17 +1138,25 @@ static int f2fs_write_node_page(struct page *page,
get_node_info(sbi, nid, &ni);
/* This page is already truncated */
- if (ni.blk_addr == NULL_ADDR)
+ if (ni.blk_addr == NULL_ADDR) {
+ dec_page_count(sbi, F2FS_DIRTY_NODES);
+ unlock_page(page);
return 0;
+ }
- set_page_writeback(page);
+ if (wbc->for_reclaim) {
+ dec_page_count(sbi, F2FS_DIRTY_NODES);
+ wbc->pages_skipped++;
+ set_page_dirty(page);
+ return AOP_WRITEPAGE_ACTIVATE;
+ }
- /* insert node offset */
+ mutex_lock(&sbi->node_write);
+ set_page_writeback(page);
write_node_page(sbi, page, nid, ni.blk_addr, &new_addr);
set_node_addr(sbi, &ni, new_addr);
dec_page_count(sbi, F2FS_DIRTY_NODES);
-
- mutex_unlock_op(sbi, NODE_WRITE);
+ mutex_unlock(&sbi->node_write);
unlock_page(page);
return 0;
}
@@ -1130,12 +1171,11 @@ static int f2fs_write_node_pages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb);
- struct block_device *bdev = sbi->sb->s_bdev;
long nr_to_write = wbc->nr_to_write;
/* First check balancing cached NAT entries */
if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) {
- write_checkpoint(sbi, false);
+ f2fs_sync_fs(sbi->sb, true);
return 0;
}
@@ -1144,10 +1184,9 @@ static int f2fs_write_node_pages(struct address_space *mapping,
return 0;
/* if mounting is failed, skip writing node pages */
- wbc->nr_to_write = bio_get_nr_vecs(bdev);
+ wbc->nr_to_write = max_hw_blocks(sbi);
sync_node_pages(sbi, 0, wbc);
- wbc->nr_to_write = nr_to_write -
- (bio_get_nr_vecs(bdev) - wbc->nr_to_write);
+ wbc->nr_to_write = nr_to_write - (max_hw_blocks(sbi) - wbc->nr_to_write);
return 0;
}
@@ -1178,7 +1217,7 @@ static void f2fs_invalidate_node_page(struct page *page, unsigned long offset)
static int f2fs_release_node_page(struct page *page, gfp_t wait)
{
ClearPagePrivate(page);
- return 0;
+ return 1;
}
/*
@@ -1195,14 +1234,13 @@ const struct address_space_operations f2fs_node_aops = {
static struct free_nid *__lookup_free_nid_list(nid_t n, struct list_head *head)
{
struct list_head *this;
- struct free_nid *i = NULL;
+ struct free_nid *i;
list_for_each(this, head) {
i = list_entry(this, struct free_nid, list);
if (i->nid == n)
- break;
- i = NULL;
+ return i;
}
- return i;
+ return NULL;
}
static void __del_from_free_nid_list(struct free_nid *i)
@@ -1211,11 +1249,29 @@ static void __del_from_free_nid_list(struct free_nid *i)
kmem_cache_free(free_nid_slab, i);
}
-static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid)
+static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid, bool build)
{
struct free_nid *i;
+ struct nat_entry *ne;
+ bool allocated = false;
if (nm_i->fcnt > 2 * MAX_FREE_NIDS)
+ return -1;
+
+ /* 0 nid should not be used */
+ if (nid == 0)
+ return 0;
+
+ if (!build)
+ goto retry;
+
+ /* do not add allocated nids */
+ read_lock(&nm_i->nat_tree_lock);
+ ne = __lookup_nat_cache(nm_i, nid);
+ if (ne && nat_get_blkaddr(ne) != NULL_ADDR)
+ allocated = true;
+ read_unlock(&nm_i->nat_tree_lock);
+ if (allocated)
return 0;
retry:
i = kmem_cache_alloc(free_nid_slab, GFP_NOFS);
@@ -1250,63 +1306,59 @@ static void remove_free_nid(struct f2fs_nm_info *nm_i, nid_t nid)
spin_unlock(&nm_i->free_nid_list_lock);
}
-static int scan_nat_page(struct f2fs_nm_info *nm_i,
+static void scan_nat_page(struct f2fs_nm_info *nm_i,
struct page *nat_page, nid_t start_nid)
{
struct f2fs_nat_block *nat_blk = page_address(nat_page);
block_t blk_addr;
- int fcnt = 0;
int i;
- /* 0 nid should not be used */
- if (start_nid == 0)
- ++start_nid;
-
i = start_nid % NAT_ENTRY_PER_BLOCK;
for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) {
- blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr);
+
+ if (start_nid >= nm_i->max_nid)
+ break;
+
+ blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr);
BUG_ON(blk_addr == NEW_ADDR);
- if (blk_addr == NULL_ADDR)
- fcnt += add_free_nid(nm_i, start_nid);
+ if (blk_addr == NULL_ADDR) {
+ if (add_free_nid(nm_i, start_nid, true) < 0)
+ break;
+ }
}
- return fcnt;
}
static void build_free_nids(struct f2fs_sb_info *sbi)
{
- struct free_nid *fnid, *next_fnid;
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk;
- nid_t nid = 0;
- bool is_cycled = false;
- int fcnt = 0;
- int i;
+ int i = 0;
+ nid_t nid = nm_i->next_scan_nid;
- nid = nm_i->next_scan_nid;
- nm_i->init_scan_nid = nid;
+ /* Enough entries */
+ if (nm_i->fcnt > NAT_ENTRY_PER_BLOCK)
+ return;
+ /* readahead nat pages to be scanned */
ra_nat_pages(sbi, nid);
while (1) {
struct page *page = get_current_nat_page(sbi, nid);
- fcnt += scan_nat_page(nm_i, page, nid);
+ scan_nat_page(nm_i, page, nid);
f2fs_put_page(page, 1);
nid += (NAT_ENTRY_PER_BLOCK - (nid % NAT_ENTRY_PER_BLOCK));
-
- if (nid >= nm_i->max_nid) {
+ if (nid >= nm_i->max_nid)
nid = 0;
- is_cycled = true;
- }
- if (fcnt > MAX_FREE_NIDS)
- break;
- if (is_cycled && nm_i->init_scan_nid <= nid)
+
+ if (i++ == FREE_NID_PAGES)
break;
}
+ /* go to the next free nat pages to find free nids abundantly */
nm_i->next_scan_nid = nid;
/* find free nids from current sum_pages */
@@ -1315,22 +1367,11 @@ static void build_free_nids(struct f2fs_sb_info *sbi)
block_t addr = le32_to_cpu(nat_in_journal(sum, i).block_addr);
nid = le32_to_cpu(nid_in_journal(sum, i));
if (addr == NULL_ADDR)
- add_free_nid(nm_i, nid);
+ add_free_nid(nm_i, nid, true);
else
remove_free_nid(nm_i, nid);
}
mutex_unlock(&curseg->curseg_mutex);
-
- /* remove the free nids from current allocated nids */
- list_for_each_entry_safe(fnid, next_fnid, &nm_i->free_nid_list, list) {
- struct nat_entry *ne;
-
- read_lock(&nm_i->nat_tree_lock);
- ne = __lookup_nat_cache(nm_i, fnid->nid);
- if (ne && nat_get_blkaddr(ne) != NULL_ADDR)
- remove_free_nid(nm_i, fnid->nid);
- read_unlock(&nm_i->nat_tree_lock);
- }
}
/*
@@ -1344,41 +1385,36 @@ bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid)
struct free_nid *i = NULL;
struct list_head *this;
retry:
- mutex_lock(&nm_i->build_lock);
- if (!nm_i->fcnt) {
- /* scan NAT in order to build free nid list */
- build_free_nids(sbi);
- if (!nm_i->fcnt) {
- mutex_unlock(&nm_i->build_lock);
- return false;
- }
- }
- mutex_unlock(&nm_i->build_lock);
+ if (sbi->total_valid_node_count + 1 >= nm_i->max_nid)
+ return false;
- /*
- * We check fcnt again since previous check is racy as
- * we didn't hold free_nid_list_lock. So other thread
- * could consume all of free nids.
- */
spin_lock(&nm_i->free_nid_list_lock);
- if (!nm_i->fcnt) {
- spin_unlock(&nm_i->free_nid_list_lock);
- goto retry;
- }
- BUG_ON(list_empty(&nm_i->free_nid_list));
- list_for_each(this, &nm_i->free_nid_list) {
- i = list_entry(this, struct free_nid, list);
- if (i->state == NID_NEW)
- break;
- }
+ /* We should not use stale free nids created by build_free_nids */
+ if (nm_i->fcnt && !sbi->on_build_free_nids) {
+ BUG_ON(list_empty(&nm_i->free_nid_list));
+ list_for_each(this, &nm_i->free_nid_list) {
+ i = list_entry(this, struct free_nid, list);
+ if (i->state == NID_NEW)
+ break;
+ }
- BUG_ON(i->state != NID_NEW);
- *nid = i->nid;
- i->state = NID_ALLOC;
- nm_i->fcnt--;
+ BUG_ON(i->state != NID_NEW);
+ *nid = i->nid;
+ i->state = NID_ALLOC;
+ nm_i->fcnt--;
+ spin_unlock(&nm_i->free_nid_list_lock);
+ return true;
+ }
spin_unlock(&nm_i->free_nid_list_lock);
- return true;
+
+ /* Let's scan nat pages and its caches to get free nids */
+ mutex_lock(&nm_i->build_lock);
+ sbi->on_build_free_nids = 1;
+ build_free_nids(sbi);
+ sbi->on_build_free_nids = 0;
+ mutex_unlock(&nm_i->build_lock);
+ goto retry;
}
/*
@@ -1391,10 +1427,8 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid)
spin_lock(&nm_i->free_nid_list_lock);
i = __lookup_free_nid_list(nid, &nm_i->free_nid_list);
- if (i) {
- BUG_ON(i->state != NID_ALLOC);
- __del_from_free_nid_list(i);
- }
+ BUG_ON(!i || i->state != NID_ALLOC);
+ __del_from_free_nid_list(i);
spin_unlock(&nm_i->free_nid_list_lock);
}
@@ -1403,8 +1437,19 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid)
*/
void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid)
{
- alloc_nid_done(sbi, nid);
- add_free_nid(NM_I(sbi), nid);
+ struct f2fs_nm_info *nm_i = NM_I(sbi);
+ struct free_nid *i;
+
+ spin_lock(&nm_i->free_nid_list_lock);
+ i = __lookup_free_nid_list(nid, &nm_i->free_nid_list);
+ BUG_ON(!i || i->state != NID_ALLOC);
+ if (nm_i->fcnt > 2 * MAX_FREE_NIDS) {
+ __del_from_free_nid_list(i);
+ } else {
+ i->state = NID_NEW;
+ nm_i->fcnt++;
+ }
+ spin_unlock(&nm_i->free_nid_list_lock);
}
void recover_node_page(struct f2fs_sb_info *sbi, struct page *page,
@@ -1475,23 +1520,24 @@ int restore_node_summary(struct f2fs_sb_info *sbi,
sum_entry = &sum->entries[0];
for (i = 0; i < last_offset; i++, sum_entry++) {
+ /*
+ * In order to read next node page,
+ * we must clear PageUptodate flag.
+ */
+ ClearPageUptodate(page);
+
if (f2fs_readpage(sbi, page, addr, READ_SYNC))
goto out;
+ lock_page(page);
rn = (struct f2fs_node *)page_address(page);
sum_entry->nid = rn->footer.nid;
sum_entry->version = 0;
sum_entry->ofs_in_node = 0;
addr++;
-
- /*
- * In order to read next node page,
- * we must clear PageUptodate flag.
- */
- ClearPageUptodate(page);
}
-out:
unlock_page(page);
+out:
__free_pages(page, 0);
return 0;
}
@@ -1614,13 +1660,11 @@ flush_now:
nid_in_journal(sum, offset) = cpu_to_le32(nid);
}
- if (nat_get_blkaddr(ne) == NULL_ADDR) {
+ if (nat_get_blkaddr(ne) == NULL_ADDR &&
+ add_free_nid(NM_I(sbi), nid, false) <= 0) {
write_lock(&nm_i->nat_tree_lock);
__del_from_nat_cache(nm_i, ne);
write_unlock(&nm_i->nat_tree_lock);
-
- /* We can reuse this freed nid at this point */
- add_free_nid(NM_I(sbi), nid);
} else {
write_lock(&nm_i->nat_tree_lock);
__clear_nat_cache_dirty(nm_i, ne);
@@ -1661,19 +1705,16 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
spin_lock_init(&nm_i->free_nid_list_lock);
rwlock_init(&nm_i->nat_tree_lock);
- nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP);
- nm_i->init_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
-
- nm_i->nat_bitmap = kzalloc(nm_i->bitmap_size, GFP_KERNEL);
- if (!nm_i->nat_bitmap)
- return -ENOMEM;
+ nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP);
version_bitmap = __bitmap_ptr(sbi, NAT_BITMAP);
if (!version_bitmap)
return -EFAULT;
- /* copy version bitmap */
- memcpy(nm_i->nat_bitmap, version_bitmap, nm_i->bitmap_size);
+ nm_i->nat_bitmap = kmemdup(version_bitmap, nm_i->bitmap_size,
+ GFP_KERNEL);
+ if (!nm_i->nat_bitmap)
+ return -ENOMEM;
return 0;
}
diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h
index afdb130f782e..0a2d72f0024d 100644
--- a/fs/f2fs/node.h
+++ b/fs/f2fs/node.h
@@ -29,6 +29,9 @@
/* vector size for gang look-up from nat cache that consists of radix tree */
#define NATVEC_SIZE 64
+/* return value for read_node_page */
+#define LOCKED_PAGE 1
+
/*
* For node information
*/
@@ -239,7 +242,7 @@ static inline bool IS_DNODE(struct page *node_page)
return false;
if (ofs >= 6 + 2 * NIDS_PER_BLOCK) {
ofs -= 6 + 2 * NIDS_PER_BLOCK;
- if ((long int)ofs % (NIDS_PER_BLOCK + 1))
+ if (!((long int)ofs % (NIDS_PER_BLOCK + 1)))
return false;
}
return true;
@@ -277,6 +280,21 @@ static inline int is_cold_file(struct inode *inode)
return F2FS_I(inode)->i_advise & FADVISE_COLD_BIT;
}
+static inline void set_cold_file(struct inode *inode)
+{
+ F2FS_I(inode)->i_advise |= FADVISE_COLD_BIT;
+}
+
+static inline int is_cp_file(struct inode *inode)
+{
+ return F2FS_I(inode)->i_advise & FADVISE_CP_BIT;
+}
+
+static inline void set_cp_file(struct inode *inode)
+{
+ F2FS_I(inode)->i_advise |= FADVISE_CP_BIT;
+}
+
static inline int is_cold_data(struct page *page)
{
return PageChecked(page);
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index b235215ac138..60c8a5097058 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -53,7 +53,7 @@ static int recover_dentry(struct page *ipage, struct inode *inode)
dir = f2fs_iget(inode->i_sb, le32_to_cpu(raw_inode->i_pino));
if (IS_ERR(dir)) {
- err = -EINVAL;
+ err = PTR_ERR(dir);
goto out;
}
@@ -112,11 +112,14 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
while (1) {
struct fsync_inode_entry *entry;
- if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC))
+ err = f2fs_readpage(sbi, page, blkaddr, READ_SYNC);
+ if (err)
goto out;
+ lock_page(page);
+
if (cp_ver != cpver_of_node(page))
- goto out;
+ goto unlock_out;
if (!is_fsync_dnode(page))
goto next;
@@ -129,24 +132,23 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
FI_INC_LINK);
} else {
if (IS_INODE(page) && is_dent_dnode(page)) {
- if (recover_inode_page(sbi, page)) {
- err = -ENOMEM;
- goto out;
- }
+ err = recover_inode_page(sbi, page);
+ if (err)
+ goto unlock_out;
}
/* add this fsync inode to the list */
entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS);
if (!entry) {
err = -ENOMEM;
- goto out;
+ goto unlock_out;
}
entry->inode = f2fs_iget(sbi->sb, ino_of_node(page));
if (IS_ERR(entry->inode)) {
err = PTR_ERR(entry->inode);
kmem_cache_free(fsync_entry_slab, entry);
- goto out;
+ goto unlock_out;
}
list_add_tail(&entry->list, head);
@@ -154,16 +156,20 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
}
if (IS_INODE(page)) {
err = recover_inode(entry->inode, page);
- if (err)
- goto out;
+ if (err == -ENOENT) {
+ goto next;
+ } else if (err) {
+ err = -EINVAL;
+ goto unlock_out;
+ }
}
next:
/* check next segment */
blkaddr = next_blkaddr_of_node(page);
- ClearPageUptodate(page);
}
-out:
+unlock_out:
unlock_page(page);
+out:
__free_pages(page, 0);
return err;
}
@@ -232,13 +238,15 @@ static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
iput(inode);
}
-static void do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
+static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
struct page *page, block_t blkaddr)
{
unsigned int start, end;
struct dnode_of_data dn;
struct f2fs_summary sum;
struct node_info ni;
+ int err = 0;
+ int ilock;
start = start_bidx_of_node(ofs_of_node(page));
if (IS_INODE(page))
@@ -246,9 +254,14 @@ static void do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
else
end = start + ADDRS_PER_BLOCK;
+ ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0);
- if (get_dnode_of_data(&dn, start, 0))
- return;
+
+ err = get_dnode_of_data(&dn, start, ALLOC_NODE);
+ if (err) {
+ mutex_unlock_op(sbi, ilock);
+ return err;
+ }
wait_on_page_writeback(dn.node_page);
@@ -293,14 +306,17 @@ static void do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
recover_node_page(sbi, dn.node_page, &sum, &ni, blkaddr);
f2fs_put_dnode(&dn);
+ mutex_unlock_op(sbi, ilock);
+ return 0;
}
-static void recover_data(struct f2fs_sb_info *sbi,
+static int recover_data(struct f2fs_sb_info *sbi,
struct list_head *head, int type)
{
unsigned long long cp_ver = le64_to_cpu(sbi->ckpt->checkpoint_ver);
struct curseg_info *curseg;
struct page *page;
+ int err = 0;
block_t blkaddr;
/* get node pages in the current segment */
@@ -310,23 +326,29 @@ static void recover_data(struct f2fs_sb_info *sbi,
/* read node page */
page = alloc_page(GFP_NOFS | __GFP_ZERO);
if (IS_ERR(page))
- return;
+ return -ENOMEM;
+
lock_page(page);
while (1) {
struct fsync_inode_entry *entry;
- if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC))
+ err = f2fs_readpage(sbi, page, blkaddr, READ_SYNC);
+ if (err)
goto out;
+ lock_page(page);
+
if (cp_ver != cpver_of_node(page))
- goto out;
+ goto unlock_out;
entry = get_fsync_inode(head, ino_of_node(page));
if (!entry)
goto next;
- do_recover_data(sbi, entry->inode, page, blkaddr);
+ err = do_recover_data(sbi, entry->inode, page, blkaddr);
+ if (err)
+ goto out;
if (entry->blkaddr == blkaddr) {
iput(entry->inode);
@@ -336,28 +358,32 @@ static void recover_data(struct f2fs_sb_info *sbi,
next:
/* check next segment */
blkaddr = next_blkaddr_of_node(page);
- ClearPageUptodate(page);
}
-out:
+unlock_out:
unlock_page(page);
+out:
__free_pages(page, 0);
- allocate_new_segments(sbi);
+ if (!err)
+ allocate_new_segments(sbi);
+ return err;
}
-void recover_fsync_data(struct f2fs_sb_info *sbi)
+int recover_fsync_data(struct f2fs_sb_info *sbi)
{
struct list_head inode_list;
+ int err;
fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
sizeof(struct fsync_inode_entry), NULL);
if (unlikely(!fsync_entry_slab))
- return;
+ return -ENOMEM;
INIT_LIST_HEAD(&inode_list);
/* step #1: find fsynced inode numbers */
- if (find_fsync_dnodes(sbi, &inode_list))
+ err = find_fsync_dnodes(sbi, &inode_list);
+ if (err)
goto out;
if (list_empty(&inode_list))
@@ -365,11 +391,12 @@ void recover_fsync_data(struct f2fs_sb_info *sbi)
/* step #2: recover data */
sbi->por_doing = 1;
- recover_data(sbi, &inode_list, CURSEG_WARM_NODE);
+ err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE);
sbi->por_doing = 0;
BUG_ON(!list_empty(&inode_list));
out:
destroy_fsync_dnodes(sbi, &inode_list);
kmem_cache_destroy(fsync_entry_slab);
write_checkpoint(sbi, false);
+ return err;
}
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 777f17e496e6..d8e84e49a5c3 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -18,6 +18,7 @@
#include "f2fs.h"
#include "segment.h"
#include "node.h"
+#include <trace/events/f2fs.h>
/*
* This function balances dirty node and dentry pages.
@@ -49,9 +50,20 @@ static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
if (dirty_type == DIRTY) {
struct seg_entry *sentry = get_seg_entry(sbi, segno);
+ enum dirty_type t = DIRTY_HOT_DATA;
+
dirty_type = sentry->type;
+
if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type]))
dirty_i->nr_dirty[dirty_type]++;
+
+ /* Only one bitmap should be set */
+ for (; t <= DIRTY_COLD_NODE; t++) {
+ if (t == dirty_type)
+ continue;
+ if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t]))
+ dirty_i->nr_dirty[t]--;
+ }
}
}
@@ -64,13 +76,16 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
dirty_i->nr_dirty[dirty_type]--;
if (dirty_type == DIRTY) {
- struct seg_entry *sentry = get_seg_entry(sbi, segno);
- dirty_type = sentry->type;
- if (test_and_clear_bit(segno,
- dirty_i->dirty_segmap[dirty_type]))
- dirty_i->nr_dirty[dirty_type]--;
- clear_bit(segno, dirty_i->victim_segmap[FG_GC]);
- clear_bit(segno, dirty_i->victim_segmap[BG_GC]);
+ enum dirty_type t = DIRTY_HOT_DATA;
+
+ /* clear all the bitmaps */
+ for (; t <= DIRTY_COLD_NODE; t++)
+ if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t]))
+ dirty_i->nr_dirty[t]--;
+
+ if (get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0)
+ clear_bit(GET_SECNO(sbi, segno),
+ dirty_i->victim_secmap);
}
}
@@ -296,13 +311,12 @@ static void write_sum_page(struct f2fs_sb_info *sbi,
f2fs_put_page(page, 1);
}
-static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi,
- int ofs_unit, int type)
+static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi, int type)
{
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
unsigned long *prefree_segmap = dirty_i->dirty_segmap[PRE];
- unsigned int segno, next_segno, i;
- int ofs = 0;
+ unsigned int segno;
+ unsigned int ofs = 0;
/*
* If there is not enough reserved sections,
@@ -318,28 +332,46 @@ static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi,
if (IS_NODESEG(type))
return NULL_SEGNO;
next:
- segno = find_next_bit(prefree_segmap, TOTAL_SEGS(sbi), ofs++);
- ofs = ((segno / ofs_unit) * ofs_unit) + ofs_unit;
+ segno = find_next_bit(prefree_segmap, TOTAL_SEGS(sbi), ofs);
+ ofs += sbi->segs_per_sec;
+
if (segno < TOTAL_SEGS(sbi)) {
+ int i;
+
/* skip intermediate segments in a section */
- if (segno % ofs_unit)
+ if (segno % sbi->segs_per_sec)
goto next;
- /* skip if whole section is not prefree */
- next_segno = find_next_zero_bit(prefree_segmap,
- TOTAL_SEGS(sbi), segno + 1);
- if (next_segno - segno < ofs_unit)
+ /* skip if the section is currently used */
+ if (sec_usage_check(sbi, GET_SECNO(sbi, segno)))
goto next;
+ /* skip if whole section is not prefree */
+ for (i = 1; i < sbi->segs_per_sec; i++)
+ if (!test_bit(segno + i, prefree_segmap))
+ goto next;
+
/* skip if whole section was not free at the last checkpoint */
- for (i = 0; i < ofs_unit; i++)
- if (get_seg_entry(sbi, segno)->ckpt_valid_blocks)
+ for (i = 0; i < sbi->segs_per_sec; i++)
+ if (get_seg_entry(sbi, segno + i)->ckpt_valid_blocks)
goto next;
+
return segno;
}
return NULL_SEGNO;
}
+static int is_next_segment_free(struct f2fs_sb_info *sbi, int type)
+{
+ struct curseg_info *curseg = CURSEG_I(sbi, type);
+ unsigned int segno = curseg->segno;
+ struct free_segmap_info *free_i = FREE_I(sbi);
+
+ if (segno + 1 < TOTAL_SEGS(sbi) && (segno + 1) % sbi->segs_per_sec)
+ return !test_bit(segno + 1, free_i->free_segmap);
+ return 0;
+}
+
/*
* Find a new segment from the free segments bitmap to right order
* This function should be returned with success, otherwise BUG
@@ -348,9 +380,8 @@ static void get_new_segment(struct f2fs_sb_info *sbi,
unsigned int *newseg, bool new_sec, int dir)
{
struct free_segmap_info *free_i = FREE_I(sbi);
- unsigned int total_secs = sbi->total_sections;
unsigned int segno, secno, zoneno;
- unsigned int total_zones = sbi->total_sections / sbi->secs_per_zone;
+ unsigned int total_zones = TOTAL_SECS(sbi) / sbi->secs_per_zone;
unsigned int hint = *newseg / sbi->segs_per_sec;
unsigned int old_zoneno = GET_ZONENO_FROM_SEGNO(sbi, *newseg);
unsigned int left_start = hint;
@@ -363,16 +394,17 @@ static void get_new_segment(struct f2fs_sb_info *sbi,
if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) {
segno = find_next_zero_bit(free_i->free_segmap,
TOTAL_SEGS(sbi), *newseg + 1);
- if (segno < TOTAL_SEGS(sbi))
+ if (segno - *newseg < sbi->segs_per_sec -
+ (*newseg % sbi->segs_per_sec))
goto got_it;
}
find_other_zone:
- secno = find_next_zero_bit(free_i->free_secmap, total_secs, hint);
- if (secno >= total_secs) {
+ secno = find_next_zero_bit(free_i->free_secmap, TOTAL_SECS(sbi), hint);
+ if (secno >= TOTAL_SECS(sbi)) {
if (dir == ALLOC_RIGHT) {
secno = find_next_zero_bit(free_i->free_secmap,
- total_secs, 0);
- BUG_ON(secno >= total_secs);
+ TOTAL_SECS(sbi), 0);
+ BUG_ON(secno >= TOTAL_SECS(sbi));
} else {
go_left = 1;
left_start = hint - 1;
@@ -387,8 +419,8 @@ find_other_zone:
continue;
}
left_start = find_next_zero_bit(free_i->free_secmap,
- total_secs, 0);
- BUG_ON(left_start >= total_secs);
+ TOTAL_SECS(sbi), 0);
+ BUG_ON(left_start >= TOTAL_SECS(sbi));
break;
}
secno = left_start;
@@ -561,20 +593,20 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
int type, bool force)
{
struct curseg_info *curseg = CURSEG_I(sbi, type);
- unsigned int ofs_unit;
if (force) {
new_curseg(sbi, type, true);
goto out;
}
- ofs_unit = need_SSR(sbi) ? 1 : sbi->segs_per_sec;
- curseg->next_segno = check_prefree_segments(sbi, ofs_unit, type);
+ curseg->next_segno = check_prefree_segments(sbi, type);
if (curseg->next_segno != NULL_SEGNO)
change_curseg(sbi, type, false);
else if (type == CURSEG_WARM_NODE)
new_curseg(sbi, type, false);
+ else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type))
+ new_curseg(sbi, type, false);
else if (need_SSR(sbi) && get_ssr_segment(sbi, type))
change_curseg(sbi, type, true);
else
@@ -656,10 +688,16 @@ static void do_submit_bio(struct f2fs_sb_info *sbi,
if (type >= META_FLUSH)
rw = WRITE_FLUSH_FUA;
+ if (btype == META)
+ rw |= REQ_META;
+
if (sbi->bio[btype]) {
struct bio_private *p = sbi->bio[btype]->bi_private;
p->sbi = sbi;
sbi->bio[btype]->bi_end_io = f2fs_end_io_write;
+
+ trace_f2fs_do_submit_bio(sbi->sb, btype, sync, sbi->bio[btype]);
+
if (type == META_FLUSH) {
DECLARE_COMPLETION_ONSTACK(wait);
p->is_sync = true;
@@ -696,7 +734,7 @@ static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page,
do_submit_bio(sbi, type, false);
alloc_new:
if (sbi->bio[type] == NULL) {
- sbi->bio[type] = f2fs_bio_alloc(bdev, bio_get_nr_vecs(bdev));
+ sbi->bio[type] = f2fs_bio_alloc(bdev, max_hw_blocks(sbi));
sbi->bio[type]->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr);
/*
* The end_io will be assigned at the sumbission phase.
@@ -714,6 +752,7 @@ alloc_new:
sbi->last_block_in_bio[type] = blk_addr;
up_write(&sbi->bio_sem);
+ trace_f2fs_submit_write_page(page, blk_addr, type);
}
static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type)
@@ -1390,7 +1429,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
}
if (sbi->segs_per_sec > 1) {
- sit_i->sec_entries = vzalloc(sbi->total_sections *
+ sit_i->sec_entries = vzalloc(TOTAL_SECS(sbi) *
sizeof(struct sec_entry));
if (!sit_i->sec_entries)
return -ENOMEM;
@@ -1403,10 +1442,9 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
bitmap_size = __bitmap_size(sbi, SIT_BITMAP);
src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP);
- dst_bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+ dst_bitmap = kmemdup(src_bitmap, bitmap_size, GFP_KERNEL);
if (!dst_bitmap)
return -ENOMEM;
- memcpy(dst_bitmap, src_bitmap, bitmap_size);
/* init SIT information */
sit_i->s_ops = &default_salloc_ops;
@@ -1442,7 +1480,7 @@ static int build_free_segmap(struct f2fs_sb_info *sbi)
if (!free_i->free_segmap)
return -ENOMEM;
- sec_bitmap_size = f2fs_bitmap_size(sbi->total_sections);
+ sec_bitmap_size = f2fs_bitmap_size(TOTAL_SECS(sbi));
free_i->free_secmap = kmalloc(sec_bitmap_size, GFP_KERNEL);
if (!free_i->free_secmap)
return -ENOMEM;
@@ -1559,14 +1597,13 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
}
}
-static int init_victim_segmap(struct f2fs_sb_info *sbi)
+static int init_victim_secmap(struct f2fs_sb_info *sbi)
{
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
- unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi));
+ unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SECS(sbi));
- dirty_i->victim_segmap[FG_GC] = kzalloc(bitmap_size, GFP_KERNEL);
- dirty_i->victim_segmap[BG_GC] = kzalloc(bitmap_size, GFP_KERNEL);
- if (!dirty_i->victim_segmap[FG_GC] || !dirty_i->victim_segmap[BG_GC])
+ dirty_i->victim_secmap = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!dirty_i->victim_secmap)
return -ENOMEM;
return 0;
}
@@ -1593,7 +1630,7 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi)
}
init_dirty_segmap(sbi);
- return init_victim_segmap(sbi);
+ return init_victim_secmap(sbi);
}
/*
@@ -1680,18 +1717,10 @@ static void discard_dirty_segmap(struct f2fs_sb_info *sbi,
mutex_unlock(&dirty_i->seglist_lock);
}
-void reset_victim_segmap(struct f2fs_sb_info *sbi)
-{
- unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi));
- memset(DIRTY_I(sbi)->victim_segmap[FG_GC], 0, bitmap_size);
-}
-
-static void destroy_victim_segmap(struct f2fs_sb_info *sbi)
+static void destroy_victim_secmap(struct f2fs_sb_info *sbi)
{
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
-
- kfree(dirty_i->victim_segmap[FG_GC]);
- kfree(dirty_i->victim_segmap[BG_GC]);
+ kfree(dirty_i->victim_secmap);
}
static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
@@ -1706,7 +1735,7 @@ static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
for (i = 0; i < NR_DIRTY_TYPE; i++)
discard_dirty_segmap(sbi, i);
- destroy_victim_segmap(sbi);
+ destroy_victim_secmap(sbi);
SM_I(sbi)->dirty_info = NULL;
kfree(dirty_i);
}
diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h
index 552dadbb2327..062424a0e4c3 100644
--- a/fs/f2fs/segment.h
+++ b/fs/f2fs/segment.h
@@ -8,10 +8,13 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/blkdev.h>
+
/* constant macro */
#define NULL_SEGNO ((unsigned int)(~0))
+#define NULL_SECNO ((unsigned int)(~0))
-/* V: Logical segment # in volume, R: Relative segment # in main area */
+/* L: Logical segment # in volume, R: Relative segment # in main area */
#define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno)
#define GET_R2L_SEGNO(free_i, segno) (segno + free_i->start_segno)
@@ -23,13 +26,13 @@
((t == CURSEG_HOT_NODE) || (t == CURSEG_COLD_NODE) || \
(t == CURSEG_WARM_NODE))
-#define IS_CURSEG(sbi, segno) \
- ((segno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \
- (segno == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \
- (segno == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \
- (segno == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \
- (segno == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \
- (segno == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno))
+#define IS_CURSEG(sbi, seg) \
+ ((seg == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \
+ (seg == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \
+ (seg == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \
+ (seg == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \
+ (seg == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \
+ (seg == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno))
#define IS_CURSEC(sbi, secno) \
((secno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \
@@ -81,9 +84,12 @@
#define f2fs_bitmap_size(nr) \
(BITS_TO_LONGS(nr) * sizeof(unsigned long))
#define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments)
+#define TOTAL_SECS(sbi) (sbi->total_sections)
#define SECTOR_FROM_BLOCK(sbi, blk_addr) \
(blk_addr << ((sbi)->log_blocksize - F2FS_LOG_SECTOR_SIZE))
+#define SECTOR_TO_BLOCK(sbi, sectors) \
+ (sectors >> ((sbi)->log_blocksize - F2FS_LOG_SECTOR_SIZE))
/* during checkpoint, bio_private is used to synchronize the last bio */
struct bio_private {
@@ -213,7 +219,7 @@ struct dirty_seglist_info {
unsigned long *dirty_segmap[NR_DIRTY_TYPE];
struct mutex seglist_lock; /* lock for segment bitmaps */
int nr_dirty[NR_DIRTY_TYPE]; /* # of dirty segments */
- unsigned long *victim_segmap[2]; /* BG_GC, FG_GC */
+ unsigned long *victim_secmap; /* background GC victims */
};
/* victim selection function for cleaning and SSR */
@@ -464,8 +470,7 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed)
static inline int utilization(struct f2fs_sb_info *sbi)
{
- return (long int)valid_user_blocks(sbi) * 100 /
- (long int)sbi->user_block_count;
+ return div_u64(valid_user_blocks(sbi) * 100, sbi->user_block_count);
}
/*
@@ -616,3 +621,17 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count)
- (base + 1) + type;
}
+
+static inline bool sec_usage_check(struct f2fs_sb_info *sbi, unsigned int secno)
+{
+ if (IS_CURSEC(sbi, secno) || (sbi->cur_victim_sec == secno))
+ return true;
+ return false;
+}
+
+static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi)
+{
+ struct block_device *bdev = sbi->sb->s_bdev;
+ struct request_queue *q = bdev_get_queue(bdev);
+ return SECTOR_TO_BLOCK(sbi, queue_max_sectors(q));
+}
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 62e017743af6..8555f7df82c7 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -12,7 +12,6 @@
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/statfs.h>
-#include <linux/proc_fs.h>
#include <linux/buffer_head.h>
#include <linux/backing-dev.h>
#include <linux/kthread.h>
@@ -21,12 +20,17 @@
#include <linux/seq_file.h>
#include <linux/random.h>
#include <linux/exportfs.h>
+#include <linux/blkdev.h>
#include <linux/f2fs_fs.h>
#include "f2fs.h"
#include "node.h"
+#include "segment.h"
#include "xattr.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/f2fs.h>
+
static struct kmem_cache *f2fs_inode_cachep;
enum {
@@ -94,6 +98,20 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
return &fi->vfs_inode;
}
+static int f2fs_drop_inode(struct inode *inode)
+{
+ /*
+ * This is to avoid a deadlock condition like below.
+ * writeback_single_inode(inode)
+ * - f2fs_write_data_page
+ * - f2fs_gc -> iput -> evict
+ * - inode_wait_for_writeback(inode)
+ */
+ if (!inode_unhashed(inode) && inode->i_state & I_SYNC)
+ return 0;
+ return generic_drop_inode(inode);
+}
+
static void f2fs_i_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
@@ -132,13 +150,18 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
+ trace_f2fs_sync_fs(sb, sync);
+
if (!sbi->s_dirty && !get_pages(sbi, F2FS_DIRTY_NODES))
return 0;
- if (sync)
+ if (sync) {
+ mutex_lock(&sbi->gc_mutex);
write_checkpoint(sbi, false);
- else
+ mutex_unlock(&sbi->gc_mutex);
+ } else {
f2fs_balance_fs(sbi);
+ }
return 0;
}
@@ -180,7 +203,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_files = sbi->total_node_count;
buf->f_ffree = sbi->total_node_count - valid_inode_count(sbi);
- buf->f_namelen = F2FS_MAX_NAME_LEN;
+ buf->f_namelen = F2FS_NAME_LEN;
buf->f_fsid.val[0] = (u32)id;
buf->f_fsid.val[1] = (u32)(id >> 32);
@@ -223,6 +246,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
static struct super_operations f2fs_sops = {
.alloc_inode = f2fs_alloc_inode,
+ .drop_inode = f2fs_drop_inode,
.destroy_inode = f2fs_destroy_inode,
.write_inode = f2fs_write_inode,
.show_options = f2fs_show_options,
@@ -457,6 +481,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
sbi->root_ino_num = le32_to_cpu(raw_super->root_ino);
sbi->node_ino_num = le32_to_cpu(raw_super->node_ino);
sbi->meta_ino_num = le32_to_cpu(raw_super->meta_ino);
+ sbi->cur_victim_sec = NULL_SECNO;
for (i = 0; i < NR_COUNT_TYPE; i++)
atomic_set(&sbi->nr_pages[i], 0);
@@ -473,7 +498,7 @@ static int validate_superblock(struct super_block *sb,
if (!*raw_super_buf) {
f2fs_msg(sb, KERN_ERR, "unable to read %s superblock",
super);
- return 1;
+ return -EIO;
}
*raw_super = (struct f2fs_super_block *)
@@ -485,7 +510,7 @@ static int validate_superblock(struct super_block *sb,
f2fs_msg(sb, KERN_ERR, "Can't find a valid F2FS filesystem "
"in %s superblock", super);
- return 1;
+ return -EINVAL;
}
static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
@@ -508,9 +533,12 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
goto free_sbi;
}
- if (validate_superblock(sb, &raw_super, &raw_super_buf, 0)) {
+ err = validate_superblock(sb, &raw_super, &raw_super_buf, 0);
+ if (err) {
brelse(raw_super_buf);
- if (validate_superblock(sb, &raw_super, &raw_super_buf, 1))
+ /* check secondary superblock when primary failed */
+ err = validate_superblock(sb, &raw_super, &raw_super_buf, 1);
+ if (err)
goto free_sb_buf;
}
/* init some FS parameters */
@@ -525,7 +553,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
set_opt(sbi, POSIX_ACL);
#endif
/* parse mount options */
- if (parse_options(sb, sbi, (char *)data))
+ err = parse_options(sb, sbi, (char *)data);
+ if (err)
goto free_sb_buf;
sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize));
@@ -547,11 +576,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
sbi->raw_super = raw_super;
sbi->raw_super_buf = raw_super_buf;
mutex_init(&sbi->gc_mutex);
- mutex_init(&sbi->write_inode);
mutex_init(&sbi->writepages);
mutex_init(&sbi->cp_mutex);
- for (i = 0; i < NR_LOCK_TYPE; i++)
+ for (i = 0; i < NR_GLOBAL_LOCKS; i++)
mutex_init(&sbi->fs_lock[i]);
+ mutex_init(&sbi->node_write);
sbi->por_doing = 0;
spin_lock_init(&sbi->stat_lock);
init_rwsem(&sbi->bio_sem);
@@ -638,8 +667,12 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
}
/* recover fsynced data */
- if (!test_opt(sbi, DISABLE_ROLL_FORWARD))
- recover_fsync_data(sbi);
+ if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
+ err = recover_fsync_data(sbi);
+ if (err)
+ f2fs_msg(sb, KERN_ERR,
+ "Cannot recover all fsync data errno=%ld", err);
+ }
/* After POR, we can run background GC thread */
err = start_gc_thread(sbi);
@@ -650,6 +683,14 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
if (err)
goto fail;
+ if (test_opt(sbi, DISCARD)) {
+ struct request_queue *q = bdev_get_queue(sb->s_bdev);
+ if (!blk_queue_discard(q))
+ f2fs_msg(sb, KERN_WARNING,
+ "mounting with \"discard\" option, but "
+ "the device does not support discard");
+ }
+
return 0;
fail:
stop_gc_thread(sbi);
diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
index 8038c0496504..0b02dce31356 100644
--- a/fs/f2fs/xattr.c
+++ b/fs/f2fs/xattr.c
@@ -307,27 +307,30 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
int error, found, free, newsize;
size_t name_len;
char *pval;
+ int ilock;
if (name == NULL)
return -EINVAL;
- name_len = strlen(name);
if (value == NULL)
value_len = 0;
- if (name_len > 255 || value_len > MAX_VALUE_LEN)
+ name_len = strlen(name);
+
+ if (name_len > F2FS_NAME_LEN || value_len > MAX_VALUE_LEN)
return -ERANGE;
f2fs_balance_fs(sbi);
- mutex_lock_op(sbi, NODE_NEW);
+ ilock = mutex_lock_op(sbi);
+
if (!fi->i_xattr_nid) {
/* Allocate new attribute block */
struct dnode_of_data dn;
if (!alloc_nid(sbi, &fi->i_xattr_nid)) {
- mutex_unlock_op(sbi, NODE_NEW);
- return -ENOSPC;
+ error = -ENOSPC;
+ goto exit;
}
set_new_dnode(&dn, inode, NULL, NULL, fi->i_xattr_nid);
mark_inode_dirty(inode);
@@ -336,8 +339,8 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
if (IS_ERR(page)) {
alloc_nid_failed(sbi, fi->i_xattr_nid);
fi->i_xattr_nid = 0;
- mutex_unlock_op(sbi, NODE_NEW);
- return PTR_ERR(page);
+ error = PTR_ERR(page);
+ goto exit;
}
alloc_nid_done(sbi, fi->i_xattr_nid);
@@ -349,8 +352,8 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
/* The inode already has an extended attribute block. */
page = get_node_page(sbi, fi->i_xattr_nid);
if (IS_ERR(page)) {
- mutex_unlock_op(sbi, NODE_NEW);
- return PTR_ERR(page);
+ error = PTR_ERR(page);
+ goto exit;
}
base_addr = page_address(page);
@@ -432,12 +435,13 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
inode->i_ctime = CURRENT_TIME;
clear_inode_flag(fi, FI_ACL_MODE);
}
- f2fs_write_inode(inode, NULL);
- mutex_unlock_op(sbi, NODE_NEW);
+ update_inode_page(inode);
+ mutex_unlock_op(sbi, ilock);
return 0;
cleanup:
f2fs_put_page(page, 1);
- mutex_unlock_op(sbi, NODE_NEW);
+exit:
+ mutex_unlock_op(sbi, ilock);
return error;
}
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 296ea08cbe4b..6aa252141158 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -19,6 +19,7 @@
#include <linux/mpage.h>
#include <linux/buffer_head.h>
#include <linux/mount.h>
+#include <linux/aio.h>
#include <linux/vfs.h>
#include <linux/parser.h>
#include <linux/uio.h>
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 99ba401551a3..b6b092696a5d 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -22,7 +22,6 @@
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/kthread.h>
-#include <linux/freezer.h>
#include <linux/writeback.h>
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
@@ -88,20 +87,6 @@ static inline struct inode *wb_inode(struct list_head *head)
#define CREATE_TRACE_POINTS
#include <trace/events/writeback.h>
-/* Wakeup flusher thread or forker thread to fork it. Requires bdi->wb_lock. */
-static void bdi_wakeup_flusher(struct backing_dev_info *bdi)
-{
- if (bdi->wb.task) {
- wake_up_process(bdi->wb.task);
- } else {
- /*
- * The bdi thread isn't there, wake up the forker thread which
- * will create and run it.
- */
- wake_up_process(default_backing_dev_info.wb.task);
- }
-}
-
static void bdi_queue_work(struct backing_dev_info *bdi,
struct wb_writeback_work *work)
{
@@ -109,10 +94,9 @@ static void bdi_queue_work(struct backing_dev_info *bdi,
spin_lock_bh(&bdi->wb_lock);
list_add_tail(&work->list, &bdi->work_list);
- if (!bdi->wb.task)
- trace_writeback_nothread(bdi, work);
- bdi_wakeup_flusher(bdi);
spin_unlock_bh(&bdi->wb_lock);
+
+ mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
}
static void
@@ -127,10 +111,8 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
*/
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
- if (bdi->wb.task) {
- trace_writeback_nowork(bdi);
- wake_up_process(bdi->wb.task);
- }
+ trace_writeback_nowork(bdi);
+ mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
return;
}
@@ -177,9 +159,7 @@ void bdi_start_background_writeback(struct backing_dev_info *bdi)
* writeback as soon as there is no other work to do.
*/
trace_writeback_wake_background(bdi);
- spin_lock_bh(&bdi->wb_lock);
- bdi_wakeup_flusher(bdi);
- spin_unlock_bh(&bdi->wb_lock);
+ mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
}
/*
@@ -1020,67 +1000,49 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait)
/*
* Handle writeback of dirty data for the device backed by this bdi. Also
- * wakes up periodically and does kupdated style flushing.
+ * reschedules periodically and does kupdated style flushing.
*/
-int bdi_writeback_thread(void *data)
+void bdi_writeback_workfn(struct work_struct *work)
{
- struct bdi_writeback *wb = data;
+ struct bdi_writeback *wb = container_of(to_delayed_work(work),
+ struct bdi_writeback, dwork);
struct backing_dev_info *bdi = wb->bdi;
long pages_written;
set_worker_desc("flush-%s", dev_name(bdi->dev));
current->flags |= PF_SWAPWRITE;
- set_freezable();
- wb->last_active = jiffies;
-
- /*
- * Our parent may run at a different priority, just set us to normal
- */
- set_user_nice(current, 0);
-
- trace_writeback_thread_start(bdi);
- while (!kthread_freezable_should_stop(NULL)) {
+ if (likely(!current_is_workqueue_rescuer() ||
+ list_empty(&bdi->bdi_list))) {
/*
- * Remove own delayed wake-up timer, since we are already awake
- * and we'll take care of the periodic write-back.
+ * The normal path. Keep writing back @bdi until its
+ * work_list is empty. Note that this path is also taken
+ * if @bdi is shutting down even when we're running off the
+ * rescuer as work_list needs to be drained.
*/
- del_timer(&wb->wakeup_timer);
-
- pages_written = wb_do_writeback(wb, 0);
-
+ do {
+ pages_written = wb_do_writeback(wb, 0);
+ trace_writeback_pages_written(pages_written);
+ } while (!list_empty(&bdi->work_list));
+ } else {
+ /*
+ * bdi_wq can't get enough workers and we're running off
+ * the emergency worker. Don't hog it. Hopefully, 1024 is
+ * enough for efficient IO.
+ */
+ pages_written = writeback_inodes_wb(&bdi->wb, 1024,
+ WB_REASON_FORKER_THREAD);
trace_writeback_pages_written(pages_written);
-
- if (pages_written)
- wb->last_active = jiffies;
-
- set_current_state(TASK_INTERRUPTIBLE);
- if (!list_empty(&bdi->work_list) || kthread_should_stop()) {
- __set_current_state(TASK_RUNNING);
- continue;
- }
-
- if (wb_has_dirty_io(wb) && dirty_writeback_interval)
- schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10));
- else {
- /*
- * We have nothing to do, so can go sleep without any
- * timeout and save power. When a work is queued or
- * something is made dirty - we will be woken up.
- */
- schedule();
- }
}
- /* Flush any work that raced with us exiting */
- if (!list_empty(&bdi->work_list))
- wb_do_writeback(wb, 1);
+ if (!list_empty(&bdi->work_list) ||
+ (wb_has_dirty_io(wb) && dirty_writeback_interval))
+ queue_delayed_work(bdi_wq, &wb->dwork,
+ msecs_to_jiffies(dirty_writeback_interval * 10));
- trace_writeback_thread_stop(bdi);
- return 0;
+ current->flags &= ~PF_SWAPWRITE;
}
-
/*
* Start writeback of `nr_pages' pages. If `nr_pages' is zero, write back
* the whole world.
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
index 6f96a8def147..aef34b1e635e 100644
--- a/fs/fuse/cuse.c
+++ b/fs/fuse/cuse.c
@@ -38,6 +38,7 @@
#include <linux/device.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/aio.h>
#include <linux/kdev_t.h>
#include <linux/kthread.h>
#include <linux/list.h>
@@ -92,8 +93,9 @@ static ssize_t cuse_read(struct file *file, char __user *buf, size_t count,
{
loff_t pos = 0;
struct iovec iov = { .iov_base = buf, .iov_len = count };
+ struct fuse_io_priv io = { .async = 0, .file = file };
- return fuse_direct_io(file, &iov, 1, count, &pos, 0);
+ return fuse_direct_io(&io, &iov, 1, count, &pos, 0);
}
static ssize_t cuse_write(struct file *file, const char __user *buf,
@@ -101,12 +103,13 @@ static ssize_t cuse_write(struct file *file, const char __user *buf,
{
loff_t pos = 0;
struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count };
+ struct fuse_io_priv io = { .async = 0, .file = file };
/*
* No locking or generic_write_checks(), the server is
* responsible for locking and sanity checks.
*/
- return fuse_direct_io(file, &iov, 1, count, &pos, 1);
+ return fuse_direct_io(&io, &iov, 1, count, &pos, 1);
}
static int cuse_open(struct inode *inode, struct file *file)
@@ -422,7 +425,7 @@ static int cuse_send_init(struct cuse_conn *cc)
BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE);
- req = fuse_get_req(fc, 1);
+ req = fuse_get_req_for_background(fc, 1);
if (IS_ERR(req)) {
rc = PTR_ERR(req);
goto err;
@@ -504,7 +507,7 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
cc->fc.release = cuse_fc_release;
cc->fc.connected = 1;
- cc->fc.blocked = 0;
+ cc->fc.initialized = 1;
rc = cuse_send_init(cc);
if (rc) {
fuse_conn_put(&cc->fc);
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index a8e10ac8b1ac..f4c05880e4a9 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/freezer.h>
+#include <linux/aio.h>
MODULE_ALIAS_MISCDEV(FUSE_MINOR);
MODULE_ALIAS("devname:fuse");
@@ -112,7 +113,7 @@ static void restore_sigs(sigset_t *oldset)
sigprocmask(SIG_SETMASK, oldset, NULL);
}
-static void __fuse_get_request(struct fuse_req *req)
+void __fuse_get_request(struct fuse_req *req)
{
atomic_inc(&req->count);
}
@@ -131,20 +132,30 @@ static void fuse_req_init_context(struct fuse_req *req)
req->in.h.pid = current->pid;
}
-struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages)
+static bool fuse_block_alloc(struct fuse_conn *fc, bool for_background)
+{
+ return !fc->initialized || (for_background && fc->blocked);
+}
+
+static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages,
+ bool for_background)
{
struct fuse_req *req;
- sigset_t oldset;
- int intr;
int err;
-
atomic_inc(&fc->num_waiting);
- block_sigs(&oldset);
- intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
- restore_sigs(&oldset);
- err = -EINTR;
- if (intr)
- goto out;
+
+ if (fuse_block_alloc(fc, for_background)) {
+ sigset_t oldset;
+ int intr;
+
+ block_sigs(&oldset);
+ intr = wait_event_interruptible_exclusive(fc->blocked_waitq,
+ !fuse_block_alloc(fc, for_background));
+ restore_sigs(&oldset);
+ err = -EINTR;
+ if (intr)
+ goto out;
+ }
err = -ENOTCONN;
if (!fc->connected)
@@ -152,19 +163,35 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages)
req = fuse_request_alloc(npages);
err = -ENOMEM;
- if (!req)
+ if (!req) {
+ if (for_background)
+ wake_up(&fc->blocked_waitq);
goto out;
+ }
fuse_req_init_context(req);
req->waiting = 1;
+ req->background = for_background;
return req;
out:
atomic_dec(&fc->num_waiting);
return ERR_PTR(err);
}
+
+struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages)
+{
+ return __fuse_get_req(fc, npages, false);
+}
EXPORT_SYMBOL_GPL(fuse_get_req);
+struct fuse_req *fuse_get_req_for_background(struct fuse_conn *fc,
+ unsigned npages)
+{
+ return __fuse_get_req(fc, npages, true);
+}
+EXPORT_SYMBOL_GPL(fuse_get_req_for_background);
+
/*
* Return request in fuse_file->reserved_req. However that may
* currently be in use. If that is the case, wait for it to become
@@ -226,19 +253,31 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc,
struct fuse_req *req;
atomic_inc(&fc->num_waiting);
- wait_event(fc->blocked_waitq, !fc->blocked);
+ wait_event(fc->blocked_waitq, fc->initialized);
req = fuse_request_alloc(0);
if (!req)
req = get_reserved_req(fc, file);
fuse_req_init_context(req);
req->waiting = 1;
+ req->background = 0;
return req;
}
void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
{
if (atomic_dec_and_test(&req->count)) {
+ if (unlikely(req->background)) {
+ /*
+ * We get here in the unlikely case that a background
+ * request was allocated but not sent
+ */
+ spin_lock(&fc->lock);
+ if (!fc->blocked)
+ wake_up(&fc->blocked_waitq);
+ spin_unlock(&fc->lock);
+ }
+
if (req->waiting)
atomic_dec(&fc->num_waiting);
@@ -336,10 +375,15 @@ __releases(fc->lock)
list_del(&req->intr_entry);
req->state = FUSE_REQ_FINISHED;
if (req->background) {
- if (fc->num_background == fc->max_background) {
+ req->background = 0;
+
+ if (fc->num_background == fc->max_background)
fc->blocked = 0;
- wake_up_all(&fc->blocked_waitq);
- }
+
+ /* Wake up next waiter, if any */
+ if (!fc->blocked && waitqueue_active(&fc->blocked_waitq))
+ wake_up(&fc->blocked_waitq);
+
if (fc->num_background == fc->congestion_threshold &&
fc->connected && fc->bdi_initialized) {
clear_bdi_congested(&fc->bdi, BLK_RW_SYNC);
@@ -446,6 +490,7 @@ __acquires(fc->lock)
static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
{
+ BUG_ON(req->background);
spin_lock(&fc->lock);
if (!fc->connected)
req->out.h.error = -ENOTCONN;
@@ -473,7 +518,7 @@ EXPORT_SYMBOL_GPL(fuse_request_send);
static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
struct fuse_req *req)
{
- req->background = 1;
+ BUG_ON(!req->background);
fc->num_background++;
if (fc->num_background == fc->max_background)
fc->blocked = 1;
@@ -2075,6 +2120,7 @@ void fuse_abort_conn(struct fuse_conn *fc)
if (fc->connected) {
fc->connected = 0;
fc->blocked = 0;
+ fc->initialized = 1;
end_io_requests(fc);
end_queued_requests(fc);
end_polls(fc);
@@ -2093,6 +2139,7 @@ int fuse_dev_release(struct inode *inode, struct file *file)
spin_lock(&fc->lock);
fc->connected = 0;
fc->blocked = 0;
+ fc->initialized = 1;
end_queued_requests(fc);
end_polls(fc);
wake_up_all(&fc->blocked_waitq);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index ff15522481d4..254df56b847b 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1562,10 +1562,9 @@ void fuse_release_nowrite(struct inode *inode)
* vmtruncate() doesn't allow for this case, so do the rlimit checking
* and the actual truncation by hand.
*/
-static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
- struct file *file)
+int fuse_do_setattr(struct inode *inode, struct iattr *attr,
+ struct file *file)
{
- struct inode *inode = entry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req;
struct fuse_setattr_in inarg;
@@ -1574,9 +1573,6 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
loff_t oldsize;
int err;
- if (!fuse_allow_current_process(fc))
- return -EACCES;
-
if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
attr->ia_valid |= ATTR_FORCE;
@@ -1671,10 +1667,15 @@ error:
static int fuse_setattr(struct dentry *entry, struct iattr *attr)
{
+ struct inode *inode = entry->d_inode;
+
+ if (!fuse_allow_current_process(get_fuse_conn(inode)))
+ return -EACCES;
+
if (attr->ia_valid & ATTR_FILE)
- return fuse_do_setattr(entry, attr, attr->ia_file);
+ return fuse_do_setattr(inode, attr, attr->ia_file);
else
- return fuse_do_setattr(entry, attr, NULL);
+ return fuse_do_setattr(inode, attr, NULL);
}
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index d15c6f21c17f..d1c9b85b3f58 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/compat.h>
#include <linux/swap.h>
+#include <linux/aio.h>
static const struct file_operations fuse_direct_io_file_operations;
@@ -126,11 +127,13 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
struct fuse_req *req = ff->reserved_req;
if (sync) {
+ req->background = 0;
fuse_request_send(ff->fc, req);
path_put(&req->misc.release.path);
fuse_put_request(ff->fc, req);
} else {
req->end = fuse_release_end;
+ req->background = 1;
fuse_request_send_background(ff->fc, req);
}
kfree(ff);
@@ -282,6 +285,7 @@ void fuse_sync_release(struct fuse_file *ff, int flags)
WARN_ON(atomic_read(&ff->count) > 1);
fuse_prepare_release(ff, flags, FUSE_RELEASE);
ff->reserved_req->force = 1;
+ ff->reserved_req->background = 0;
fuse_request_send(ff->fc, ff->reserved_req);
fuse_put_request(ff->fc, ff->reserved_req);
kfree(ff);
@@ -491,9 +495,115 @@ void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,
req->out.args[0].size = count;
}
-static size_t fuse_send_read(struct fuse_req *req, struct file *file,
+static void fuse_release_user_pages(struct fuse_req *req, int write)
+{
+ unsigned i;
+
+ for (i = 0; i < req->num_pages; i++) {
+ struct page *page = req->pages[i];
+ if (write)
+ set_page_dirty_lock(page);
+ put_page(page);
+ }
+}
+
+/**
+ * In case of short read, the caller sets 'pos' to the position of
+ * actual end of fuse request in IO request. Otherwise, if bytes_requested
+ * == bytes_transferred or rw == WRITE, the caller sets 'pos' to -1.
+ *
+ * An example:
+ * User requested DIO read of 64K. It was splitted into two 32K fuse requests,
+ * both submitted asynchronously. The first of them was ACKed by userspace as
+ * fully completed (req->out.args[0].size == 32K) resulting in pos == -1. The
+ * second request was ACKed as short, e.g. only 1K was read, resulting in
+ * pos == 33K.
+ *
+ * Thus, when all fuse requests are completed, the minimal non-negative 'pos'
+ * will be equal to the length of the longest contiguous fragment of
+ * transferred data starting from the beginning of IO request.
+ */
+static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
+{
+ int left;
+
+ spin_lock(&io->lock);
+ if (err)
+ io->err = io->err ? : err;
+ else if (pos >= 0 && (io->bytes < 0 || pos < io->bytes))
+ io->bytes = pos;
+
+ left = --io->reqs;
+ spin_unlock(&io->lock);
+
+ if (!left) {
+ long res;
+
+ if (io->err)
+ res = io->err;
+ else if (io->bytes >= 0 && io->write)
+ res = -EIO;
+ else {
+ res = io->bytes < 0 ? io->size : io->bytes;
+
+ if (!is_sync_kiocb(io->iocb)) {
+ struct path *path = &io->iocb->ki_filp->f_path;
+ struct inode *inode = path->dentry->d_inode;
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_inode *fi = get_fuse_inode(inode);
+
+ spin_lock(&fc->lock);
+ fi->attr_version = ++fc->attr_version;
+ spin_unlock(&fc->lock);
+ }
+ }
+
+ aio_complete(io->iocb, res, 0);
+ kfree(io);
+ }
+}
+
+static void fuse_aio_complete_req(struct fuse_conn *fc, struct fuse_req *req)
+{
+ struct fuse_io_priv *io = req->io;
+ ssize_t pos = -1;
+
+ fuse_release_user_pages(req, !io->write);
+
+ if (io->write) {
+ if (req->misc.write.in.size != req->misc.write.out.size)
+ pos = req->misc.write.in.offset - io->offset +
+ req->misc.write.out.size;
+ } else {
+ if (req->misc.read.in.size != req->out.args[0].size)
+ pos = req->misc.read.in.offset - io->offset +
+ req->out.args[0].size;
+ }
+
+ fuse_aio_complete(io, req->out.h.error, pos);
+}
+
+static size_t fuse_async_req_send(struct fuse_conn *fc, struct fuse_req *req,
+ size_t num_bytes, struct fuse_io_priv *io)
+{
+ spin_lock(&io->lock);
+ io->size += num_bytes;
+ io->reqs++;
+ spin_unlock(&io->lock);
+
+ req->io = io;
+ req->end = fuse_aio_complete_req;
+
+ __fuse_get_request(req);
+ fuse_request_send_background(fc, req);
+
+ return num_bytes;
+}
+
+static size_t fuse_send_read(struct fuse_req *req, struct fuse_io_priv *io,
loff_t pos, size_t count, fl_owner_t owner)
{
+ struct file *file = io->file;
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = ff->fc;
@@ -504,6 +614,10 @@ static size_t fuse_send_read(struct fuse_req *req, struct file *file,
inarg->read_flags |= FUSE_READ_LOCKOWNER;
inarg->lock_owner = fuse_lock_owner_id(fc, owner);
}
+
+ if (io->async)
+ return fuse_async_req_send(fc, req, count, io);
+
fuse_request_send(fc, req);
return req->out.args[0].size;
}
@@ -524,6 +638,7 @@ static void fuse_read_update_size(struct inode *inode, loff_t size,
static int fuse_readpage(struct file *file, struct page *page)
{
+ struct fuse_io_priv io = { .async = 0, .file = file };
struct inode *inode = page->mapping->host;
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req;
@@ -556,7 +671,7 @@ static int fuse_readpage(struct file *file, struct page *page)
req->num_pages = 1;
req->pages[0] = page;
req->page_descs[0].length = count;
- num_read = fuse_send_read(req, file, pos, count, NULL);
+ num_read = fuse_send_read(req, &io, pos, count, NULL);
err = req->out.h.error;
fuse_put_request(fc, req);
@@ -661,7 +776,12 @@ static int fuse_readpages_fill(void *_data, struct page *page)
int nr_alloc = min_t(unsigned, data->nr_pages,
FUSE_MAX_PAGES_PER_REQ);
fuse_send_readpages(req, data->file);
- data->req = req = fuse_get_req(fc, nr_alloc);
+ if (fc->async_read)
+ req = fuse_get_req_for_background(fc, nr_alloc);
+ else
+ req = fuse_get_req(fc, nr_alloc);
+
+ data->req = req;
if (IS_ERR(req)) {
unlock_page(page);
return PTR_ERR(req);
@@ -696,7 +816,10 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
data.file = file;
data.inode = inode;
- data.req = fuse_get_req(fc, nr_alloc);
+ if (fc->async_read)
+ data.req = fuse_get_req_for_background(fc, nr_alloc);
+ else
+ data.req = fuse_get_req(fc, nr_alloc);
data.nr_pages = nr_pages;
err = PTR_ERR(data.req);
if (IS_ERR(data.req))
@@ -758,9 +881,10 @@ static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
req->out.args[0].value = outarg;
}
-static size_t fuse_send_write(struct fuse_req *req, struct file *file,
+static size_t fuse_send_write(struct fuse_req *req, struct fuse_io_priv *io,
loff_t pos, size_t count, fl_owner_t owner)
{
+ struct file *file = io->file;
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = ff->fc;
struct fuse_write_in *inarg = &req->misc.write.in;
@@ -771,6 +895,10 @@ static size_t fuse_send_write(struct fuse_req *req, struct file *file,
inarg->write_flags |= FUSE_WRITE_LOCKOWNER;
inarg->lock_owner = fuse_lock_owner_id(fc, owner);
}
+
+ if (io->async)
+ return fuse_async_req_send(fc, req, count, io);
+
fuse_request_send(fc, req);
return req->misc.write.out.size;
}
@@ -794,11 +922,12 @@ static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file,
size_t res;
unsigned offset;
unsigned i;
+ struct fuse_io_priv io = { .async = 0, .file = file };
for (i = 0; i < req->num_pages; i++)
fuse_wait_on_page_writeback(inode, req->pages[i]->index);
- res = fuse_send_write(req, file, pos, count, NULL);
+ res = fuse_send_write(req, &io, pos, count, NULL);
offset = req->page_descs[0].offset;
count = res;
@@ -1033,18 +1162,6 @@ out:
return written ? written : err;
}
-static void fuse_release_user_pages(struct fuse_req *req, int write)
-{
- unsigned i;
-
- for (i = 0; i < req->num_pages; i++) {
- struct page *page = req->pages[i];
- if (write)
- set_page_dirty_lock(page);
- put_page(page);
- }
-}
-
static inline void fuse_page_descs_length_init(struct fuse_req *req,
unsigned index, unsigned nr_pages)
{
@@ -1146,10 +1263,11 @@ static inline int fuse_iter_npages(const struct iov_iter *ii_p)
return min(npages, FUSE_MAX_PAGES_PER_REQ);
}
-ssize_t fuse_direct_io(struct file *file, const struct iovec *iov,
+ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
unsigned long nr_segs, size_t count, loff_t *ppos,
int write)
{
+ struct file *file = io->file;
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = ff->fc;
size_t nmax = write ? fc->max_write : fc->max_read;
@@ -1175,11 +1293,12 @@ ssize_t fuse_direct_io(struct file *file, const struct iovec *iov,
}
if (write)
- nres = fuse_send_write(req, file, pos, nbytes, owner);
+ nres = fuse_send_write(req, io, pos, nbytes, owner);
else
- nres = fuse_send_read(req, file, pos, nbytes, owner);
+ nres = fuse_send_read(req, io, pos, nbytes, owner);
- fuse_release_user_pages(req, !write);
+ if (!io->async)
+ fuse_release_user_pages(req, !write);
if (req->out.h.error) {
if (!res)
res = req->out.h.error;
@@ -1209,17 +1328,19 @@ ssize_t fuse_direct_io(struct file *file, const struct iovec *iov,
}
EXPORT_SYMBOL_GPL(fuse_direct_io);
-static ssize_t __fuse_direct_read(struct file *file, const struct iovec *iov,
- unsigned long nr_segs, loff_t *ppos)
+static ssize_t __fuse_direct_read(struct fuse_io_priv *io,
+ const struct iovec *iov,
+ unsigned long nr_segs, loff_t *ppos,
+ size_t count)
{
ssize_t res;
+ struct file *file = io->file;
struct inode *inode = file_inode(file);
if (is_bad_inode(inode))
return -EIO;
- res = fuse_direct_io(file, iov, nr_segs, iov_length(iov, nr_segs),
- ppos, 0);
+ res = fuse_direct_io(io, iov, nr_segs, count, ppos, 0);
fuse_invalidate_attr(inode);
@@ -1229,23 +1350,23 @@ static ssize_t __fuse_direct_read(struct file *file, const struct iovec *iov,
static ssize_t fuse_direct_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct fuse_io_priv io = { .async = 0, .file = file };
struct iovec iov = { .iov_base = buf, .iov_len = count };
- return __fuse_direct_read(file, &iov, 1, ppos);
+ return __fuse_direct_read(&io, &iov, 1, ppos, count);
}
-static ssize_t __fuse_direct_write(struct file *file, const struct iovec *iov,
+static ssize_t __fuse_direct_write(struct fuse_io_priv *io,
+ const struct iovec *iov,
unsigned long nr_segs, loff_t *ppos)
{
+ struct file *file = io->file;
struct inode *inode = file_inode(file);
size_t count = iov_length(iov, nr_segs);
ssize_t res;
res = generic_write_checks(file, ppos, &count, 0);
- if (!res) {
- res = fuse_direct_io(file, iov, nr_segs, count, ppos, 1);
- if (res > 0)
- fuse_write_update_size(inode, *ppos);
- }
+ if (!res)
+ res = fuse_direct_io(io, iov, nr_segs, count, ppos, 1);
fuse_invalidate_attr(inode);
@@ -1258,13 +1379,16 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count };
struct inode *inode = file_inode(file);
ssize_t res;
+ struct fuse_io_priv io = { .async = 0, .file = file };
if (is_bad_inode(inode))
return -EIO;
/* Don't allow parallel writes to the same file */
mutex_lock(&inode->i_mutex);
- res = __fuse_direct_write(file, &iov, 1, ppos);
+ res = __fuse_direct_write(&io, &iov, 1, ppos);
+ if (res > 0)
+ fuse_write_update_size(inode, *ppos);
mutex_unlock(&inode->i_mutex);
return res;
@@ -1373,6 +1497,7 @@ static int fuse_writepage_locked(struct page *page)
if (!req)
goto err;
+ req->background = 1; /* writeback always goes to bg_queue */
tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
if (!tmp_page)
goto err_free;
@@ -2226,21 +2351,93 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
return 0;
}
+static void fuse_do_truncate(struct file *file)
+{
+ struct inode *inode = file->f_mapping->host;
+ struct iattr attr;
+
+ attr.ia_valid = ATTR_SIZE;
+ attr.ia_size = i_size_read(inode);
+
+ attr.ia_file = file;
+ attr.ia_valid |= ATTR_FILE;
+
+ fuse_do_setattr(inode, &attr, file);
+}
+
static ssize_t
fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
loff_t offset, unsigned long nr_segs)
{
ssize_t ret = 0;
- struct file *file = NULL;
+ struct file *file = iocb->ki_filp;
+ struct fuse_file *ff = file->private_data;
loff_t pos = 0;
+ struct inode *inode;
+ loff_t i_size;
+ size_t count = iov_length(iov, nr_segs);
+ struct fuse_io_priv *io;
- file = iocb->ki_filp;
pos = offset;
+ inode = file->f_mapping->host;
+ i_size = i_size_read(inode);
+
+ /* optimization for short read */
+ if (rw != WRITE && offset + count > i_size) {
+ if (offset >= i_size)
+ return 0;
+ count = i_size - offset;
+ }
+
+ io = kmalloc(sizeof(struct fuse_io_priv), GFP_KERNEL);
+ if (!io)
+ return -ENOMEM;
+ spin_lock_init(&io->lock);
+ io->reqs = 1;
+ io->bytes = -1;
+ io->size = 0;
+ io->offset = offset;
+ io->write = (rw == WRITE);
+ io->err = 0;
+ io->file = file;
+ /*
+ * By default, we want to optimize all I/Os with async request
+ * submission to the client filesystem if supported.
+ */
+ io->async = ff->fc->async_dio;
+ io->iocb = iocb;
+
+ /*
+ * We cannot asynchronously extend the size of a file. We have no method
+ * to wait on real async I/O requests, so we must submit this request
+ * synchronously.
+ */
+ if (!is_sync_kiocb(iocb) && (offset + count > i_size))
+ io->async = false;
if (rw == WRITE)
- ret = __fuse_direct_write(file, iov, nr_segs, &pos);
+ ret = __fuse_direct_write(io, iov, nr_segs, &pos);
else
- ret = __fuse_direct_read(file, iov, nr_segs, &pos);
+ ret = __fuse_direct_read(io, iov, nr_segs, &pos, count);
+
+ if (io->async) {
+ fuse_aio_complete(io, ret < 0 ? ret : 0, -1);
+
+ /* we have a non-extending, async request, so return */
+ if (ret > 0 && !is_sync_kiocb(iocb))
+ return -EIOCBQUEUED;
+
+ ret = wait_on_sync_kiocb(iocb);
+ } else {
+ kfree(io);
+ }
+
+ if (rw == WRITE) {
+ if (ret > 0)
+ fuse_write_update_size(inode, pos);
+ else if (ret < 0 && offset + count > i_size)
+ fuse_do_truncate(file);
+ }
return ret;
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 6aeba864f070..fde7249a3a96 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -228,6 +228,20 @@ enum fuse_req_state {
FUSE_REQ_FINISHED
};
+/** The request IO state (for asynchronous processing) */
+struct fuse_io_priv {
+ int async;
+ spinlock_t lock;
+ unsigned reqs;
+ ssize_t bytes;
+ size_t size;
+ __u64 offset;
+ bool write;
+ int err;
+ struct kiocb *iocb;
+ struct file *file;
+};
+
/**
* A request to the client
*/
@@ -332,6 +346,9 @@ struct fuse_req {
/** Inode used in the request or NULL */
struct inode *inode;
+ /** AIO control block */
+ struct fuse_io_priv *io;
+
/** Link on fi->writepages */
struct list_head writepages_entry;
@@ -417,6 +434,10 @@ struct fuse_conn {
/** Batching of FORGET requests (positive indicates FORGET batch) */
int forget_batch;
+ /** Flag indicating that INIT reply has been received. Allocating
+ * any fuse request will be suspended until the flag is set */
+ int initialized;
+
/** Flag indicating if connection is blocked. This will be
the case before the INIT reply is received, and if there
are too many outstading backgrounds requests */
@@ -520,6 +541,9 @@ struct fuse_conn {
/** Does the filesystem want adaptive readdirplus? */
unsigned readdirplus_auto:1;
+ /** Does the filesystem support asynchronous direct-IO submission? */
+ unsigned async_dio:1;
+
/** The number of requests waiting for completion */
atomic_t num_waiting;
@@ -708,6 +732,13 @@ void fuse_request_free(struct fuse_req *req);
* caller should specify # elements in req->pages[] explicitly
*/
struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages);
+struct fuse_req *fuse_get_req_for_background(struct fuse_conn *fc,
+ unsigned npages);
+
+/*
+ * Increment reference count on request
+ */
+void __fuse_get_request(struct fuse_req *req);
/**
* Get a request, may fail with -ENOMEM,
@@ -823,7 +854,7 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
bool isdir);
-ssize_t fuse_direct_io(struct file *file, const struct iovec *iov,
+ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
unsigned long nr_segs, size_t count, loff_t *ppos,
int write);
long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
@@ -835,4 +866,7 @@ int fuse_dev_release(struct inode *inode, struct file *file);
void fuse_write_update_size(struct inode *inode, loff_t pos);
+int fuse_do_setattr(struct inode *inode, struct iattr *attr,
+ struct file *file);
+
#endif /* _FS_FUSE_I_H */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 137185c3884f..6201f81e4d3a 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -346,6 +346,7 @@ static void fuse_send_destroy(struct fuse_conn *fc)
fc->destroy_req = NULL;
req->in.h.opcode = FUSE_DESTROY;
req->force = 1;
+ req->background = 0;
fuse_request_send(fc, req);
fuse_put_request(fc, req);
}
@@ -362,6 +363,7 @@ void fuse_conn_kill(struct fuse_conn *fc)
spin_lock(&fc->lock);
fc->connected = 0;
fc->blocked = 0;
+ fc->initialized = 1;
spin_unlock(&fc->lock);
/* Flush all readers on this fs */
kill_fasync(&fc->fasync, SIGIO, POLL_IN);
@@ -581,7 +583,8 @@ void fuse_conn_init(struct fuse_conn *fc)
fc->khctr = 0;
fc->polled_files = RB_ROOT;
fc->reqctr = 0;
- fc->blocked = 1;
+ fc->blocked = 0;
+ fc->initialized = 0;
fc->attr_version = 1;
get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
}
@@ -868,6 +871,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
fc->do_readdirplus = 1;
if (arg->flags & FUSE_READDIRPLUS_AUTO)
fc->readdirplus_auto = 1;
+ if (arg->flags & FUSE_ASYNC_DIO)
+ fc->async_dio = 1;
} else {
ra_pages = fc->max_read / PAGE_CACHE_SIZE;
fc->no_lock = 1;
@@ -880,7 +885,7 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
fc->max_write = max_t(unsigned, 4096, fc->max_write);
fc->conn_init = 1;
}
- fc->blocked = 0;
+ fc->initialized = 1;
wake_up_all(&fc->blocked_waitq);
}
@@ -895,7 +900,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
- FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO;
+ FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO;
req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg);
@@ -1043,6 +1048,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
init_req = fuse_request_alloc(0);
if (!init_req)
goto err_put_root;
+ init_req->background = 1;
if (is_bdev) {
fc->destroy_req = fuse_request_alloc(0);
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 9883694f1e7c..0bad69ed6336 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -20,6 +20,7 @@
#include <linux/swap.h>
#include <linux/gfs2_ondisk.h>
#include <linux/backing-dev.h>
+#include <linux/aio.h>
#include "gfs2.h"
#include "incore.h"
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d79c2dadc536..acd16764b133 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -25,6 +25,7 @@
#include <asm/uaccess.h>
#include <linux/dlm.h>
#include <linux/dlm_plock.h>
+#include <linux/aio.h>
#include "gfs2.h"
#include "incore.h"
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 7318abf9d0fb..c5fa758fd844 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -300,7 +300,7 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno)
u64 nblk;
if (bio) {
- nblk = bio->bi_sector + bio_sectors(bio);
+ nblk = bio_end_sector(bio);
nblk >>= sdp->sd_fsb2bb_shift;
if (blkno == nblk)
return bio;
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index 17c22a8fd40a..e0101b6fb0d7 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -176,7 +176,9 @@ static int hfs_dir_release(struct inode *inode, struct file *file)
{
struct hfs_readdir_data *rd = file->private_data;
if (rd) {
+ mutex_lock(&inode->i_mutex);
list_del(&rd->list);
+ mutex_unlock(&inode->i_mutex);
kfree(rd);
}
return 0;
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 716e1aafb2e2..f9299d8a64e3 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -14,6 +14,7 @@
#include <linux/pagemap.h>
#include <linux/mpage.h>
#include <linux/sched.h>
+#include <linux/aio.h>
#include "hfs_fs.h"
#include "btree.h"
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 7faaa964968e..f833d35630ab 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -14,6 +14,7 @@
#include <linux/pagemap.h>
#include <linux/mpage.h>
#include <linux/sched.h>
+#include <linux/aio.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 523464e62849..a3f868ae3fd4 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -909,11 +909,8 @@ static int can_do_hugetlb_shm(void)
static int get_hstate_idx(int page_size_log)
{
- struct hstate *h;
+ struct hstate *h = hstate_sizelog(page_size_log);
- if (!page_size_log)
- return default_hstate_idx;
- h = size_to_hstate(1 << page_size_log);
if (!h)
return -1;
return h - hstates;
@@ -929,9 +926,12 @@ static struct dentry_operations anon_ops = {
.d_dname = hugetlb_dname
};
-struct file *hugetlb_file_setup(const char *name, unsigned long addr,
- size_t size, vm_flags_t acctflag,
- struct user_struct **user,
+/*
+ * Note that size should be aligned to proper hugepage size in caller side,
+ * otherwise hugetlb_reserve_pages reserves one less hugepages than intended.
+ */
+struct file *hugetlb_file_setup(const char *name, size_t size,
+ vm_flags_t acctflag, struct user_struct **user,
int creat_flags, int page_size_log)
{
struct file *file = ERR_PTR(-ENOMEM);
@@ -939,8 +939,6 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
struct path path;
struct super_block *sb;
struct qstr quick_string;
- struct hstate *hstate;
- unsigned long num_pages;
int hstate_idx;
hstate_idx = get_hstate_idx(page_size_log);
@@ -980,12 +978,10 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
if (!inode)
goto out_dentry;
- hstate = hstate_inode(inode);
- size += addr & ~huge_page_mask(hstate);
- num_pages = ALIGN(size, huge_page_size(hstate)) >>
- huge_page_shift(hstate);
file = ERR_PTR(-ENOMEM);
- if (hugetlb_reserve_pages(inode, 0, num_pages, NULL, acctflag))
+ if (hugetlb_reserve_pages(inode, 0,
+ size >> huge_page_shift(hstate_inode(inode)), NULL,
+ acctflag))
goto out_inode;
d_instantiate(path.dentry, inode);
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 77554b61d124..730f24e282a6 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -23,6 +23,7 @@
#include <linux/pagemap.h>
#include <linux/quotaops.h>
#include <linux/writeback.h>
+#include <linux/aio.h>
#include "jfs_incore.h"
#include "jfs_inode.h"
#include "jfs_filsys.h"
diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
index cbe48ea9318e..c57499dca89c 100644
--- a/fs/jfs/jfs_logmgr.c
+++ b/fs/jfs/jfs_logmgr.c
@@ -2005,7 +2005,6 @@ static int lbmRead(struct jfs_log * log, int pn, struct lbuf ** bpp)
bio->bi_io_vec[0].bv_offset = bp->l_offset;
bio->bi_vcnt = 1;
- bio->bi_idx = 0;
bio->bi_size = LOGPSIZE;
bio->bi_end_io = lbmIODone;
@@ -2146,7 +2145,6 @@ static void lbmStartIO(struct lbuf * bp)
bio->bi_io_vec[0].bv_offset = bp->l_offset;
bio->bi_vcnt = 1;
- bio->bi_idx = 0;
bio->bi_size = LOGPSIZE;
bio->bi_end_io = lbmIODone;
diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c
index e784a217b500..550475ca6a0e 100644
--- a/fs/logfs/dev_bdev.c
+++ b/fs/logfs/dev_bdev.c
@@ -32,7 +32,6 @@ static int sync_request(struct page *page, struct block_device *bdev, int rw)
bio_vec.bv_len = PAGE_SIZE;
bio_vec.bv_offset = 0;
bio.bi_vcnt = 1;
- bio.bi_idx = 0;
bio.bi_size = PAGE_SIZE;
bio.bi_bdev = bdev;
bio.bi_sector = page->index * (PAGE_SIZE >> 9);
@@ -108,7 +107,6 @@ static int __bdev_writeseg(struct super_block *sb, u64 ofs, pgoff_t index,
if (i >= max_pages) {
/* Block layer cannot split bios :( */
bio->bi_vcnt = i;
- bio->bi_idx = 0;
bio->bi_size = i * PAGE_SIZE;
bio->bi_bdev = super->s_bdev;
bio->bi_sector = ofs >> 9;
@@ -136,7 +134,6 @@ static int __bdev_writeseg(struct super_block *sb, u64 ofs, pgoff_t index,
unlock_page(page);
}
bio->bi_vcnt = nr_pages;
- bio->bi_idx = 0;
bio->bi_size = nr_pages * PAGE_SIZE;
bio->bi_bdev = super->s_bdev;
bio->bi_sector = ofs >> 9;
@@ -202,7 +199,6 @@ static int do_erase(struct super_block *sb, u64 ofs, pgoff_t index,
if (i >= max_pages) {
/* Block layer cannot split bios :( */
bio->bi_vcnt = i;
- bio->bi_idx = 0;
bio->bi_size = i * PAGE_SIZE;
bio->bi_bdev = super->s_bdev;
bio->bi_sector = ofs >> 9;
@@ -224,7 +220,6 @@ static int do_erase(struct super_block *sb, u64 ofs, pgoff_t index,
bio->bi_io_vec[i].bv_offset = 0;
}
bio->bi_vcnt = nr_pages;
- bio->bi_idx = 0;
bio->bi_size = nr_pages * PAGE_SIZE;
bio->bi_bdev = super->s_bdev;
bio->bi_sector = ofs >> 9;
diff --git a/fs/nfs/blocklayout/blocklayout.h b/fs/nfs/blocklayout/blocklayout.h
index f4891bde8851..8485978993e8 100644
--- a/fs/nfs/blocklayout/blocklayout.h
+++ b/fs/nfs/blocklayout/blocklayout.h
@@ -173,7 +173,7 @@ struct bl_msg_hdr {
/* blocklayoutdev.c */
ssize_t bl_pipe_downcall(struct file *, const char __user *, size_t);
void bl_pipe_destroy_msg(struct rpc_pipe_msg *);
-int nfs4_blkdev_put(struct block_device *bdev);
+void nfs4_blkdev_put(struct block_device *bdev);
struct pnfs_block_dev *nfs4_blk_decode_device(struct nfs_server *server,
struct pnfs_device *dev);
int nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo,
diff --git a/fs/nfs/blocklayout/blocklayoutdev.c b/fs/nfs/blocklayout/blocklayoutdev.c
index a86c5bdad9e3..04303b5c9361 100644
--- a/fs/nfs/blocklayout/blocklayoutdev.c
+++ b/fs/nfs/blocklayout/blocklayoutdev.c
@@ -56,11 +56,11 @@ static int decode_sector_number(__be32 **rp, sector_t *sp)
/*
* Release the block device
*/
-int nfs4_blkdev_put(struct block_device *bdev)
+void nfs4_blkdev_put(struct block_device *bdev)
{
dprintk("%s for device %d:%d\n", __func__, MAJOR(bdev->bd_dev),
MINOR(bdev->bd_dev));
- return blkdev_put(bdev, FMODE_READ);
+ blkdev_put(bdev, FMODE_READ);
}
ssize_t bl_pipe_downcall(struct file *filp, const char __user *src,
diff --git a/fs/nfs/blocklayout/blocklayoutdm.c b/fs/nfs/blocklayout/blocklayoutdm.c
index 6fc7b5cae92b..8999cfddd866 100644
--- a/fs/nfs/blocklayout/blocklayoutdm.c
+++ b/fs/nfs/blocklayout/blocklayoutdm.c
@@ -88,14 +88,8 @@ out:
*/
static void nfs4_blk_metadev_release(struct pnfs_block_dev *bdev)
{
- int rv;
-
dprintk("%s Releasing\n", __func__);
- rv = nfs4_blkdev_put(bdev->bm_mdev);
- if (rv)
- printk(KERN_ERR "NFS: %s nfs4_blkdev_put returns %d\n",
- __func__, rv);
-
+ nfs4_blkdev_put(bdev->bm_mdev);
dev_remove(bdev->net, bdev->bm_mdev->bd_dev);
}
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index cf02f5530713..689fb608648e 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -25,7 +25,7 @@
#include <linux/gfp.h>
#include <linux/mpage.h>
#include <linux/writeback.h>
-#include <linux/uio.h>
+#include <linux/aio.h>
#include "nilfs.h"
#include "btnode.h"
#include "segment.h"
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index 1da4b81e6f76..c5670b8d198c 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -27,6 +27,7 @@
#include <linux/swap.h>
#include <linux/uio.h>
#include <linux/writeback.h>
+#include <linux/aio.h>
#include <asm/page.h>
#include <asm/uaccess.h>
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index d3e118cc6ffa..2778b0255dc6 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -28,6 +28,7 @@
#include <linux/quotaops.h>
#include <linux/slab.h>
#include <linux/log2.h>
+#include <linux/aio.h>
#include "aops.h"
#include "attrib.h"
diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h
index ffb2da370a99..f671e49beb34 100644
--- a/fs/ocfs2/aops.h
+++ b/fs/ocfs2/aops.h
@@ -22,6 +22,8 @@
#ifndef OCFS2_AOPS_H
#define OCFS2_AOPS_H
+#include <linux/aio.h>
+
handle_t *ocfs2_start_walk_page_trans(struct inode *inode,
struct page *page,
unsigned from,
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 12ae194ac943..3a44a648dae7 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -2322,7 +2322,7 @@ int ocfs2_inode_lock_full_nested(struct inode *inode,
status = __ocfs2_cluster_lock(osb, lockres, level, dlm_flags,
arg_flags, subclass, _RET_IP_);
if (status < 0) {
- if (status != -EAGAIN && status != -EIOCBRETRY)
+ if (status != -EAGAIN)
mlog_errno(status);
goto bail;
}
diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h
index 88924a3133fa..621fc73bf23d 100644
--- a/fs/ocfs2/inode.h
+++ b/fs/ocfs2/inode.h
@@ -147,8 +147,6 @@ void ocfs2_refresh_inode(struct inode *inode,
int ocfs2_mark_inode_dirty(handle_t *handle,
struct inode *inode,
struct buffer_head *bh);
-int ocfs2_aio_read(struct file *file, struct kiocb *req, struct iocb *iocb);
-int ocfs2_aio_write(struct file *file, struct kiocb *req, struct iocb *iocb);
struct buffer_head *ocfs2_bread(struct inode *inode,
int block, int *err, int reada);
diff --git a/fs/pipe.c b/fs/pipe.c
index a029a14bacf1..d2c45e14e6d8 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -21,6 +21,7 @@
#include <linux/audit.h>
#include <linux/syscalls.h>
#include <linux/fcntl.h>
+#include <linux/aio.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
diff --git a/fs/proc/stat.c b/fs/proc/stat.c
index e296572c73ed..1cf86c0e8689 100644
--- a/fs/proc/stat.c
+++ b/fs/proc/stat.c
@@ -184,7 +184,7 @@ static int show_stat(struct seq_file *p, void *v)
static int stat_open(struct inode *inode, struct file *file)
{
- unsigned size = 1024 + 128 * num_possible_cpus();
+ size_t size = 1024 + 128 * num_possible_cpus();
char *buf;
struct seq_file *m;
int res;
diff --git a/fs/read_write.c b/fs/read_write.c
index 90ba3b350e50..03430008704e 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -9,6 +9,7 @@
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/uio.h>
+#include <linux/aio.h>
#include <linux/fsnotify.h>
#include <linux/security.h>
#include <linux/export.h>
@@ -329,16 +330,6 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count
return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;
}
-static void wait_on_retry_sync_kiocb(struct kiocb *iocb)
-{
- set_current_state(TASK_UNINTERRUPTIBLE);
- if (!kiocbIsKicked(iocb))
- schedule();
- else
- kiocbClearKicked(iocb);
- __set_current_state(TASK_RUNNING);
-}
-
ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
struct iovec iov = { .iov_base = buf, .iov_len = len };
@@ -350,13 +341,7 @@ ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *pp
kiocb.ki_left = len;
kiocb.ki_nbytes = len;
- for (;;) {
- ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);
- if (ret != -EIOCBRETRY)
- break;
- wait_on_retry_sync_kiocb(&kiocb);
- }
-
+ ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);
if (-EIOCBQUEUED == ret)
ret = wait_on_sync_kiocb(&kiocb);
*ppos = kiocb.ki_pos;
@@ -406,13 +391,7 @@ ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, lof
kiocb.ki_left = len;
kiocb.ki_nbytes = len;
- for (;;) {
- ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
- if (ret != -EIOCBRETRY)
- break;
- wait_on_retry_sync_kiocb(&kiocb);
- }
-
+ ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
if (-EIOCBQUEUED == ret)
ret = wait_on_sync_kiocb(&kiocb);
*ppos = kiocb.ki_pos;
@@ -592,13 +571,7 @@ static ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov,
kiocb.ki_left = len;
kiocb.ki_nbytes = len;
- for (;;) {
- ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos);
- if (ret != -EIOCBRETRY)
- break;
- wait_on_retry_sync_kiocb(&kiocb);
- }
-
+ ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos);
if (ret == -EIOCBQUEUED)
ret = wait_on_sync_kiocb(&kiocb);
*ppos = kiocb.ki_pos;
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index ea5061fd4f3e..77d6d47abc83 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -18,6 +18,7 @@
#include <linux/writeback.h>
#include <linux/quotaops.h>
#include <linux/swap.h>
+#include <linux/aio.h>
int reiserfs_commit_write(struct file *f, struct page *page,
unsigned from, unsigned to);
diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
index afcadcc03e8a..742fdd4c209a 100644
--- a/fs/reiserfs/journal.c
+++ b/fs/reiserfs/journal.c
@@ -97,7 +97,7 @@ static int flush_commit_list(struct super_block *s,
static int can_dirty(struct reiserfs_journal_cnode *cn);
static int journal_join(struct reiserfs_transaction_handle *th,
struct super_block *sb, unsigned long nblocks);
-static int release_journal_dev(struct super_block *super,
+static void release_journal_dev(struct super_block *super,
struct reiserfs_journal *journal);
static int dirty_one_transaction(struct super_block *s,
struct reiserfs_journal_list *jl);
@@ -2532,23 +2532,13 @@ static void journal_list_init(struct super_block *sb)
SB_JOURNAL(sb)->j_current_jl = alloc_journal_list(sb);
}
-static int release_journal_dev(struct super_block *super,
+static void release_journal_dev(struct super_block *super,
struct reiserfs_journal *journal)
{
- int result;
-
- result = 0;
-
if (journal->j_dev_bd != NULL) {
- result = blkdev_put(journal->j_dev_bd, journal->j_dev_mode);
+ blkdev_put(journal->j_dev_bd, journal->j_dev_mode);
journal->j_dev_bd = NULL;
}
-
- if (result != 0) {
- reiserfs_warning(super, "sh-457",
- "Cannot release journal device: %i", result);
- }
- return result;
}
static int journal_init_dev(struct super_block *super,
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index f12189d2db1d..14374530784c 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -50,6 +50,7 @@
*/
#include "ubifs.h"
+#include <linux/aio.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/slab.h>
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 7a12e48ad819..b6d15d349810 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -38,6 +38,7 @@
#include <linux/slab.h>
#include <linux/crc-itu-t.h>
#include <linux/mpage.h>
+#include <linux/aio.h>
#include "udf_i.h"
#include "udf_sb.h"
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 3244c988d379..2b2691b73428 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -31,6 +31,7 @@
#include "xfs_vnodeops.h"
#include "xfs_trace.h"
#include "xfs_bmap.h"
+#include <linux/aio.h>
#include <linux/gfp.h>
#include <linux/mpage.h>
#include <linux/pagevec.h>
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 054d60c0ac57..a5f2042aec8b 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -36,6 +36,7 @@
#include "xfs_ioctl.h"
#include "xfs_trace.h"
+#include <linux/aio.h>
#include <linux/dcache.h>
#include <linux/falloc.h>
#include <linux/pagevec.h>
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index 2603267b1a29..e6c9c4cc9b23 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -31,18 +31,12 @@
#ifdef CONFIG_ARM_ARCH_TIMER
-extern int arch_timer_init(void);
extern u32 arch_timer_get_rate(void);
extern u64 (*arch_timer_read_counter)(void);
extern struct timecounter *arch_timer_get_timecounter(void);
#else
-static inline int arch_timer_init(void)
-{
- return -ENXIO;
-}
-
static inline u32 arch_timer_get_rate(void)
{
return 0;
diff --git a/include/clocksource/samsung_pwm.h b/include/clocksource/samsung_pwm.h
new file mode 100644
index 000000000000..5c449c8199e9
--- /dev/null
+++ b/include/clocksource/samsung_pwm.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co., 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.
+ *
+ * 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/>.
+ */
+#ifndef __CLOCKSOURCE_SAMSUNG_PWM_H
+#define __CLOCKSOURCE_SAMSUNG_PWM_H
+
+#include <linux/spinlock.h>
+
+#define SAMSUNG_PWM_NUM 5
+
+extern spinlock_t samsung_pwm_lock;
+
+struct samsung_pwm_variant {
+ u8 bits;
+ u8 div_base;
+ u8 tclk_mask;
+ u8 output_mask;
+ bool has_tint_cstat;
+};
+
+void samsung_pwm_clocksource_init(void __iomem *base,
+ unsigned int *irqs, struct samsung_pwm_variant *variant);
+
+#endif /* __CLOCKSOURCE_SAMSUNG_PWM_H */
diff --git a/include/linux/acpi_gpio.h b/include/linux/acpi_gpio.h
index b76ebd08ff8e..4c120a1e0ca3 100644
--- a/include/linux/acpi_gpio.h
+++ b/include/linux/acpi_gpio.h
@@ -1,13 +1,25 @@
#ifndef _LINUX_ACPI_GPIO_H_
#define _LINUX_ACPI_GPIO_H_
+#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio.h>
+/**
+ * struct acpi_gpio_info - ACPI GPIO specific information
+ * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
+ */
+struct acpi_gpio_info {
+ bool gpioint;
+};
+
#ifdef CONFIG_GPIO_ACPI
int acpi_get_gpio(char *path, int pin);
+int acpi_get_gpio_by_index(struct device *dev, int index,
+ struct acpi_gpio_info *info);
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
+void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
#else /* CONFIG_GPIO_ACPI */
@@ -16,7 +28,14 @@ static inline int acpi_get_gpio(char *path, int pin)
return -ENODEV;
}
+static inline int acpi_get_gpio_by_index(struct device *dev, int index,
+ struct acpi_gpio_info *info)
+{
+ return -ENODEV;
+}
+
static inline void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
+static inline void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
#endif /* CONFIG_GPIO_ACPI */
diff --git a/include/linux/aio.h b/include/linux/aio.h
index 31ff6dba4872..1bdf965339f9 100644
--- a/include/linux/aio.h
+++ b/include/linux/aio.h
@@ -9,91 +9,32 @@
#include <linux/atomic.h>
-#define AIO_MAXSEGS 4
-#define AIO_KIOGRP_NR_ATOMIC 8
-
struct kioctx;
+struct kiocb;
-/* Notes on cancelling a kiocb:
- * If a kiocb is cancelled, aio_complete may return 0 to indicate
- * that cancel has not yet disposed of the kiocb. All cancel
- * operations *must* call aio_put_req to dispose of the kiocb
- * to guard against races with the completion code.
- */
-#define KIOCB_C_CANCELLED 0x01
-#define KIOCB_C_COMPLETE 0x02
-
-#define KIOCB_SYNC_KEY (~0U)
+#define KIOCB_KEY 0
-/* ki_flags bits */
/*
- * This may be used for cancel/retry serialization in the future, but
- * for now it's unused and we probably don't want modules to even
- * think they can use it.
+ * We use ki_cancel == KIOCB_CANCELLED to indicate that a kiocb has been either
+ * cancelled or completed (this makes a certain amount of sense because
+ * successful cancellation - io_cancel() - does deliver the completion to
+ * userspace).
+ *
+ * And since most things don't implement kiocb cancellation and we'd really like
+ * kiocb completion to be lockless when possible, we use ki_cancel to
+ * synchronize cancellation and completion - we only set it to KIOCB_CANCELLED
+ * with xchg() or cmpxchg(), see batch_complete_aio() and kiocb_cancel().
*/
-/* #define KIF_LOCKED 0 */
-#define KIF_KICKED 1
-#define KIF_CANCELLED 2
-
-#define kiocbTryLock(iocb) test_and_set_bit(KIF_LOCKED, &(iocb)->ki_flags)
-#define kiocbTryKick(iocb) test_and_set_bit(KIF_KICKED, &(iocb)->ki_flags)
+#define KIOCB_CANCELLED ((void *) (~0ULL))
-#define kiocbSetLocked(iocb) set_bit(KIF_LOCKED, &(iocb)->ki_flags)
-#define kiocbSetKicked(iocb) set_bit(KIF_KICKED, &(iocb)->ki_flags)
-#define kiocbSetCancelled(iocb) set_bit(KIF_CANCELLED, &(iocb)->ki_flags)
+typedef int (kiocb_cancel_fn)(struct kiocb *, struct io_event *);
-#define kiocbClearLocked(iocb) clear_bit(KIF_LOCKED, &(iocb)->ki_flags)
-#define kiocbClearKicked(iocb) clear_bit(KIF_KICKED, &(iocb)->ki_flags)
-#define kiocbClearCancelled(iocb) clear_bit(KIF_CANCELLED, &(iocb)->ki_flags)
-
-#define kiocbIsLocked(iocb) test_bit(KIF_LOCKED, &(iocb)->ki_flags)
-#define kiocbIsKicked(iocb) test_bit(KIF_KICKED, &(iocb)->ki_flags)
-#define kiocbIsCancelled(iocb) test_bit(KIF_CANCELLED, &(iocb)->ki_flags)
-
-/* is there a better place to document function pointer methods? */
-/**
- * ki_retry - iocb forward progress callback
- * @kiocb: The kiocb struct to advance by performing an operation.
- *
- * This callback is called when the AIO core wants a given AIO operation
- * to make forward progress. The kiocb argument describes the operation
- * that is to be performed. As the operation proceeds, perhaps partially,
- * ki_retry is expected to update the kiocb with progress made. Typically
- * ki_retry is set in the AIO core and it itself calls file_operations
- * helpers.
- *
- * ki_retry's return value determines when the AIO operation is completed
- * and an event is generated in the AIO event ring. Except the special
- * return values described below, the value that is returned from ki_retry
- * is transferred directly into the completion ring as the operation's
- * resulting status. Once this has happened ki_retry *MUST NOT* reference
- * the kiocb pointer again.
- *
- * If ki_retry returns -EIOCBQUEUED it has made a promise that aio_complete()
- * will be called on the kiocb pointer in the future. The AIO core will
- * not ask the method again -- ki_retry must ensure forward progress.
- * aio_complete() must be called once and only once in the future, multiple
- * calls may result in undefined behaviour.
- *
- * If ki_retry returns -EIOCBRETRY it has made a promise that kick_iocb()
- * will be called on the kiocb pointer in the future. This may happen
- * through generic helpers that associate kiocb->ki_wait with a wait
- * queue head that ki_retry uses via current->io_wait. It can also happen
- * with custom tracking and manual calls to kick_iocb(), though that is
- * discouraged. In either case, kick_iocb() must be called once and only
- * once. ki_retry must ensure forward progress, the AIO core will wait
- * indefinitely for kick_iocb() to be called.
- */
struct kiocb {
- struct list_head ki_run_list;
- unsigned long ki_flags;
- int ki_users;
- unsigned ki_key; /* id of this request */
+ atomic_t ki_users;
struct file *ki_filp;
- struct kioctx *ki_ctx; /* may be NULL for sync ops */
- int (*ki_cancel)(struct kiocb *, struct io_event *);
- ssize_t (*ki_retry)(struct kiocb *);
+ struct kioctx *ki_ctx; /* NULL for sync ops */
+ kiocb_cancel_fn *ki_cancel;
void (*ki_dtor)(struct kiocb *);
union {
@@ -117,7 +58,6 @@ struct kiocb {
struct list_head ki_list; /* the aio core uses this
* for cancellation */
- struct list_head ki_batch; /* batch allocation */
/*
* If the aio_resfd field of the userspace iocb is not zero,
@@ -128,106 +68,40 @@ struct kiocb {
static inline bool is_sync_kiocb(struct kiocb *kiocb)
{
- return kiocb->ki_key == KIOCB_SYNC_KEY;
+ return kiocb->ki_ctx == NULL;
}
static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp)
{
*kiocb = (struct kiocb) {
- .ki_users = 1,
- .ki_key = KIOCB_SYNC_KEY,
+ .ki_users = ATOMIC_INIT(1),
+ .ki_ctx = NULL,
.ki_filp = filp,
.ki_obj.tsk = current,
};
}
-#define AIO_RING_MAGIC 0xa10a10a1
-#define AIO_RING_COMPAT_FEATURES 1
-#define AIO_RING_INCOMPAT_FEATURES 0
-struct aio_ring {
- unsigned id; /* kernel internal index number */
- unsigned nr; /* number of io_events */
- unsigned head;
- unsigned tail;
-
- unsigned magic;
- unsigned compat_features;
- unsigned incompat_features;
- unsigned header_length; /* size of aio_ring */
-
-
- struct io_event io_events[0];
-}; /* 128 bytes + ring size */
-
-#define AIO_RING_PAGES 8
-struct aio_ring_info {
- unsigned long mmap_base;
- unsigned long mmap_size;
-
- struct page **ring_pages;
- spinlock_t ring_lock;
- long nr_pages;
-
- unsigned nr, tail;
-
- struct page *internal_pages[AIO_RING_PAGES];
-};
-
-static inline unsigned aio_ring_avail(struct aio_ring_info *info,
- struct aio_ring *ring)
-{
- return (ring->head + info->nr - 1 - ring->tail) % info->nr;
-}
-
-struct kioctx {
- atomic_t users;
- int dead;
- struct mm_struct *mm;
-
- /* This needs improving */
- unsigned long user_id;
- struct hlist_node list;
-
- wait_queue_head_t wait;
-
- spinlock_t ctx_lock;
-
- int reqs_active;
- struct list_head active_reqs; /* used for cancellation */
- struct list_head run_list; /* used for kicked reqs */
-
- /* sys_io_setup currently limits this to an unsigned int */
- unsigned max_reqs;
-
- struct aio_ring_info ring_info;
-
- struct delayed_work wq;
-
- struct rcu_head rcu_head;
-};
-
/* prototypes */
-extern unsigned aio_max_size;
-
#ifdef CONFIG_AIO
extern ssize_t wait_on_sync_kiocb(struct kiocb *iocb);
-extern int aio_put_req(struct kiocb *iocb);
-extern void kick_iocb(struct kiocb *iocb);
-extern int aio_complete(struct kiocb *iocb, long res, long res2);
+extern void aio_put_req(struct kiocb *iocb);
+extern void aio_complete(struct kiocb *iocb, long res, long res2);
struct mm_struct;
extern void exit_aio(struct mm_struct *mm);
extern long do_io_submit(aio_context_t ctx_id, long nr,
struct iocb __user *__user *iocbpp, bool compat);
+void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel);
#else
static inline ssize_t wait_on_sync_kiocb(struct kiocb *iocb) { return 0; }
-static inline int aio_put_req(struct kiocb *iocb) { return 0; }
-static inline void kick_iocb(struct kiocb *iocb) { }
-static inline int aio_complete(struct kiocb *iocb, long res, long res2) { return 0; }
+static inline void aio_put_req(struct kiocb *iocb) { }
+static inline void aio_complete(struct kiocb *iocb, long res, long res2) { }
struct mm_struct;
static inline void exit_aio(struct mm_struct *mm) { }
static inline long do_io_submit(aio_context_t ctx_id, long nr,
struct iocb __user * __user *iocbpp,
bool compat) { return 0; }
+static inline void kiocb_set_cancel_fn(struct kiocb *req,
+ kiocb_cancel_fn *cancel) { }
#endif /* CONFIG_AIO */
static inline struct kiocb *list_kiocb(struct list_head *h)
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index 350459910fe1..c3881553f7d1 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -18,6 +18,7 @@
#include <linux/writeback.h>
#include <linux/atomic.h>
#include <linux/sysctl.h>
+#include <linux/workqueue.h>
struct page;
struct device;
@@ -27,7 +28,6 @@ struct dentry;
* Bits in backing_dev_info.state
*/
enum bdi_state {
- BDI_pending, /* On its way to being activated */
BDI_wb_alloc, /* Default embedded wb allocated */
BDI_async_congested, /* The async (write) queue is getting full */
BDI_sync_congested, /* The sync queue is getting full */
@@ -53,10 +53,8 @@ struct bdi_writeback {
unsigned int nr;
unsigned long last_old_flush; /* last old data flush */
- unsigned long last_active; /* last time bdi thread was active */
- struct task_struct *task; /* writeback thread */
- struct timer_list wakeup_timer; /* used for delayed bdi thread wakeup */
+ struct delayed_work dwork; /* work item used for writeback */
struct list_head b_dirty; /* dirty inodes */
struct list_head b_io; /* parked for writeback */
struct list_head b_more_io; /* parked for more writeback */
@@ -123,14 +121,15 @@ int bdi_setup_and_register(struct backing_dev_info *, char *, unsigned int);
void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
enum wb_reason reason);
void bdi_start_background_writeback(struct backing_dev_info *bdi);
-int bdi_writeback_thread(void *data);
+void bdi_writeback_workfn(struct work_struct *work);
int bdi_has_dirty_io(struct backing_dev_info *bdi);
void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi);
void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2);
extern spinlock_t bdi_lock;
extern struct list_head bdi_list;
-extern struct list_head bdi_pending_list;
+
+extern struct workqueue_struct *bdi_wq;
static inline int wb_has_dirty_io(struct bdi_writeback *wb)
{
@@ -336,11 +335,6 @@ static inline bool bdi_cap_swap_backed(struct backing_dev_info *bdi)
return bdi->capabilities & BDI_CAP_SWAP_BACKED;
}
-static inline bool bdi_cap_flush_forker(struct backing_dev_info *bdi)
-{
- return bdi == &default_backing_dev_info;
-}
-
static inline bool mapping_cap_writeback_dirty(struct address_space *mapping)
{
return bdi_cap_writeback_dirty(mapping->backing_dev_info);
diff --git a/include/linux/basic_mmio_gpio.h b/include/linux/basic_mmio_gpio.h
index 1c504ca5bdb3..d8a97ec0e2b8 100644
--- a/include/linux/basic_mmio_gpio.h
+++ b/include/linux/basic_mmio_gpio.h
@@ -72,5 +72,6 @@ int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
#define BGPIOF_BIG_ENDIAN BIT(0)
#define BGPIOF_UNREADABLE_REG_SET BIT(1) /* reg_set is unreadable */
#define BGPIOF_UNREADABLE_REG_DIR BIT(2) /* reg_dir is unreadable */
+#define BGPIOF_BIG_ENDIAN_BYTE_ORDER BIT(3)
#endif /* __BASIC_MMIO_GPIO_H */
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 820e7aaad4fd..ef24466d8f82 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -67,6 +67,7 @@
#define bio_offset(bio) bio_iovec((bio))->bv_offset
#define bio_segments(bio) ((bio)->bi_vcnt - (bio)->bi_idx)
#define bio_sectors(bio) ((bio)->bi_size >> 9)
+#define bio_end_sector(bio) ((bio)->bi_sector + bio_sectors((bio)))
static inline unsigned int bio_cur_bytes(struct bio *bio)
{
@@ -84,11 +85,6 @@ static inline void *bio_data(struct bio *bio)
return NULL;
}
-static inline int bio_has_allocated_vec(struct bio *bio)
-{
- return bio->bi_io_vec && bio->bi_io_vec != bio->bi_inline_vecs;
-}
-
/*
* will die
*/
@@ -136,16 +132,27 @@ static inline int bio_has_allocated_vec(struct bio *bio)
#define bio_io_error(bio) bio_endio((bio), -EIO)
/*
- * drivers should not use the __ version unless they _really_ want to
- * run through the entire bio and not just pending pieces
+ * drivers should not use the __ version unless they _really_ know what
+ * they're doing
*/
#define __bio_for_each_segment(bvl, bio, i, start_idx) \
for (bvl = bio_iovec_idx((bio), (start_idx)), i = (start_idx); \
i < (bio)->bi_vcnt; \
bvl++, i++)
+/*
+ * drivers should _never_ use the all version - the bio may have been split
+ * before it got to the driver and the driver won't own all of it
+ */
+#define bio_for_each_segment_all(bvl, bio, i) \
+ for (i = 0; \
+ bvl = bio_iovec_idx((bio), (i)), i < (bio)->bi_vcnt; \
+ i++)
+
#define bio_for_each_segment(bvl, bio, i) \
- __bio_for_each_segment(bvl, bio, i, (bio)->bi_idx)
+ for (i = (bio)->bi_idx; \
+ bvl = bio_iovec_idx((bio), (i)), i < (bio)->bi_vcnt; \
+ i++)
/*
* get a reference to a bio, so it won't disappear. the intended use is
@@ -180,9 +187,12 @@ struct bio_integrity_payload {
unsigned short bip_slab; /* slab the bip came from */
unsigned short bip_vcnt; /* # of integrity bio_vecs */
unsigned short bip_idx; /* current bip_vec index */
+ unsigned bip_owns_buf:1; /* should free bip_buf */
struct work_struct bip_work; /* I/O completion */
- struct bio_vec bip_vec[0]; /* embedded bvec array */
+
+ struct bio_vec *bip_vec;
+ struct bio_vec bip_inline_vecs[0];/* embedded bvec array */
};
#endif /* CONFIG_BLK_DEV_INTEGRITY */
@@ -211,6 +221,7 @@ extern void bio_pair_release(struct bio_pair *dbio);
extern struct bio_set *bioset_create(unsigned int, unsigned int);
extern void bioset_free(struct bio_set *);
+extern mempool_t *biovec_create_pool(struct bio_set *bs, int pool_entries);
extern struct bio *bio_alloc_bioset(gfp_t, int, struct bio_set *);
extern void bio_put(struct bio *);
@@ -245,6 +256,9 @@ extern void bio_endio(struct bio *, int);
struct request_queue;
extern int bio_phys_segments(struct request_queue *, struct bio *);
+extern int submit_bio_wait(int rw, struct bio *bio);
+extern void bio_advance(struct bio *, unsigned);
+
extern void bio_init(struct bio *);
extern void bio_reset(struct bio *);
@@ -279,6 +293,9 @@ static inline void bio_flush_dcache_pages(struct bio *bi)
}
#endif
+extern void bio_copy_data(struct bio *dst, struct bio *src);
+extern int bio_alloc_pages(struct bio *bio, gfp_t gfp);
+
extern struct bio *bio_copy_user(struct request_queue *, struct rq_map_data *,
unsigned long, unsigned int, int, gfp_t);
extern struct bio *bio_copy_user_iov(struct request_queue *,
@@ -286,8 +303,8 @@ extern struct bio *bio_copy_user_iov(struct request_queue *,
int, int, gfp_t);
extern int bio_uncopy_user(struct bio *);
void zero_fill_bio(struct bio *bio);
-extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *);
-extern void bvec_free_bs(struct bio_set *, struct bio_vec *, unsigned int);
+extern struct bio_vec *bvec_alloc(gfp_t, int, unsigned long *, mempool_t *);
+extern void bvec_free(mempool_t *, struct bio_vec *, unsigned int);
extern unsigned int bvec_nr_vecs(unsigned short idx);
#ifdef CONFIG_BLK_CGROUP
@@ -298,39 +315,6 @@ static inline int bio_associate_current(struct bio *bio) { return -ENOENT; }
static inline void bio_disassociate_task(struct bio *bio) { }
#endif /* CONFIG_BLK_CGROUP */
-/*
- * bio_set is used to allow other portions of the IO system to
- * allocate their own private memory pools for bio and iovec structures.
- * These memory pools in turn all allocate from the bio_slab
- * and the bvec_slabs[].
- */
-#define BIO_POOL_SIZE 2
-#define BIOVEC_NR_POOLS 6
-#define BIOVEC_MAX_IDX (BIOVEC_NR_POOLS - 1)
-
-struct bio_set {
- struct kmem_cache *bio_slab;
- unsigned int front_pad;
-
- mempool_t *bio_pool;
-#if defined(CONFIG_BLK_DEV_INTEGRITY)
- mempool_t *bio_integrity_pool;
-#endif
- mempool_t *bvec_pool;
-};
-
-struct biovec_slab {
- int nr_vecs;
- char *name;
- struct kmem_cache *slab;
-};
-
-/*
- * a small number of entries is fine, not going to be performance critical.
- * basically we just need to survive
- */
-#define BIO_SPLIT_ENTRIES 2
-
#ifdef CONFIG_HIGHMEM
/*
* remember never ever reenable interrupts between a bvec_kmap_irq and
@@ -527,6 +511,49 @@ static inline struct bio *bio_list_get(struct bio_list *bl)
return bio;
}
+/*
+ * bio_set is used to allow other portions of the IO system to
+ * allocate their own private memory pools for bio and iovec structures.
+ * These memory pools in turn all allocate from the bio_slab
+ * and the bvec_slabs[].
+ */
+#define BIO_POOL_SIZE 2
+#define BIOVEC_NR_POOLS 6
+#define BIOVEC_MAX_IDX (BIOVEC_NR_POOLS - 1)
+
+struct bio_set {
+ struct kmem_cache *bio_slab;
+ unsigned int front_pad;
+
+ mempool_t *bio_pool;
+ mempool_t *bvec_pool;
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+ mempool_t *bio_integrity_pool;
+ mempool_t *bvec_integrity_pool;
+#endif
+
+ /*
+ * Deadlock avoidance for stacking block drivers: see comments in
+ * bio_alloc_bioset() for details
+ */
+ spinlock_t rescue_lock;
+ struct bio_list rescue_list;
+ struct work_struct rescue_work;
+ struct workqueue_struct *rescue_workqueue;
+};
+
+struct biovec_slab {
+ int nr_vecs;
+ char *name;
+ struct kmem_cache *slab;
+};
+
+/*
+ * a small number of entries is fine, not going to be performance critical.
+ * basically we just need to survive
+ */
+#define BIO_SPLIT_ENTRIES 2
+
#if defined(CONFIG_BLK_DEV_INTEGRITY)
#define bip_vec_idx(bip, idx) (&(bip->bip_vec[(idx)]))
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 22990cf4439d..fa1abeb45b76 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -118,6 +118,7 @@ struct bio {
* BIO_POOL_IDX()
*/
#define BIO_RESET_BITS 13
+#define BIO_OWNS_VEC 13 /* bio_free() should free bvec */
#define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag)))
@@ -176,6 +177,7 @@ enum rq_flag_bits {
__REQ_IO_STAT, /* account I/O stat */
__REQ_MIXED_MERGE, /* merge of different types, fail separately */
__REQ_KERNEL, /* direct IO to kernel pages */
+ __REQ_PM, /* runtime pm request */
__REQ_NR_BITS, /* stops here */
};
@@ -198,6 +200,8 @@ enum rq_flag_bits {
REQ_SECURE)
#define REQ_CLONE_MASK REQ_COMMON_MASK
+#define BIO_NO_ADVANCE_ITER_MASK (REQ_DISCARD|REQ_WRITE_SAME)
+
/* This mask is used for both bio and request merge checking */
#define REQ_NOMERGE_FLAGS \
(REQ_NOMERGE | REQ_STARTED | REQ_SOFTBARRIER | REQ_FLUSH | REQ_FUA)
@@ -224,5 +228,6 @@ enum rq_flag_bits {
#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
#define REQ_SECURE (1 << __REQ_SECURE)
#define REQ_KERNEL (1 << __REQ_KERNEL)
+#define REQ_PM (1 << __REQ_PM)
#endif /* __LINUX_BLK_TYPES_H */
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 78feda9bbae2..2fdb4a451b49 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -361,6 +361,12 @@ struct request_queue {
*/
struct kobject kobj;
+#ifdef CONFIG_PM_RUNTIME
+ struct device *dev;
+ int rpm_status;
+ unsigned int nr_pending;
+#endif
+
/*
* queue settings
*/
@@ -838,7 +844,7 @@ static inline unsigned int blk_queue_get_max_sectors(struct request_queue *q,
unsigned int cmd_flags)
{
if (unlikely(cmd_flags & REQ_DISCARD))
- return q->limits.max_discard_sectors;
+ return min(q->limits.max_discard_sectors, UINT_MAX >> 9);
if (unlikely(cmd_flags & REQ_WRITE_SAME))
return q->limits.max_write_same_sectors;
@@ -961,6 +967,27 @@ struct request_queue *blk_alloc_queue_node(gfp_t, int);
extern void blk_put_queue(struct request_queue *);
/*
+ * block layer runtime pm functions
+ */
+#ifdef CONFIG_PM_RUNTIME
+extern void blk_pm_runtime_init(struct request_queue *q, struct device *dev);
+extern int blk_pre_runtime_suspend(struct request_queue *q);
+extern void blk_post_runtime_suspend(struct request_queue *q, int err);
+extern void blk_pre_runtime_resume(struct request_queue *q);
+extern void blk_post_runtime_resume(struct request_queue *q, int err);
+#else
+static inline void blk_pm_runtime_init(struct request_queue *q,
+ struct device *dev) {}
+static inline int blk_pre_runtime_suspend(struct request_queue *q)
+{
+ return -ENOSYS;
+}
+static inline void blk_post_runtime_suspend(struct request_queue *q, int err) {}
+static inline void blk_pre_runtime_resume(struct request_queue *q) {}
+static inline void blk_post_runtime_resume(struct request_queue *q, int err) {}
+#endif
+
+/*
* blk_plug permits building a queue of related requests by holding the I/O
* fragments for a short period. This allows merging of sequential requests
* into single larger request. As the requests are moved from a per-task list to
@@ -1484,7 +1511,7 @@ static inline bool blk_integrity_is_initialized(struct gendisk *g)
struct block_device_operations {
int (*open) (struct block_device *, fmode_t);
- int (*release) (struct gendisk *, fmode_t);
+ void (*release) (struct gendisk *, fmode_t);
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*direct_access) (struct block_device *, sector_t,
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index c51d8a4436c9..610cdd48a3be 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -28,6 +28,7 @@ struct cgroup_subsys;
struct inode;
struct cgroup;
struct css_id;
+struct eventfd_ctx;
extern int cgroup_init_early(void);
extern int cgroup_init(void);
diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h
index f204a7a9cf38..6e7ec64b69ab 100644
--- a/include/linux/cgroup_subsys.h
+++ b/include/linux/cgroup_subsys.h
@@ -78,3 +78,9 @@ SUBSYS(hugetlb)
#endif
/* */
+
+#ifdef CONFIG_CGROUP_BCACHE
+SUBSYS(bcache)
+#endif
+
+/* */
diff --git a/include/linux/drbd.h b/include/linux/drbd.h
index 0c5a18ec322c..1b4d4ee1168f 100644
--- a/include/linux/drbd.h
+++ b/include/linux/drbd.h
@@ -52,7 +52,7 @@
#endif
extern const char *drbd_buildtag(void);
-#define REL_VERSION "8.4.2"
+#define REL_VERSION "8.4.3"
#define API_VERSION 1
#define PRO_VERSION_MIN 86
#define PRO_VERSION_MAX 101
@@ -319,7 +319,8 @@ enum drbd_state_rv {
SS_IN_TRANSIENT_STATE = -18, /* Retry after the next state change */
SS_CONCURRENT_ST_CHG = -19, /* Concurrent cluster side state change! */
SS_O_VOL_PEER_PRI = -20,
- SS_AFTER_LAST_ERROR = -21, /* Keep this at bottom */
+ SS_OUTDATE_WO_CONN = -21,
+ SS_AFTER_LAST_ERROR = -22, /* Keep this at bottom */
};
/* from drbd_strings.c */
diff --git a/include/linux/drbd_limits.h b/include/linux/drbd_limits.h
index 1fa19c5f5e64..1fedf2b17cc8 100644
--- a/include/linux/drbd_limits.h
+++ b/include/linux/drbd_limits.h
@@ -126,13 +126,12 @@
#define DRBD_RESYNC_RATE_DEF 250
#define DRBD_RESYNC_RATE_SCALE 'k' /* kilobytes */
- /* less than 7 would hit performance unnecessarily.
- * 919 slots context information per transaction,
- * 32k activity log, 4k transaction size,
- * one transaction in flight:
- * 919 * 7 = 6433 */
+ /* less than 7 would hit performance unnecessarily. */
#define DRBD_AL_EXTENTS_MIN 7
-#define DRBD_AL_EXTENTS_MAX 6433
+ /* we use u16 as "slot number", (u16)~0 is "FREE".
+ * If you use >= 292 kB on-disk ring buffer,
+ * this is the maximum you can use: */
+#define DRBD_AL_EXTENTS_MAX 0xfffe
#define DRBD_AL_EXTENTS_DEF 1237
#define DRBD_AL_EXTENTS_SCALE '1'
diff --git a/include/linux/errno.h b/include/linux/errno.h
index f6bf082d4d4f..89627b9187f9 100644
--- a/include/linux/errno.h
+++ b/include/linux/errno.h
@@ -28,6 +28,5 @@
#define EBADTYPE 527 /* Type not supported by server */
#define EJUKEBOX 528 /* Request initiated, but will not complete before timeout */
#define EIOCBQUEUED 529 /* iocb queued, will get completion event */
-#define EIOCBRETRY 530 /* iocb queued, will trigger a retry */
#endif
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index f9a12f6243a5..df6fab82f87e 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -139,7 +139,7 @@ struct f2fs_extent {
__le32 len; /* lengh of the extent */
} __packed;
-#define F2FS_MAX_NAME_LEN 256
+#define F2FS_NAME_LEN 255
#define ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
@@ -165,7 +165,8 @@ struct f2fs_inode {
__le32 i_flags; /* file attributes */
__le32 i_pino; /* parent inode number */
__le32 i_namelen; /* file name length */
- __u8 i_name[F2FS_MAX_NAME_LEN]; /* file name for SPOR */
+ __u8 i_name[F2FS_NAME_LEN]; /* file name for SPOR */
+ __u8 i_reserved2; /* for backward compatibility */
struct f2fs_extent i_ext; /* caching a largest extent */
@@ -362,10 +363,10 @@ struct f2fs_summary_block {
typedef __le32 f2fs_hash_t;
/* One directory entry slot covers 8bytes-long file name */
-#define F2FS_NAME_LEN 8
-#define F2FS_NAME_LEN_BITS 3
+#define F2FS_SLOT_LEN 8
+#define F2FS_SLOT_LEN_BITS 3
-#define GET_DENTRY_SLOTS(x) ((x + F2FS_NAME_LEN - 1) >> F2FS_NAME_LEN_BITS)
+#define GET_DENTRY_SLOTS(x) ((x + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS)
/* the number of dentry in a block */
#define NR_DENTRY_IN_BLOCK 214
@@ -377,10 +378,10 @@ typedef __le32 f2fs_hash_t;
#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \
BITS_PER_BYTE)
#define SIZE_OF_RESERVED (PAGE_SIZE - ((SIZE_OF_DIR_ENTRY + \
- F2FS_NAME_LEN) * \
+ F2FS_SLOT_LEN) * \
NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP))
-/* One directory entry slot representing F2FS_NAME_LEN-sized file name */
+/* One directory entry slot representing F2FS_SLOT_LEN-sized file name */
struct f2fs_dir_entry {
__le32 hash_code; /* hash code of file name */
__le32 ino; /* inode number */
@@ -394,7 +395,7 @@ struct f2fs_dentry_block {
__u8 dentry_bitmap[SIZE_OF_DENTRY_BITMAP];
__u8 reserved[SIZE_OF_RESERVED];
struct f2fs_dir_entry dentry[NR_DENTRY_IN_BLOCK];
- __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_NAME_LEN];
+ __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN];
} __packed;
/* file types used in inode_info->flags */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b5a24ba83b6f..43db02e9c9fa 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2091,7 +2091,7 @@ extern struct block_device *blkdev_get_by_path(const char *path, fmode_t mode,
void *holder);
extern struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode,
void *holder);
-extern int blkdev_put(struct block_device *bdev, fmode_t mode);
+extern void blkdev_put(struct block_device *bdev, fmode_t mode);
#ifdef CONFIG_SYSFS
extern int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk);
extern void bd_unlink_disk_holder(struct block_device *bdev,
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 3a62df310f2e..6b4890fa57e7 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -189,8 +189,7 @@ static inline struct hugetlbfs_sb_info *HUGETLBFS_SB(struct super_block *sb)
extern const struct file_operations hugetlbfs_file_operations;
extern const struct vm_operations_struct hugetlb_vm_ops;
-struct file *hugetlb_file_setup(const char *name, unsigned long addr,
- size_t size, vm_flags_t acct,
+struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct,
struct user_struct **user, int creat_flags,
int page_size_log);
@@ -209,8 +208,8 @@ static inline int is_file_hugepages(struct file *file)
#define is_file_hugepages(file) 0
static inline struct file *
-hugetlb_file_setup(const char *name, unsigned long addr, size_t size,
- vm_flags_t acctflag, struct user_struct **user, int creat_flags,
+hugetlb_file_setup(const char *name, size_t size, vm_flags_t acctflag,
+ struct user_struct **user, int creat_flags,
int page_size_log)
{
return ERR_PTR(-ENOSYS);
@@ -288,6 +287,13 @@ static inline struct hstate *hstate_file(struct file *f)
return hstate_inode(file_inode(f));
}
+static inline struct hstate *hstate_sizelog(int page_size_log)
+{
+ if (!page_size_log)
+ return &default_hstate;
+ return size_to_hstate(1 << page_size_log);
+}
+
static inline struct hstate *hstate_vma(struct vm_area_struct *vma)
{
return hstate_file(vma->vm_file);
@@ -352,11 +358,12 @@ static inline int hstate_index(struct hstate *h)
return h - hstates;
}
-#else
+#else /* CONFIG_HUGETLB_PAGE */
struct hstate {};
#define alloc_huge_page_node(h, nid) NULL
#define alloc_bootmem_huge_page(h) NULL
#define hstate_file(f) NULL
+#define hstate_sizelog(s) NULL
#define hstate_vma(v) NULL
#define hstate_inode(i) NULL
#define huge_page_size(h) PAGE_SIZE
@@ -371,6 +378,6 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
}
#define hstate_index_to_shift(index) 0
#define hstate_index(h) 0
-#endif
+#endif /* CONFIG_HUGETLB_PAGE */
#endif /* _LINUX_HUGETLB_H */
diff --git a/include/linux/idr.h b/include/linux/idr.h
index a470ac3ef49d..871a213a8477 100644
--- a/include/linux/idr.h
+++ b/include/linux/idr.h
@@ -124,11 +124,13 @@ static inline void *idr_find(struct idr *idr, int id)
* @idp: idr handle
* @entry: the type * to use as cursor
* @id: id entry's key
+ *
+ * @entry and @id do not need to be initialized before the loop, and
+ * after normal terminatinon @entry is left with the value NULL. This
+ * is convenient for a "not found" value.
*/
-#define idr_for_each_entry(idp, entry, id) \
- for (id = 0, entry = (typeof(entry))idr_get_next((idp), &(id)); \
- entry != NULL; \
- ++id, entry = (typeof(entry))idr_get_next((idp), &(id)))
+#define idr_for_each_entry(idp, entry, id) \
+ for (id = 0; ((entry) = idr_get_next(idp, &(id))) != NULL; ++id)
/*
* Don't use the following functions. These exist only to suppress
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index ba3b8a98a049..3aeb7305e2f5 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -20,6 +20,7 @@
#define __LINUX_IOMMU_H
#include <linux/errno.h>
+#include <linux/err.h>
#include <linux/types.h>
#define IOMMU_READ (1)
@@ -91,8 +92,7 @@ struct iommu_ops {
phys_addr_t paddr, size_t size, int prot);
size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
size_t size);
- phys_addr_t (*iova_to_phys)(struct iommu_domain *domain,
- unsigned long iova);
+ phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
int (*domain_has_cap)(struct iommu_domain *domain,
unsigned long cap);
int (*add_device)(struct device *dev);
@@ -105,7 +105,7 @@ struct iommu_ops {
/* Window handling functions */
int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr,
- phys_addr_t paddr, u64 size);
+ phys_addr_t paddr, u64 size, int prot);
void (*domain_window_disable)(struct iommu_domain *domain, u32 wnd_nr);
/* Set the numer of window per domain */
int (*domain_set_windows)(struct iommu_domain *domain, u32 w_count);
@@ -125,6 +125,7 @@ struct iommu_ops {
extern int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops);
extern bool iommu_present(struct bus_type *bus);
extern struct iommu_domain *iommu_domain_alloc(struct bus_type *bus);
+extern struct iommu_group *iommu_group_get_by_id(int id);
extern void iommu_domain_free(struct iommu_domain *domain);
extern int iommu_attach_device(struct iommu_domain *domain,
struct device *dev);
@@ -134,8 +135,7 @@ extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot);
extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
size_t size);
-extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long iova);
+extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova);
extern int iommu_domain_has_cap(struct iommu_domain *domain,
unsigned long cap);
extern void iommu_set_fault_handler(struct iommu_domain *domain,
@@ -171,7 +171,8 @@ extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr,
/* Window handling function prototypes */
extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
- phys_addr_t offset, u64 size);
+ phys_addr_t offset, u64 size,
+ int prot);
extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr);
/**
* report_iommu_fault() - report about an IOMMU fault to the IOMMU framework
@@ -257,7 +258,7 @@ static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova,
static inline int iommu_domain_window_enable(struct iommu_domain *domain,
u32 wnd_nr, phys_addr_t paddr,
- u64 size)
+ u64 size, int prot)
{
return -ENODEV;
}
@@ -267,8 +268,7 @@ static inline void iommu_domain_window_disable(struct iommu_domain *domain,
{
}
-static inline phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
- unsigned long iova)
+static inline phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
{
return 0;
}
diff --git a/include/linux/kmalloc_sizes.h b/include/linux/kmalloc_sizes.h
deleted file mode 100644
index e576b848ce10..000000000000
--- a/include/linux/kmalloc_sizes.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#if (PAGE_SIZE == 4096)
- CACHE(32)
-#endif
- CACHE(64)
-#if L1_CACHE_BYTES < 64
- CACHE(96)
-#endif
- CACHE(128)
-#if L1_CACHE_BYTES < 128
- CACHE(192)
-#endif
- CACHE(256)
- CACHE(512)
- CACHE(1024)
- CACHE(2048)
- CACHE(4096)
- CACHE(8192)
- CACHE(16384)
- CACHE(32768)
- CACHE(65536)
- CACHE(131072)
-#if KMALLOC_MAX_SIZE >= 262144
- CACHE(262144)
-#endif
-#if KMALLOC_MAX_SIZE >= 524288
- CACHE(524288)
-#endif
-#if KMALLOC_MAX_SIZE >= 1048576
- CACHE(1048576)
-#endif
-#if KMALLOC_MAX_SIZE >= 2097152
- CACHE(2097152)
-#endif
-#if KMALLOC_MAX_SIZE >= 4194304
- CACHE(4194304)
-#endif
-#if KMALLOC_MAX_SIZE >= 8388608
- CACHE(8388608)
-#endif
-#if KMALLOC_MAX_SIZE >= 16777216
- CACHE(16777216)
-#endif
-#if KMALLOC_MAX_SIZE >= 33554432
- CACHE(33554432)
-#endif
diff --git a/include/linux/kref.h b/include/linux/kref.h
index 4972e6e9ca93..e15828fd71f1 100644
--- a/include/linux/kref.h
+++ b/include/linux/kref.h
@@ -39,8 +39,11 @@ static inline void kref_init(struct kref *kref)
*/
static inline void kref_get(struct kref *kref)
{
- WARN_ON(!atomic_read(&kref->refcount));
- atomic_inc(&kref->refcount);
+ /* If refcount was 0 before incrementing then we have a race
+ * condition when this kref is freeing by some other thread right now.
+ * In this case one should use kref_get_unless_zero()
+ */
+ WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
}
/**
@@ -100,7 +103,7 @@ static inline int kref_put_mutex(struct kref *kref,
struct mutex *lock)
{
WARN_ON(release == NULL);
- if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) {
+ if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) {
mutex_lock(lock);
if (unlikely(!atomic_dec_and_test(&kref->refcount))) {
mutex_unlock(lock);
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 0d9b5eed714e..0287ab296689 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -142,6 +142,10 @@ extern void led_set_brightness(struct led_classdev *led_cdev,
/*
* LED Triggers
*/
+/* Registration functions for simple triggers */
+#define DEFINE_LED_TRIGGER(x) static struct led_trigger *x;
+#define DEFINE_LED_TRIGGER_GLOBAL(x) struct led_trigger *x;
+
#ifdef CONFIG_LEDS_TRIGGERS
#define TRIG_NAME_MAX 50
@@ -164,9 +168,6 @@ struct led_trigger {
extern int led_trigger_register(struct led_trigger *trigger);
extern void led_trigger_unregister(struct led_trigger *trigger);
-/* Registration functions for simple triggers */
-#define DEFINE_LED_TRIGGER(x) static struct led_trigger *x;
-#define DEFINE_LED_TRIGGER_GLOBAL(x) struct led_trigger *x;
extern void led_trigger_register_simple(const char *name,
struct led_trigger **trigger);
extern void led_trigger_unregister_simple(struct led_trigger *trigger);
@@ -199,20 +200,30 @@ extern void led_trigger_rename_static(const char *name,
#else
-/* Triggers aren't active - null macros */
-#define DEFINE_LED_TRIGGER(x)
-#define DEFINE_LED_TRIGGER_GLOBAL(x)
-#define led_trigger_register_simple(x, y) do {} while(0)
-#define led_trigger_unregister_simple(x) do {} while(0)
-#define led_trigger_event(x, y) do {} while(0)
+/* Trigger has no members */
+struct led_trigger {};
-#endif
+/* Trigger inline empty functions */
+static inline void led_trigger_register_simple(const char *name,
+ struct led_trigger **trigger) {}
+static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
+static inline void led_trigger_event(struct led_trigger *trigger,
+ enum led_brightness event) {}
+#endif /* CONFIG_LEDS_TRIGGERS */
/* Trigger specific functions */
#ifdef CONFIG_LEDS_TRIGGER_IDE_DISK
extern void ledtrig_ide_activity(void);
#else
-#define ledtrig_ide_activity() do {} while(0)
+static inline void ledtrig_ide_activity(void) {}
+#endif
+
+#if defined(CONFIG_LEDS_TRIGGER_CAMERA) || defined(CONFIG_LEDS_TRIGGER_CAMERA_MODULE)
+extern void ledtrig_flash_ctrl(bool on);
+extern void ledtrig_torch_ctrl(bool on);
+#else
+static inline void ledtrig_flash_ctrl(bool on) {}
+static inline void ledtrig_torch_ctrl(bool on) {}
#endif
/*
diff --git a/include/linux/lru_cache.h b/include/linux/lru_cache.h
index 4019013c6593..46262284de47 100644
--- a/include/linux/lru_cache.h
+++ b/include/linux/lru_cache.h
@@ -256,6 +256,7 @@ extern void lc_destroy(struct lru_cache *lc);
extern void lc_set(struct lru_cache *lc, unsigned int enr, int index);
extern void lc_del(struct lru_cache *lc, struct lc_element *element);
+extern struct lc_element *lc_get_cumulative(struct lru_cache *lc, unsigned int enr);
extern struct lc_element *lc_try_get(struct lru_cache *lc, unsigned int enr);
extern struct lc_element *lc_find(struct lru_cache *lc, unsigned int enr);
extern struct lc_element *lc_get(struct lru_cache *lc, unsigned int enr);
diff --git a/include/linux/mbus.h b/include/linux/mbus.h
index efa1a6d7aca8..dba482e31a13 100644
--- a/include/linux/mbus.h
+++ b/include/linux/mbus.h
@@ -32,6 +32,20 @@ struct mbus_dram_target_info
} cs[4];
};
+/* Flags for PCI/PCIe address decoding regions */
+#define MVEBU_MBUS_PCI_IO 0x1
+#define MVEBU_MBUS_PCI_MEM 0x2
+#define MVEBU_MBUS_PCI_WA 0x3
+
+/*
+ * Magic value that explicits that we don't need a remapping-capable
+ * address decoding window.
+ */
+#define MVEBU_MBUS_NO_REMAP (0xffffffff)
+
+/* Maximum size of a mbus window name */
+#define MVEBU_MBUS_MAX_WINNAME_SZ 32
+
/*
* The Marvell mbus is to be found only on SOCs from the Orion family
* at the moment. Provide a dummy stub for other architectures.
@@ -44,4 +58,15 @@ static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void)
return NULL;
}
#endif
-#endif
+
+int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base,
+ size_t size, phys_addr_t remap,
+ unsigned int flags);
+int mvebu_mbus_add_window(const char *devname, phys_addr_t base,
+ size_t size);
+int mvebu_mbus_del_window(phys_addr_t base, size_t size);
+int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base,
+ size_t mbus_size, phys_addr_t sdram_phys_base,
+ size_t sdram_size);
+
+#endif /* __LINUX_MBUS_H */
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index 53acaf64189f..a51b0134ce18 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -903,11 +903,12 @@ static inline int map_hw_to_sw_id(u16 header_id)
}
enum mlx4_net_trans_promisc_mode {
- MLX4_FS_PROMISC_NONE = 0,
- MLX4_FS_PROMISC_UPLINK,
- /* For future use. Not implemented yet */
- MLX4_FS_PROMISC_FUNCTION_PORT,
- MLX4_FS_PROMISC_ALL_MULTI,
+ MLX4_FS_REGULAR = 1,
+ MLX4_FS_ALL_DEFAULT,
+ MLX4_FS_MC_DEFAULT,
+ MLX4_FS_UC_SNIFFER,
+ MLX4_FS_MC_SNIFFER,
+ MLX4_FS_MODE_NUM, /* should be last */
};
struct mlx4_spec_eth {
@@ -936,7 +937,7 @@ struct mlx4_spec_ipv4 {
};
struct mlx4_spec_ib {
- __be32 r_qpn;
+ __be32 l3_qpn;
__be32 qpn_msk;
u8 dst_gid[16];
u8 dst_gid_msk[16];
@@ -969,6 +970,92 @@ struct mlx4_net_trans_rule {
u32 qpn;
};
+struct mlx4_net_trans_rule_hw_ctrl {
+ __be16 prio;
+ u8 type;
+ u8 flags;
+ u8 rsvd1;
+ u8 funcid;
+ u8 vep;
+ u8 port;
+ __be32 qpn;
+ __be32 rsvd2;
+};
+
+struct mlx4_net_trans_rule_hw_ib {
+ u8 size;
+ u8 rsvd1;
+ __be16 id;
+ u32 rsvd2;
+ __be32 l3_qpn;
+ __be32 qpn_mask;
+ u8 dst_gid[16];
+ u8 dst_gid_msk[16];
+} __packed;
+
+struct mlx4_net_trans_rule_hw_eth {
+ u8 size;
+ u8 rsvd;
+ __be16 id;
+ u8 rsvd1[6];
+ u8 dst_mac[6];
+ u16 rsvd2;
+ u8 dst_mac_msk[6];
+ u16 rsvd3;
+ u8 src_mac[6];
+ u16 rsvd4;
+ u8 src_mac_msk[6];
+ u8 rsvd5;
+ u8 ether_type_enable;
+ __be16 ether_type;
+ __be16 vlan_tag_msk;
+ __be16 vlan_tag;
+} __packed;
+
+struct mlx4_net_trans_rule_hw_tcp_udp {
+ u8 size;
+ u8 rsvd;
+ __be16 id;
+ __be16 rsvd1[3];
+ __be16 dst_port;
+ __be16 rsvd2;
+ __be16 dst_port_msk;
+ __be16 rsvd3;
+ __be16 src_port;
+ __be16 rsvd4;
+ __be16 src_port_msk;
+} __packed;
+
+struct mlx4_net_trans_rule_hw_ipv4 {
+ u8 size;
+ u8 rsvd;
+ __be16 id;
+ __be32 rsvd1;
+ __be32 dst_ip;
+ __be32 dst_ip_msk;
+ __be32 src_ip;
+ __be32 src_ip_msk;
+} __packed;
+
+struct _rule_hw {
+ union {
+ struct {
+ u8 size;
+ u8 rsvd;
+ __be16 id;
+ };
+ struct mlx4_net_trans_rule_hw_eth eth;
+ struct mlx4_net_trans_rule_hw_ib ib;
+ struct mlx4_net_trans_rule_hw_ipv4 ipv4;
+ struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp;
+ };
+};
+
+/* translating DMFS verbs sniffer rule to the FW API would need two reg IDs */
+struct mlx4_flow_handle {
+ u64 reg_id[2];
+};
+
int mlx4_flow_steer_promisc_add(struct mlx4_dev *dev, u8 port, u32 qpn,
enum mlx4_net_trans_promisc_mode mode);
int mlx4_flow_steer_promisc_remove(struct mlx4_dev *dev, u8 port,
@@ -1018,6 +1105,11 @@ void mlx4_counter_free(struct mlx4_dev *dev, u32 idx);
int mlx4_flow_attach(struct mlx4_dev *dev,
struct mlx4_net_trans_rule *rule, u64 *reg_id);
int mlx4_flow_detach(struct mlx4_dev *dev, u64 reg_id);
+int mlx4_map_sw_to_hw_steering_mode(struct mlx4_dev *dev,
+ enum mlx4_net_trans_promisc_mode flow_type);
+int mlx4_map_sw_to_hw_steering_id(struct mlx4_dev *dev,
+ enum mlx4_net_trans_rule_id id);
+int mlx4_hw_rule_sz(struct mlx4_dev *dev, enum mlx4_net_trans_rule_id id);
void mlx4_sync_pkey_table(struct mlx4_dev *dev, int slave, int port,
int i, int val);
diff --git a/include/linux/mlx4/srq.h b/include/linux/mlx4/srq.h
index 799a0697a383..192e0f7784f2 100644
--- a/include/linux/mlx4/srq.h
+++ b/include/linux/mlx4/srq.h
@@ -39,4 +39,6 @@ struct mlx4_wqe_srq_next_seg {
u32 reserved2[3];
};
+struct mlx4_srq *mlx4_srq_lookup(struct mlx4_dev *dev, u32 srqn);
+
#endif /* MLX4_SRQ_H */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index d250a2460bb0..bd5679ddcd3f 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -952,13 +952,19 @@ void unmap_vmas(struct mmu_gather *tlb, struct vm_area_struct *start_vma,
* (see walk_page_range for more details)
*/
struct mm_walk {
- int (*pgd_entry)(pgd_t *, unsigned long, unsigned long, struct mm_walk *);
- int (*pud_entry)(pud_t *, unsigned long, unsigned long, struct mm_walk *);
- int (*pmd_entry)(pmd_t *, unsigned long, unsigned long, struct mm_walk *);
- int (*pte_entry)(pte_t *, unsigned long, unsigned long, struct mm_walk *);
- int (*pte_hole)(unsigned long, unsigned long, struct mm_walk *);
- int (*hugetlb_entry)(pte_t *, unsigned long,
- unsigned long, unsigned long, struct mm_walk *);
+ int (*pgd_entry)(pgd_t *pgd, unsigned long addr,
+ unsigned long next, struct mm_walk *walk);
+ int (*pud_entry)(pud_t *pud, unsigned long addr,
+ unsigned long next, struct mm_walk *walk);
+ int (*pmd_entry)(pmd_t *pmd, unsigned long addr,
+ unsigned long next, struct mm_walk *walk);
+ int (*pte_entry)(pte_t *pte, unsigned long addr,
+ unsigned long next, struct mm_walk *walk);
+ int (*pte_hole)(unsigned long addr, unsigned long next,
+ struct mm_walk *walk);
+ int (*hugetlb_entry)(pte_t *pte, unsigned long hmask,
+ unsigned long addr, unsigned long next,
+ struct mm_walk *walk);
struct mm_struct *mm;
void *private;
};
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 059d7d801652..ee877c3a8c5c 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -348,9 +348,7 @@ struct mmc_host {
mmc_pm_flag_t pm_flags; /* requested pm features */
-#ifdef CONFIG_LEDS_TRIGGERS
struct led_trigger *led; /* activity led */
-#endif
#ifdef CONFIG_REGULATOR
bool regulator_enabled; /* regulator state */
diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h
index 4eb0a50d0c55..e93837f647de 100644
--- a/include/linux/mtd/blktrans.h
+++ b/include/linux/mtd/blktrans.h
@@ -74,7 +74,7 @@ struct mtd_blktrans_ops {
/* Called with mtd_table_mutex held; no race with add/remove */
int (*open)(struct mtd_blktrans_dev *dev);
- int (*release)(struct mtd_blktrans_dev *dev);
+ void (*release)(struct mtd_blktrans_dev *dev);
/* Called on {de,}registration and on subsequent addition/removal
of devices, with mtd_table_mutex held. */
diff --git a/include/linux/mxsfb.h b/include/linux/mxsfb.h
deleted file mode 100644
index f80af8674342..000000000000
--- a/include/linux/mxsfb.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef __LINUX_MXSFB_H
-#define __LINUX_MXSFB_H
-
-#include <linux/fb.h>
-
-#define STMLCDIF_8BIT 1 /** pixel data bus to the display is of 8 bit width */
-#define STMLCDIF_16BIT 0 /** pixel data bus to the display is of 16 bit width */
-#define STMLCDIF_18BIT 2 /** pixel data bus to the display is of 18 bit width */
-#define STMLCDIF_24BIT 3 /** pixel data bus to the display is of 24 bit width */
-
-#define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6)
-#define MXSFB_SYNC_DOTCLK_FAILING_ACT (1 << 7) /* failing/negtive edge sampling */
-
-struct mxsfb_platform_data {
- struct fb_videomode *mode_list;
- unsigned mode_count;
-
- unsigned default_bpp;
-
- unsigned dotclk_delay; /* refer manual HW_LCDIF_VDCTRL4 register */
- unsigned ld_intf_width; /* refer STMLCDIF_* macros */
-
- unsigned fb_size; /* Size of the video memory. If zero a
- * default will be used
- */
- unsigned long fb_phys; /* physical address for the video memory. If
- * zero the framebuffer memory will be dynamically
- * allocated. If specified,fb_size must also be specified.
- * fb_phys must be unused by Linux.
- */
- u32 sync; /* sync mask, contains MXSFB specifics not
- * carried in fb_info->var.sync
- */
-};
-
-#endif /* __LINUX_MXSFB_H */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index f8898a435dc5..a94a5a0ab122 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -593,7 +593,6 @@ struct rps_dev_flow {
struct rps_dev_flow_table {
unsigned int mask;
struct rcu_head rcu;
- struct work_struct free_work;
struct rps_dev_flow flows[0];
};
#define RPS_DEV_FLOW_TABLE_SIZE(_num) (sizeof(struct rps_dev_flow_table) + \
diff --git a/include/linux/of.h b/include/linux/of.h
index 1b671c3809b8..1fd08ca23106 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -387,6 +387,11 @@ static inline int of_device_is_compatible(const struct device_node *device,
return 0;
}
+static inline int of_device_is_available(const struct device_node *device)
+{
+ return 0;
+}
+
static inline struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)
diff --git a/include/linux/pata_arasan_cf_data.h b/include/linux/pata_arasan_cf_data.h
index a7b4fc386e63..3cc21c9cc1e8 100644
--- a/include/linux/pata_arasan_cf_data.h
+++ b/include/linux/pata_arasan_cf_data.h
@@ -37,8 +37,6 @@ struct arasan_cf_pdata {
#define CF_BROKEN_PIO (1)
#define CF_BROKEN_MWDMA (1 << 1)
#define CF_BROKEN_UDMA (1 << 2)
- /* This is platform specific data for the DMA controller */
- void *dma_priv;
};
static inline void
diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h
index 731e4ecee3bd..e2772666f004 100644
--- a/include/linux/pid_namespace.h
+++ b/include/linux/pid_namespace.h
@@ -4,6 +4,7 @@
#include <linux/sched.h>
#include <linux/bug.h>
#include <linux/mm.h>
+#include <linux/workqueue.h>
#include <linux/threads.h>
#include <linux/nsproxy.h>
#include <linux/kref.h>
diff --git a/include/linux/platform_data/gpio-rcar.h b/include/linux/platform_data/gpio-rcar.h
new file mode 100644
index 000000000000..b253f77a7ddf
--- /dev/null
+++ b/include/linux/platform_data/gpio-rcar.h
@@ -0,0 +1,26 @@
+/*
+ * Renesas R-Car GPIO Support
+ *
+ * Copyright (C) 2013 Magnus Damm
+ *
+ * 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.
+ */
+
+#ifndef __GPIO_RCAR_H__
+#define __GPIO_RCAR_H__
+
+struct gpio_rcar_config {
+ unsigned int gpio_base;
+ unsigned int irq_base;
+ unsigned int number_of_pins;
+ const char *pctl_name;
+};
+
+#endif /* __GPIO_RCAR_H__ */
diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h
index 1509570d5a3f..202e290faea8 100644
--- a/include/linux/platform_data/leds-lp55xx.h
+++ b/include/linux/platform_data/leds-lp55xx.h
@@ -20,18 +20,6 @@
#define LP55XX_CLOCK_INT 1
#define LP55XX_CLOCK_EXT 2
-/* Bits in LP5521 CONFIG register. 'update_config' in lp55xx_platform_data */
-#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
-#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
-#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
-#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
-#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
-#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
-#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */
-#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */
-#define LP5521_CLK_INT 1 /* Internal clock */
-#define LP5521_CLK_AUTO 2 /* Automatic clock selection */
-
struct lp55xx_led_config {
const char *name;
u8 chan_nr;
@@ -40,9 +28,9 @@ struct lp55xx_led_config {
};
struct lp55xx_predef_pattern {
- u8 *r;
- u8 *g;
- u8 *b;
+ const u8 *r;
+ const u8 *g;
+ const u8 *b;
u8 size_r;
u8 size_g;
u8 size_b;
@@ -79,9 +67,6 @@ struct lp55xx_platform_data {
/* Predefined pattern data */
struct lp55xx_predef_pattern *patterns;
unsigned int num_patterns;
-
- /* _CONFIG register */
- u8 update_config;
};
#endif /* _LEDS_LP55XX_H */
diff --git a/include/linux/random.h b/include/linux/random.h
index 347ce553a306..3b9377d6b7a5 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -29,13 +29,6 @@ u32 prandom_u32(void);
void prandom_bytes(void *buf, int nbytes);
void prandom_seed(u32 seed);
-/*
- * These macros are preserved for backward compatibility and should be
- * removed as soon as a transition is finished.
- */
-#define random32() prandom_u32()
-#define srandom32(seed) prandom_seed(seed)
-
u32 prandom_u32_state(struct rnd_state *);
void prandom_bytes_state(struct rnd_state *state, void *buf, int nbytes);
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index faf33324c78f..9e7e745dac55 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -401,6 +401,9 @@ enum rproc_crash_type {
* @crash_comp: completion used to sync crash handler and the rproc reload
* @recovery_disabled: flag that state if recovery was disabled
* @max_notifyid: largest allocated notify id.
+ * @table_ptr: pointer to the resource table in effect
+ * @cached_table: copy of the resource table
+ * @table_csum: checksum of the resource table
*/
struct rproc {
struct klist_node node;
@@ -429,9 +432,13 @@ struct rproc {
struct completion crash_comp;
bool recovery_disabled;
int max_notifyid;
+ struct resource_table *table_ptr;
+ struct resource_table *cached_table;
+ u32 table_csum;
};
/* we currently support only two vrings per rvdev */
+
#define RVDEV_NUM_VRINGS 2
/**
@@ -462,16 +469,14 @@ struct rproc_vring {
* @rproc: the rproc handle
* @vdev: the virio device
* @vring: the vrings for this vdev
- * @dfeatures: virtio device features
- * @gfeatures: virtio guest features
+ * @rsc_offset: offset of the vdev's resource entry
*/
struct rproc_vdev {
struct list_head node;
struct rproc *rproc;
struct virtio_device vdev;
struct rproc_vring vring[RVDEV_NUM_VRINGS];
- unsigned long dfeatures;
- unsigned long gfeatures;
+ u32 rsc_offset;
};
struct rproc *rproc_alloc(struct device *dev, const char *name,
diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
index 8da67d625e13..0616ffe45702 100644
--- a/include/linux/rwsem.h
+++ b/include/linux/rwsem.h
@@ -133,10 +133,20 @@ do { \
_down_write_nest_lock(sem, &(nest_lock)->dep_map); \
} while (0);
+/*
+ * Take/release a lock when not the owner will release it.
+ *
+ * [ This API should be avoided as much as possible - the
+ * proper abstraction for this case is completions. ]
+ */
+extern void down_read_non_owner(struct rw_semaphore *sem);
+extern void up_read_non_owner(struct rw_semaphore *sem);
#else
# define down_read_nested(sem, subclass) down_read(sem)
# define down_write_nest_lock(sem, nest_lock) down_write(sem)
# define down_write_nested(sem, subclass) down_write(sem)
+# define down_read_non_owner(sem) down_read(sem)
+# define up_read_non_owner(sem) up_read(sem)
#endif
#endif /* _LINUX_RWSEM_H */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 8b46cd5b893e..0e209aa7959b 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -313,8 +313,6 @@ extern void schedule_preempt_disabled(void);
struct nsproxy;
struct user_namespace;
-#include <linux/aio.h>
-
#ifdef CONFIG_MMU
extern void arch_pick_mmap_layout(struct mm_struct *mm);
extern unsigned long
@@ -1413,6 +1411,10 @@ struct task_struct {
#ifdef CONFIG_UPROBES
struct uprobe_task *utask;
#endif
+#if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE)
+ unsigned int sequential_io;
+ unsigned int sequential_io_avg;
+#endif
};
/* Future-safe accessor for struct task_struct's cpus_allowed. */
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 5d168d7e0a28..0c621752caa6 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -94,29 +94,6 @@
#define ZERO_OR_NULL_PTR(x) ((unsigned long)(x) <= \
(unsigned long)ZERO_SIZE_PTR)
-/*
- * Common fields provided in kmem_cache by all slab allocators
- * This struct is either used directly by the allocator (SLOB)
- * or the allocator must include definitions for all fields
- * provided in kmem_cache_common in their definition of kmem_cache.
- *
- * Once we can do anonymous structs (C11 standard) we could put a
- * anonymous struct definition in these allocators so that the
- * separate allocations in the kmem_cache structure of SLAB and
- * SLUB is no longer needed.
- */
-#ifdef CONFIG_SLOB
-struct kmem_cache {
- unsigned int object_size;/* The original size of the object */
- unsigned int size; /* The aligned/padded/added on size */
- unsigned int align; /* Alignment as calculated */
- unsigned long flags; /* Active flags on the slab */
- const char *name; /* Slab name for sysfs */
- int refcount; /* Use counter */
- void (*ctor)(void *); /* Called on object slot creation */
- struct list_head list; /* List of all slab caches on the system */
-};
-#endif
struct mem_cgroup;
/*
@@ -148,7 +125,63 @@ void kmem_cache_free(struct kmem_cache *, void *);
(__flags), NULL)
/*
- * The largest kmalloc size supported by the slab allocators is
+ * Common kmalloc functions provided by all allocators
+ */
+void * __must_check __krealloc(const void *, size_t, gfp_t);
+void * __must_check krealloc(const void *, size_t, gfp_t);
+void kfree(const void *);
+void kzfree(const void *);
+size_t ksize(const void *);
+
+/*
+ * Some archs want to perform DMA into kmalloc caches and need a guaranteed
+ * alignment larger than the alignment of a 64-bit integer.
+ * Setting ARCH_KMALLOC_MINALIGN in arch headers allows that.
+ */
+#if defined(ARCH_DMA_MINALIGN) && ARCH_DMA_MINALIGN > 8
+#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN
+#define KMALLOC_MIN_SIZE ARCH_DMA_MINALIGN
+#define KMALLOC_SHIFT_LOW ilog2(ARCH_DMA_MINALIGN)
+#else
+#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long)
+#endif
+
+#ifdef CONFIG_SLOB
+/*
+ * Common fields provided in kmem_cache by all slab allocators
+ * This struct is either used directly by the allocator (SLOB)
+ * or the allocator must include definitions for all fields
+ * provided in kmem_cache_common in their definition of kmem_cache.
+ *
+ * Once we can do anonymous structs (C11 standard) we could put a
+ * anonymous struct definition in these allocators so that the
+ * separate allocations in the kmem_cache structure of SLAB and
+ * SLUB is no longer needed.
+ */
+struct kmem_cache {
+ unsigned int object_size;/* The original size of the object */
+ unsigned int size; /* The aligned/padded/added on size */
+ unsigned int align; /* Alignment as calculated */
+ unsigned long flags; /* Active flags on the slab */
+ const char *name; /* Slab name for sysfs */
+ int refcount; /* Use counter */
+ void (*ctor)(void *); /* Called on object slot creation */
+ struct list_head list; /* List of all slab caches on the system */
+};
+
+#define KMALLOC_MAX_SIZE (1UL << 30)
+
+#include <linux/slob_def.h>
+
+#else /* CONFIG_SLOB */
+
+/*
+ * Kmalloc array related definitions
+ */
+
+#ifdef CONFIG_SLAB
+/*
+ * The largest kmalloc size supported by the SLAB allocators is
* 32 megabyte (2^25) or the maximum allocatable page order if that is
* less than 32 MB.
*
@@ -158,22 +191,120 @@ void kmem_cache_free(struct kmem_cache *, void *);
*/
#define KMALLOC_SHIFT_HIGH ((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \
(MAX_ORDER + PAGE_SHIFT - 1) : 25)
+#define KMALLOC_SHIFT_MAX KMALLOC_SHIFT_HIGH
+#ifndef KMALLOC_SHIFT_LOW
+#define KMALLOC_SHIFT_LOW 5
+#endif
+#else
+/*
+ * SLUB allocates up to order 2 pages directly and otherwise
+ * passes the request to the page allocator.
+ */
+#define KMALLOC_SHIFT_HIGH (PAGE_SHIFT + 1)
+#define KMALLOC_SHIFT_MAX (MAX_ORDER + PAGE_SHIFT)
+#ifndef KMALLOC_SHIFT_LOW
+#define KMALLOC_SHIFT_LOW 3
+#endif
+#endif
-#define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_HIGH)
-#define KMALLOC_MAX_ORDER (KMALLOC_SHIFT_HIGH - PAGE_SHIFT)
+/* Maximum allocatable size */
+#define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_MAX)
+/* Maximum size for which we actually use a slab cache */
+#define KMALLOC_MAX_CACHE_SIZE (1UL << KMALLOC_SHIFT_HIGH)
+/* Maximum order allocatable via the slab allocagtor */
+#define KMALLOC_MAX_ORDER (KMALLOC_SHIFT_MAX - PAGE_SHIFT)
/*
- * Some archs want to perform DMA into kmalloc caches and need a guaranteed
- * alignment larger than the alignment of a 64-bit integer.
- * Setting ARCH_KMALLOC_MINALIGN in arch headers allows that.
+ * Kmalloc subsystem.
*/
-#ifdef ARCH_DMA_MINALIGN
-#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN
+#ifndef KMALLOC_MIN_SIZE
+#define KMALLOC_MIN_SIZE (1 << KMALLOC_SHIFT_LOW)
+#endif
+
+extern struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1];
+#ifdef CONFIG_ZONE_DMA
+extern struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1];
+#endif
+
+/*
+ * Figure out which kmalloc slab an allocation of a certain size
+ * belongs to.
+ * 0 = zero alloc
+ * 1 = 65 .. 96 bytes
+ * 2 = 120 .. 192 bytes
+ * n = 2^(n-1) .. 2^n -1
+ */
+static __always_inline int kmalloc_index(size_t size)
+{
+ if (!size)
+ return 0;
+
+ if (size <= KMALLOC_MIN_SIZE)
+ return KMALLOC_SHIFT_LOW;
+
+ if (KMALLOC_MIN_SIZE <= 32 && size > 64 && size <= 96)
+ return 1;
+ if (KMALLOC_MIN_SIZE <= 64 && size > 128 && size <= 192)
+ return 2;
+ if (size <= 8) return 3;
+ if (size <= 16) return 4;
+ if (size <= 32) return 5;
+ if (size <= 64) return 6;
+ if (size <= 128) return 7;
+ if (size <= 256) return 8;
+ if (size <= 512) return 9;
+ if (size <= 1024) return 10;
+ if (size <= 2 * 1024) return 11;
+ if (size <= 4 * 1024) return 12;
+ if (size <= 8 * 1024) return 13;
+ if (size <= 16 * 1024) return 14;
+ if (size <= 32 * 1024) return 15;
+ if (size <= 64 * 1024) return 16;
+ if (size <= 128 * 1024) return 17;
+ if (size <= 256 * 1024) return 18;
+ if (size <= 512 * 1024) return 19;
+ if (size <= 1024 * 1024) return 20;
+ if (size <= 2 * 1024 * 1024) return 21;
+ if (size <= 4 * 1024 * 1024) return 22;
+ if (size <= 8 * 1024 * 1024) return 23;
+ if (size <= 16 * 1024 * 1024) return 24;
+ if (size <= 32 * 1024 * 1024) return 25;
+ if (size <= 64 * 1024 * 1024) return 26;
+ BUG();
+
+ /* Will never be reached. Needed because the compiler may complain */
+ return -1;
+}
+
+#ifdef CONFIG_SLAB
+#include <linux/slab_def.h>
+#elif defined(CONFIG_SLUB)
+#include <linux/slub_def.h>
#else
-#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long)
+#error "Unknown slab allocator"
#endif
/*
+ * Determine size used for the nth kmalloc cache.
+ * return size or 0 if a kmalloc cache for that
+ * size does not exist
+ */
+static __always_inline int kmalloc_size(int n)
+{
+ if (n > 2)
+ return 1 << n;
+
+ if (n == 1 && KMALLOC_MIN_SIZE <= 32)
+ return 96;
+
+ if (n == 2 && KMALLOC_MIN_SIZE <= 64)
+ return 192;
+
+ return 0;
+}
+#endif /* !CONFIG_SLOB */
+
+/*
* Setting ARCH_SLAB_MINALIGN in arch headers allows a different alignment.
* Intended for arches that get misalignment faults even for 64 bit integer
* aligned buffers.
@@ -224,42 +355,6 @@ struct seq_file;
int cache_show(struct kmem_cache *s, struct seq_file *m);
void print_slabinfo_header(struct seq_file *m);
-/*
- * Common kmalloc functions provided by all allocators
- */
-void * __must_check __krealloc(const void *, size_t, gfp_t);
-void * __must_check krealloc(const void *, size_t, gfp_t);
-void kfree(const void *);
-void kzfree(const void *);
-size_t ksize(const void *);
-
-/*
- * Allocator specific definitions. These are mainly used to establish optimized
- * ways to convert kmalloc() calls to kmem_cache_alloc() invocations by
- * selecting the appropriate general cache at compile time.
- *
- * Allocators must define at least:
- *
- * kmem_cache_alloc()
- * __kmalloc()
- * kmalloc()
- *
- * Those wishing to support NUMA must also define:
- *
- * kmem_cache_alloc_node()
- * kmalloc_node()
- *
- * See each allocator definition file for additional comments and
- * implementation notes.
- */
-#ifdef CONFIG_SLUB
-#include <linux/slub_def.h>
-#elif defined(CONFIG_SLOB)
-#include <linux/slob_def.h>
-#else
-#include <linux/slab_def.h>
-#endif
-
/**
* kmalloc_array - allocate memory for an array.
* @n: number of elements.
diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h
index 8bb6e0eaf3c6..cd401580bdd3 100644
--- a/include/linux/slab_def.h
+++ b/include/linux/slab_def.h
@@ -11,8 +11,6 @@
*/
#include <linux/init.h>
-#include <asm/page.h> /* kmalloc_sizes.h needs PAGE_SIZE */
-#include <asm/cache.h> /* kmalloc_sizes.h needs L1_CACHE_BYTES */
#include <linux/compiler.h>
/*
@@ -97,23 +95,13 @@ struct kmem_cache {
* pointer for each node since "nodelists" uses the remainder of
* available pointers.
*/
- struct kmem_list3 **nodelists;
+ struct kmem_cache_node **node;
struct array_cache *array[NR_CPUS + MAX_NUMNODES];
/*
* Do not add fields after array[]
*/
};
-/* Size description struct for general caches. */
-struct cache_sizes {
- size_t cs_size;
- struct kmem_cache *cs_cachep;
-#ifdef CONFIG_ZONE_DMA
- struct kmem_cache *cs_dmacachep;
-#endif
-};
-extern struct cache_sizes malloc_sizes[];
-
void *kmem_cache_alloc(struct kmem_cache *, gfp_t);
void *__kmalloc(size_t size, gfp_t flags);
@@ -133,26 +121,22 @@ static __always_inline void *kmalloc(size_t size, gfp_t flags)
void *ret;
if (__builtin_constant_p(size)) {
- int i = 0;
+ int i;
if (!size)
return ZERO_SIZE_PTR;
-#define CACHE(x) \
- if (size <= x) \
- goto found; \
- else \
- i++;
-#include <linux/kmalloc_sizes.h>
-#undef CACHE
- return NULL;
-found:
+ if (WARN_ON_ONCE(size > KMALLOC_MAX_SIZE))
+ return NULL;
+
+ i = kmalloc_index(size);
+
#ifdef CONFIG_ZONE_DMA
if (flags & GFP_DMA)
- cachep = malloc_sizes[i].cs_dmacachep;
+ cachep = kmalloc_dma_caches[i];
else
#endif
- cachep = malloc_sizes[i].cs_cachep;
+ cachep = kmalloc_caches[i];
ret = kmem_cache_alloc_trace(cachep, flags, size);
@@ -186,26 +170,22 @@ static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node)
struct kmem_cache *cachep;
if (__builtin_constant_p(size)) {
- int i = 0;
+ int i;
if (!size)
return ZERO_SIZE_PTR;
-#define CACHE(x) \
- if (size <= x) \
- goto found; \
- else \
- i++;
-#include <linux/kmalloc_sizes.h>
-#undef CACHE
- return NULL;
-found:
+ if (WARN_ON_ONCE(size > KMALLOC_MAX_SIZE))
+ return NULL;
+
+ i = kmalloc_index(size);
+
#ifdef CONFIG_ZONE_DMA
if (flags & GFP_DMA)
- cachep = malloc_sizes[i].cs_dmacachep;
+ cachep = kmalloc_dma_caches[i];
else
#endif
- cachep = malloc_sizes[i].cs_cachep;
+ cachep = kmalloc_caches[i];
return kmem_cache_alloc_node_trace(cachep, flags, node, size);
}
diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
index 9db4825cd393..027276fa8713 100644
--- a/include/linux/slub_def.h
+++ b/include/linux/slub_def.h
@@ -53,17 +53,6 @@ struct kmem_cache_cpu {
#endif
};
-struct kmem_cache_node {
- spinlock_t list_lock; /* Protect partial list and nr_partial */
- unsigned long nr_partial;
- struct list_head partial;
-#ifdef CONFIG_SLUB_DEBUG
- atomic_long_t nr_slabs;
- atomic_long_t total_objects;
- struct list_head full;
-#endif
-};
-
/*
* Word size structure that can be atomically updated or read and that
* contains both the order and the number of objects that a slab of the
@@ -115,111 +104,6 @@ struct kmem_cache {
struct kmem_cache_node *node[MAX_NUMNODES];
};
-/*
- * Kmalloc subsystem.
- */
-#if defined(ARCH_DMA_MINALIGN) && ARCH_DMA_MINALIGN > 8
-#define KMALLOC_MIN_SIZE ARCH_DMA_MINALIGN
-#else
-#define KMALLOC_MIN_SIZE 8
-#endif
-
-#define KMALLOC_SHIFT_LOW ilog2(KMALLOC_MIN_SIZE)
-
-/*
- * Maximum kmalloc object size handled by SLUB. Larger object allocations
- * are passed through to the page allocator. The page allocator "fastpath"
- * is relatively slow so we need this value sufficiently high so that
- * performance critical objects are allocated through the SLUB fastpath.
- *
- * This should be dropped to PAGE_SIZE / 2 once the page allocator
- * "fastpath" becomes competitive with the slab allocator fastpaths.
- */
-#define SLUB_MAX_SIZE (2 * PAGE_SIZE)
-
-#define SLUB_PAGE_SHIFT (PAGE_SHIFT + 2)
-
-#ifdef CONFIG_ZONE_DMA
-#define SLUB_DMA __GFP_DMA
-#else
-/* Disable DMA functionality */
-#define SLUB_DMA (__force gfp_t)0
-#endif
-
-/*
- * We keep the general caches in an array of slab caches that are used for
- * 2^x bytes of allocations.
- */
-extern struct kmem_cache *kmalloc_caches[SLUB_PAGE_SHIFT];
-
-/*
- * Sorry that the following has to be that ugly but some versions of GCC
- * have trouble with constant propagation and loops.
- */
-static __always_inline int kmalloc_index(size_t size)
-{
- if (!size)
- return 0;
-
- if (size <= KMALLOC_MIN_SIZE)
- return KMALLOC_SHIFT_LOW;
-
- if (KMALLOC_MIN_SIZE <= 32 && size > 64 && size <= 96)
- return 1;
- if (KMALLOC_MIN_SIZE <= 64 && size > 128 && size <= 192)
- return 2;
- if (size <= 8) return 3;
- if (size <= 16) return 4;
- if (size <= 32) return 5;
- if (size <= 64) return 6;
- if (size <= 128) return 7;
- if (size <= 256) return 8;
- if (size <= 512) return 9;
- if (size <= 1024) return 10;
- if (size <= 2 * 1024) return 11;
- if (size <= 4 * 1024) return 12;
-/*
- * The following is only needed to support architectures with a larger page
- * size than 4k. We need to support 2 * PAGE_SIZE here. So for a 64k page
- * size we would have to go up to 128k.
- */
- if (size <= 8 * 1024) return 13;
- if (size <= 16 * 1024) return 14;
- if (size <= 32 * 1024) return 15;
- if (size <= 64 * 1024) return 16;
- if (size <= 128 * 1024) return 17;
- if (size <= 256 * 1024) return 18;
- if (size <= 512 * 1024) return 19;
- if (size <= 1024 * 1024) return 20;
- if (size <= 2 * 1024 * 1024) return 21;
- BUG();
- return -1; /* Will never be reached */
-
-/*
- * What we really wanted to do and cannot do because of compiler issues is:
- * int i;
- * for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++)
- * if (size <= (1 << i))
- * return i;
- */
-}
-
-/*
- * Find the slab cache for a given combination of allocation flags and size.
- *
- * This ought to end up with a global pointer to the right cache
- * in kmalloc_caches.
- */
-static __always_inline struct kmem_cache *kmalloc_slab(size_t size)
-{
- int index = kmalloc_index(size);
-
- if (index == 0)
- return NULL;
-
- return kmalloc_caches[index];
-}
-
void *kmem_cache_alloc(struct kmem_cache *, gfp_t);
void *__kmalloc(size_t size, gfp_t flags);
@@ -274,16 +158,17 @@ static __always_inline void *kmalloc_large(size_t size, gfp_t flags)
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
if (__builtin_constant_p(size)) {
- if (size > SLUB_MAX_SIZE)
+ if (size > KMALLOC_MAX_CACHE_SIZE)
return kmalloc_large(size, flags);
- if (!(flags & SLUB_DMA)) {
- struct kmem_cache *s = kmalloc_slab(size);
+ if (!(flags & GFP_DMA)) {
+ int index = kmalloc_index(size);
- if (!s)
+ if (!index)
return ZERO_SIZE_PTR;
- return kmem_cache_alloc_trace(s, flags, size);
+ return kmem_cache_alloc_trace(kmalloc_caches[index],
+ flags, size);
}
}
return __kmalloc(size, flags);
@@ -310,13 +195,14 @@ kmem_cache_alloc_node_trace(struct kmem_cache *s,
static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node)
{
if (__builtin_constant_p(size) &&
- size <= SLUB_MAX_SIZE && !(flags & SLUB_DMA)) {
- struct kmem_cache *s = kmalloc_slab(size);
+ size <= KMALLOC_MAX_CACHE_SIZE && !(flags & GFP_DMA)) {
+ int index = kmalloc_index(size);
- if (!s)
+ if (!index)
return ZERO_SIZE_PTR;
- return kmem_cache_alloc_node_trace(s, flags, node, size);
+ return kmem_cache_alloc_node_trace(kmalloc_caches[index],
+ flags, node, size);
}
return __kmalloc_node(size, flags, node);
}
diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h
index 61ae1306db23..4835486f58e5 100644
--- a/include/linux/spi/mxs-spi.h
+++ b/include/linux/spi/mxs-spi.h
@@ -24,7 +24,7 @@
#ifndef __LINUX_SPI_MXS_SPI_H__
#define __LINUX_SPI_MXS_SPI_H__
-#include <linux/fsl/mxs-dma.h>
+#include <linux/dmaengine.h>
#define ssp_is_old(host) ((host)->devid == IMX23_SSP)
@@ -137,9 +137,7 @@ struct mxs_ssp {
unsigned int clk_rate;
enum mxs_ssp_id devid;
- int dma_channel;
struct dma_chan *dmach;
- struct mxs_dma_data dma_data;
unsigned int dma_dir;
enum dma_transfer_direction slave_dirn;
u32 ssp_pio_words[SSP_PIO_NUM];
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 7cb64d4b499d..ac38be2692d8 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -330,6 +330,92 @@ do { \
__ret; \
})
+#define __wait_event_hrtimeout(wq, condition, timeout, state) \
+({ \
+ int __ret = 0; \
+ DEFINE_WAIT(__wait); \
+ struct hrtimer_sleeper __t; \
+ \
+ hrtimer_init_on_stack(&__t.timer, CLOCK_MONOTONIC, \
+ HRTIMER_MODE_REL); \
+ hrtimer_init_sleeper(&__t, current); \
+ if ((timeout).tv64 != KTIME_MAX) \
+ hrtimer_start_range_ns(&__t.timer, timeout, \
+ current->timer_slack_ns, \
+ HRTIMER_MODE_REL); \
+ \
+ for (;;) { \
+ prepare_to_wait(&wq, &__wait, state); \
+ if (condition) \
+ break; \
+ if (state == TASK_INTERRUPTIBLE && \
+ signal_pending(current)) { \
+ __ret = -ERESTARTSYS; \
+ break; \
+ } \
+ if (!__t.task) { \
+ __ret = -ETIME; \
+ break; \
+ } \
+ schedule(); \
+ } \
+ \
+ hrtimer_cancel(&__t.timer); \
+ destroy_hrtimer_on_stack(&__t.timer); \
+ finish_wait(&wq, &__wait); \
+ __ret; \
+})
+
+/**
+ * wait_event_hrtimeout - sleep until a condition gets true or a timeout elapses
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ * @timeout: timeout, as a ktime_t
+ *
+ * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
+ * @condition evaluates to true or a signal is received.
+ * The @condition is checked each time the waitqueue @wq is woken up.
+ *
+ * wake_up() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * The function returns 0 if @condition became true, or -ETIME if the timeout
+ * elapsed.
+ */
+#define wait_event_hrtimeout(wq, condition, timeout) \
+({ \
+ int __ret = 0; \
+ if (!(condition)) \
+ __ret = __wait_event_hrtimeout(wq, condition, timeout, \
+ TASK_UNINTERRUPTIBLE); \
+ __ret; \
+})
+
+/**
+ * wait_event_interruptible_hrtimeout - sleep until a condition gets true or a timeout elapses
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ * @timeout: timeout, as a ktime_t
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or a signal is received.
+ * The @condition is checked each time the waitqueue @wq is woken up.
+ *
+ * wake_up() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * The function returns 0 if @condition became true, -ERESTARTSYS if it was
+ * interrupted by a signal, or -ETIME if the timeout elapsed.
+ */
+#define wait_event_interruptible_hrtimeout(wq, condition, timeout) \
+({ \
+ long __ret = 0; \
+ if (!(condition)) \
+ __ret = __wait_event_hrtimeout(wq, condition, timeout, \
+ TASK_INTERRUPTIBLE); \
+ __ret; \
+})
+
#define __wait_event_interruptible_exclusive(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); \
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index 9a9367c0c076..579a5007c696 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -5,6 +5,7 @@
#define WRITEBACK_H
#include <linux/sched.h>
+#include <linux/workqueue.h>
#include <linux/fs.h>
DECLARE_PER_CPU(int, dirty_throttle_leaks);
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index 4182c9be8bb5..bfcbc0017950 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -141,14 +141,15 @@ static inline int sum_frag_mem_limit(struct netns_frags *nf)
static inline void inet_frag_lru_move(struct inet_frag_queue *q)
{
spin_lock(&q->net->lru_lock);
- list_move_tail(&q->lru_list, &q->net->lru_list);
+ if (!list_empty(&q->lru_list))
+ list_move_tail(&q->lru_list, &q->net->lru_list);
spin_unlock(&q->net->lru_lock);
}
static inline void inet_frag_lru_del(struct inet_frag_queue *q)
{
spin_lock(&q->net->lru_lock);
- list_del(&q->lru_list);
+ list_del_init(&q->lru_list);
q->net->nqueues--;
spin_unlock(&q->net->lru_lock);
}
diff --git a/include/trace/events/bcache.h b/include/trace/events/bcache.h
new file mode 100644
index 000000000000..3cc5a0b278c3
--- /dev/null
+++ b/include/trace/events/bcache.h
@@ -0,0 +1,271 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM bcache
+
+#if !defined(_TRACE_BCACHE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_BCACHE_H
+
+#include <linux/tracepoint.h>
+
+struct search;
+
+DECLARE_EVENT_CLASS(bcache_request,
+
+ TP_PROTO(struct search *s, struct bio *bio),
+
+ TP_ARGS(s, bio),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev )
+ __field(unsigned int, orig_major )
+ __field(unsigned int, orig_minor )
+ __field(sector_t, sector )
+ __field(dev_t, orig_sector )
+ __field(unsigned int, nr_sector )
+ __array(char, rwbs, 6 )
+ __array(char, comm, TASK_COMM_LEN )
+ ),
+
+ TP_fast_assign(
+ __entry->dev = bio->bi_bdev->bd_dev;
+ __entry->orig_major = s->d->disk->major;
+ __entry->orig_minor = s->d->disk->first_minor;
+ __entry->sector = bio->bi_sector;
+ __entry->orig_sector = bio->bi_sector - 16;
+ __entry->nr_sector = bio->bi_size >> 9;
+ blk_fill_rwbs(__entry->rwbs, bio->bi_rw, bio->bi_size);
+ memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
+ ),
+
+ TP_printk("%d,%d %s %llu + %u [%s] (from %d,%d @ %llu)",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->rwbs,
+ (unsigned long long)__entry->sector,
+ __entry->nr_sector, __entry->comm,
+ __entry->orig_major, __entry->orig_minor,
+ (unsigned long long)__entry->orig_sector)
+);
+
+DEFINE_EVENT(bcache_request, bcache_request_start,
+
+ TP_PROTO(struct search *s, struct bio *bio),
+
+ TP_ARGS(s, bio)
+);
+
+DEFINE_EVENT(bcache_request, bcache_request_end,
+
+ TP_PROTO(struct search *s, struct bio *bio),
+
+ TP_ARGS(s, bio)
+);
+
+DECLARE_EVENT_CLASS(bcache_bio,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev )
+ __field(sector_t, sector )
+ __field(unsigned int, nr_sector )
+ __array(char, rwbs, 6 )
+ __array(char, comm, TASK_COMM_LEN )
+ ),
+
+ TP_fast_assign(
+ __entry->dev = bio->bi_bdev->bd_dev;
+ __entry->sector = bio->bi_sector;
+ __entry->nr_sector = bio->bi_size >> 9;
+ blk_fill_rwbs(__entry->rwbs, bio->bi_rw, bio->bi_size);
+ memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
+ ),
+
+ TP_printk("%d,%d %s %llu + %u [%s]",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->rwbs,
+ (unsigned long long)__entry->sector,
+ __entry->nr_sector, __entry->comm)
+);
+
+
+DEFINE_EVENT(bcache_bio, bcache_passthrough,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_cache_hit,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_cache_miss,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_read_retry,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_writethrough,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_writeback,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_write_skip,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_btree_read,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_btree_write,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_write_dirty,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_read_dirty,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_write_moving,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_read_moving,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DEFINE_EVENT(bcache_bio, bcache_journal_write,
+
+ TP_PROTO(struct bio *bio),
+
+ TP_ARGS(bio)
+);
+
+DECLARE_EVENT_CLASS(bcache_cache_bio,
+
+ TP_PROTO(struct bio *bio,
+ sector_t orig_sector,
+ struct block_device* orig_bdev),
+
+ TP_ARGS(bio, orig_sector, orig_bdev),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev )
+ __field(dev_t, orig_dev )
+ __field(sector_t, sector )
+ __field(sector_t, orig_sector )
+ __field(unsigned int, nr_sector )
+ __array(char, rwbs, 6 )
+ __array(char, comm, TASK_COMM_LEN )
+ ),
+
+ TP_fast_assign(
+ __entry->dev = bio->bi_bdev->bd_dev;
+ __entry->orig_dev = orig_bdev->bd_dev;
+ __entry->sector = bio->bi_sector;
+ __entry->orig_sector = orig_sector;
+ __entry->nr_sector = bio->bi_size >> 9;
+ blk_fill_rwbs(__entry->rwbs, bio->bi_rw, bio->bi_size);
+ memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
+ ),
+
+ TP_printk("%d,%d %s %llu + %u [%s] (from %d,%d %llu)",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->rwbs,
+ (unsigned long long)__entry->sector,
+ __entry->nr_sector, __entry->comm,
+ MAJOR(__entry->orig_dev), MINOR(__entry->orig_dev),
+ (unsigned long long)__entry->orig_sector)
+);
+
+DEFINE_EVENT(bcache_cache_bio, bcache_cache_insert,
+
+ TP_PROTO(struct bio *bio,
+ sector_t orig_sector,
+ struct block_device *orig_bdev),
+
+ TP_ARGS(bio, orig_sector, orig_bdev)
+);
+
+DECLARE_EVENT_CLASS(bcache_gc,
+
+ TP_PROTO(uint8_t *uuid),
+
+ TP_ARGS(uuid),
+
+ TP_STRUCT__entry(
+ __field(uint8_t *, uuid)
+ ),
+
+ TP_fast_assign(
+ __entry->uuid = uuid;
+ ),
+
+ TP_printk("%pU", __entry->uuid)
+);
+
+
+DEFINE_EVENT(bcache_gc, bcache_gc_start,
+
+ TP_PROTO(uint8_t *uuid),
+
+ TP_ARGS(uuid)
+);
+
+DEFINE_EVENT(bcache_gc, bcache_gc_end,
+
+ TP_PROTO(uint8_t *uuid),
+
+ TP_ARGS(uuid)
+);
+
+#endif /* _TRACE_BCACHE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/block.h b/include/trace/events/block.h
index 9c1467357b03..60ae7c3db912 100644
--- a/include/trace/events/block.h
+++ b/include/trace/events/block.h
@@ -244,7 +244,7 @@ TRACE_EVENT(block_bio_bounce,
__entry->dev = bio->bi_bdev ?
bio->bi_bdev->bd_dev : 0;
__entry->sector = bio->bi_sector;
- __entry->nr_sector = bio->bi_size >> 9;
+ __entry->nr_sector = bio_sectors(bio);
blk_fill_rwbs(__entry->rwbs, bio->bi_rw, bio->bi_size);
memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
),
@@ -281,7 +281,7 @@ TRACE_EVENT(block_bio_complete,
TP_fast_assign(
__entry->dev = bio->bi_bdev->bd_dev;
__entry->sector = bio->bi_sector;
- __entry->nr_sector = bio->bi_size >> 9;
+ __entry->nr_sector = bio_sectors(bio);
__entry->error = error;
blk_fill_rwbs(__entry->rwbs, bio->bi_rw, bio->bi_size);
),
@@ -309,7 +309,7 @@ DECLARE_EVENT_CLASS(block_bio_merge,
TP_fast_assign(
__entry->dev = bio->bi_bdev->bd_dev;
__entry->sector = bio->bi_sector;
- __entry->nr_sector = bio->bi_size >> 9;
+ __entry->nr_sector = bio_sectors(bio);
blk_fill_rwbs(__entry->rwbs, bio->bi_rw, bio->bi_size);
memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
),
@@ -376,7 +376,7 @@ TRACE_EVENT(block_bio_queue,
TP_fast_assign(
__entry->dev = bio->bi_bdev->bd_dev;
__entry->sector = bio->bi_sector;
- __entry->nr_sector = bio->bi_size >> 9;
+ __entry->nr_sector = bio_sectors(bio);
blk_fill_rwbs(__entry->rwbs, bio->bi_rw, bio->bi_size);
memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
),
@@ -404,7 +404,7 @@ DECLARE_EVENT_CLASS(block_get_rq,
TP_fast_assign(
__entry->dev = bio ? bio->bi_bdev->bd_dev : 0;
__entry->sector = bio ? bio->bi_sector : 0;
- __entry->nr_sector = bio ? bio->bi_size >> 9 : 0;
+ __entry->nr_sector = bio ? bio_sectors(bio) : 0;
blk_fill_rwbs(__entry->rwbs,
bio ? bio->bi_rw : 0, __entry->nr_sector);
memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
@@ -580,7 +580,7 @@ TRACE_EVENT(block_bio_remap,
TP_fast_assign(
__entry->dev = bio->bi_bdev->bd_dev;
__entry->sector = bio->bi_sector;
- __entry->nr_sector = bio->bi_size >> 9;
+ __entry->nr_sector = bio_sectors(bio);
__entry->old_dev = dev;
__entry->old_sector = from;
blk_fill_rwbs(__entry->rwbs, bio->bi_rw, bio->bi_size);
diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h
new file mode 100644
index 000000000000..52ae54828eda
--- /dev/null
+++ b/include/trace/events/f2fs.h
@@ -0,0 +1,682 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM f2fs
+
+#if !defined(_TRACE_F2FS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_F2FS_H
+
+#include <linux/tracepoint.h>
+
+#define show_dev(entry) MAJOR(entry->dev), MINOR(entry->dev)
+#define show_dev_ino(entry) show_dev(entry), (unsigned long)entry->ino
+
+#define show_block_type(type) \
+ __print_symbolic(type, \
+ { NODE, "NODE" }, \
+ { DATA, "DATA" }, \
+ { META, "META" }, \
+ { META_FLUSH, "META_FLUSH" })
+
+#define show_bio_type(type) \
+ __print_symbolic(type, \
+ { READ, "READ" }, \
+ { READA, "READAHEAD" }, \
+ { READ_SYNC, "READ_SYNC" }, \
+ { WRITE, "WRITE" }, \
+ { WRITE_SYNC, "WRITE_SYNC" }, \
+ { WRITE_FLUSH, "WRITE_FLUSH" }, \
+ { WRITE_FUA, "WRITE_FUA" })
+
+#define show_data_type(type) \
+ __print_symbolic(type, \
+ { CURSEG_HOT_DATA, "Hot DATA" }, \
+ { CURSEG_WARM_DATA, "Warm DATA" }, \
+ { CURSEG_COLD_DATA, "Cold DATA" }, \
+ { CURSEG_HOT_NODE, "Hot NODE" }, \
+ { CURSEG_WARM_NODE, "Warm NODE" }, \
+ { CURSEG_COLD_NODE, "Cold NODE" }, \
+ { NO_CHECK_TYPE, "No TYPE" })
+
+#define show_gc_type(type) \
+ __print_symbolic(type, \
+ { FG_GC, "Foreground GC" }, \
+ { BG_GC, "Background GC" })
+
+#define show_alloc_mode(type) \
+ __print_symbolic(type, \
+ { LFS, "LFS-mode" }, \
+ { SSR, "SSR-mode" })
+
+#define show_victim_policy(type) \
+ __print_symbolic(type, \
+ { GC_GREEDY, "Greedy" }, \
+ { GC_CB, "Cost-Benefit" })
+
+struct victim_sel_policy;
+
+DECLARE_EVENT_CLASS(f2fs__inode,
+
+ TP_PROTO(struct inode *inode),
+
+ TP_ARGS(inode),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(ino_t, pino)
+ __field(umode_t, mode)
+ __field(loff_t, size)
+ __field(unsigned int, nlink)
+ __field(blkcnt_t, blocks)
+ __field(__u8, advise)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->pino = F2FS_I(inode)->i_pino;
+ __entry->mode = inode->i_mode;
+ __entry->nlink = inode->i_nlink;
+ __entry->size = inode->i_size;
+ __entry->blocks = inode->i_blocks;
+ __entry->advise = F2FS_I(inode)->i_advise;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, pino = %lu, i_mode = 0x%hx, "
+ "i_size = %lld, i_nlink = %u, i_blocks = %llu, i_advise = 0x%x",
+ show_dev_ino(__entry),
+ (unsigned long)__entry->pino,
+ __entry->mode,
+ __entry->size,
+ (unsigned int)__entry->nlink,
+ (unsigned long long)__entry->blocks,
+ (unsigned char)__entry->advise)
+);
+
+DECLARE_EVENT_CLASS(f2fs__inode_exit,
+
+ TP_PROTO(struct inode *inode, int ret),
+
+ TP_ARGS(inode, ret),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, ret = %d",
+ show_dev_ino(__entry),
+ __entry->ret)
+);
+
+DEFINE_EVENT(f2fs__inode, f2fs_sync_file_enter,
+
+ TP_PROTO(struct inode *inode),
+
+ TP_ARGS(inode)
+);
+
+TRACE_EVENT(f2fs_sync_file_exit,
+
+ TP_PROTO(struct inode *inode, bool need_cp, int datasync, int ret),
+
+ TP_ARGS(inode, need_cp, datasync, ret),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(bool, need_cp)
+ __field(int, datasync)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->need_cp = need_cp;
+ __entry->datasync = datasync;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, checkpoint is %s, "
+ "datasync = %d, ret = %d",
+ show_dev_ino(__entry),
+ __entry->need_cp ? "needed" : "not needed",
+ __entry->datasync,
+ __entry->ret)
+);
+
+TRACE_EVENT(f2fs_sync_fs,
+
+ TP_PROTO(struct super_block *sb, int wait),
+
+ TP_ARGS(sb, wait),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(int, dirty)
+ __field(int, wait)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = sb->s_dev;
+ __entry->dirty = F2FS_SB(sb)->s_dirty;
+ __entry->wait = wait;
+ ),
+
+ TP_printk("dev = (%d,%d), superblock is %s, wait = %d",
+ show_dev(__entry),
+ __entry->dirty ? "dirty" : "not dirty",
+ __entry->wait)
+);
+
+DEFINE_EVENT(f2fs__inode, f2fs_iget,
+
+ TP_PROTO(struct inode *inode),
+
+ TP_ARGS(inode)
+);
+
+DEFINE_EVENT(f2fs__inode_exit, f2fs_iget_exit,
+
+ TP_PROTO(struct inode *inode, int ret),
+
+ TP_ARGS(inode, ret)
+);
+
+DEFINE_EVENT(f2fs__inode, f2fs_evict_inode,
+
+ TP_PROTO(struct inode *inode),
+
+ TP_ARGS(inode)
+);
+
+DEFINE_EVENT(f2fs__inode_exit, f2fs_new_inode,
+
+ TP_PROTO(struct inode *inode, int ret),
+
+ TP_ARGS(inode, ret)
+);
+
+TRACE_EVENT(f2fs_unlink_enter,
+
+ TP_PROTO(struct inode *dir, struct dentry *dentry),
+
+ TP_ARGS(dir, dentry),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(loff_t, size)
+ __field(blkcnt_t, blocks)
+ __field(const char *, name)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = dir->i_sb->s_dev;
+ __entry->ino = dir->i_ino;
+ __entry->size = dir->i_size;
+ __entry->blocks = dir->i_blocks;
+ __entry->name = dentry->d_name.name;
+ ),
+
+ TP_printk("dev = (%d,%d), dir ino = %lu, i_size = %lld, "
+ "i_blocks = %llu, name = %s",
+ show_dev_ino(__entry),
+ __entry->size,
+ (unsigned long long)__entry->blocks,
+ __entry->name)
+);
+
+DEFINE_EVENT(f2fs__inode_exit, f2fs_unlink_exit,
+
+ TP_PROTO(struct inode *inode, int ret),
+
+ TP_ARGS(inode, ret)
+);
+
+DEFINE_EVENT(f2fs__inode, f2fs_truncate,
+
+ TP_PROTO(struct inode *inode),
+
+ TP_ARGS(inode)
+);
+
+TRACE_EVENT(f2fs_truncate_data_blocks_range,
+
+ TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs, int free),
+
+ TP_ARGS(inode, nid, ofs, free),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(nid_t, nid)
+ __field(unsigned int, ofs)
+ __field(int, free)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->nid = nid;
+ __entry->ofs = ofs;
+ __entry->free = free;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, nid = %u, offset = %u, freed = %d",
+ show_dev_ino(__entry),
+ (unsigned int)__entry->nid,
+ __entry->ofs,
+ __entry->free)
+);
+
+DECLARE_EVENT_CLASS(f2fs__truncate_op,
+
+ TP_PROTO(struct inode *inode, u64 from),
+
+ TP_ARGS(inode, from),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(loff_t, size)
+ __field(blkcnt_t, blocks)
+ __field(u64, from)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->size = inode->i_size;
+ __entry->blocks = inode->i_blocks;
+ __entry->from = from;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, i_size = %lld, i_blocks = %llu, "
+ "start file offset = %llu",
+ show_dev_ino(__entry),
+ __entry->size,
+ (unsigned long long)__entry->blocks,
+ (unsigned long long)__entry->from)
+);
+
+DEFINE_EVENT(f2fs__truncate_op, f2fs_truncate_blocks_enter,
+
+ TP_PROTO(struct inode *inode, u64 from),
+
+ TP_ARGS(inode, from)
+);
+
+DEFINE_EVENT(f2fs__inode_exit, f2fs_truncate_blocks_exit,
+
+ TP_PROTO(struct inode *inode, int ret),
+
+ TP_ARGS(inode, ret)
+);
+
+DEFINE_EVENT(f2fs__truncate_op, f2fs_truncate_inode_blocks_enter,
+
+ TP_PROTO(struct inode *inode, u64 from),
+
+ TP_ARGS(inode, from)
+);
+
+DEFINE_EVENT(f2fs__inode_exit, f2fs_truncate_inode_blocks_exit,
+
+ TP_PROTO(struct inode *inode, int ret),
+
+ TP_ARGS(inode, ret)
+);
+
+DECLARE_EVENT_CLASS(f2fs__truncate_node,
+
+ TP_PROTO(struct inode *inode, nid_t nid, block_t blk_addr),
+
+ TP_ARGS(inode, nid, blk_addr),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(nid_t, nid)
+ __field(block_t, blk_addr)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->nid = nid;
+ __entry->blk_addr = blk_addr;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, nid = %u, block_address = 0x%llx",
+ show_dev_ino(__entry),
+ (unsigned int)__entry->nid,
+ (unsigned long long)__entry->blk_addr)
+);
+
+DEFINE_EVENT(f2fs__truncate_node, f2fs_truncate_nodes_enter,
+
+ TP_PROTO(struct inode *inode, nid_t nid, block_t blk_addr),
+
+ TP_ARGS(inode, nid, blk_addr)
+);
+
+DEFINE_EVENT(f2fs__inode_exit, f2fs_truncate_nodes_exit,
+
+ TP_PROTO(struct inode *inode, int ret),
+
+ TP_ARGS(inode, ret)
+);
+
+DEFINE_EVENT(f2fs__truncate_node, f2fs_truncate_node,
+
+ TP_PROTO(struct inode *inode, nid_t nid, block_t blk_addr),
+
+ TP_ARGS(inode, nid, blk_addr)
+);
+
+TRACE_EVENT(f2fs_truncate_partial_nodes,
+
+ TP_PROTO(struct inode *inode, nid_t nid[], int depth, int err),
+
+ TP_ARGS(inode, nid, depth, err),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(nid_t, nid[3])
+ __field(int, depth)
+ __field(int, err)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->nid[0] = nid[0];
+ __entry->nid[1] = nid[1];
+ __entry->nid[2] = nid[2];
+ __entry->depth = depth;
+ __entry->err = err;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, "
+ "nid[0] = %u, nid[1] = %u, nid[2] = %u, depth = %d, err = %d",
+ show_dev_ino(__entry),
+ (unsigned int)__entry->nid[0],
+ (unsigned int)__entry->nid[1],
+ (unsigned int)__entry->nid[2],
+ __entry->depth,
+ __entry->err)
+);
+
+TRACE_EVENT_CONDITION(f2fs_readpage,
+
+ TP_PROTO(struct page *page, sector_t blkaddr, int type),
+
+ TP_ARGS(page, blkaddr, type),
+
+ TP_CONDITION(page->mapping),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(pgoff_t, index)
+ __field(sector_t, blkaddr)
+ __field(int, type)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = page->mapping->host->i_sb->s_dev;
+ __entry->ino = page->mapping->host->i_ino;
+ __entry->index = page->index;
+ __entry->blkaddr = blkaddr;
+ __entry->type = type;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
+ "blkaddr = 0x%llx, bio_type = %s",
+ show_dev_ino(__entry),
+ (unsigned long)__entry->index,
+ (unsigned long long)__entry->blkaddr,
+ show_bio_type(__entry->type))
+);
+
+TRACE_EVENT(f2fs_get_data_block,
+ TP_PROTO(struct inode *inode, sector_t iblock,
+ struct buffer_head *bh, int ret),
+
+ TP_ARGS(inode, iblock, bh, ret),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(sector_t, iblock)
+ __field(sector_t, bh_start)
+ __field(size_t, bh_size)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->iblock = iblock;
+ __entry->bh_start = bh->b_blocknr;
+ __entry->bh_size = bh->b_size;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, file offset = %llu, "
+ "start blkaddr = 0x%llx, len = 0x%llx bytes, err = %d",
+ show_dev_ino(__entry),
+ (unsigned long long)__entry->iblock,
+ (unsigned long long)__entry->bh_start,
+ (unsigned long long)__entry->bh_size,
+ __entry->ret)
+);
+
+TRACE_EVENT(f2fs_get_victim,
+
+ TP_PROTO(struct super_block *sb, int type, int gc_type,
+ struct victim_sel_policy *p, unsigned int pre_victim,
+ unsigned int prefree, unsigned int free),
+
+ TP_ARGS(sb, type, gc_type, p, pre_victim, prefree, free),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(int, type)
+ __field(int, gc_type)
+ __field(int, alloc_mode)
+ __field(int, gc_mode)
+ __field(unsigned int, victim)
+ __field(unsigned int, ofs_unit)
+ __field(unsigned int, pre_victim)
+ __field(unsigned int, prefree)
+ __field(unsigned int, free)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = sb->s_dev;
+ __entry->type = type;
+ __entry->gc_type = gc_type;
+ __entry->alloc_mode = p->alloc_mode;
+ __entry->gc_mode = p->gc_mode;
+ __entry->victim = p->min_segno;
+ __entry->ofs_unit = p->ofs_unit;
+ __entry->pre_victim = pre_victim;
+ __entry->prefree = prefree;
+ __entry->free = free;
+ ),
+
+ TP_printk("dev = (%d,%d), type = %s, policy = (%s, %s, %s), victim = %u "
+ "ofs_unit = %u, pre_victim_secno = %d, prefree = %u, free = %u",
+ show_dev(__entry),
+ show_data_type(__entry->type),
+ show_gc_type(__entry->gc_type),
+ show_alloc_mode(__entry->alloc_mode),
+ show_victim_policy(__entry->gc_mode),
+ __entry->victim,
+ __entry->ofs_unit,
+ (int)__entry->pre_victim,
+ __entry->prefree,
+ __entry->free)
+);
+
+TRACE_EVENT(f2fs_fallocate,
+
+ TP_PROTO(struct inode *inode, int mode,
+ loff_t offset, loff_t len, int ret),
+
+ TP_ARGS(inode, mode, offset, len, ret),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(int, mode)
+ __field(loff_t, offset)
+ __field(loff_t, len)
+ __field(loff_t, size)
+ __field(blkcnt_t, blocks)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->mode = mode;
+ __entry->offset = offset;
+ __entry->len = len;
+ __entry->size = inode->i_size;
+ __entry->blocks = inode->i_blocks;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, mode = %x, offset = %lld, "
+ "len = %lld, i_size = %lld, i_blocks = %llu, ret = %d",
+ show_dev_ino(__entry),
+ __entry->mode,
+ (unsigned long long)__entry->offset,
+ (unsigned long long)__entry->len,
+ (unsigned long long)__entry->size,
+ (unsigned long long)__entry->blocks,
+ __entry->ret)
+);
+
+TRACE_EVENT(f2fs_reserve_new_block,
+
+ TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs_in_node),
+
+ TP_ARGS(inode, nid, ofs_in_node),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(nid_t, nid)
+ __field(unsigned int, ofs_in_node)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->nid = nid;
+ __entry->ofs_in_node = ofs_in_node;
+ ),
+
+ TP_printk("dev = (%d,%d), nid = %u, ofs_in_node = %u",
+ show_dev(__entry),
+ (unsigned int)__entry->nid,
+ __entry->ofs_in_node)
+);
+
+TRACE_EVENT(f2fs_do_submit_bio,
+
+ TP_PROTO(struct super_block *sb, int btype, bool sync, struct bio *bio),
+
+ TP_ARGS(sb, btype, sync, bio),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(int, btype)
+ __field(bool, sync)
+ __field(sector_t, sector)
+ __field(unsigned int, size)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = sb->s_dev;
+ __entry->btype = btype;
+ __entry->sync = sync;
+ __entry->sector = bio->bi_sector;
+ __entry->size = bio->bi_size;
+ ),
+
+ TP_printk("dev = (%d,%d), type = %s, io = %s, sector = %lld, size = %u",
+ show_dev(__entry),
+ show_block_type(__entry->btype),
+ __entry->sync ? "sync" : "no sync",
+ (unsigned long long)__entry->sector,
+ __entry->size)
+);
+
+TRACE_EVENT(f2fs_submit_write_page,
+
+ TP_PROTO(struct page *page, block_t blk_addr, int type),
+
+ TP_ARGS(page, blk_addr, type),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(int, type)
+ __field(pgoff_t, index)
+ __field(block_t, block)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = page->mapping->host->i_sb->s_dev;
+ __entry->ino = page->mapping->host->i_ino;
+ __entry->type = type;
+ __entry->index = page->index;
+ __entry->block = blk_addr;
+ ),
+
+ TP_printk("dev = (%d,%d), ino = %lu, %s, index = %lu, blkaddr = 0x%llx",
+ show_dev_ino(__entry),
+ show_block_type(__entry->type),
+ (unsigned long)__entry->index,
+ (unsigned long long)__entry->block)
+);
+
+TRACE_EVENT(f2fs_write_checkpoint,
+
+ TP_PROTO(struct super_block *sb, bool is_umount, char *msg),
+
+ TP_ARGS(sb, is_umount, msg),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(bool, is_umount)
+ __field(char *, msg)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = sb->s_dev;
+ __entry->is_umount = is_umount;
+ __entry->msg = msg;
+ ),
+
+ TP_printk("dev = (%d,%d), checkpoint for %s, state = %s",
+ show_dev(__entry),
+ __entry->is_umount ? "clean umount" : "consistency",
+ __entry->msg)
+);
+
+#endif /* _TRACE_F2FS_H */
+
+ /* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h
index 6a16fd2e70ed..464ea82e10db 100644
--- a/include/trace/events/writeback.h
+++ b/include/trace/events/writeback.h
@@ -183,7 +183,6 @@ DECLARE_EVENT_CLASS(writeback_work_class,
DEFINE_EVENT(writeback_work_class, name, \
TP_PROTO(struct backing_dev_info *bdi, struct wb_writeback_work *work), \
TP_ARGS(bdi, work))
-DEFINE_WRITEBACK_WORK_EVENT(writeback_nothread);
DEFINE_WRITEBACK_WORK_EVENT(writeback_queue);
DEFINE_WRITEBACK_WORK_EVENT(writeback_exec);
DEFINE_WRITEBACK_WORK_EVENT(writeback_start);
@@ -222,12 +221,8 @@ DEFINE_EVENT(writeback_class, name, \
DEFINE_WRITEBACK_EVENT(writeback_nowork);
DEFINE_WRITEBACK_EVENT(writeback_wake_background);
-DEFINE_WRITEBACK_EVENT(writeback_wake_thread);
-DEFINE_WRITEBACK_EVENT(writeback_wake_forker_thread);
DEFINE_WRITEBACK_EVENT(writeback_bdi_register);
DEFINE_WRITEBACK_EVENT(writeback_bdi_unregister);
-DEFINE_WRITEBACK_EVENT(writeback_thread_start);
-DEFINE_WRITEBACK_EVENT(writeback_thread_stop);
DECLARE_EVENT_CLASS(wbc_class,
TP_PROTO(struct writeback_control *wbc, struct backing_dev_info *bdi),
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 706d035fa748..60bb2f9f7b74 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -90,6 +90,9 @@
* 7.21
* - add FUSE_READDIRPLUS
* - send the requested events in POLL request
+ *
+ * 7.22
+ * - add FUSE_ASYNC_DIO
*/
#ifndef _LINUX_FUSE_H
@@ -125,7 +128,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 21
+#define FUSE_KERNEL_MINOR_VERSION 22
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -215,6 +218,7 @@ struct fuse_file_lock {
* FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages
* FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one)
* FUSE_READDIRPLUS_AUTO: adaptive readdirplus
+ * FUSE_ASYNC_DIO: asynchronous direct I/O submission
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -231,6 +235,7 @@ struct fuse_file_lock {
#define FUSE_AUTO_INVAL_DATA (1 << 12)
#define FUSE_DO_READDIRPLUS (1 << 13)
#define FUSE_READDIRPLUS_AUTO (1 << 14)
+#define FUSE_ASYNC_DIO (1 << 15)
/**
* CUSE INIT request/reply flags
diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
index a5a8c88753b9..c520203fac2f 100644
--- a/include/uapi/linux/virtio_net.h
+++ b/include/uapi/linux/virtio_net.h
@@ -191,7 +191,7 @@ struct virtio_net_ctrl_mac {
* specified.
*/
struct virtio_net_ctrl_mq {
- u16 virtqueue_pairs;
+ __u16 virtqueue_pairs;
};
#define VIRTIO_NET_CTRL_MQ 4
diff --git a/ipc/shm.c b/ipc/shm.c
index 8247c49ec073..34af1fe34701 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -491,10 +491,14 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
sprintf (name, "SYSV%08x", key);
if (shmflg & SHM_HUGETLB) {
+ struct hstate *hs = hstate_sizelog((shmflg >> SHM_HUGE_SHIFT)
+ & SHM_HUGE_MASK);
+ size_t hugesize = ALIGN(size, huge_page_size(hs));
+
/* hugetlb_file_setup applies strict accounting */
if (shmflg & SHM_NORESERVE)
acctflag = VM_NORESERVE;
- file = hugetlb_file_setup(name, 0, size, acctflag,
+ file = hugetlb_file_setup(name, hugesize, acctflag,
&shp->mlock_user, HUGETLB_SHMFS_INODE,
(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
} else {
diff --git a/kernel/fork.c b/kernel/fork.c
index f828d8ba0692..41671a5d637d 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -70,6 +70,7 @@
#include <linux/khugepaged.h>
#include <linux/signalfd.h>
#include <linux/uprobes.h>
+#include <linux/aio.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
@@ -1320,6 +1321,10 @@ static struct task_struct *copy_process(unsigned long clone_flags,
p->memcg_batch.do_batch = 0;
p->memcg_batch.memcg = NULL;
#endif
+#ifdef CONFIG_BCACHE
+ p->sequential_io = 0;
+ p->sequential_io_avg = 0;
+#endif
/* Perform scheduler related setup. Assign this task to a CPU. */
sched_fork(p);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 96f3a1d9c379..5a83dde8ca0c 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -462,9 +462,23 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
if (domain->ops->map) {
ret = domain->ops->map(domain, virq, hwirq);
if (ret != 0) {
- pr_err("irq-%i==>hwirq-0x%lx mapping failed: %d\n",
- virq, hwirq, ret);
- WARN_ON(1);
+ /*
+ * If map() returns -EPERM, this interrupt is protected
+ * by the firmware or some other service and shall not
+ * be mapped.
+ *
+ * Since on some platforms we blindly try to map everything
+ * we end up with a log full of backtraces.
+ *
+ * So instead, we silently fail on -EPERM, it is the
+ * responsibility of the PIC driver to display a relevant
+ * message if needed.
+ */
+ if (ret != -EPERM) {
+ pr_err("irq-%i==>hwirq-0x%lx mapping failed: %d\n",
+ virq, hwirq, ret);
+ WARN_ON(1);
+ }
irq_data->domain = NULL;
irq_data->hwirq = 0;
goto err_unmap;
diff --git a/kernel/lockdep.c b/kernel/lockdep.c
index 6a3bccba7e7d..1f3186b37fd5 100644
--- a/kernel/lockdep.c
+++ b/kernel/lockdep.c
@@ -2998,6 +2998,7 @@ void lockdep_init_map(struct lockdep_map *lock, const char *name,
EXPORT_SYMBOL_GPL(lockdep_init_map);
struct lock_class_key __lockdep_no_validate__;
+EXPORT_SYMBOL_GPL(__lockdep_no_validate__);
static int
print_lock_nested_lock_not_held(struct task_struct *curr,
diff --git a/kernel/printk.c b/kernel/printk.c
index 42b1c32d8088..a13c094b7f3d 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -32,6 +32,7 @@
#include <linux/security.h>
#include <linux/bootmem.h>
#include <linux/memblock.h>
+#include <linux/aio.h>
#include <linux/syscalls.h>
#include <linux/kexec.h>
#include <linux/kdb.h>
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 17ae54da0ec2..aed981a3f69c 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -17,6 +17,7 @@
#include <linux/ptrace.h>
#include <linux/security.h>
#include <linux/signal.h>
+#include <linux/uio.h>
#include <linux/audit.h>
#include <linux/pid_namespace.h>
#include <linux/syscalls.h>
diff --git a/kernel/relay.c b/kernel/relay.c
index eef0d113b79e..b91488ba2e5a 100644
--- a/kernel/relay.c
+++ b/kernel/relay.c
@@ -234,7 +234,6 @@ static void relay_destroy_buf(struct rchan_buf *buf)
static void relay_remove_buf(struct kref *kref)
{
struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref);
- buf->chan->cb->remove_buf_file(buf->dentry);
relay_destroy_buf(buf);
}
@@ -484,6 +483,7 @@ static void relay_close_buf(struct rchan_buf *buf)
{
buf->finalized = 1;
del_timer_sync(&buf->timer);
+ buf->chan->cb->remove_buf_file(buf->dentry);
kref_put(&buf->kref, relay_remove_buf);
}
diff --git a/kernel/rwsem.c b/kernel/rwsem.c
index b3c6c3fcd847..cfff1435bdfb 100644
--- a/kernel/rwsem.c
+++ b/kernel/rwsem.c
@@ -126,6 +126,15 @@ void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest)
EXPORT_SYMBOL(_down_write_nest_lock);
+void down_read_non_owner(struct rw_semaphore *sem)
+{
+ might_sleep();
+
+ __down_read(sem);
+}
+
+EXPORT_SYMBOL(down_read_non_owner);
+
void down_write_nested(struct rw_semaphore *sem, int subclass)
{
might_sleep();
@@ -136,6 +145,13 @@ void down_write_nested(struct rw_semaphore *sem, int subclass)
EXPORT_SYMBOL(down_write_nested);
+void up_read_non_owner(struct rw_semaphore *sem)
+{
+ __up_read(sem);
+}
+
+EXPORT_SYMBOL(up_read_non_owner);
+
#endif
diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c
index ed58a3216a6d..b8b8560bfb95 100644
--- a/kernel/trace/blktrace.c
+++ b/kernel/trace/blktrace.c
@@ -1808,6 +1808,7 @@ void blk_fill_rwbs(char *rwbs, u32 rw, int bytes)
rwbs[i] = '\0';
}
+EXPORT_SYMBOL_GPL(blk_fill_rwbs);
#endif /* CONFIG_EVENT_TRACING */
diff --git a/lib/kobject.c b/lib/kobject.c
index a65486613d79..b7e29a6056d3 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -529,7 +529,7 @@ struct kobject *kobject_get(struct kobject *kobj)
return kobj;
}
-static struct kobject *kobject_get_unless_zero(struct kobject *kobj)
+static struct kobject * __must_check kobject_get_unless_zero(struct kobject *kobj)
{
if (!kref_get_unless_zero(&kobj->kref))
kobj = NULL;
diff --git a/lib/lru_cache.c b/lib/lru_cache.c
index 8335d39d2ccd..4a83ecd03650 100644
--- a/lib/lru_cache.c
+++ b/lib/lru_cache.c
@@ -365,7 +365,13 @@ static int lc_unused_element_available(struct lru_cache *lc)
return 0;
}
-static struct lc_element *__lc_get(struct lru_cache *lc, unsigned int enr, bool may_change)
+/* used as internal flags to __lc_get */
+enum {
+ LC_GET_MAY_CHANGE = 1,
+ LC_GET_MAY_USE_UNCOMMITTED = 2,
+};
+
+static struct lc_element *__lc_get(struct lru_cache *lc, unsigned int enr, unsigned int flags)
{
struct lc_element *e;
@@ -380,22 +386,31 @@ static struct lc_element *__lc_get(struct lru_cache *lc, unsigned int enr, bool
* this enr is currently being pulled in already,
* and will be available once the pending transaction
* has been committed. */
- if (e && e->lc_new_number == e->lc_number) {
+ if (e) {
+ if (e->lc_new_number != e->lc_number) {
+ /* It has been found above, but on the "to_be_changed"
+ * list, not yet committed. Don't pull it in twice,
+ * wait for the transaction, then try again...
+ */
+ if (!(flags & LC_GET_MAY_USE_UNCOMMITTED))
+ RETURN(NULL);
+ /* ... unless the caller is aware of the implications,
+ * probably preparing a cumulative transaction. */
+ ++e->refcnt;
+ ++lc->hits;
+ RETURN(e);
+ }
+ /* else: lc_new_number == lc_number; a real hit. */
++lc->hits;
if (e->refcnt++ == 0)
lc->used++;
list_move(&e->list, &lc->in_use); /* Not evictable... */
RETURN(e);
}
+ /* e == NULL */
++lc->misses;
- if (!may_change)
- RETURN(NULL);
-
- /* It has been found above, but on the "to_be_changed" list, not yet
- * committed. Don't pull it in twice, wait for the transaction, then
- * try again */
- if (e)
+ if (!(flags & LC_GET_MAY_CHANGE))
RETURN(NULL);
/* To avoid races with lc_try_lock(), first, mark us dirty
@@ -477,7 +492,27 @@ static struct lc_element *__lc_get(struct lru_cache *lc, unsigned int enr, bool
*/
struct lc_element *lc_get(struct lru_cache *lc, unsigned int enr)
{
- return __lc_get(lc, enr, 1);
+ return __lc_get(lc, enr, LC_GET_MAY_CHANGE);
+}
+
+/**
+ * lc_get_cumulative - like lc_get; also finds to-be-changed elements
+ * @lc: the lru cache to operate on
+ * @enr: the label to look up
+ *
+ * Unlike lc_get this also returns the element for @enr, if it is belonging to
+ * a pending transaction, so the return values are like for lc_get(),
+ * plus:
+ *
+ * pointer to an element already on the "to_be_changed" list.
+ * In this case, the cache was already marked %LC_DIRTY.
+ *
+ * Caller needs to make sure that the pending transaction is completed,
+ * before proceeding to actually use this element.
+ */
+struct lc_element *lc_get_cumulative(struct lru_cache *lc, unsigned int enr)
+{
+ return __lc_get(lc, enr, LC_GET_MAY_CHANGE|LC_GET_MAY_USE_UNCOMMITTED);
}
/**
@@ -648,3 +683,4 @@ EXPORT_SYMBOL(lc_seq_printf_stats);
EXPORT_SYMBOL(lc_seq_dump_details);
EXPORT_SYMBOL(lc_try_lock);
EXPORT_SYMBOL(lc_is_used);
+EXPORT_SYMBOL(lc_get_cumulative);
diff --git a/lib/rwsem-spinlock.c b/lib/rwsem-spinlock.c
index 7542afbb22b3..9be8a9144978 100644
--- a/lib/rwsem-spinlock.c
+++ b/lib/rwsem-spinlock.c
@@ -9,12 +9,15 @@
#include <linux/sched.h>
#include <linux/export.h>
+enum rwsem_waiter_type {
+ RWSEM_WAITING_FOR_WRITE,
+ RWSEM_WAITING_FOR_READ
+};
+
struct rwsem_waiter {
struct list_head list;
struct task_struct *task;
- unsigned int flags;
-#define RWSEM_WAITING_FOR_READ 0x00000001
-#define RWSEM_WAITING_FOR_WRITE 0x00000002
+ enum rwsem_waiter_type type;
};
int rwsem_is_locked(struct rw_semaphore *sem)
@@ -67,26 +70,17 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wakewrite)
waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
- if (!wakewrite) {
- if (waiter->flags & RWSEM_WAITING_FOR_WRITE)
- goto out;
- goto dont_wake_writers;
- }
-
- /*
- * as we support write lock stealing, we can't set sem->activity
- * to -1 here to indicate we get the lock. Instead, we wake it up
- * to let it go get it again.
- */
- if (waiter->flags & RWSEM_WAITING_FOR_WRITE) {
- wake_up_process(waiter->task);
+ if (waiter->type == RWSEM_WAITING_FOR_WRITE) {
+ if (wakewrite)
+ /* Wake up a writer. Note that we do not grant it the
+ * lock - it will have to acquire it when it runs. */
+ wake_up_process(waiter->task);
goto out;
}
/* grant an infinite number of read locks to the front of the queue */
- dont_wake_writers:
woken = 0;
- while (waiter->flags & RWSEM_WAITING_FOR_READ) {
+ do {
struct list_head *next = waiter->list.next;
list_del(&waiter->list);
@@ -96,10 +90,10 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wakewrite)
wake_up_process(tsk);
put_task_struct(tsk);
woken++;
- if (list_empty(&sem->wait_list))
+ if (next == &sem->wait_list)
break;
waiter = list_entry(next, struct rwsem_waiter, list);
- }
+ } while (waiter->type != RWSEM_WAITING_FOR_WRITE);
sem->activity += woken;
@@ -144,7 +138,7 @@ void __sched __down_read(struct rw_semaphore *sem)
/* set up my own style of waitqueue */
waiter.task = tsk;
- waiter.flags = RWSEM_WAITING_FOR_READ;
+ waiter.type = RWSEM_WAITING_FOR_READ;
get_task_struct(tsk);
list_add_tail(&waiter.list, &sem->wait_list);
@@ -201,7 +195,7 @@ void __sched __down_write_nested(struct rw_semaphore *sem, int subclass)
/* set up my own style of waitqueue */
tsk = current;
waiter.task = tsk;
- waiter.flags = RWSEM_WAITING_FOR_WRITE;
+ waiter.type = RWSEM_WAITING_FOR_WRITE;
list_add_tail(&waiter.list, &sem->wait_list);
/* wait for someone to release the lock */
diff --git a/lib/rwsem.c b/lib/rwsem.c
index ad5e0df16ab4..19c5fa95e0b4 100644
--- a/lib/rwsem.c
+++ b/lib/rwsem.c
@@ -4,6 +4,7 @@
* Derived from arch/i386/kernel/semaphore.c
*
* Writer lock-stealing by Alex Shi <alex.shi@intel.com>
+ * and Michel Lespinasse <walken@google.com>
*/
#include <linux/rwsem.h>
#include <linux/sched.h>
@@ -30,21 +31,22 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name,
EXPORT_SYMBOL(__init_rwsem);
+enum rwsem_waiter_type {
+ RWSEM_WAITING_FOR_WRITE,
+ RWSEM_WAITING_FOR_READ
+};
+
struct rwsem_waiter {
struct list_head list;
struct task_struct *task;
- unsigned int flags;
-#define RWSEM_WAITING_FOR_READ 0x00000001
-#define RWSEM_WAITING_FOR_WRITE 0x00000002
+ enum rwsem_waiter_type type;
};
-/* Wake types for __rwsem_do_wake(). Note that RWSEM_WAKE_NO_ACTIVE and
- * RWSEM_WAKE_READ_OWNED imply that the spinlock must have been kept held
- * since the rwsem value was observed.
- */
-#define RWSEM_WAKE_ANY 0 /* Wake whatever's at head of wait list */
-#define RWSEM_WAKE_NO_ACTIVE 1 /* rwsem was observed with no active thread */
-#define RWSEM_WAKE_READ_OWNED 2 /* rwsem was observed to be read owned */
+enum rwsem_wake_type {
+ RWSEM_WAKE_ANY, /* Wake whatever's at head of wait list */
+ RWSEM_WAKE_READERS, /* Wake readers only */
+ RWSEM_WAKE_READ_OWNED /* Waker thread holds the read lock */
+};
/*
* handle the lock release when processes blocked on it that can now run
@@ -57,46 +59,43 @@ struct rwsem_waiter {
* - writers are only woken if downgrading is false
*/
static struct rw_semaphore *
-__rwsem_do_wake(struct rw_semaphore *sem, int wake_type)
+__rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)
{
struct rwsem_waiter *waiter;
struct task_struct *tsk;
struct list_head *next;
- signed long woken, loop, adjustment;
+ long oldcount, woken, loop, adjustment;
waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
- if (!(waiter->flags & RWSEM_WAITING_FOR_WRITE))
- goto readers_only;
-
- if (wake_type == RWSEM_WAKE_READ_OWNED)
- /* Another active reader was observed, so wakeup is not
- * likely to succeed. Save the atomic op.
- */
+ if (waiter->type == RWSEM_WAITING_FOR_WRITE) {
+ if (wake_type == RWSEM_WAKE_ANY)
+ /* Wake writer at the front of the queue, but do not
+ * grant it the lock yet as we want other writers
+ * to be able to steal it. Readers, on the other hand,
+ * will block as they will notice the queued writer.
+ */
+ wake_up_process(waiter->task);
goto out;
+ }
- /* Wake up the writing waiter and let the task grab the sem: */
- wake_up_process(waiter->task);
- goto out;
-
- readers_only:
- /* If we come here from up_xxxx(), another thread might have reached
- * rwsem_down_failed_common() before we acquired the spinlock and
- * woken up a waiter, making it now active. We prefer to check for
- * this first in order to not spend too much time with the spinlock
- * held if we're not going to be able to wake up readers in the end.
- *
- * Note that we do not need to update the rwsem count: any writer
- * trying to acquire rwsem will run rwsem_down_write_failed() due
- * to the waiting threads and block trying to acquire the spinlock.
- *
- * We use a dummy atomic update in order to acquire the cache line
- * exclusively since we expect to succeed and run the final rwsem
- * count adjustment pretty soon.
+ /* Writers might steal the lock before we grant it to the next reader.
+ * We prefer to do the first reader grant before counting readers
+ * so we can bail out early if a writer stole the lock.
*/
- if (wake_type == RWSEM_WAKE_ANY &&
- rwsem_atomic_update(0, sem) < RWSEM_WAITING_BIAS)
- /* Someone grabbed the sem for write already */
- goto out;
+ adjustment = 0;
+ if (wake_type != RWSEM_WAKE_READ_OWNED) {
+ adjustment = RWSEM_ACTIVE_READ_BIAS;
+ try_reader_grant:
+ oldcount = rwsem_atomic_update(adjustment, sem) - adjustment;
+ if (unlikely(oldcount < RWSEM_WAITING_BIAS)) {
+ /* A writer stole the lock. Undo our reader grant. */
+ if (rwsem_atomic_update(-adjustment, sem) &
+ RWSEM_ACTIVE_MASK)
+ goto out;
+ /* Last active locker left. Retry waking readers. */
+ goto try_reader_grant;
+ }
+ }
/* Grant an infinite number of read locks to the readers at the front
* of the queue. Note we increment the 'active part' of the count by
@@ -112,17 +111,19 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wake_type)
waiter = list_entry(waiter->list.next,
struct rwsem_waiter, list);
- } while (waiter->flags & RWSEM_WAITING_FOR_READ);
+ } while (waiter->type != RWSEM_WAITING_FOR_WRITE);
- adjustment = woken * RWSEM_ACTIVE_READ_BIAS;
- if (waiter->flags & RWSEM_WAITING_FOR_READ)
+ adjustment = woken * RWSEM_ACTIVE_READ_BIAS - adjustment;
+ if (waiter->type != RWSEM_WAITING_FOR_WRITE)
/* hit end of list above */
adjustment -= RWSEM_WAITING_BIAS;
- rwsem_atomic_add(adjustment, sem);
+ if (adjustment)
+ rwsem_atomic_add(adjustment, sem);
next = sem->wait_list.next;
- for (loop = woken; loop > 0; loop--) {
+ loop = woken;
+ do {
waiter = list_entry(next, struct rwsem_waiter, list);
next = waiter->list.next;
tsk = waiter->task;
@@ -130,7 +131,7 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wake_type)
waiter->task = NULL;
wake_up_process(tsk);
put_task_struct(tsk);
- }
+ } while (--loop);
sem->wait_list.next = next;
next->prev = &sem->wait_list;
@@ -139,60 +140,21 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wake_type)
return sem;
}
-/* Try to get write sem, caller holds sem->wait_lock: */
-static int try_get_writer_sem(struct rw_semaphore *sem,
- struct rwsem_waiter *waiter)
-{
- struct rwsem_waiter *fwaiter;
- long oldcount, adjustment;
-
- /* only steal when first waiter is writing */
- fwaiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
- if (!(fwaiter->flags & RWSEM_WAITING_FOR_WRITE))
- return 0;
-
- adjustment = RWSEM_ACTIVE_WRITE_BIAS;
- /* Only one waiter in the queue: */
- if (fwaiter == waiter && waiter->list.next == &sem->wait_list)
- adjustment -= RWSEM_WAITING_BIAS;
-
-try_again_write:
- oldcount = rwsem_atomic_update(adjustment, sem) - adjustment;
- if (!(oldcount & RWSEM_ACTIVE_MASK)) {
- /* No active lock: */
- struct task_struct *tsk = waiter->task;
-
- list_del(&waiter->list);
- smp_mb();
- put_task_struct(tsk);
- tsk->state = TASK_RUNNING;
- return 1;
- }
- /* some one grabbed the sem already */
- if (rwsem_atomic_update(-adjustment, sem) & RWSEM_ACTIVE_MASK)
- return 0;
- goto try_again_write;
-}
-
/*
- * wait for a lock to be granted
+ * wait for the read lock to be granted
*/
-static struct rw_semaphore __sched *
-rwsem_down_failed_common(struct rw_semaphore *sem,
- unsigned int flags, signed long adjustment)
+struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
{
+ long count, adjustment = -RWSEM_ACTIVE_READ_BIAS;
struct rwsem_waiter waiter;
struct task_struct *tsk = current;
- signed long count;
-
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
/* set up my own style of waitqueue */
- raw_spin_lock_irq(&sem->wait_lock);
waiter.task = tsk;
- waiter.flags = flags;
+ waiter.type = RWSEM_WAITING_FOR_READ;
get_task_struct(tsk);
+ raw_spin_lock_irq(&sem->wait_lock);
if (list_empty(&sem->wait_list))
adjustment += RWSEM_WAITING_BIAS;
list_add_tail(&waiter.list, &sem->wait_list);
@@ -200,35 +162,24 @@ rwsem_down_failed_common(struct rw_semaphore *sem,
/* we're now waiting on the lock, but no longer actively locking */
count = rwsem_atomic_update(adjustment, sem);
- /* If there are no active locks, wake the front queued process(es) up.
+ /* If there are no active locks, wake the front queued process(es).
*
- * Alternatively, if we're called from a failed down_write(), there
- * were already threads queued before us and there are no active
- * writers, the lock must be read owned; so we try to wake any read
- * locks that were queued ahead of us. */
- if (count == RWSEM_WAITING_BIAS)
- sem = __rwsem_do_wake(sem, RWSEM_WAKE_NO_ACTIVE);
- else if (count > RWSEM_WAITING_BIAS &&
- adjustment == -RWSEM_ACTIVE_WRITE_BIAS)
- sem = __rwsem_do_wake(sem, RWSEM_WAKE_READ_OWNED);
+ * If there are no writers and we are first in the queue,
+ * wake our own waiter to join the existing active readers !
+ */
+ if (count == RWSEM_WAITING_BIAS ||
+ (count > RWSEM_WAITING_BIAS &&
+ adjustment != -RWSEM_ACTIVE_READ_BIAS))
+ sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);
raw_spin_unlock_irq(&sem->wait_lock);
/* wait to be given the lock */
- for (;;) {
+ while (true) {
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
if (!waiter.task)
break;
-
- raw_spin_lock_irq(&sem->wait_lock);
- /* Try to get the writer sem, may steal from the head writer: */
- if (flags == RWSEM_WAITING_FOR_WRITE)
- if (try_get_writer_sem(sem, &waiter)) {
- raw_spin_unlock_irq(&sem->wait_lock);
- return sem;
- }
- raw_spin_unlock_irq(&sem->wait_lock);
schedule();
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
}
tsk->state = TASK_RUNNING;
@@ -237,21 +188,64 @@ rwsem_down_failed_common(struct rw_semaphore *sem,
}
/*
- * wait for the read lock to be granted
- */
-struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
-{
- return rwsem_down_failed_common(sem, RWSEM_WAITING_FOR_READ,
- -RWSEM_ACTIVE_READ_BIAS);
-}
-
-/*
- * wait for the write lock to be granted
+ * wait until we successfully acquire the write lock
*/
struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem)
{
- return rwsem_down_failed_common(sem, RWSEM_WAITING_FOR_WRITE,
- -RWSEM_ACTIVE_WRITE_BIAS);
+ long count, adjustment = -RWSEM_ACTIVE_WRITE_BIAS;
+ struct rwsem_waiter waiter;
+ struct task_struct *tsk = current;
+
+ /* set up my own style of waitqueue */
+ waiter.task = tsk;
+ waiter.type = RWSEM_WAITING_FOR_WRITE;
+
+ raw_spin_lock_irq(&sem->wait_lock);
+ if (list_empty(&sem->wait_list))
+ adjustment += RWSEM_WAITING_BIAS;
+ list_add_tail(&waiter.list, &sem->wait_list);
+
+ /* we're now waiting on the lock, but no longer actively locking */
+ count = rwsem_atomic_update(adjustment, sem);
+
+ /* If there were already threads queued before us and there are no
+ * active writers, the lock must be read owned; so we try to wake
+ * any read locks that were queued ahead of us. */
+ if (count > RWSEM_WAITING_BIAS &&
+ adjustment == -RWSEM_ACTIVE_WRITE_BIAS)
+ sem = __rwsem_do_wake(sem, RWSEM_WAKE_READERS);
+
+ /* wait until we successfully acquire the lock */
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ while (true) {
+ if (!(count & RWSEM_ACTIVE_MASK)) {
+ /* Try acquiring the write lock. */
+ count = RWSEM_ACTIVE_WRITE_BIAS;
+ if (!list_is_singular(&sem->wait_list))
+ count += RWSEM_WAITING_BIAS;
+
+ if (sem->count == RWSEM_WAITING_BIAS &&
+ cmpxchg(&sem->count, RWSEM_WAITING_BIAS, count) ==
+ RWSEM_WAITING_BIAS)
+ break;
+ }
+
+ raw_spin_unlock_irq(&sem->wait_lock);
+
+ /* Block until there are no active lockers. */
+ do {
+ schedule();
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ } while ((count = sem->count) & RWSEM_ACTIVE_MASK);
+
+ raw_spin_lock_irq(&sem->wait_lock);
+ }
+
+ list_del(&waiter.list);
+ raw_spin_unlock_irq(&sem->wait_lock);
+ tsk->state = TASK_RUNNING;
+
+ return sem;
}
/*
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 41733c5dc820..502517492258 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -31,13 +31,14 @@ EXPORT_SYMBOL_GPL(noop_backing_dev_info);
static struct class *bdi_class;
/*
- * bdi_lock protects updates to bdi_list and bdi_pending_list, as well as
- * reader side protection for bdi_pending_list. bdi_list has RCU reader side
+ * bdi_lock protects updates to bdi_list. bdi_list has RCU reader side
* locking.
*/
DEFINE_SPINLOCK(bdi_lock);
LIST_HEAD(bdi_list);
-LIST_HEAD(bdi_pending_list);
+
+/* bdi_wq serves all asynchronous writeback tasks */
+struct workqueue_struct *bdi_wq;
void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2)
{
@@ -257,6 +258,11 @@ static int __init default_bdi_init(void)
{
int err;
+ bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | WQ_FREEZABLE |
+ WQ_UNBOUND | WQ_SYSFS, 0);
+ if (!bdi_wq)
+ return -ENOMEM;
+
err = bdi_init(&default_backing_dev_info);
if (!err)
bdi_register(&default_backing_dev_info, NULL, "default");
@@ -271,26 +277,6 @@ int bdi_has_dirty_io(struct backing_dev_info *bdi)
return wb_has_dirty_io(&bdi->wb);
}
-static void wakeup_timer_fn(unsigned long data)
-{
- struct backing_dev_info *bdi = (struct backing_dev_info *)data;
-
- spin_lock_bh(&bdi->wb_lock);
- if (bdi->wb.task) {
- trace_writeback_wake_thread(bdi);
- wake_up_process(bdi->wb.task);
- } else if (bdi->dev) {
- /*
- * When bdi tasks are inactive for long time, they are killed.
- * In this case we have to wake-up the forker thread which
- * should create and run the bdi thread.
- */
- trace_writeback_wake_forker_thread(bdi);
- wake_up_process(default_backing_dev_info.wb.task);
- }
- spin_unlock_bh(&bdi->wb_lock);
-}
-
/*
* This function is used when the first inode for this bdi is marked dirty. It
* wakes-up the corresponding bdi thread which should then take care of the
@@ -307,176 +293,7 @@ void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi)
unsigned long timeout;
timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
- mod_timer(&bdi->wb.wakeup_timer, jiffies + timeout);
-}
-
-/*
- * Calculate the longest interval (jiffies) bdi threads are allowed to be
- * inactive.
- */
-static unsigned long bdi_longest_inactive(void)
-{
- unsigned long interval;
-
- interval = msecs_to_jiffies(dirty_writeback_interval * 10);
- return max(5UL * 60 * HZ, interval);
-}
-
-/*
- * Clear pending bit and wakeup anybody waiting for flusher thread creation or
- * shutdown
- */
-static void bdi_clear_pending(struct backing_dev_info *bdi)
-{
- clear_bit(BDI_pending, &bdi->state);
- smp_mb__after_clear_bit();
- wake_up_bit(&bdi->state, BDI_pending);
-}
-
-static int bdi_forker_thread(void *ptr)
-{
- struct bdi_writeback *me = ptr;
-
- current->flags |= PF_SWAPWRITE;
- set_freezable();
-
- /*
- * Our parent may run at a different priority, just set us to normal
- */
- set_user_nice(current, 0);
-
- for (;;) {
- struct task_struct *task = NULL;
- struct backing_dev_info *bdi;
- enum {
- NO_ACTION, /* Nothing to do */
- FORK_THREAD, /* Fork bdi thread */
- KILL_THREAD, /* Kill inactive bdi thread */
- } action = NO_ACTION;
-
- /*
- * Temporary measure, we want to make sure we don't see
- * dirty data on the default backing_dev_info
- */
- if (wb_has_dirty_io(me) || !list_empty(&me->bdi->work_list)) {
- del_timer(&me->wakeup_timer);
- wb_do_writeback(me, 0);
- }
-
- spin_lock_bh(&bdi_lock);
- /*
- * In the following loop we are going to check whether we have
- * some work to do without any synchronization with tasks
- * waking us up to do work for them. Set the task state here
- * so that we don't miss wakeups after verifying conditions.
- */
- set_current_state(TASK_INTERRUPTIBLE);
-
- list_for_each_entry(bdi, &bdi_list, bdi_list) {
- bool have_dirty_io;
-
- if (!bdi_cap_writeback_dirty(bdi) ||
- bdi_cap_flush_forker(bdi))
- continue;
-
- WARN(!test_bit(BDI_registered, &bdi->state),
- "bdi %p/%s is not registered!\n", bdi, bdi->name);
-
- have_dirty_io = !list_empty(&bdi->work_list) ||
- wb_has_dirty_io(&bdi->wb);
-
- /*
- * If the bdi has work to do, but the thread does not
- * exist - create it.
- */
- if (!bdi->wb.task && have_dirty_io) {
- /*
- * Set the pending bit - if someone will try to
- * unregister this bdi - it'll wait on this bit.
- */
- set_bit(BDI_pending, &bdi->state);
- action = FORK_THREAD;
- break;
- }
-
- spin_lock(&bdi->wb_lock);
-
- /*
- * If there is no work to do and the bdi thread was
- * inactive long enough - kill it. The wb_lock is taken
- * to make sure no-one adds more work to this bdi and
- * wakes the bdi thread up.
- */
- if (bdi->wb.task && !have_dirty_io &&
- time_after(jiffies, bdi->wb.last_active +
- bdi_longest_inactive())) {
- task = bdi->wb.task;
- bdi->wb.task = NULL;
- spin_unlock(&bdi->wb_lock);
- set_bit(BDI_pending, &bdi->state);
- action = KILL_THREAD;
- break;
- }
- spin_unlock(&bdi->wb_lock);
- }
- spin_unlock_bh(&bdi_lock);
-
- /* Keep working if default bdi still has things to do */
- if (!list_empty(&me->bdi->work_list))
- __set_current_state(TASK_RUNNING);
-
- switch (action) {
- case FORK_THREAD:
- __set_current_state(TASK_RUNNING);
- task = kthread_create(bdi_writeback_thread, &bdi->wb,
- "flush-%s", dev_name(bdi->dev));
- if (IS_ERR(task)) {
- /*
- * If thread creation fails, force writeout of
- * the bdi from the thread. Hopefully 1024 is
- * large enough for efficient IO.
- */
- writeback_inodes_wb(&bdi->wb, 1024,
- WB_REASON_FORKER_THREAD);
- } else {
- /*
- * The spinlock makes sure we do not lose
- * wake-ups when racing with 'bdi_queue_work()'.
- * And as soon as the bdi thread is visible, we
- * can start it.
- */
- spin_lock_bh(&bdi->wb_lock);
- bdi->wb.task = task;
- spin_unlock_bh(&bdi->wb_lock);
- wake_up_process(task);
- }
- bdi_clear_pending(bdi);
- break;
-
- case KILL_THREAD:
- __set_current_state(TASK_RUNNING);
- kthread_stop(task);
- bdi_clear_pending(bdi);
- break;
-
- case NO_ACTION:
- if (!wb_has_dirty_io(me) || !dirty_writeback_interval)
- /*
- * There are no dirty data. The only thing we
- * should now care about is checking for
- * inactive bdi threads and killing them. Thus,
- * let's sleep for longer time, save energy and
- * be friendly for battery-driven devices.
- */
- schedule_timeout(bdi_longest_inactive());
- else
- schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10));
- try_to_freeze();
- break;
- }
- }
-
- return 0;
+ mod_delayed_work(bdi_wq, &bdi->wb.dwork, timeout);
}
/*
@@ -489,6 +306,9 @@ static void bdi_remove_from_list(struct backing_dev_info *bdi)
spin_unlock_bh(&bdi_lock);
synchronize_rcu_expedited();
+
+ /* bdi_list is now unused, clear it to mark @bdi dying */
+ INIT_LIST_HEAD(&bdi->bdi_list);
}
int bdi_register(struct backing_dev_info *bdi, struct device *parent,
@@ -508,20 +328,6 @@ int bdi_register(struct backing_dev_info *bdi, struct device *parent,
bdi->dev = dev;
- /*
- * Just start the forker thread for our default backing_dev_info,
- * and add other bdi's to the list. They will get a thread created
- * on-demand when they need it.
- */
- if (bdi_cap_flush_forker(bdi)) {
- struct bdi_writeback *wb = &bdi->wb;
-
- wb->task = kthread_run(bdi_forker_thread, wb, "bdi-%s",
- dev_name(dev));
- if (IS_ERR(wb->task))
- return PTR_ERR(wb->task);
- }
-
bdi_debug_register(bdi, dev_name(dev));
set_bit(BDI_registered, &bdi->state);
@@ -545,8 +351,6 @@ EXPORT_SYMBOL(bdi_register_dev);
*/
static void bdi_wb_shutdown(struct backing_dev_info *bdi)
{
- struct task_struct *task;
-
if (!bdi_cap_writeback_dirty(bdi))
return;
@@ -556,22 +360,20 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi)
bdi_remove_from_list(bdi);
/*
- * If setup is pending, wait for that to complete first
+ * Drain work list and shutdown the delayed_work. At this point,
+ * @bdi->bdi_list is empty telling bdi_Writeback_workfn() that @bdi
+ * is dying and its work_list needs to be drained no matter what.
*/
- wait_on_bit(&bdi->state, BDI_pending, bdi_sched_wait,
- TASK_UNINTERRUPTIBLE);
+ mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
+ flush_delayed_work(&bdi->wb.dwork);
+ WARN_ON(!list_empty(&bdi->work_list));
/*
- * Finally, kill the kernel thread. We don't need to be RCU
- * safe anymore, since the bdi is gone from visibility.
+ * This shouldn't be necessary unless @bdi for some reason has
+ * unflushed dirty IO after work_list is drained. Do it anyway
+ * just in case.
*/
- spin_lock_bh(&bdi->wb_lock);
- task = bdi->wb.task;
- bdi->wb.task = NULL;
- spin_unlock_bh(&bdi->wb_lock);
-
- if (task)
- kthread_stop(task);
+ cancel_delayed_work_sync(&bdi->wb.dwork);
}
/*
@@ -597,10 +399,8 @@ void bdi_unregister(struct backing_dev_info *bdi)
bdi_set_min_ratio(bdi, 0);
trace_writeback_bdi_unregister(bdi);
bdi_prune_sb(bdi);
- del_timer_sync(&bdi->wb.wakeup_timer);
- if (!bdi_cap_flush_forker(bdi))
- bdi_wb_shutdown(bdi);
+ bdi_wb_shutdown(bdi);
bdi_debug_unregister(bdi);
spin_lock_bh(&bdi->wb_lock);
@@ -622,7 +422,7 @@ static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi)
INIT_LIST_HEAD(&wb->b_io);
INIT_LIST_HEAD(&wb->b_more_io);
spin_lock_init(&wb->list_lock);
- setup_timer(&wb->wakeup_timer, wakeup_timer_fn, (unsigned long)bdi);
+ INIT_DELAYED_WORK(&wb->dwork, bdi_writeback_workfn);
}
/*
@@ -695,12 +495,11 @@ void bdi_destroy(struct backing_dev_info *bdi)
bdi_unregister(bdi);
/*
- * If bdi_unregister() had already been called earlier, the
- * wakeup_timer could still be armed because bdi_prune_sb()
- * can race with the bdi_wakeup_thread_delayed() calls from
- * __mark_inode_dirty().
+ * If bdi_unregister() had already been called earlier, the dwork
+ * could still be pending because bdi_prune_sb() can race with the
+ * bdi_wakeup_thread_delayed() calls from __mark_inode_dirty().
*/
- del_timer_sync(&bdi->wb.wakeup_timer);
+ cancel_delayed_work_sync(&bdi->wb.dwork);
for (i = 0; i < NR_BDI_STAT_ITEMS; i++)
percpu_counter_destroy(&bdi->bdi_stat[i]);
diff --git a/mm/bounce.c b/mm/bounce.c
index a5c2ec3589cb..c9f0a4339a7d 100644
--- a/mm/bounce.c
+++ b/mm/bounce.c
@@ -101,7 +101,7 @@ static void copy_to_high_bio_irq(struct bio *to, struct bio *from)
struct bio_vec *tovec, *fromvec;
int i;
- __bio_for_each_segment(tovec, to, i, 0) {
+ bio_for_each_segment(tovec, to, i) {
fromvec = from->bi_io_vec + i;
/*
@@ -134,7 +134,7 @@ static void bounce_end_io(struct bio *bio, mempool_t *pool, int err)
/*
* free up bounce indirect pages used
*/
- __bio_for_each_segment(bvec, bio, i, 0) {
+ bio_for_each_segment_all(bvec, bio, i) {
org_vec = bio_orig->bi_io_vec + i;
if (bvec->bv_page == org_vec->bv_page)
continue;
@@ -199,78 +199,43 @@ static int must_snapshot_stable_pages(struct request_queue *q, struct bio *bio)
static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig,
mempool_t *pool, int force)
{
- struct page *page;
- struct bio *bio = NULL;
- int i, rw = bio_data_dir(*bio_orig);
+ struct bio *bio;
+ int rw = bio_data_dir(*bio_orig);
struct bio_vec *to, *from;
+ unsigned i;
- bio_for_each_segment(from, *bio_orig, i) {
- page = from->bv_page;
+ bio_for_each_segment(from, *bio_orig, i)
+ if (page_to_pfn(from->bv_page) > queue_bounce_pfn(q))
+ goto bounce;
- /*
- * is destination page below bounce pfn?
- */
- if (page_to_pfn(page) <= queue_bounce_pfn(q) && !force)
- continue;
-
- /*
- * irk, bounce it
- */
- if (!bio) {
- unsigned int cnt = (*bio_orig)->bi_vcnt;
+ return;
+bounce:
+ bio = bio_clone_bioset(*bio_orig, GFP_NOIO, fs_bio_set);
- bio = bio_alloc(GFP_NOIO, cnt);
- memset(bio->bi_io_vec, 0, cnt * sizeof(struct bio_vec));
- }
-
+ bio_for_each_segment_all(to, bio, i) {
+ struct page *page = to->bv_page;
- to = bio->bi_io_vec + i;
+ if (page_to_pfn(page) <= queue_bounce_pfn(q) && !force)
+ continue;
- to->bv_page = mempool_alloc(pool, q->bounce_gfp);
- to->bv_len = from->bv_len;
- to->bv_offset = from->bv_offset;
inc_zone_page_state(to->bv_page, NR_BOUNCE);
+ to->bv_page = mempool_alloc(pool, q->bounce_gfp);
if (rw == WRITE) {
char *vto, *vfrom;
- flush_dcache_page(from->bv_page);
+ flush_dcache_page(page);
+
vto = page_address(to->bv_page) + to->bv_offset;
- vfrom = kmap(from->bv_page) + from->bv_offset;
+ vfrom = kmap_atomic(page) + to->bv_offset;
memcpy(vto, vfrom, to->bv_len);
- kunmap(from->bv_page);
+ kunmap_atomic(vfrom);
}
}
- /*
- * no pages bounced
- */
- if (!bio)
- return;
-
trace_block_bio_bounce(q, *bio_orig);
- /*
- * at least one page was bounced, fill in possible non-highmem
- * pages
- */
- __bio_for_each_segment(from, *bio_orig, i, 0) {
- to = bio_iovec_idx(bio, i);
- if (!to->bv_page) {
- to->bv_page = from->bv_page;
- to->bv_len = from->bv_len;
- to->bv_offset = from->bv_offset;
- }
- }
-
- bio->bi_bdev = (*bio_orig)->bi_bdev;
bio->bi_flags |= (1 << BIO_BOUNCED);
- bio->bi_sector = (*bio_orig)->bi_sector;
- bio->bi_rw = (*bio_orig)->bi_rw;
-
- bio->bi_vcnt = (*bio_orig)->bi_vcnt;
- bio->bi_idx = (*bio_orig)->bi_idx;
- bio->bi_size = (*bio_orig)->bi_size;
if (pool == page_pool) {
bio->bi_end_io = bounce_end_io_write;
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 0f1d92163f30..cb1c9dedf9b6 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -92,16 +92,18 @@ enum mem_cgroup_stat_index {
/*
* For MEM_CONTAINER_TYPE_ALL, usage = pagecache + rss.
*/
- MEM_CGROUP_STAT_CACHE, /* # of pages charged as cache */
- MEM_CGROUP_STAT_RSS, /* # of pages charged as anon rss */
- MEM_CGROUP_STAT_FILE_MAPPED, /* # of pages charged as file rss */
- MEM_CGROUP_STAT_SWAP, /* # of pages, swapped out */
+ MEM_CGROUP_STAT_CACHE, /* # of pages charged as cache */
+ MEM_CGROUP_STAT_RSS, /* # of pages charged as anon rss */
+ MEM_CGROUP_STAT_RSS_HUGE, /* # of pages charged as anon huge */
+ MEM_CGROUP_STAT_FILE_MAPPED, /* # of pages charged as file rss */
+ MEM_CGROUP_STAT_SWAP, /* # of pages, swapped out */
MEM_CGROUP_STAT_NSTATS,
};
static const char * const mem_cgroup_stat_names[] = {
"cache",
"rss",
+ "rss_huge",
"mapped_file",
"swap",
};
@@ -917,6 +919,7 @@ static unsigned long mem_cgroup_read_events(struct mem_cgroup *memcg,
}
static void mem_cgroup_charge_statistics(struct mem_cgroup *memcg,
+ struct page *page,
bool anon, int nr_pages)
{
preempt_disable();
@@ -932,6 +935,10 @@ static void mem_cgroup_charge_statistics(struct mem_cgroup *memcg,
__this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_CACHE],
nr_pages);
+ if (PageTransHuge(page))
+ __this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_RSS_HUGE],
+ nr_pages);
+
/* pagein of a big page is an event. So, ignore page size */
if (nr_pages > 0)
__this_cpu_inc(memcg->stat->events[MEM_CGROUP_EVENTS_PGPGIN]);
@@ -2914,7 +2921,7 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
else
anon = false;
- mem_cgroup_charge_statistics(memcg, anon, nr_pages);
+ mem_cgroup_charge_statistics(memcg, page, anon, nr_pages);
unlock_page_cgroup(pc);
/*
@@ -3708,16 +3715,21 @@ void mem_cgroup_split_huge_fixup(struct page *head)
{
struct page_cgroup *head_pc = lookup_page_cgroup(head);
struct page_cgroup *pc;
+ struct mem_cgroup *memcg;
int i;
if (mem_cgroup_disabled())
return;
+
+ memcg = head_pc->mem_cgroup;
for (i = 1; i < HPAGE_PMD_NR; i++) {
pc = head_pc + i;
- pc->mem_cgroup = head_pc->mem_cgroup;
+ pc->mem_cgroup = memcg;
smp_wmb();/* see __commit_charge() */
pc->flags = head_pc->flags & ~PCGF_NOCOPY_AT_SPLIT;
}
+ __this_cpu_sub(memcg->stat->count[MEM_CGROUP_STAT_RSS_HUGE],
+ HPAGE_PMD_NR);
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
@@ -3773,11 +3785,11 @@ static int mem_cgroup_move_account(struct page *page,
__this_cpu_inc(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]);
preempt_enable();
}
- mem_cgroup_charge_statistics(from, anon, -nr_pages);
+ mem_cgroup_charge_statistics(from, page, anon, -nr_pages);
/* caller should have done css_get */
pc->mem_cgroup = to;
- mem_cgroup_charge_statistics(to, anon, nr_pages);
+ mem_cgroup_charge_statistics(to, page, anon, nr_pages);
move_unlock_mem_cgroup(from, &flags);
ret = 0;
unlock:
@@ -4152,7 +4164,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype,
break;
}
- mem_cgroup_charge_statistics(memcg, anon, -nr_pages);
+ mem_cgroup_charge_statistics(memcg, page, anon, -nr_pages);
ClearPageCgroupUsed(pc);
/*
@@ -4502,7 +4514,7 @@ void mem_cgroup_replace_page_cache(struct page *oldpage,
lock_page_cgroup(pc);
if (PageCgroupUsed(pc)) {
memcg = pc->mem_cgroup;
- mem_cgroup_charge_statistics(memcg, false, -1);
+ mem_cgroup_charge_statistics(memcg, oldpage, false, -1);
ClearPageCgroupUsed(pc);
}
unlock_page_cgroup(pc);
@@ -5030,6 +5042,10 @@ static inline u64 mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
return res_counter_read_u64(&memcg->memsw, RES_USAGE);
}
+ /*
+ * Transparent hugepages are still accounted for in MEM_CGROUP_STAT_RSS
+ * as well as in MEM_CGROUP_STAT_RSS_HUGE.
+ */
val = mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_CACHE);
val += mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_RSS);
diff --git a/mm/mmap.c b/mm/mmap.c
index da3e9c04bf37..1ae21d645c68 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1363,15 +1363,20 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
file = fget(fd);
if (!file)
goto out;
+ if (is_file_hugepages(file))
+ len = ALIGN(len, huge_page_size(hstate_file(file)));
} else if (flags & MAP_HUGETLB) {
struct user_struct *user = NULL;
+
+ len = ALIGN(len, huge_page_size(hstate_sizelog(
+ (flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK)));
/*
* VM_NORESERVE is used because the reservations will be
* taken when vm_ops->mmap() is called
* A dummy user value is used because we are not locking
* memory so no accounting is necessary
*/
- file = hugetlb_file_setup(HUGETLB_ANON_FILE, addr, len,
+ file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,
VM_NORESERVE,
&user, HUGETLB_ANONHUGE_INODE,
(flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);
diff --git a/mm/mmu_context.c b/mm/mmu_context.c
index 3dcfaf4ed355..8a8cd0265e52 100644
--- a/mm/mmu_context.c
+++ b/mm/mmu_context.c
@@ -14,9 +14,6 @@
* use_mm
* Makes the calling kernel thread take on the specified
* mm context.
- * Called by the retry thread execute retries within the
- * iocb issuer's mm context, so that copy_from/to_user
- * operations work seamlessly for aio.
* (Note: this routine is intended to be called only
* from a kernel thread context)
*/
diff --git a/mm/page_io.c b/mm/page_io.c
index bb5d75274686..a8a3ef45fed7 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -20,6 +20,7 @@
#include <linux/buffer_head.h>
#include <linux/writeback.h>
#include <linux/frontswap.h>
+#include <linux/aio.h>
#include <asm/pgtable.h>
static struct bio *get_swap_bio(gfp_t gfp_flags,
@@ -35,7 +36,6 @@ static struct bio *get_swap_bio(gfp_t gfp_flags,
bio->bi_io_vec[0].bv_len = PAGE_SIZE;
bio->bi_io_vec[0].bv_offset = 0;
bio->bi_vcnt = 1;
- bio->bi_idx = 0;
bio->bi_size = PAGE_SIZE;
bio->bi_end_io = end_io;
}
diff --git a/mm/shmem.c b/mm/shmem.c
index 6636c1d29bfa..695db3b6216b 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -31,6 +31,7 @@
#include <linux/mm.h>
#include <linux/export.h>
#include <linux/swap.h>
+#include <linux/aio.h>
static struct vfsmount *shm_mnt;
diff --git a/mm/slab.c b/mm/slab.c
index 96079244c860..8ccd296c6d9c 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -286,68 +286,27 @@ struct arraycache_init {
};
/*
- * The slab lists for all objects.
- */
-struct kmem_list3 {
- struct list_head slabs_partial; /* partial list first, better asm code */
- struct list_head slabs_full;
- struct list_head slabs_free;
- unsigned long free_objects;
- unsigned int free_limit;
- unsigned int colour_next; /* Per-node cache coloring */
- spinlock_t list_lock;
- struct array_cache *shared; /* shared per node */
- struct array_cache **alien; /* on other nodes */
- unsigned long next_reap; /* updated without locking */
- int free_touched; /* updated without locking */
-};
-
-/*
* Need this for bootstrapping a per node allocator.
*/
#define NUM_INIT_LISTS (3 * MAX_NUMNODES)
-static struct kmem_list3 __initdata initkmem_list3[NUM_INIT_LISTS];
+static struct kmem_cache_node __initdata init_kmem_cache_node[NUM_INIT_LISTS];
#define CACHE_CACHE 0
#define SIZE_AC MAX_NUMNODES
-#define SIZE_L3 (2 * MAX_NUMNODES)
+#define SIZE_NODE (2 * MAX_NUMNODES)
static int drain_freelist(struct kmem_cache *cache,
- struct kmem_list3 *l3, int tofree);
+ struct kmem_cache_node *n, int tofree);
static void free_block(struct kmem_cache *cachep, void **objpp, int len,
int node);
static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp);
static void cache_reap(struct work_struct *unused);
-/*
- * This function must be completely optimized away if a constant is passed to
- * it. Mostly the same as what is in linux/slab.h except it returns an index.
- */
-static __always_inline int index_of(const size_t size)
-{
- extern void __bad_size(void);
-
- if (__builtin_constant_p(size)) {
- int i = 0;
-
-#define CACHE(x) \
- if (size <=x) \
- return i; \
- else \
- i++;
-#include <linux/kmalloc_sizes.h>
-#undef CACHE
- __bad_size();
- } else
- __bad_size();
- return 0;
-}
-
static int slab_early_init = 1;
-#define INDEX_AC index_of(sizeof(struct arraycache_init))
-#define INDEX_L3 index_of(sizeof(struct kmem_list3))
+#define INDEX_AC kmalloc_index(sizeof(struct arraycache_init))
+#define INDEX_NODE kmalloc_index(sizeof(struct kmem_cache_node))
-static void kmem_list3_init(struct kmem_list3 *parent)
+static void kmem_cache_node_init(struct kmem_cache_node *parent)
{
INIT_LIST_HEAD(&parent->slabs_full);
INIT_LIST_HEAD(&parent->slabs_partial);
@@ -363,7 +322,7 @@ static void kmem_list3_init(struct kmem_list3 *parent)
#define MAKE_LIST(cachep, listp, slab, nodeid) \
do { \
INIT_LIST_HEAD(listp); \
- list_splice(&(cachep->nodelists[nodeid]->slab), listp); \
+ list_splice(&(cachep->node[nodeid]->slab), listp); \
} while (0)
#define MAKE_ALL_LISTS(cachep, ptr, nodeid) \
@@ -524,30 +483,6 @@ static inline unsigned int obj_to_index(const struct kmem_cache *cache,
return reciprocal_divide(offset, cache->reciprocal_buffer_size);
}
-/*
- * These are the default caches for kmalloc. Custom caches can have other sizes.
- */
-struct cache_sizes malloc_sizes[] = {
-#define CACHE(x) { .cs_size = (x) },
-#include <linux/kmalloc_sizes.h>
- CACHE(ULONG_MAX)
-#undef CACHE
-};
-EXPORT_SYMBOL(malloc_sizes);
-
-/* Must match cache_sizes above. Out of line to keep cache footprint low. */
-struct cache_names {
- char *name;
- char *name_dma;
-};
-
-static struct cache_names __initdata cache_names[] = {
-#define CACHE(x) { .name = "size-" #x, .name_dma = "size-" #x "(DMA)" },
-#include <linux/kmalloc_sizes.h>
- {NULL,}
-#undef CACHE
-};
-
static struct arraycache_init initarray_generic =
{ {0, BOOT_CPUCACHE_ENTRIES, 1, 0} };
@@ -586,15 +521,15 @@ static void slab_set_lock_classes(struct kmem_cache *cachep,
int q)
{
struct array_cache **alc;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
int r;
- l3 = cachep->nodelists[q];
- if (!l3)
+ n = cachep->node[q];
+ if (!n)
return;
- lockdep_set_class(&l3->list_lock, l3_key);
- alc = l3->alien;
+ lockdep_set_class(&n->list_lock, l3_key);
+ alc = n->alien;
/*
* FIXME: This check for BAD_ALIEN_MAGIC
* should go away when common slab code is taught to
@@ -625,28 +560,30 @@ static void slab_set_debugobj_lock_classes(struct kmem_cache *cachep)
static void init_node_lock_keys(int q)
{
- struct cache_sizes *s = malloc_sizes;
+ int i;
if (slab_state < UP)
return;
- for (s = malloc_sizes; s->cs_size != ULONG_MAX; s++) {
- struct kmem_list3 *l3;
+ for (i = 1; i < PAGE_SHIFT + MAX_ORDER; i++) {
+ struct kmem_cache_node *n;
+ struct kmem_cache *cache = kmalloc_caches[i];
+
+ if (!cache)
+ continue;
- l3 = s->cs_cachep->nodelists[q];
- if (!l3 || OFF_SLAB(s->cs_cachep))
+ n = cache->node[q];
+ if (!n || OFF_SLAB(cache))
continue;
- slab_set_lock_classes(s->cs_cachep, &on_slab_l3_key,
+ slab_set_lock_classes(cache, &on_slab_l3_key,
&on_slab_alc_key, q);
}
}
static void on_slab_lock_classes_node(struct kmem_cache *cachep, int q)
{
- struct kmem_list3 *l3;
- l3 = cachep->nodelists[q];
- if (!l3)
+ if (!cachep->node[q])
return;
slab_set_lock_classes(cachep, &on_slab_l3_key,
@@ -702,41 +639,6 @@ static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep)
return cachep->array[smp_processor_id()];
}
-static inline struct kmem_cache *__find_general_cachep(size_t size,
- gfp_t gfpflags)
-{
- struct cache_sizes *csizep = malloc_sizes;
-
-#if DEBUG
- /* This happens if someone tries to call
- * kmem_cache_create(), or __kmalloc(), before
- * the generic caches are initialized.
- */
- BUG_ON(malloc_sizes[INDEX_AC].cs_cachep == NULL);
-#endif
- if (!size)
- return ZERO_SIZE_PTR;
-
- while (size > csizep->cs_size)
- csizep++;
-
- /*
- * Really subtle: The last entry with cs->cs_size==ULONG_MAX
- * has cs_{dma,}cachep==NULL. Thus no special case
- * for large kmalloc calls required.
- */
-#ifdef CONFIG_ZONE_DMA
- if (unlikely(gfpflags & GFP_DMA))
- return csizep->cs_dmacachep;
-#endif
- return csizep->cs_cachep;
-}
-
-static struct kmem_cache *kmem_find_general_cachep(size_t size, gfp_t gfpflags)
-{
- return __find_general_cachep(size, gfpflags);
-}
-
static size_t slab_mgmt_size(size_t nr_objs, size_t align)
{
return ALIGN(sizeof(struct slab)+nr_objs*sizeof(kmem_bufctl_t), align);
@@ -938,29 +840,29 @@ static inline bool is_slab_pfmemalloc(struct slab *slabp)
static void recheck_pfmemalloc_active(struct kmem_cache *cachep,
struct array_cache *ac)
{
- struct kmem_list3 *l3 = cachep->nodelists[numa_mem_id()];
+ struct kmem_cache_node *n = cachep->node[numa_mem_id()];
struct slab *slabp;
unsigned long flags;
if (!pfmemalloc_active)
return;
- spin_lock_irqsave(&l3->list_lock, flags);
- list_for_each_entry(slabp, &l3->slabs_full, list)
+ spin_lock_irqsave(&n->list_lock, flags);
+ list_for_each_entry(slabp, &n->slabs_full, list)
if (is_slab_pfmemalloc(slabp))
goto out;
- list_for_each_entry(slabp, &l3->slabs_partial, list)
+ list_for_each_entry(slabp, &n->slabs_partial, list)
if (is_slab_pfmemalloc(slabp))
goto out;
- list_for_each_entry(slabp, &l3->slabs_free, list)
+ list_for_each_entry(slabp, &n->slabs_free, list)
if (is_slab_pfmemalloc(slabp))
goto out;
pfmemalloc_active = false;
out:
- spin_unlock_irqrestore(&l3->list_lock, flags);
+ spin_unlock_irqrestore(&n->list_lock, flags);
}
static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
@@ -971,7 +873,7 @@ static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
/* Ensure the caller is allowed to use objects from PFMEMALLOC slab */
if (unlikely(is_obj_pfmemalloc(objp))) {
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
if (gfp_pfmemalloc_allowed(flags)) {
clear_obj_pfmemalloc(&objp);
@@ -993,8 +895,8 @@ static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
* If there are empty slabs on the slabs_free list and we are
* being forced to refill the cache, mark this one !pfmemalloc.
*/
- l3 = cachep->nodelists[numa_mem_id()];
- if (!list_empty(&l3->slabs_free) && force_refill) {
+ n = cachep->node[numa_mem_id()];
+ if (!list_empty(&n->slabs_free) && force_refill) {
struct slab *slabp = virt_to_slab(objp);
ClearPageSlabPfmemalloc(virt_to_head_page(slabp->s_mem));
clear_obj_pfmemalloc(&objp);
@@ -1071,7 +973,7 @@ static int transfer_objects(struct array_cache *to,
#ifndef CONFIG_NUMA
#define drain_alien_cache(cachep, alien) do { } while (0)
-#define reap_alien(cachep, l3) do { } while (0)
+#define reap_alien(cachep, n) do { } while (0)
static inline struct array_cache **alloc_alien_cache(int node, int limit, gfp_t gfp)
{
@@ -1143,33 +1045,33 @@ static void free_alien_cache(struct array_cache **ac_ptr)
static void __drain_alien_cache(struct kmem_cache *cachep,
struct array_cache *ac, int node)
{
- struct kmem_list3 *rl3 = cachep->nodelists[node];
+ struct kmem_cache_node *n = cachep->node[node];
if (ac->avail) {
- spin_lock(&rl3->list_lock);
+ spin_lock(&n->list_lock);
/*
* Stuff objects into the remote nodes shared array first.
* That way we could avoid the overhead of putting the objects
* into the free lists and getting them back later.
*/
- if (rl3->shared)
- transfer_objects(rl3->shared, ac, ac->limit);
+ if (n->shared)
+ transfer_objects(n->shared, ac, ac->limit);
free_block(cachep, ac->entry, ac->avail, node);
ac->avail = 0;
- spin_unlock(&rl3->list_lock);
+ spin_unlock(&n->list_lock);
}
}
/*
* Called from cache_reap() to regularly drain alien caches round robin.
*/
-static void reap_alien(struct kmem_cache *cachep, struct kmem_list3 *l3)
+static void reap_alien(struct kmem_cache *cachep, struct kmem_cache_node *n)
{
int node = __this_cpu_read(slab_reap_node);
- if (l3->alien) {
- struct array_cache *ac = l3->alien[node];
+ if (n->alien) {
+ struct array_cache *ac = n->alien[node];
if (ac && ac->avail && spin_trylock_irq(&ac->lock)) {
__drain_alien_cache(cachep, ac, node);
@@ -1199,7 +1101,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
{
struct slab *slabp = virt_to_slab(objp);
int nodeid = slabp->nodeid;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
struct array_cache *alien = NULL;
int node;
@@ -1212,10 +1114,10 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
if (likely(slabp->nodeid == node))
return 0;
- l3 = cachep->nodelists[node];
+ n = cachep->node[node];
STATS_INC_NODEFREES(cachep);
- if (l3->alien && l3->alien[nodeid]) {
- alien = l3->alien[nodeid];
+ if (n->alien && n->alien[nodeid]) {
+ alien = n->alien[nodeid];
spin_lock(&alien->lock);
if (unlikely(alien->avail == alien->limit)) {
STATS_INC_ACOVERFLOW(cachep);
@@ -1224,28 +1126,28 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
ac_put_obj(cachep, alien, objp);
spin_unlock(&alien->lock);
} else {
- spin_lock(&(cachep->nodelists[nodeid])->list_lock);
+ spin_lock(&(cachep->node[nodeid])->list_lock);
free_block(cachep, &objp, 1, nodeid);
- spin_unlock(&(cachep->nodelists[nodeid])->list_lock);
+ spin_unlock(&(cachep->node[nodeid])->list_lock);
}
return 1;
}
#endif
/*
- * Allocates and initializes nodelists for a node on each slab cache, used for
- * either memory or cpu hotplug. If memory is being hot-added, the kmem_list3
+ * Allocates and initializes node for a node on each slab cache, used for
+ * either memory or cpu hotplug. If memory is being hot-added, the kmem_cache_node
* will be allocated off-node since memory is not yet online for the new node.
- * When hotplugging memory or a cpu, existing nodelists are not replaced if
+ * When hotplugging memory or a cpu, existing node are not replaced if
* already in use.
*
* Must hold slab_mutex.
*/
-static int init_cache_nodelists_node(int node)
+static int init_cache_node_node(int node)
{
struct kmem_cache *cachep;
- struct kmem_list3 *l3;
- const int memsize = sizeof(struct kmem_list3);
+ struct kmem_cache_node *n;
+ const int memsize = sizeof(struct kmem_cache_node);
list_for_each_entry(cachep, &slab_caches, list) {
/*
@@ -1253,12 +1155,12 @@ static int init_cache_nodelists_node(int node)
* begin anything. Make sure some other cpu on this
* node has not already allocated this
*/
- if (!cachep->nodelists[node]) {
- l3 = kmalloc_node(memsize, GFP_KERNEL, node);
- if (!l3)
+ if (!cachep->node[node]) {
+ n = kmalloc_node(memsize, GFP_KERNEL, node);
+ if (!n)
return -ENOMEM;
- kmem_list3_init(l3);
- l3->next_reap = jiffies + REAPTIMEOUT_LIST3 +
+ kmem_cache_node_init(n);
+ n->next_reap = jiffies + REAPTIMEOUT_LIST3 +
((unsigned long)cachep) % REAPTIMEOUT_LIST3;
/*
@@ -1266,14 +1168,14 @@ static int init_cache_nodelists_node(int node)
* go. slab_mutex is sufficient
* protection here.
*/
- cachep->nodelists[node] = l3;
+ cachep->node[node] = n;
}
- spin_lock_irq(&cachep->nodelists[node]->list_lock);
- cachep->nodelists[node]->free_limit =
+ spin_lock_irq(&cachep->node[node]->list_lock);
+ cachep->node[node]->free_limit =
(1 + nr_cpus_node(node)) *
cachep->batchcount + cachep->num;
- spin_unlock_irq(&cachep->nodelists[node]->list_lock);
+ spin_unlock_irq(&cachep->node[node]->list_lock);
}
return 0;
}
@@ -1281,7 +1183,7 @@ static int init_cache_nodelists_node(int node)
static void __cpuinit cpuup_canceled(long cpu)
{
struct kmem_cache *cachep;
- struct kmem_list3 *l3 = NULL;
+ struct kmem_cache_node *n = NULL;
int node = cpu_to_mem(cpu);
const struct cpumask *mask = cpumask_of_node(node);
@@ -1293,34 +1195,34 @@ static void __cpuinit cpuup_canceled(long cpu)
/* cpu is dead; no one can alloc from it. */
nc = cachep->array[cpu];
cachep->array[cpu] = NULL;
- l3 = cachep->nodelists[node];
+ n = cachep->node[node];
- if (!l3)
+ if (!n)
goto free_array_cache;
- spin_lock_irq(&l3->list_lock);
+ spin_lock_irq(&n->list_lock);
- /* Free limit for this kmem_list3 */
- l3->free_limit -= cachep->batchcount;
+ /* Free limit for this kmem_cache_node */
+ n->free_limit -= cachep->batchcount;
if (nc)
free_block(cachep, nc->entry, nc->avail, node);
if (!cpumask_empty(mask)) {
- spin_unlock_irq(&l3->list_lock);
+ spin_unlock_irq(&n->list_lock);
goto free_array_cache;
}
- shared = l3->shared;
+ shared = n->shared;
if (shared) {
free_block(cachep, shared->entry,
shared->avail, node);
- l3->shared = NULL;
+ n->shared = NULL;
}
- alien = l3->alien;
- l3->alien = NULL;
+ alien = n->alien;
+ n->alien = NULL;
- spin_unlock_irq(&l3->list_lock);
+ spin_unlock_irq(&n->list_lock);
kfree(shared);
if (alien) {
@@ -1336,17 +1238,17 @@ free_array_cache:
* shrink each nodelist to its limit.
*/
list_for_each_entry(cachep, &slab_caches, list) {
- l3 = cachep->nodelists[node];
- if (!l3)
+ n = cachep->node[node];
+ if (!n)
continue;
- drain_freelist(cachep, l3, l3->free_objects);
+ drain_freelist(cachep, n, n->free_objects);
}
}
static int __cpuinit cpuup_prepare(long cpu)
{
struct kmem_cache *cachep;
- struct kmem_list3 *l3 = NULL;
+ struct kmem_cache_node *n = NULL;
int node = cpu_to_mem(cpu);
int err;
@@ -1354,9 +1256,9 @@ static int __cpuinit cpuup_prepare(long cpu)
* We need to do this right in the beginning since
* alloc_arraycache's are going to use this list.
* kmalloc_node allows us to add the slab to the right
- * kmem_list3 and not this cpu's kmem_list3
+ * kmem_cache_node and not this cpu's kmem_cache_node
*/
- err = init_cache_nodelists_node(node);
+ err = init_cache_node_node(node);
if (err < 0)
goto bad;
@@ -1391,25 +1293,25 @@ static int __cpuinit cpuup_prepare(long cpu)
}
}
cachep->array[cpu] = nc;
- l3 = cachep->nodelists[node];
- BUG_ON(!l3);
+ n = cachep->node[node];
+ BUG_ON(!n);
- spin_lock_irq(&l3->list_lock);
- if (!l3->shared) {
+ spin_lock_irq(&n->list_lock);
+ if (!n->shared) {
/*
* We are serialised from CPU_DEAD or
* CPU_UP_CANCELLED by the cpucontrol lock
*/
- l3->shared = shared;
+ n->shared = shared;
shared = NULL;
}
#ifdef CONFIG_NUMA
- if (!l3->alien) {
- l3->alien = alien;
+ if (!n->alien) {
+ n->alien = alien;
alien = NULL;
}
#endif
- spin_unlock_irq(&l3->list_lock);
+ spin_unlock_irq(&n->list_lock);
kfree(shared);
free_alien_cache(alien);
if (cachep->flags & SLAB_DEBUG_OBJECTS)
@@ -1464,9 +1366,9 @@ static int __cpuinit cpuup_callback(struct notifier_block *nfb,
case CPU_DEAD_FROZEN:
/*
* Even if all the cpus of a node are down, we don't free the
- * kmem_list3 of any cache. This to avoid a race between
+ * kmem_cache_node of any cache. This to avoid a race between
* cpu_down, and a kmalloc allocation from another cpu for
- * memory from the node of the cpu going down. The list3
+ * memory from the node of the cpu going down. The node
* structure is usually allocated from kmem_cache_create() and
* gets destroyed at kmem_cache_destroy().
*/
@@ -1494,22 +1396,22 @@ static struct notifier_block __cpuinitdata cpucache_notifier = {
*
* Must hold slab_mutex.
*/
-static int __meminit drain_cache_nodelists_node(int node)
+static int __meminit drain_cache_node_node(int node)
{
struct kmem_cache *cachep;
int ret = 0;
list_for_each_entry(cachep, &slab_caches, list) {
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
- l3 = cachep->nodelists[node];
- if (!l3)
+ n = cachep->node[node];
+ if (!n)
continue;
- drain_freelist(cachep, l3, l3->free_objects);
+ drain_freelist(cachep, n, n->free_objects);
- if (!list_empty(&l3->slabs_full) ||
- !list_empty(&l3->slabs_partial)) {
+ if (!list_empty(&n->slabs_full) ||
+ !list_empty(&n->slabs_partial)) {
ret = -EBUSY;
break;
}
@@ -1531,12 +1433,12 @@ static int __meminit slab_memory_callback(struct notifier_block *self,
switch (action) {
case MEM_GOING_ONLINE:
mutex_lock(&slab_mutex);
- ret = init_cache_nodelists_node(nid);
+ ret = init_cache_node_node(nid);
mutex_unlock(&slab_mutex);
break;
case MEM_GOING_OFFLINE:
mutex_lock(&slab_mutex);
- ret = drain_cache_nodelists_node(nid);
+ ret = drain_cache_node_node(nid);
mutex_unlock(&slab_mutex);
break;
case MEM_ONLINE:
@@ -1551,37 +1453,37 @@ out:
#endif /* CONFIG_NUMA && CONFIG_MEMORY_HOTPLUG */
/*
- * swap the static kmem_list3 with kmalloced memory
+ * swap the static kmem_cache_node with kmalloced memory
*/
-static void __init init_list(struct kmem_cache *cachep, struct kmem_list3 *list,
+static void __init init_list(struct kmem_cache *cachep, struct kmem_cache_node *list,
int nodeid)
{
- struct kmem_list3 *ptr;
+ struct kmem_cache_node *ptr;
- ptr = kmalloc_node(sizeof(struct kmem_list3), GFP_NOWAIT, nodeid);
+ ptr = kmalloc_node(sizeof(struct kmem_cache_node), GFP_NOWAIT, nodeid);
BUG_ON(!ptr);
- memcpy(ptr, list, sizeof(struct kmem_list3));
+ memcpy(ptr, list, sizeof(struct kmem_cache_node));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->list_lock);
MAKE_ALL_LISTS(cachep, ptr, nodeid);
- cachep->nodelists[nodeid] = ptr;
+ cachep->node[nodeid] = ptr;
}
/*
- * For setting up all the kmem_list3s for cache whose buffer_size is same as
- * size of kmem_list3.
+ * For setting up all the kmem_cache_node for cache whose buffer_size is same as
+ * size of kmem_cache_node.
*/
-static void __init set_up_list3s(struct kmem_cache *cachep, int index)
+static void __init set_up_node(struct kmem_cache *cachep, int index)
{
int node;
for_each_online_node(node) {
- cachep->nodelists[node] = &initkmem_list3[index + node];
- cachep->nodelists[node]->next_reap = jiffies +
+ cachep->node[node] = &init_kmem_cache_node[index + node];
+ cachep->node[node]->next_reap = jiffies +
REAPTIMEOUT_LIST3 +
((unsigned long)cachep) % REAPTIMEOUT_LIST3;
}
@@ -1589,11 +1491,11 @@ static void __init set_up_list3s(struct kmem_cache *cachep, int index)
/*
* The memory after the last cpu cache pointer is used for the
- * the nodelists pointer.
+ * the node pointer.
*/
-static void setup_nodelists_pointer(struct kmem_cache *cachep)
+static void setup_node_pointer(struct kmem_cache *cachep)
{
- cachep->nodelists = (struct kmem_list3 **)&cachep->array[nr_cpu_ids];
+ cachep->node = (struct kmem_cache_node **)&cachep->array[nr_cpu_ids];
}
/*
@@ -1602,20 +1504,18 @@ static void setup_nodelists_pointer(struct kmem_cache *cachep)
*/
void __init kmem_cache_init(void)
{
- struct cache_sizes *sizes;
- struct cache_names *names;
int i;
kmem_cache = &kmem_cache_boot;
- setup_nodelists_pointer(kmem_cache);
+ setup_node_pointer(kmem_cache);
if (num_possible_nodes() == 1)
use_alien_caches = 0;
for (i = 0; i < NUM_INIT_LISTS; i++)
- kmem_list3_init(&initkmem_list3[i]);
+ kmem_cache_node_init(&init_kmem_cache_node[i]);
- set_up_list3s(kmem_cache, CACHE_CACHE);
+ set_up_node(kmem_cache, CACHE_CACHE);
/*
* Fragmentation resistance on low memory - only use bigger
@@ -1631,7 +1531,7 @@ void __init kmem_cache_init(void)
* kmem_cache structures of all caches, except kmem_cache itself:
* kmem_cache is statically allocated.
* Initially an __init data area is used for the head array and the
- * kmem_list3 structures, it's replaced with a kmalloc allocated
+ * kmem_cache_node structures, it's replaced with a kmalloc allocated
* array at the end of the bootstrap.
* 2) Create the first kmalloc cache.
* The struct kmem_cache for the new cache is allocated normally.
@@ -1640,7 +1540,7 @@ void __init kmem_cache_init(void)
* head arrays.
* 4) Replace the __init data head arrays for kmem_cache and the first
* kmalloc cache with kmalloc allocated arrays.
- * 5) Replace the __init data for kmem_list3 for kmem_cache and
+ * 5) Replace the __init data for kmem_cache_node for kmem_cache and
* the other cache's with kmalloc allocated memory.
* 6) Resize the head arrays of the kmalloc caches to their final sizes.
*/
@@ -1652,50 +1552,28 @@ void __init kmem_cache_init(void)
*/
create_boot_cache(kmem_cache, "kmem_cache",
offsetof(struct kmem_cache, array[nr_cpu_ids]) +
- nr_node_ids * sizeof(struct kmem_list3 *),
+ nr_node_ids * sizeof(struct kmem_cache_node *),
SLAB_HWCACHE_ALIGN);
list_add(&kmem_cache->list, &slab_caches);
/* 2+3) create the kmalloc caches */
- sizes = malloc_sizes;
- names = cache_names;
/*
* Initialize the caches that provide memory for the array cache and the
- * kmem_list3 structures first. Without this, further allocations will
+ * kmem_cache_node structures first. Without this, further allocations will
* bug.
*/
- sizes[INDEX_AC].cs_cachep = create_kmalloc_cache(names[INDEX_AC].name,
- sizes[INDEX_AC].cs_size, ARCH_KMALLOC_FLAGS);
+ kmalloc_caches[INDEX_AC] = create_kmalloc_cache("kmalloc-ac",
+ kmalloc_size(INDEX_AC), ARCH_KMALLOC_FLAGS);
- if (INDEX_AC != INDEX_L3)
- sizes[INDEX_L3].cs_cachep =
- create_kmalloc_cache(names[INDEX_L3].name,
- sizes[INDEX_L3].cs_size, ARCH_KMALLOC_FLAGS);
+ if (INDEX_AC != INDEX_NODE)
+ kmalloc_caches[INDEX_NODE] =
+ create_kmalloc_cache("kmalloc-node",
+ kmalloc_size(INDEX_NODE), ARCH_KMALLOC_FLAGS);
slab_early_init = 0;
- while (sizes->cs_size != ULONG_MAX) {
- /*
- * For performance, all the general caches are L1 aligned.
- * This should be particularly beneficial on SMP boxes, as it
- * eliminates "false sharing".
- * Note for systems short on memory removing the alignment will
- * allow tighter packing of the smaller caches.
- */
- if (!sizes->cs_cachep)
- sizes->cs_cachep = create_kmalloc_cache(names->name,
- sizes->cs_size, ARCH_KMALLOC_FLAGS);
-
-#ifdef CONFIG_ZONE_DMA
- sizes->cs_dmacachep = create_kmalloc_cache(
- names->name_dma, sizes->cs_size,
- SLAB_CACHE_DMA|ARCH_KMALLOC_FLAGS);
-#endif
- sizes++;
- names++;
- }
/* 4) Replace the bootstrap head arrays */
{
struct array_cache *ptr;
@@ -1713,36 +1591,35 @@ void __init kmem_cache_init(void)
ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);
- BUG_ON(cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep)
+ BUG_ON(cpu_cache_get(kmalloc_caches[INDEX_AC])
!= &initarray_generic.cache);
- memcpy(ptr, cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep),
+ memcpy(ptr, cpu_cache_get(kmalloc_caches[INDEX_AC]),
sizeof(struct arraycache_init));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->lock);
- malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] =
- ptr;
+ kmalloc_caches[INDEX_AC]->array[smp_processor_id()] = ptr;
}
- /* 5) Replace the bootstrap kmem_list3's */
+ /* 5) Replace the bootstrap kmem_cache_node */
{
int nid;
for_each_online_node(nid) {
- init_list(kmem_cache, &initkmem_list3[CACHE_CACHE + nid], nid);
+ init_list(kmem_cache, &init_kmem_cache_node[CACHE_CACHE + nid], nid);
- init_list(malloc_sizes[INDEX_AC].cs_cachep,
- &initkmem_list3[SIZE_AC + nid], nid);
+ init_list(kmalloc_caches[INDEX_AC],
+ &init_kmem_cache_node[SIZE_AC + nid], nid);
- if (INDEX_AC != INDEX_L3) {
- init_list(malloc_sizes[INDEX_L3].cs_cachep,
- &initkmem_list3[SIZE_L3 + nid], nid);
+ if (INDEX_AC != INDEX_NODE) {
+ init_list(kmalloc_caches[INDEX_NODE],
+ &init_kmem_cache_node[SIZE_NODE + nid], nid);
}
}
}
- slab_state = UP;
+ create_kmalloc_caches(ARCH_KMALLOC_FLAGS);
}
void __init kmem_cache_init_late(void)
@@ -1773,7 +1650,7 @@ void __init kmem_cache_init_late(void)
#ifdef CONFIG_NUMA
/*
* Register a memory hotplug callback that initializes and frees
- * nodelists.
+ * node.
*/
hotplug_memory_notifier(slab_memory_callback, SLAB_CALLBACK_PRI);
#endif
@@ -1803,7 +1680,7 @@ __initcall(cpucache_init);
static noinline void
slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid)
{
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
struct slab *slabp;
unsigned long flags;
int node;
@@ -1818,24 +1695,24 @@ slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid)
unsigned long active_objs = 0, num_objs = 0, free_objects = 0;
unsigned long active_slabs = 0, num_slabs = 0;
- l3 = cachep->nodelists[node];
- if (!l3)
+ n = cachep->node[node];
+ if (!n)
continue;
- spin_lock_irqsave(&l3->list_lock, flags);
- list_for_each_entry(slabp, &l3->slabs_full, list) {
+ spin_lock_irqsave(&n->list_lock, flags);
+ list_for_each_entry(slabp, &n->slabs_full, list) {
active_objs += cachep->num;
active_slabs++;
}
- list_for_each_entry(slabp, &l3->slabs_partial, list) {
+ list_for_each_entry(slabp, &n->slabs_partial, list) {
active_objs += slabp->inuse;
active_slabs++;
}
- list_for_each_entry(slabp, &l3->slabs_free, list)
+ list_for_each_entry(slabp, &n->slabs_free, list)
num_slabs++;
- free_objects += l3->free_objects;
- spin_unlock_irqrestore(&l3->list_lock, flags);
+ free_objects += n->free_objects;
+ spin_unlock_irqrestore(&n->list_lock, flags);
num_slabs += active_slabs;
num_objs = num_slabs * cachep->num;
@@ -2258,7 +2135,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
if (slab_state == DOWN) {
/*
* Note: Creation of first cache (kmem_cache).
- * The setup_list3s is taken care
+ * The setup_node is taken care
* of by the caller of __kmem_cache_create
*/
cachep->array[smp_processor_id()] = &initarray_generic.cache;
@@ -2272,13 +2149,13 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
cachep->array[smp_processor_id()] = &initarray_generic.cache;
/*
- * If the cache that's used by kmalloc(sizeof(kmem_list3)) is
- * the second cache, then we need to set up all its list3s,
+ * If the cache that's used by kmalloc(sizeof(kmem_cache_node)) is
+ * the second cache, then we need to set up all its node/,
* otherwise the creation of further caches will BUG().
*/
- set_up_list3s(cachep, SIZE_AC);
- if (INDEX_AC == INDEX_L3)
- slab_state = PARTIAL_L3;
+ set_up_node(cachep, SIZE_AC);
+ if (INDEX_AC == INDEX_NODE)
+ slab_state = PARTIAL_NODE;
else
slab_state = PARTIAL_ARRAYCACHE;
} else {
@@ -2287,20 +2164,20 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
kmalloc(sizeof(struct arraycache_init), gfp);
if (slab_state == PARTIAL_ARRAYCACHE) {
- set_up_list3s(cachep, SIZE_L3);
- slab_state = PARTIAL_L3;
+ set_up_node(cachep, SIZE_NODE);
+ slab_state = PARTIAL_NODE;
} else {
int node;
for_each_online_node(node) {
- cachep->nodelists[node] =
- kmalloc_node(sizeof(struct kmem_list3),
+ cachep->node[node] =
+ kmalloc_node(sizeof(struct kmem_cache_node),
gfp, node);
- BUG_ON(!cachep->nodelists[node]);
- kmem_list3_init(cachep->nodelists[node]);
+ BUG_ON(!cachep->node[node]);
+ kmem_cache_node_init(cachep->node[node]);
}
}
}
- cachep->nodelists[numa_mem_id()]->next_reap =
+ cachep->node[numa_mem_id()]->next_reap =
jiffies + REAPTIMEOUT_LIST3 +
((unsigned long)cachep) % REAPTIMEOUT_LIST3;
@@ -2403,7 +2280,7 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags)
else
gfp = GFP_NOWAIT;
- setup_nodelists_pointer(cachep);
+ setup_node_pointer(cachep);
#if DEBUG
/*
@@ -2426,7 +2303,7 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags)
size += BYTES_PER_WORD;
}
#if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC)
- if (size >= malloc_sizes[INDEX_L3 + 1].cs_size
+ if (size >= kmalloc_size(INDEX_NODE + 1)
&& cachep->object_size > cache_line_size()
&& ALIGN(size, cachep->align) < PAGE_SIZE) {
cachep->obj_offset += PAGE_SIZE - ALIGN(size, cachep->align);
@@ -2497,7 +2374,7 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags)
cachep->reciprocal_buffer_size = reciprocal_value(size);
if (flags & CFLGS_OFF_SLAB) {
- cachep->slabp_cache = kmem_find_general_cachep(slab_size, 0u);
+ cachep->slabp_cache = kmalloc_slab(slab_size, 0u);
/*
* This is a possibility for one of the malloc_sizes caches.
* But since we go off slab only for object size greater than
@@ -2543,7 +2420,7 @@ static void check_spinlock_acquired(struct kmem_cache *cachep)
{
#ifdef CONFIG_SMP
check_irq_off();
- assert_spin_locked(&cachep->nodelists[numa_mem_id()]->list_lock);
+ assert_spin_locked(&cachep->node[numa_mem_id()]->list_lock);
#endif
}
@@ -2551,7 +2428,7 @@ static void check_spinlock_acquired_node(struct kmem_cache *cachep, int node)
{
#ifdef CONFIG_SMP
check_irq_off();
- assert_spin_locked(&cachep->nodelists[node]->list_lock);
+ assert_spin_locked(&cachep->node[node]->list_lock);
#endif
}
@@ -2562,7 +2439,7 @@ static void check_spinlock_acquired_node(struct kmem_cache *cachep, int node)
#define check_spinlock_acquired_node(x, y) do { } while(0)
#endif
-static void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3,
+static void drain_array(struct kmem_cache *cachep, struct kmem_cache_node *n,
struct array_cache *ac,
int force, int node);
@@ -2574,29 +2451,29 @@ static void do_drain(void *arg)
check_irq_off();
ac = cpu_cache_get(cachep);
- spin_lock(&cachep->nodelists[node]->list_lock);
+ spin_lock(&cachep->node[node]->list_lock);
free_block(cachep, ac->entry, ac->avail, node);
- spin_unlock(&cachep->nodelists[node]->list_lock);
+ spin_unlock(&cachep->node[node]->list_lock);
ac->avail = 0;
}
static void drain_cpu_caches(struct kmem_cache *cachep)
{
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
int node;
on_each_cpu(do_drain, cachep, 1);
check_irq_on();
for_each_online_node(node) {
- l3 = cachep->nodelists[node];
- if (l3 && l3->alien)
- drain_alien_cache(cachep, l3->alien);
+ n = cachep->node[node];
+ if (n && n->alien)
+ drain_alien_cache(cachep, n->alien);
}
for_each_online_node(node) {
- l3 = cachep->nodelists[node];
- if (l3)
- drain_array(cachep, l3, l3->shared, 1, node);
+ n = cachep->node[node];
+ if (n)
+ drain_array(cachep, n, n->shared, 1, node);
}
}
@@ -2607,19 +2484,19 @@ static void drain_cpu_caches(struct kmem_cache *cachep)
* Returns the actual number of slabs released.
*/
static int drain_freelist(struct kmem_cache *cache,
- struct kmem_list3 *l3, int tofree)
+ struct kmem_cache_node *n, int tofree)
{
struct list_head *p;
int nr_freed;
struct slab *slabp;
nr_freed = 0;
- while (nr_freed < tofree && !list_empty(&l3->slabs_free)) {
+ while (nr_freed < tofree && !list_empty(&n->slabs_free)) {
- spin_lock_irq(&l3->list_lock);
- p = l3->slabs_free.prev;
- if (p == &l3->slabs_free) {
- spin_unlock_irq(&l3->list_lock);
+ spin_lock_irq(&n->list_lock);
+ p = n->slabs_free.prev;
+ if (p == &n->slabs_free) {
+ spin_unlock_irq(&n->list_lock);
goto out;
}
@@ -2632,8 +2509,8 @@ static int drain_freelist(struct kmem_cache *cache,
* Safe to drop the lock. The slab is no longer linked
* to the cache.
*/
- l3->free_objects -= cache->num;
- spin_unlock_irq(&l3->list_lock);
+ n->free_objects -= cache->num;
+ spin_unlock_irq(&n->list_lock);
slab_destroy(cache, slabp);
nr_freed++;
}
@@ -2645,20 +2522,20 @@ out:
static int __cache_shrink(struct kmem_cache *cachep)
{
int ret = 0, i = 0;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
drain_cpu_caches(cachep);
check_irq_on();
for_each_online_node(i) {
- l3 = cachep->nodelists[i];
- if (!l3)
+ n = cachep->node[i];
+ if (!n)
continue;
- drain_freelist(cachep, l3, l3->free_objects);
+ drain_freelist(cachep, n, n->free_objects);
- ret += !list_empty(&l3->slabs_full) ||
- !list_empty(&l3->slabs_partial);
+ ret += !list_empty(&n->slabs_full) ||
+ !list_empty(&n->slabs_partial);
}
return (ret ? 1 : 0);
}
@@ -2687,7 +2564,7 @@ EXPORT_SYMBOL(kmem_cache_shrink);
int __kmem_cache_shutdown(struct kmem_cache *cachep)
{
int i;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
int rc = __cache_shrink(cachep);
if (rc)
@@ -2696,13 +2573,13 @@ int __kmem_cache_shutdown(struct kmem_cache *cachep)
for_each_online_cpu(i)
kfree(cachep->array[i]);
- /* NUMA: free the list3 structures */
+ /* NUMA: free the node structures */
for_each_online_node(i) {
- l3 = cachep->nodelists[i];
- if (l3) {
- kfree(l3->shared);
- free_alien_cache(l3->alien);
- kfree(l3);
+ n = cachep->node[i];
+ if (n) {
+ kfree(n->shared);
+ free_alien_cache(n->alien);
+ kfree(n);
}
}
return 0;
@@ -2884,7 +2761,7 @@ static int cache_grow(struct kmem_cache *cachep,
struct slab *slabp;
size_t offset;
gfp_t local_flags;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
/*
* Be lazy and only check for valid flags here, keeping it out of the
@@ -2893,17 +2770,17 @@ static int cache_grow(struct kmem_cache *cachep,
BUG_ON(flags & GFP_SLAB_BUG_MASK);
local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
- /* Take the l3 list lock to change the colour_next on this node */
+ /* Take the node list lock to change the colour_next on this node */
check_irq_off();
- l3 = cachep->nodelists[nodeid];
- spin_lock(&l3->list_lock);
+ n = cachep->node[nodeid];
+ spin_lock(&n->list_lock);
/* Get colour for the slab, and cal the next value. */
- offset = l3->colour_next;
- l3->colour_next++;
- if (l3->colour_next >= cachep->colour)
- l3->colour_next = 0;
- spin_unlock(&l3->list_lock);
+ offset = n->colour_next;
+ n->colour_next++;
+ if (n->colour_next >= cachep->colour)
+ n->colour_next = 0;
+ spin_unlock(&n->list_lock);
offset *= cachep->colour_off;
@@ -2940,13 +2817,13 @@ static int cache_grow(struct kmem_cache *cachep,
if (local_flags & __GFP_WAIT)
local_irq_disable();
check_irq_off();
- spin_lock(&l3->list_lock);
+ spin_lock(&n->list_lock);
/* Make slab active. */
- list_add_tail(&slabp->list, &(l3->slabs_free));
+ list_add_tail(&slabp->list, &(n->slabs_free));
STATS_INC_GROWN(cachep);
- l3->free_objects += cachep->num;
- spin_unlock(&l3->list_lock);
+ n->free_objects += cachep->num;
+ spin_unlock(&n->list_lock);
return 1;
opps1:
kmem_freepages(cachep, objp);
@@ -3074,7 +2951,7 @@ static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags,
bool force_refill)
{
int batchcount;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
struct array_cache *ac;
int node;
@@ -3093,14 +2970,14 @@ retry:
*/
batchcount = BATCHREFILL_LIMIT;
}
- l3 = cachep->nodelists[node];
+ n = cachep->node[node];
- BUG_ON(ac->avail > 0 || !l3);
- spin_lock(&l3->list_lock);
+ BUG_ON(ac->avail > 0 || !n);
+ spin_lock(&n->list_lock);
/* See if we can refill from the shared array */
- if (l3->shared && transfer_objects(ac, l3->shared, batchcount)) {
- l3->shared->touched = 1;
+ if (n->shared && transfer_objects(ac, n->shared, batchcount)) {
+ n->shared->touched = 1;
goto alloc_done;
}
@@ -3108,11 +2985,11 @@ retry:
struct list_head *entry;
struct slab *slabp;
/* Get slab alloc is to come from. */
- entry = l3->slabs_partial.next;
- if (entry == &l3->slabs_partial) {
- l3->free_touched = 1;
- entry = l3->slabs_free.next;
- if (entry == &l3->slabs_free)
+ entry = n->slabs_partial.next;
+ if (entry == &n->slabs_partial) {
+ n->free_touched = 1;
+ entry = n->slabs_free.next;
+ if (entry == &n->slabs_free)
goto must_grow;
}
@@ -3140,15 +3017,15 @@ retry:
/* move slabp to correct slabp list: */
list_del(&slabp->list);
if (slabp->free == BUFCTL_END)
- list_add(&slabp->list, &l3->slabs_full);
+ list_add(&slabp->list, &n->slabs_full);
else
- list_add(&slabp->list, &l3->slabs_partial);
+ list_add(&slabp->list, &n->slabs_partial);
}
must_grow:
- l3->free_objects -= ac->avail;
+ n->free_objects -= ac->avail;
alloc_done:
- spin_unlock(&l3->list_lock);
+ spin_unlock(&n->list_lock);
if (unlikely(!ac->avail)) {
int x;
@@ -3315,7 +3192,7 @@ static void *alternate_node_alloc(struct kmem_cache *cachep, gfp_t flags)
/*
* Fallback function if there was no memory available and no objects on a
* certain node and fall back is permitted. First we scan all the
- * available nodelists for available objects. If that fails then we
+ * available node for available objects. If that fails then we
* perform an allocation without specifying a node. This allows the page
* allocator to do its reclaim / fallback magic. We then insert the
* slab into the proper nodelist and then allocate from it.
@@ -3349,8 +3226,8 @@ retry:
nid = zone_to_nid(zone);
if (cpuset_zone_allowed_hardwall(zone, flags) &&
- cache->nodelists[nid] &&
- cache->nodelists[nid]->free_objects) {
+ cache->node[nid] &&
+ cache->node[nid]->free_objects) {
obj = ____cache_alloc_node(cache,
flags | GFP_THISNODE, nid);
if (obj)
@@ -3406,21 +3283,22 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags,
{
struct list_head *entry;
struct slab *slabp;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
void *obj;
int x;
- l3 = cachep->nodelists[nodeid];
- BUG_ON(!l3);
+ VM_BUG_ON(nodeid > num_online_nodes());
+ n = cachep->node[nodeid];
+ BUG_ON(!n);
retry:
check_irq_off();
- spin_lock(&l3->list_lock);
- entry = l3->slabs_partial.next;
- if (entry == &l3->slabs_partial) {
- l3->free_touched = 1;
- entry = l3->slabs_free.next;
- if (entry == &l3->slabs_free)
+ spin_lock(&n->list_lock);
+ entry = n->slabs_partial.next;
+ if (entry == &n->slabs_partial) {
+ n->free_touched = 1;
+ entry = n->slabs_free.next;
+ if (entry == &n->slabs_free)
goto must_grow;
}
@@ -3436,20 +3314,20 @@ retry:
obj = slab_get_obj(cachep, slabp, nodeid);
check_slabp(cachep, slabp);
- l3->free_objects--;
+ n->free_objects--;
/* move slabp to correct slabp list: */
list_del(&slabp->list);
if (slabp->free == BUFCTL_END)
- list_add(&slabp->list, &l3->slabs_full);
+ list_add(&slabp->list, &n->slabs_full);
else
- list_add(&slabp->list, &l3->slabs_partial);
+ list_add(&slabp->list, &n->slabs_partial);
- spin_unlock(&l3->list_lock);
+ spin_unlock(&n->list_lock);
goto done;
must_grow:
- spin_unlock(&l3->list_lock);
+ spin_unlock(&n->list_lock);
x = cache_grow(cachep, flags | GFP_THISNODE, nodeid, NULL);
if (x)
goto retry;
@@ -3495,7 +3373,7 @@ slab_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid,
if (nodeid == NUMA_NO_NODE)
nodeid = slab_node;
- if (unlikely(!cachep->nodelists[nodeid])) {
+ if (unlikely(!cachep->node[nodeid])) {
/* Node not bootstrapped yet */
ptr = fallback_alloc(cachep, flags);
goto out;
@@ -3601,7 +3479,7 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
int node)
{
int i;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
for (i = 0; i < nr_objects; i++) {
void *objp;
@@ -3611,19 +3489,19 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
objp = objpp[i];
slabp = virt_to_slab(objp);
- l3 = cachep->nodelists[node];
+ n = cachep->node[node];
list_del(&slabp->list);
check_spinlock_acquired_node(cachep, node);
check_slabp(cachep, slabp);
slab_put_obj(cachep, slabp, objp, node);
STATS_DEC_ACTIVE(cachep);
- l3->free_objects++;
+ n->free_objects++;
check_slabp(cachep, slabp);
/* fixup slab chains */
if (slabp->inuse == 0) {
- if (l3->free_objects > l3->free_limit) {
- l3->free_objects -= cachep->num;
+ if (n->free_objects > n->free_limit) {
+ n->free_objects -= cachep->num;
/* No need to drop any previously held
* lock here, even if we have a off-slab slab
* descriptor it is guaranteed to come from
@@ -3632,14 +3510,14 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
*/
slab_destroy(cachep, slabp);
} else {
- list_add(&slabp->list, &l3->slabs_free);
+ list_add(&slabp->list, &n->slabs_free);
}
} else {
/* Unconditionally move a slab to the end of the
* partial list on free - maximum time for the
* other objects to be freed, too.
*/
- list_add_tail(&slabp->list, &l3->slabs_partial);
+ list_add_tail(&slabp->list, &n->slabs_partial);
}
}
}
@@ -3647,7 +3525,7 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
{
int batchcount;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
int node = numa_mem_id();
batchcount = ac->batchcount;
@@ -3655,10 +3533,10 @@ static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
BUG_ON(!batchcount || batchcount > ac->avail);
#endif
check_irq_off();
- l3 = cachep->nodelists[node];
- spin_lock(&l3->list_lock);
- if (l3->shared) {
- struct array_cache *shared_array = l3->shared;
+ n = cachep->node[node];
+ spin_lock(&n->list_lock);
+ if (n->shared) {
+ struct array_cache *shared_array = n->shared;
int max = shared_array->limit - shared_array->avail;
if (max) {
if (batchcount > max)
@@ -3677,8 +3555,8 @@ free_done:
int i = 0;
struct list_head *p;
- p = l3->slabs_free.next;
- while (p != &(l3->slabs_free)) {
+ p = n->slabs_free.next;
+ while (p != &(n->slabs_free)) {
struct slab *slabp;
slabp = list_entry(p, struct slab, list);
@@ -3690,7 +3568,7 @@ free_done:
STATS_SET_FREEABLE(cachep, i);
}
#endif
- spin_unlock(&l3->list_lock);
+ spin_unlock(&n->list_lock);
ac->avail -= batchcount;
memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail);
}
@@ -3800,7 +3678,7 @@ __do_kmalloc_node(size_t size, gfp_t flags, int node, unsigned long caller)
{
struct kmem_cache *cachep;
- cachep = kmem_find_general_cachep(size, flags);
+ cachep = kmalloc_slab(size, flags);
if (unlikely(ZERO_OR_NULL_PTR(cachep)))
return cachep;
return kmem_cache_alloc_node_trace(cachep, flags, node, size);
@@ -3845,7 +3723,7 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
* Then kmalloc uses the uninlined functions instead of the inline
* functions.
*/
- cachep = __find_general_cachep(size, flags);
+ cachep = kmalloc_slab(size, flags);
if (unlikely(ZERO_OR_NULL_PTR(cachep)))
return cachep;
ret = slab_alloc(cachep, flags, caller);
@@ -3934,12 +3812,12 @@ void kfree(const void *objp)
EXPORT_SYMBOL(kfree);
/*
- * This initializes kmem_list3 or resizes various caches for all nodes.
+ * This initializes kmem_cache_node or resizes various caches for all nodes.
*/
static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp)
{
int node;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
struct array_cache *new_shared;
struct array_cache **new_alien = NULL;
@@ -3962,43 +3840,43 @@ static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp)
}
}
- l3 = cachep->nodelists[node];
- if (l3) {
- struct array_cache *shared = l3->shared;
+ n = cachep->node[node];
+ if (n) {
+ struct array_cache *shared = n->shared;
- spin_lock_irq(&l3->list_lock);
+ spin_lock_irq(&n->list_lock);
if (shared)
free_block(cachep, shared->entry,
shared->avail, node);
- l3->shared = new_shared;
- if (!l3->alien) {
- l3->alien = new_alien;
+ n->shared = new_shared;
+ if (!n->alien) {
+ n->alien = new_alien;
new_alien = NULL;
}
- l3->free_limit = (1 + nr_cpus_node(node)) *
+ n->free_limit = (1 + nr_cpus_node(node)) *
cachep->batchcount + cachep->num;
- spin_unlock_irq(&l3->list_lock);
+ spin_unlock_irq(&n->list_lock);
kfree(shared);
free_alien_cache(new_alien);
continue;
}
- l3 = kmalloc_node(sizeof(struct kmem_list3), gfp, node);
- if (!l3) {
+ n = kmalloc_node(sizeof(struct kmem_cache_node), gfp, node);
+ if (!n) {
free_alien_cache(new_alien);
kfree(new_shared);
goto fail;
}
- kmem_list3_init(l3);
- l3->next_reap = jiffies + REAPTIMEOUT_LIST3 +
+ kmem_cache_node_init(n);
+ n->next_reap = jiffies + REAPTIMEOUT_LIST3 +
((unsigned long)cachep) % REAPTIMEOUT_LIST3;
- l3->shared = new_shared;
- l3->alien = new_alien;
- l3->free_limit = (1 + nr_cpus_node(node)) *
+ n->shared = new_shared;
+ n->alien = new_alien;
+ n->free_limit = (1 + nr_cpus_node(node)) *
cachep->batchcount + cachep->num;
- cachep->nodelists[node] = l3;
+ cachep->node[node] = n;
}
return 0;
@@ -4007,13 +3885,13 @@ fail:
/* Cache is not active yet. Roll back what we did */
node--;
while (node >= 0) {
- if (cachep->nodelists[node]) {
- l3 = cachep->nodelists[node];
+ if (cachep->node[node]) {
+ n = cachep->node[node];
- kfree(l3->shared);
- free_alien_cache(l3->alien);
- kfree(l3);
- cachep->nodelists[node] = NULL;
+ kfree(n->shared);
+ free_alien_cache(n->alien);
+ kfree(n);
+ cachep->node[node] = NULL;
}
node--;
}
@@ -4073,9 +3951,9 @@ static int __do_tune_cpucache(struct kmem_cache *cachep, int limit,
struct array_cache *ccold = new->new[i];
if (!ccold)
continue;
- spin_lock_irq(&cachep->nodelists[cpu_to_mem(i)]->list_lock);
+ spin_lock_irq(&cachep->node[cpu_to_mem(i)]->list_lock);
free_block(cachep, ccold->entry, ccold->avail, cpu_to_mem(i));
- spin_unlock_irq(&cachep->nodelists[cpu_to_mem(i)]->list_lock);
+ spin_unlock_irq(&cachep->node[cpu_to_mem(i)]->list_lock);
kfree(ccold);
}
kfree(new);
@@ -4176,11 +4054,11 @@ skip_setup:
}
/*
- * Drain an array if it contains any elements taking the l3 lock only if
- * necessary. Note that the l3 listlock also protects the array_cache
+ * Drain an array if it contains any elements taking the node lock only if
+ * necessary. Note that the node listlock also protects the array_cache
* if drain_array() is used on the shared array.
*/
-static void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3,
+static void drain_array(struct kmem_cache *cachep, struct kmem_cache_node *n,
struct array_cache *ac, int force, int node)
{
int tofree;
@@ -4190,7 +4068,7 @@ static void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3,
if (ac->touched && !force) {
ac->touched = 0;
} else {
- spin_lock_irq(&l3->list_lock);
+ spin_lock_irq(&n->list_lock);
if (ac->avail) {
tofree = force ? ac->avail : (ac->limit + 4) / 5;
if (tofree > ac->avail)
@@ -4200,7 +4078,7 @@ static void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3,
memmove(ac->entry, &(ac->entry[tofree]),
sizeof(void *) * ac->avail);
}
- spin_unlock_irq(&l3->list_lock);
+ spin_unlock_irq(&n->list_lock);
}
}
@@ -4219,7 +4097,7 @@ static void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3,
static void cache_reap(struct work_struct *w)
{
struct kmem_cache *searchp;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
int node = numa_mem_id();
struct delayed_work *work = to_delayed_work(w);
@@ -4231,33 +4109,33 @@ static void cache_reap(struct work_struct *w)
check_irq_on();
/*
- * We only take the l3 lock if absolutely necessary and we
+ * We only take the node lock if absolutely necessary and we
* have established with reasonable certainty that
* we can do some work if the lock was obtained.
*/
- l3 = searchp->nodelists[node];
+ n = searchp->node[node];
- reap_alien(searchp, l3);
+ reap_alien(searchp, n);
- drain_array(searchp, l3, cpu_cache_get(searchp), 0, node);
+ drain_array(searchp, n, cpu_cache_get(searchp), 0, node);
/*
* These are racy checks but it does not matter
* if we skip one check or scan twice.
*/
- if (time_after(l3->next_reap, jiffies))
+ if (time_after(n->next_reap, jiffies))
goto next;
- l3->next_reap = jiffies + REAPTIMEOUT_LIST3;
+ n->next_reap = jiffies + REAPTIMEOUT_LIST3;
- drain_array(searchp, l3, l3->shared, 0, node);
+ drain_array(searchp, n, n->shared, 0, node);
- if (l3->free_touched)
- l3->free_touched = 0;
+ if (n->free_touched)
+ n->free_touched = 0;
else {
int freed;
- freed = drain_freelist(searchp, l3, (l3->free_limit +
+ freed = drain_freelist(searchp, n, (n->free_limit +
5 * searchp->num - 1) / (5 * searchp->num));
STATS_ADD_REAPED(searchp, freed);
}
@@ -4283,25 +4161,25 @@ void get_slabinfo(struct kmem_cache *cachep, struct slabinfo *sinfo)
const char *name;
char *error = NULL;
int node;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
active_objs = 0;
num_slabs = 0;
for_each_online_node(node) {
- l3 = cachep->nodelists[node];
- if (!l3)
+ n = cachep->node[node];
+ if (!n)
continue;
check_irq_on();
- spin_lock_irq(&l3->list_lock);
+ spin_lock_irq(&n->list_lock);
- list_for_each_entry(slabp, &l3->slabs_full, list) {
+ list_for_each_entry(slabp, &n->slabs_full, list) {
if (slabp->inuse != cachep->num && !error)
error = "slabs_full accounting error";
active_objs += cachep->num;
active_slabs++;
}
- list_for_each_entry(slabp, &l3->slabs_partial, list) {
+ list_for_each_entry(slabp, &n->slabs_partial, list) {
if (slabp->inuse == cachep->num && !error)
error = "slabs_partial inuse accounting error";
if (!slabp->inuse && !error)
@@ -4309,16 +4187,16 @@ void get_slabinfo(struct kmem_cache *cachep, struct slabinfo *sinfo)
active_objs += slabp->inuse;
active_slabs++;
}
- list_for_each_entry(slabp, &l3->slabs_free, list) {
+ list_for_each_entry(slabp, &n->slabs_free, list) {
if (slabp->inuse && !error)
error = "slabs_free/inuse accounting error";
num_slabs++;
}
- free_objects += l3->free_objects;
- if (l3->shared)
- shared_avail += l3->shared->avail;
+ free_objects += n->free_objects;
+ if (n->shared)
+ shared_avail += n->shared->avail;
- spin_unlock_irq(&l3->list_lock);
+ spin_unlock_irq(&n->list_lock);
}
num_slabs += active_slabs;
num_objs = num_slabs * cachep->num;
@@ -4344,7 +4222,7 @@ void get_slabinfo(struct kmem_cache *cachep, struct slabinfo *sinfo)
void slabinfo_show_stats(struct seq_file *m, struct kmem_cache *cachep)
{
#if STATS
- { /* list3 stats */
+ { /* node stats */
unsigned long high = cachep->high_mark;
unsigned long allocs = cachep->num_allocations;
unsigned long grown = cachep->grown;
@@ -4497,9 +4375,9 @@ static int leaks_show(struct seq_file *m, void *p)
{
struct kmem_cache *cachep = list_entry(p, struct kmem_cache, list);
struct slab *slabp;
- struct kmem_list3 *l3;
+ struct kmem_cache_node *n;
const char *name;
- unsigned long *n = m->private;
+ unsigned long *x = m->private;
int node;
int i;
@@ -4510,43 +4388,43 @@ static int leaks_show(struct seq_file *m, void *p)
/* OK, we can do it */
- n[1] = 0;
+ x[1] = 0;
for_each_online_node(node) {
- l3 = cachep->nodelists[node];
- if (!l3)
+ n = cachep->node[node];
+ if (!n)
continue;
check_irq_on();
- spin_lock_irq(&l3->list_lock);
+ spin_lock_irq(&n->list_lock);
- list_for_each_entry(slabp, &l3->slabs_full, list)
- handle_slab(n, cachep, slabp);
- list_for_each_entry(slabp, &l3->slabs_partial, list)
- handle_slab(n, cachep, slabp);
- spin_unlock_irq(&l3->list_lock);
+ list_for_each_entry(slabp, &n->slabs_full, list)
+ handle_slab(x, cachep, slabp);
+ list_for_each_entry(slabp, &n->slabs_partial, list)
+ handle_slab(x, cachep, slabp);
+ spin_unlock_irq(&n->list_lock);
}
name = cachep->name;
- if (n[0] == n[1]) {
+ if (x[0] == x[1]) {
/* Increase the buffer size */
mutex_unlock(&slab_mutex);
- m->private = kzalloc(n[0] * 4 * sizeof(unsigned long), GFP_KERNEL);
+ m->private = kzalloc(x[0] * 4 * sizeof(unsigned long), GFP_KERNEL);
if (!m->private) {
/* Too bad, we are really out */
- m->private = n;
+ m->private = x;
mutex_lock(&slab_mutex);
return -ENOMEM;
}
- *(unsigned long *)m->private = n[0] * 2;
- kfree(n);
+ *(unsigned long *)m->private = x[0] * 2;
+ kfree(x);
mutex_lock(&slab_mutex);
/* Now make sure this entry will be retried */
m->count = m->size;
return 0;
}
- for (i = 0; i < n[1]; i++) {
- seq_printf(m, "%s: %lu ", name, n[2*i+3]);
- show_symbol(m, n[2*i+2]);
+ for (i = 0; i < x[1]; i++) {
+ seq_printf(m, "%s: %lu ", name, x[2*i+3]);
+ show_symbol(m, x[2*i+2]);
seq_putc(m, '\n');
}
diff --git a/mm/slab.h b/mm/slab.h
index 34a98d642196..f96b49e4704e 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -16,7 +16,7 @@ enum slab_state {
DOWN, /* No slab functionality yet */
PARTIAL, /* SLUB: kmem_cache_node available */
PARTIAL_ARRAYCACHE, /* SLAB: kmalloc size for arraycache available */
- PARTIAL_L3, /* SLAB: kmalloc size for l3 struct available */
+ PARTIAL_NODE, /* SLAB: kmalloc size for node struct available */
UP, /* Slab caches usable but not all extras yet */
FULL /* Everything is working */
};
@@ -35,6 +35,15 @@ extern struct kmem_cache *kmem_cache;
unsigned long calculate_alignment(unsigned long flags,
unsigned long align, unsigned long size);
+#ifndef CONFIG_SLOB
+/* Kmalloc array related functions */
+void create_kmalloc_caches(unsigned long);
+
+/* Find the kmalloc slab corresponding for a certain size */
+struct kmem_cache *kmalloc_slab(size_t, gfp_t);
+#endif
+
+
/* Functions provided by the slab allocators */
extern int __kmem_cache_create(struct kmem_cache *, unsigned long flags);
@@ -230,3 +239,35 @@ static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x)
return s;
}
#endif
+
+
+/*
+ * The slab lists for all objects.
+ */
+struct kmem_cache_node {
+ spinlock_t list_lock;
+
+#ifdef CONFIG_SLAB
+ struct list_head slabs_partial; /* partial list first, better asm code */
+ struct list_head slabs_full;
+ struct list_head slabs_free;
+ unsigned long free_objects;
+ unsigned int free_limit;
+ unsigned int colour_next; /* Per-node cache coloring */
+ struct array_cache *shared; /* shared per node */
+ struct array_cache **alien; /* on other nodes */
+ unsigned long next_reap; /* updated without locking */
+ int free_touched; /* updated without locking */
+#endif
+
+#ifdef CONFIG_SLUB
+ unsigned long nr_partial;
+ struct list_head partial;
+#ifdef CONFIG_SLUB_DEBUG
+ atomic_long_t nr_slabs;
+ atomic_long_t total_objects;
+ struct list_head full;
+#endif
+#endif
+
+};
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 3f3cd97d3fdf..ff3218a0f5e1 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -299,7 +299,7 @@ void __init create_boot_cache(struct kmem_cache *s, const char *name, size_t siz
err = __kmem_cache_create(s, flags);
if (err)
- panic("Creation of kmalloc slab %s size=%zd failed. Reason %d\n",
+ panic("Creation of kmalloc slab %s size=%zu failed. Reason %d\n",
name, size, err);
s->refcount = -1; /* Exempt from merging for now */
@@ -319,6 +319,178 @@ struct kmem_cache *__init create_kmalloc_cache(const char *name, size_t size,
return s;
}
+struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1];
+EXPORT_SYMBOL(kmalloc_caches);
+
+#ifdef CONFIG_ZONE_DMA
+struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1];
+EXPORT_SYMBOL(kmalloc_dma_caches);
+#endif
+
+/*
+ * Conversion table for small slabs sizes / 8 to the index in the
+ * kmalloc array. This is necessary for slabs < 192 since we have non power
+ * of two cache sizes there. The size of larger slabs can be determined using
+ * fls.
+ */
+static s8 size_index[24] = {
+ 3, /* 8 */
+ 4, /* 16 */
+ 5, /* 24 */
+ 5, /* 32 */
+ 6, /* 40 */
+ 6, /* 48 */
+ 6, /* 56 */
+ 6, /* 64 */
+ 1, /* 72 */
+ 1, /* 80 */
+ 1, /* 88 */
+ 1, /* 96 */
+ 7, /* 104 */
+ 7, /* 112 */
+ 7, /* 120 */
+ 7, /* 128 */
+ 2, /* 136 */
+ 2, /* 144 */
+ 2, /* 152 */
+ 2, /* 160 */
+ 2, /* 168 */
+ 2, /* 176 */
+ 2, /* 184 */
+ 2 /* 192 */
+};
+
+static inline int size_index_elem(size_t bytes)
+{
+ return (bytes - 1) / 8;
+}
+
+/*
+ * Find the kmem_cache structure that serves a given size of
+ * allocation
+ */
+struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
+{
+ int index;
+
+ if (WARN_ON_ONCE(size > KMALLOC_MAX_SIZE))
+ return NULL;
+
+ if (size <= 192) {
+ if (!size)
+ return ZERO_SIZE_PTR;
+
+ index = size_index[size_index_elem(size)];
+ } else
+ index = fls(size - 1);
+
+#ifdef CONFIG_ZONE_DMA
+ if (unlikely((flags & GFP_DMA)))
+ return kmalloc_dma_caches[index];
+
+#endif
+ return kmalloc_caches[index];
+}
+
+/*
+ * Create the kmalloc array. Some of the regular kmalloc arrays
+ * may already have been created because they were needed to
+ * enable allocations for slab creation.
+ */
+void __init create_kmalloc_caches(unsigned long flags)
+{
+ int i;
+
+ /*
+ * Patch up the size_index table if we have strange large alignment
+ * requirements for the kmalloc array. This is only the case for
+ * MIPS it seems. The standard arches will not generate any code here.
+ *
+ * Largest permitted alignment is 256 bytes due to the way we
+ * handle the index determination for the smaller caches.
+ *
+ * Make sure that nothing crazy happens if someone starts tinkering
+ * around with ARCH_KMALLOC_MINALIGN
+ */
+ BUILD_BUG_ON(KMALLOC_MIN_SIZE > 256 ||
+ (KMALLOC_MIN_SIZE & (KMALLOC_MIN_SIZE - 1)));
+
+ for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) {
+ int elem = size_index_elem(i);
+
+ if (elem >= ARRAY_SIZE(size_index))
+ break;
+ size_index[elem] = KMALLOC_SHIFT_LOW;
+ }
+
+ if (KMALLOC_MIN_SIZE >= 64) {
+ /*
+ * The 96 byte size cache is not used if the alignment
+ * is 64 byte.
+ */
+ for (i = 64 + 8; i <= 96; i += 8)
+ size_index[size_index_elem(i)] = 7;
+
+ }
+
+ if (KMALLOC_MIN_SIZE >= 128) {
+ /*
+ * The 192 byte sized cache is not used if the alignment
+ * is 128 byte. Redirect kmalloc to use the 256 byte cache
+ * instead.
+ */
+ for (i = 128 + 8; i <= 192; i += 8)
+ size_index[size_index_elem(i)] = 8;
+ }
+ for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
+ if (!kmalloc_caches[i]) {
+ kmalloc_caches[i] = create_kmalloc_cache(NULL,
+ 1 << i, flags);
+ }
+
+ /*
+ * Caches that are not of the two-to-the-power-of size.
+ * These have to be created immediately after the
+ * earlier power of two caches
+ */
+ if (KMALLOC_MIN_SIZE <= 32 && !kmalloc_caches[1] && i == 6)
+ kmalloc_caches[1] = create_kmalloc_cache(NULL, 96, flags);
+
+ if (KMALLOC_MIN_SIZE <= 64 && !kmalloc_caches[2] && i == 7)
+ kmalloc_caches[2] = create_kmalloc_cache(NULL, 192, flags);
+ }
+
+ /* Kmalloc array is now usable */
+ slab_state = UP;
+
+ for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
+ struct kmem_cache *s = kmalloc_caches[i];
+ char *n;
+
+ if (s) {
+ n = kasprintf(GFP_NOWAIT, "kmalloc-%d", kmalloc_size(i));
+
+ BUG_ON(!n);
+ s->name = n;
+ }
+ }
+
+#ifdef CONFIG_ZONE_DMA
+ for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
+ struct kmem_cache *s = kmalloc_caches[i];
+
+ if (s) {
+ int size = kmalloc_size(i);
+ char *n = kasprintf(GFP_NOWAIT,
+ "dma-kmalloc-%d", size);
+
+ BUG_ON(!n);
+ kmalloc_dma_caches[i] = create_kmalloc_cache(n,
+ size, SLAB_CACHE_DMA | flags);
+ }
+ }
+#endif
+}
#endif /* !CONFIG_SLOB */
diff --git a/mm/slub.c b/mm/slub.c
index a0206df88aba..57707f01bcfb 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1006,7 +1006,7 @@ static inline void inc_slabs_node(struct kmem_cache *s, int node, int objects)
* dilemma by deferring the increment of the count during
* bootstrap (see early_kmem_cache_node_alloc).
*/
- if (n) {
+ if (likely(n)) {
atomic_long_inc(&n->nr_slabs);
atomic_long_add(objects, &n->total_objects);
}
@@ -1494,7 +1494,7 @@ static inline void remove_partial(struct kmem_cache_node *n,
*/
static inline void *acquire_slab(struct kmem_cache *s,
struct kmem_cache_node *n, struct page *page,
- int mode)
+ int mode, int *objects)
{
void *freelist;
unsigned long counters;
@@ -1508,6 +1508,7 @@ static inline void *acquire_slab(struct kmem_cache *s,
freelist = page->freelist;
counters = page->counters;
new.counters = counters;
+ *objects = new.objects - new.inuse;
if (mode) {
new.inuse = page->objects;
new.freelist = NULL;
@@ -1529,7 +1530,7 @@ static inline void *acquire_slab(struct kmem_cache *s,
return freelist;
}
-static int put_cpu_partial(struct kmem_cache *s, struct page *page, int drain);
+static void put_cpu_partial(struct kmem_cache *s, struct page *page, int drain);
static inline bool pfmemalloc_match(struct page *page, gfp_t gfpflags);
/*
@@ -1540,6 +1541,8 @@ static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
{
struct page *page, *page2;
void *object = NULL;
+ int available = 0;
+ int objects;
/*
* Racy check. If we mistakenly see no partial slabs then we
@@ -1553,22 +1556,21 @@ static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
spin_lock(&n->list_lock);
list_for_each_entry_safe(page, page2, &n->partial, lru) {
void *t;
- int available;
if (!pfmemalloc_match(page, flags))
continue;
- t = acquire_slab(s, n, page, object == NULL);
+ t = acquire_slab(s, n, page, object == NULL, &objects);
if (!t)
break;
+ available += objects;
if (!object) {
c->page = page;
stat(s, ALLOC_FROM_PARTIAL);
object = t;
- available = page->objects - page->inuse;
} else {
- available = put_cpu_partial(s, page, 0);
+ put_cpu_partial(s, page, 0);
stat(s, CPU_PARTIAL_NODE);
}
if (kmem_cache_debug(s) || available > s->cpu_partial / 2)
@@ -1947,7 +1949,7 @@ static void unfreeze_partials(struct kmem_cache *s,
* If we did not find a slot then simply move all the partials to the
* per node partial list.
*/
-static int put_cpu_partial(struct kmem_cache *s, struct page *page, int drain)
+static void put_cpu_partial(struct kmem_cache *s, struct page *page, int drain)
{
struct page *oldpage;
int pages;
@@ -1985,7 +1987,6 @@ static int put_cpu_partial(struct kmem_cache *s, struct page *page, int drain)
page->next = oldpage;
} while (this_cpu_cmpxchg(s->cpu_slab->partial, oldpage, page) != oldpage);
- return pobjects;
}
static inline void flush_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
@@ -2042,7 +2043,7 @@ static void flush_all(struct kmem_cache *s)
static inline int node_match(struct page *page, int node)
{
#ifdef CONFIG_NUMA
- if (node != NUMA_NO_NODE && page_to_nid(page) != node)
+ if (!page || (node != NUMA_NO_NODE && page_to_nid(page) != node))
return 0;
#endif
return 1;
@@ -2332,13 +2333,18 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s,
s = memcg_kmem_get_cache(s, gfpflags);
redo:
-
/*
* Must read kmem_cache cpu data via this cpu ptr. Preemption is
* enabled. We may switch back and forth between cpus while
* reading from one cpu area. That does not matter as long
* as we end up on the original cpu again when doing the cmpxchg.
+ *
+ * Preemption is disabled for the retrieval of the tid because that
+ * must occur from the current processor. We cannot allow rescheduling
+ * on a different processor between the determination of the pointer
+ * and the retrieval of the tid.
*/
+ preempt_disable();
c = __this_cpu_ptr(s->cpu_slab);
/*
@@ -2348,7 +2354,7 @@ redo:
* linked list in between.
*/
tid = c->tid;
- barrier();
+ preempt_enable();
object = c->freelist;
page = c->page;
@@ -2595,10 +2601,11 @@ redo:
* data is retrieved via this pointer. If we are on the same cpu
* during the cmpxchg then the free will succedd.
*/
+ preempt_disable();
c = __this_cpu_ptr(s->cpu_slab);
tid = c->tid;
- barrier();
+ preempt_enable();
if (likely(page == c->page)) {
set_freepointer(s, object, c->freelist);
@@ -2776,7 +2783,7 @@ init_kmem_cache_node(struct kmem_cache_node *n)
static inline int alloc_kmem_cache_cpus(struct kmem_cache *s)
{
BUILD_BUG_ON(PERCPU_DYNAMIC_EARLY_SIZE <
- SLUB_PAGE_SHIFT * sizeof(struct kmem_cache_cpu));
+ KMALLOC_SHIFT_HIGH * sizeof(struct kmem_cache_cpu));
/*
* Must align to double word boundary for the double cmpxchg
@@ -2983,7 +2990,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order)
s->allocflags |= __GFP_COMP;
if (s->flags & SLAB_CACHE_DMA)
- s->allocflags |= SLUB_DMA;
+ s->allocflags |= GFP_DMA;
if (s->flags & SLAB_RECLAIM_ACCOUNT)
s->allocflags |= __GFP_RECLAIMABLE;
@@ -3175,13 +3182,6 @@ int __kmem_cache_shutdown(struct kmem_cache *s)
* Kmalloc subsystem
*******************************************************************/
-struct kmem_cache *kmalloc_caches[SLUB_PAGE_SHIFT];
-EXPORT_SYMBOL(kmalloc_caches);
-
-#ifdef CONFIG_ZONE_DMA
-static struct kmem_cache *kmalloc_dma_caches[SLUB_PAGE_SHIFT];
-#endif
-
static int __init setup_slub_min_order(char *str)
{
get_option(&str, &slub_min_order);
@@ -3218,73 +3218,15 @@ static int __init setup_slub_nomerge(char *str)
__setup("slub_nomerge", setup_slub_nomerge);
-/*
- * Conversion table for small slabs sizes / 8 to the index in the
- * kmalloc array. This is necessary for slabs < 192 since we have non power
- * of two cache sizes there. The size of larger slabs can be determined using
- * fls.
- */
-static s8 size_index[24] = {
- 3, /* 8 */
- 4, /* 16 */
- 5, /* 24 */
- 5, /* 32 */
- 6, /* 40 */
- 6, /* 48 */
- 6, /* 56 */
- 6, /* 64 */
- 1, /* 72 */
- 1, /* 80 */
- 1, /* 88 */
- 1, /* 96 */
- 7, /* 104 */
- 7, /* 112 */
- 7, /* 120 */
- 7, /* 128 */
- 2, /* 136 */
- 2, /* 144 */
- 2, /* 152 */
- 2, /* 160 */
- 2, /* 168 */
- 2, /* 176 */
- 2, /* 184 */
- 2 /* 192 */
-};
-
-static inline int size_index_elem(size_t bytes)
-{
- return (bytes - 1) / 8;
-}
-
-static struct kmem_cache *get_slab(size_t size, gfp_t flags)
-{
- int index;
-
- if (size <= 192) {
- if (!size)
- return ZERO_SIZE_PTR;
-
- index = size_index[size_index_elem(size)];
- } else
- index = fls(size - 1);
-
-#ifdef CONFIG_ZONE_DMA
- if (unlikely((flags & SLUB_DMA)))
- return kmalloc_dma_caches[index];
-
-#endif
- return kmalloc_caches[index];
-}
-
void *__kmalloc(size_t size, gfp_t flags)
{
struct kmem_cache *s;
void *ret;
- if (unlikely(size > SLUB_MAX_SIZE))
+ if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))
return kmalloc_large(size, flags);
- s = get_slab(size, flags);
+ s = kmalloc_slab(size, flags);
if (unlikely(ZERO_OR_NULL_PTR(s)))
return s;
@@ -3317,7 +3259,7 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node)
struct kmem_cache *s;
void *ret;
- if (unlikely(size > SLUB_MAX_SIZE)) {
+ if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) {
ret = kmalloc_large_node(size, flags, node);
trace_kmalloc_node(_RET_IP_, ret,
@@ -3327,7 +3269,7 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node)
return ret;
}
- s = get_slab(size, flags);
+ s = kmalloc_slab(size, flags);
if (unlikely(ZERO_OR_NULL_PTR(s)))
return s;
@@ -3620,6 +3562,12 @@ static struct kmem_cache * __init bootstrap(struct kmem_cache *static_cache)
memcpy(s, static_cache, kmem_cache->object_size);
+ /*
+ * This runs very early, and only the boot processor is supposed to be
+ * up. Even if it weren't true, IRQs are not up so we couldn't fire
+ * IPIs around.
+ */
+ __flush_cpu_slab(s, smp_processor_id());
for_each_node_state(node, N_NORMAL_MEMORY) {
struct kmem_cache_node *n = get_node(s, node);
struct page *p;
@@ -3642,8 +3590,6 @@ void __init kmem_cache_init(void)
{
static __initdata struct kmem_cache boot_kmem_cache,
boot_kmem_cache_node;
- int i;
- int caches = 2;
if (debug_guardpage_minorder())
slub_max_order = 0;
@@ -3674,103 +3620,16 @@ void __init kmem_cache_init(void)
kmem_cache_node = bootstrap(&boot_kmem_cache_node);
/* Now we can use the kmem_cache to allocate kmalloc slabs */
-
- /*
- * Patch up the size_index table if we have strange large alignment
- * requirements for the kmalloc array. This is only the case for
- * MIPS it seems. The standard arches will not generate any code here.
- *
- * Largest permitted alignment is 256 bytes due to the way we
- * handle the index determination for the smaller caches.
- *
- * Make sure that nothing crazy happens if someone starts tinkering
- * around with ARCH_KMALLOC_MINALIGN
- */
- BUILD_BUG_ON(KMALLOC_MIN_SIZE > 256 ||
- (KMALLOC_MIN_SIZE & (KMALLOC_MIN_SIZE - 1)));
-
- for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) {
- int elem = size_index_elem(i);
- if (elem >= ARRAY_SIZE(size_index))
- break;
- size_index[elem] = KMALLOC_SHIFT_LOW;
- }
-
- if (KMALLOC_MIN_SIZE == 64) {
- /*
- * The 96 byte size cache is not used if the alignment
- * is 64 byte.
- */
- for (i = 64 + 8; i <= 96; i += 8)
- size_index[size_index_elem(i)] = 7;
- } else if (KMALLOC_MIN_SIZE == 128) {
- /*
- * The 192 byte sized cache is not used if the alignment
- * is 128 byte. Redirect kmalloc to use the 256 byte cache
- * instead.
- */
- for (i = 128 + 8; i <= 192; i += 8)
- size_index[size_index_elem(i)] = 8;
- }
-
- /* Caches that are not of the two-to-the-power-of size */
- if (KMALLOC_MIN_SIZE <= 32) {
- kmalloc_caches[1] = create_kmalloc_cache("kmalloc-96", 96, 0);
- caches++;
- }
-
- if (KMALLOC_MIN_SIZE <= 64) {
- kmalloc_caches[2] = create_kmalloc_cache("kmalloc-192", 192, 0);
- caches++;
- }
-
- for (i = KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++) {
- kmalloc_caches[i] = create_kmalloc_cache("kmalloc", 1 << i, 0);
- caches++;
- }
-
- slab_state = UP;
-
- /* Provide the correct kmalloc names now that the caches are up */
- if (KMALLOC_MIN_SIZE <= 32) {
- kmalloc_caches[1]->name = kstrdup(kmalloc_caches[1]->name, GFP_NOWAIT);
- BUG_ON(!kmalloc_caches[1]->name);
- }
-
- if (KMALLOC_MIN_SIZE <= 64) {
- kmalloc_caches[2]->name = kstrdup(kmalloc_caches[2]->name, GFP_NOWAIT);
- BUG_ON(!kmalloc_caches[2]->name);
- }
-
- for (i = KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++) {
- char *s = kasprintf(GFP_NOWAIT, "kmalloc-%d", 1 << i);
-
- BUG_ON(!s);
- kmalloc_caches[i]->name = s;
- }
+ create_kmalloc_caches(0);
#ifdef CONFIG_SMP
register_cpu_notifier(&slab_notifier);
#endif
-#ifdef CONFIG_ZONE_DMA
- for (i = 0; i < SLUB_PAGE_SHIFT; i++) {
- struct kmem_cache *s = kmalloc_caches[i];
-
- if (s && s->size) {
- char *name = kasprintf(GFP_NOWAIT,
- "dma-kmalloc-%d", s->object_size);
-
- BUG_ON(!name);
- kmalloc_dma_caches[i] = create_kmalloc_cache(name,
- s->object_size, SLAB_CACHE_DMA);
- }
- }
-#endif
printk(KERN_INFO
- "SLUB: Genslabs=%d, HWalign=%d, Order=%d-%d, MinObjects=%d,"
+ "SLUB: HWalign=%d, Order=%d-%d, MinObjects=%d,"
" CPUs=%d, Nodes=%d\n",
- caches, cache_line_size(),
+ cache_line_size(),
slub_min_order, slub_max_order, slub_min_objects,
nr_cpu_ids, nr_node_ids);
}
@@ -3933,10 +3792,10 @@ void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, unsigned long caller)
struct kmem_cache *s;
void *ret;
- if (unlikely(size > SLUB_MAX_SIZE))
+ if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))
return kmalloc_large(size, gfpflags);
- s = get_slab(size, gfpflags);
+ s = kmalloc_slab(size, gfpflags);
if (unlikely(ZERO_OR_NULL_PTR(s)))
return s;
@@ -3956,7 +3815,7 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags,
struct kmem_cache *s;
void *ret;
- if (unlikely(size > SLUB_MAX_SIZE)) {
+ if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) {
ret = kmalloc_large_node(size, gfpflags, node);
trace_kmalloc_node(caller, ret,
@@ -3966,7 +3825,7 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags,
return ret;
}
- s = get_slab(size, gfpflags);
+ s = kmalloc_slab(size, gfpflags);
if (unlikely(ZERO_OR_NULL_PTR(s)))
return s;
@@ -4315,7 +4174,7 @@ static void resiliency_test(void)
{
u8 *p;
- BUILD_BUG_ON(KMALLOC_MIN_SIZE > 16 || SLUB_PAGE_SHIFT < 10);
+ BUILD_BUG_ON(KMALLOC_MIN_SIZE > 16 || KMALLOC_SHIFT_HIGH < 10);
printk(KERN_ERR "SLUB resiliency testing\n");
printk(KERN_ERR "-----------------------\n");
diff --git a/mm/swap.c b/mm/swap.c
index acd40bfffa82..dfd7d71d6841 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -30,6 +30,7 @@
#include <linux/backing-dev.h>
#include <linux/memcontrol.h>
#include <linux/gfp.h>
+#include <linux/uio.h>
#include "internal.h"
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index b12fd8612604..d365724feb05 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -1522,6 +1522,8 @@ static void __vunmap(const void *addr, int deallocate_pages)
* Must not be called in NMI context (strictly speaking, only if we don't
* have CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG, but making the calling
* conventions for vfree() arch-depenedent would be a really bad idea)
+ *
+ * NOTE: assumes that the object at *addr has a size >= sizeof(llist_node)
*
*/
void vfree(const void *addr)
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 7427ab5e27d8..981fed397d1d 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -606,21 +606,11 @@ static ssize_t show_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue,
return sprintf(buf, "%lu\n", val);
}
-static void rps_dev_flow_table_release_work(struct work_struct *work)
-{
- struct rps_dev_flow_table *table = container_of(work,
- struct rps_dev_flow_table, free_work);
-
- vfree(table);
-}
-
static void rps_dev_flow_table_release(struct rcu_head *rcu)
{
struct rps_dev_flow_table *table = container_of(rcu,
struct rps_dev_flow_table, rcu);
-
- INIT_WORK(&table->free_work, rps_dev_flow_table_release_work);
- schedule_work(&table->free_work);
+ vfree(table);
}
static ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue,
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index a5802a8b652f..cec074be8c43 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -206,7 +206,7 @@ static void netpoll_poll_dev(struct net_device *dev)
* the dev_open/close paths use this to block netpoll activity
* while changing device state
*/
- if (!down_trylock(&ni->dev_lock))
+ if (down_trylock(&ni->dev_lock))
return;
if (!netif_running(dev)) {
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index ff06b7543d9f..49616fed9340 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -125,7 +125,6 @@ struct tnode {
unsigned int empty_children; /* KEYLENGTH bits needed */
union {
struct rcu_head rcu;
- struct work_struct work;
struct tnode *tnode_free;
};
struct rt_trie_node __rcu *child[0];
@@ -383,12 +382,6 @@ static struct tnode *tnode_alloc(size_t size)
return vzalloc(size);
}
-static void __tnode_vfree(struct work_struct *arg)
-{
- struct tnode *tn = container_of(arg, struct tnode, work);
- vfree(tn);
-}
-
static void __tnode_free_rcu(struct rcu_head *head)
{
struct tnode *tn = container_of(head, struct tnode, rcu);
@@ -397,10 +390,8 @@ static void __tnode_free_rcu(struct rcu_head *head)
if (size <= PAGE_SIZE)
kfree(tn);
- else {
- INIT_WORK(&tn->work, __tnode_vfree);
- schedule_work(&tn->work);
- }
+ else
+ vfree(tn);
}
static inline void tnode_free(struct tnode *tn)
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index e97d66a1fdde..7e06641e36ae 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -305,6 +305,7 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf,
setup_timer(&q->timer, f->frag_expire, (unsigned long)q);
spin_lock_init(&q->lock);
atomic_set(&q->refcnt, 1);
+ INIT_LIST_HEAD(&q->lru_list);
return q;
}
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index f696d7c2e9fa..f6a005c485a9 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -96,7 +96,8 @@ struct tcpm_hash_bucket {
static DEFINE_SPINLOCK(tcp_metrics_lock);
-static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst)
+static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst,
+ bool fastopen_clear)
{
u32 val;
@@ -122,9 +123,11 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst)
tm->tcpm_vals[TCP_METRIC_REORDERING] = dst_metric_raw(dst, RTAX_REORDERING);
tm->tcpm_ts = 0;
tm->tcpm_ts_stamp = 0;
- tm->tcpm_fastopen.mss = 0;
- tm->tcpm_fastopen.syn_loss = 0;
- tm->tcpm_fastopen.cookie.len = 0;
+ if (fastopen_clear) {
+ tm->tcpm_fastopen.mss = 0;
+ tm->tcpm_fastopen.syn_loss = 0;
+ tm->tcpm_fastopen.cookie.len = 0;
+ }
}
static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst,
@@ -154,7 +157,7 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst,
}
tm->tcpm_addr = *addr;
- tcpm_suck_dst(tm, dst);
+ tcpm_suck_dst(tm, dst, true);
if (likely(!reclaim)) {
tm->tcpm_next = net->ipv4.tcp_metrics_hash[hash].chain;
@@ -171,7 +174,7 @@ out_unlock:
static void tcpm_check_stamp(struct tcp_metrics_block *tm, struct dst_entry *dst)
{
if (tm && unlikely(time_after(jiffies, tm->tcpm_stamp + TCP_METRICS_TIMEOUT)))
- tcpm_suck_dst(tm, dst);
+ tcpm_suck_dst(tm, dst, false);
}
#define TCP_METRICS_RECLAIM_DEPTH 5
diff --git a/net/tipc/link.c b/net/tipc/link.c
index daa6080a2a0c..a80feee5197a 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -2306,8 +2306,11 @@ static int link_recv_changeover_msg(struct tipc_link **l_ptr,
struct tipc_msg *tunnel_msg = buf_msg(tunnel_buf);
u32 msg_typ = msg_type(tunnel_msg);
u32 msg_count = msg_msgcnt(tunnel_msg);
+ u32 bearer_id = msg_bearer_id(tunnel_msg);
- dest_link = (*l_ptr)->owner->links[msg_bearer_id(tunnel_msg)];
+ if (bearer_id >= MAX_BEARERS)
+ goto exit;
+ dest_link = (*l_ptr)->owner->links[bearer_id];
if (!dest_link)
goto exit;
if (dest_link == *l_ptr) {
@@ -2521,14 +2524,16 @@ int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb,
struct tipc_msg *imsg = (struct tipc_msg *)msg_data(fragm);
u32 msg_sz = msg_size(imsg);
u32 fragm_sz = msg_data_sz(fragm);
- u32 exp_fragm_cnt = msg_sz/fragm_sz + !!(msg_sz % fragm_sz);
+ u32 exp_fragm_cnt;
u32 max = TIPC_MAX_USER_MSG_SIZE + NAMED_H_SIZE;
+
if (msg_type(imsg) == TIPC_MCAST_MSG)
max = TIPC_MAX_USER_MSG_SIZE + MCAST_H_SIZE;
- if (msg_size(imsg) > max) {
+ if (fragm_sz == 0 || msg_size(imsg) > max) {
kfree_skb(fbuf);
return 0;
}
+ exp_fragm_cnt = msg_sz / fragm_sz + !!(msg_sz % fragm_sz);
pbuf = tipc_buf_acquire(msg_size(imsg));
if (pbuf != NULL) {
pbuf->next = *pending;
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index 978416dd31ca..547e15daf03d 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -148,7 +148,7 @@ cc-ldoption = $(call try-run,\
# ld-option
# Usage: LDFLAGS += $(call ld-option, -X)
ld-option = $(call try-run,\
- $(CC) /dev/null -c -o "$$TMPO" ; $(LD) $(1) "$$TMPO" -o "$$TMP",$(1),$(2))
+ $(CC) -x c /dev/null -c -o "$$TMPO" ; $(LD) $(1) "$$TMPO" -o "$$TMP",$(1),$(2))
# ar-option
# Usage: KBUILD_ARFLAGS := $(call ar-option,D)
diff --git a/scripts/Makefile.headersinst b/scripts/Makefile.headersinst
index 477d137c0557..182084d728c8 100644
--- a/scripts/Makefile.headersinst
+++ b/scripts/Makefile.headersinst
@@ -72,7 +72,7 @@ printdir = $(patsubst $(INSTALL_HDR_PATH)/%/,%,$(dir $@))
quiet_cmd_install = INSTALL $(printdir) ($(words $(all-files))\
file$(if $(word 2, $(all-files)),s))
cmd_install = \
- $(PERL) $< $(installdir) $(SRCARCH) $(input-files); \
+ $(CONFIG_SHELL) $< $(installdir) $(input-files); \
for F in $(wrapper-files); do \
echo "\#include <asm-generic/$$F>" > $(installdir)/$$F; \
done; \
@@ -98,7 +98,7 @@ __headersinst: $(subdirs) $(install-file)
@:
targets += $(install-file)
-$(install-file): scripts/headers_install.pl $(input-files) FORCE
+$(install-file): scripts/headers_install.sh $(input-files) FORCE
$(if $(unwanted),$(call cmd,remove),)
$(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@)))
$(call if_changed,install)
diff --git a/scripts/coccicheck b/scripts/coccicheck
index 85d31899ad98..06fcb3333247 100755
--- a/scripts/coccicheck
+++ b/scripts/coccicheck
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
SPATCH="`which ${SPATCH:=spatch}`"
@@ -11,27 +11,32 @@ else
VERBOSE=0
fi
+FLAGS="$SPFLAGS -very_quiet"
+
+# spatch only allows include directories with the syntax "-I include"
+# while gcc also allows "-Iinclude" and "-include include"
+COCCIINCLUDE=${LINUXINCLUDE//-I/-I }
+COCCIINCLUDE=${COCCIINCLUDE//-include/-I}
+
if [ "$C" = "1" -o "$C" = "2" ]; then
ONLINE=1
-# This requires Coccinelle >= 0.2.3
-# FLAGS="-ignore_unknown_options -very_quiet"
-# OPTIONS=$*
-
-# Workaround for Coccinelle < 0.2.3
- FLAGS="-I $srctree/include -very_quiet"
- shift $(( $# - 1 ))
- OPTIONS=$1
+ # Take only the last argument, which is the C file to test
+ shift $(( $# - 1 ))
+ OPTIONS="$COCCIINCLUDE $1"
else
ONLINE=0
- FLAGS="-very_quiet"
if [ "$KBUILD_EXTMOD" = "" ] ; then
- OPTIONS="-dir $srctree"
+ OPTIONS="-dir $srctree $COCCIINCLUDE"
else
- OPTIONS="-dir $KBUILD_EXTMOD -patch $srctree -I $srctree/include -I $KBUILD_EXTMOD/include"
+ OPTIONS="-dir $KBUILD_EXTMOD $COCCIINCLUDE"
fi
fi
+if [ "$KBUILD_EXTMOD" != "" ] ; then
+ OPTIONS="-patch $srctree $OPTIONS"
+fi
+
if [ ! -x "$SPATCH" ]; then
echo 'spatch is part of the Coccinelle project and is available at http://coccinelle.lip6.fr/'
exit 1
@@ -72,7 +77,7 @@ coccinelle () {
#
# $SPATCH -D $MODE $FLAGS -parse_cocci $COCCI $OPT > /dev/null
- if [ $VERBOSE -ne 0 ] ; then
+ if [ $VERBOSE -ne 0 -a $ONLINE -eq 0 ] ; then
FILE=`echo $COCCI | sed "s|$srctree/||"`
diff --git a/scripts/headers_install.pl b/scripts/headers_install.pl
deleted file mode 100644
index 581ca99c96f2..000000000000
--- a/scripts/headers_install.pl
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/perl -w
-#
-# headers_install prepare the listed header files for use in
-# user space and copy the files to their destination.
-#
-# Usage: headers_install.pl readdir installdir arch [files...]
-# installdir: dir to install the files to
-# arch: current architecture
-# arch is used to force a reinstallation when the arch
-# changes because kbuild then detect a command line change.
-# files: list of files to check
-#
-# Step in preparation for users space:
-# 1) Drop all use of compiler.h definitions
-# 2) Drop include of compiler.h
-# 3) Drop all sections defined out by __KERNEL__ (using unifdef)
-
-use strict;
-
-my ($installdir, $arch, @files) = @ARGV;
-
-my $unifdef = "scripts/unifdef -U__KERNEL__ -D__EXPORTED_HEADERS__";
-
-foreach my $filename (@files) {
- my $file = $filename;
- $file =~ s!^.*/!!;
-
- my $tmpfile = "$installdir/$file.tmp";
-
- open(my $in, '<', $filename)
- or die "$filename: $!\n";
- open(my $out, '>', $tmpfile)
- or die "$tmpfile: $!\n";
- while (my $line = <$in>) {
- $line =~ s/([\s(])__user\s/$1/g;
- $line =~ s/([\s(])__force\s/$1/g;
- $line =~ s/([\s(])__iomem\s/$1/g;
- $line =~ s/\s__attribute_const__\s/ /g;
- $line =~ s/\s__attribute_const__$//g;
- $line =~ s/\b__packed\b/__attribute__((packed))/g;
- $line =~ s/^#include <linux\/compiler.h>//;
- $line =~ s/(^|\s)(inline)\b/$1__$2__/g;
- $line =~ s/(^|\s)(asm)\b(\s|[(]|$)/$1__$2__$3/g;
- $line =~ s/(^|\s|[(])(volatile)\b(\s|[(]|$)/$1__$2__$3/g;
- $line =~ s/#ifndef\s+_UAPI/#ifndef /;
- $line =~ s/#define\s+_UAPI/#define /;
- $line =~ s!#endif\s+/[*]\s*_UAPI!#endif /* !;
- printf {$out} "%s", $line;
- }
- close $out;
- close $in;
-
- system $unifdef . " $tmpfile > $installdir/$file";
- # unifdef will exit 0 on success, and will exit 1 when the
- # file was processed successfully but no changes were made,
- # so abort only when it's higher than that.
- my $e = $? >> 8;
- if ($e > 1) {
- die "$tmpfile: $!\n";
- }
- unlink $tmpfile;
-}
-exit 0;
diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh
new file mode 100644
index 000000000000..643764f53ea7
--- /dev/null
+++ b/scripts/headers_install.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+if [ $# -lt 1 ]
+then
+ echo "Usage: headers_install.sh OUTDIR [FILES...]
+ echo
+ echo "Prepares kernel header files for use by user space, by removing"
+ echo "all compiler.h definitions and #includes, removing any"
+ echo "#ifdef __KERNEL__ sections, and putting __underscores__ around"
+ echo "asm/inline/volatile keywords."
+ echo
+ echo "OUTDIR: directory to write each userspace header FILE to."
+ echo "FILES: list of header files to operate on."
+
+ exit 1
+fi
+
+# Grab arguments
+
+OUTDIR="$1"
+shift
+
+# Iterate through files listed on command line
+
+FILE=
+trap 'rm -f "$OUTDIR/$FILE" "$OUTDIR/$FILE.sed"' EXIT
+for i in "$@"
+do
+ FILE="$(basename "$i")"
+ sed -r \
+ -e 's/([ \t(])(__user|__force|__iomem)[ \t]/\1/g' \
+ -e 's/__attribute_const__([ \t]|$)/\1/g' \
+ -e 's@^#include <linux/compiler.h>@@' \
+ -e 's/(^|[^a-zA-Z0-9])__packed([^a-zA-Z0-9_]|$)/\1__attribute__((packed))\2/g' \
+ -e 's/(^|[ \t(])(inline|asm|volatile)([ \t(]|$)/\1__\2__\3/g' \
+ -e 's@#(ifndef|define|endif[ \t]*/[*])[ \t]*_UAPI@#\1 @' \
+ "$i" > "$OUTDIR/$FILE.sed" || exit 1
+ scripts/unifdef -U__KERNEL__ -D__EXPORTED_HEADERS__ "$OUTDIR/$FILE.sed" \
+ > "$OUTDIR/$FILE"
+ [ $? -gt 1 ] && exit 1
+ rm -f "$OUTDIR/$FILE.sed"
+done
+trap - EXIT
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 231b4759c714..844bc9da08da 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -219,7 +219,9 @@ HOSTCFLAGS_gconf.o = `pkg-config --cflags gtk+-2.0 gmodule-2.0 libglade-2.0` \
HOSTLOADLIBES_mconf = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC))
-HOSTLOADLIBES_nconf = -lmenu -lpanel -lncurses
+HOSTLOADLIBES_nconf = $(shell \
+ pkg-config --libs menu panel ncurses 2>/dev/null \
+ || echo "-lmenu -lpanel -lncurses" )
$(obj)/qconf.o: $(obj)/.tmp_qtcheck
ifeq ($(qconf-target),1)
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index e39fcd8143ea..bde5b95c8c19 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -13,6 +13,7 @@
#include <getopt.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <errno.h>
#include "lkc.h"
@@ -514,14 +515,23 @@ int main(int ac, char **av)
{
struct timeval now;
unsigned int seed;
+ char *seed_env;
/*
* Use microseconds derived seed,
* compensate for systems where it may be zero
*/
gettimeofday(&now, NULL);
-
seed = (unsigned int)((now.tv_sec + 1) * (now.tv_usec + 1));
+
+ seed_env = getenv("KCONFIG_SEED");
+ if( seed_env && *seed_env ) {
+ char *endp;
+ int tmp = (int)strtol(seed_env, &endp, 10);
+ if (*endp == '\0') {
+ seed = tmp;
+ }
+ }
srand(seed);
break;
}
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 13ddf1126c2a..43eda40c3838 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -1106,10 +1106,54 @@ static void set_all_choice_values(struct symbol *csym)
void conf_set_all_new_symbols(enum conf_def_mode mode)
{
struct symbol *sym, *csym;
- int i, cnt;
+ int i, cnt, pby, pty, ptm; /* pby: probability of boolean = y
+ * pty: probability of tristate = y
+ * ptm: probability of tristate = m
+ */
+
+ pby = 50; pty = ptm = 33; /* can't go as the default in switch-case
+ * below, otherwise gcc whines about
+ * -Wmaybe-uninitialized */
+ if (mode == def_random) {
+ int n, p[3];
+ char *env = getenv("KCONFIG_PROBABILITY");
+ n = 0;
+ while( env && *env ) {
+ char *endp;
+ int tmp = strtol( env, &endp, 10 );
+ if( tmp >= 0 && tmp <= 100 ) {
+ p[n++] = tmp;
+ } else {
+ errno = ERANGE;
+ perror( "KCONFIG_PROBABILITY" );
+ exit( 1 );
+ }
+ env = (*endp == ':') ? endp+1 : endp;
+ if( n >=3 ) {
+ break;
+ }
+ }
+ switch( n ) {
+ case 1:
+ pby = p[0]; ptm = pby/2; pty = pby-ptm;
+ break;
+ case 2:
+ pty = p[0]; ptm = p[1]; pby = pty + ptm;
+ break;
+ case 3:
+ pby = p[0]; pty = p[1]; ptm = p[2];
+ break;
+ }
+
+ if( pty+ptm > 100 ) {
+ errno = ERANGE;
+ perror( "KCONFIG_PROBABILITY" );
+ exit( 1 );
+ }
+ }
for_all_symbols(i, sym) {
- if (sym_has_value(sym))
+ if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID))
continue;
switch (sym_get_type(sym)) {
case S_BOOLEAN:
@@ -1125,8 +1169,15 @@ void conf_set_all_new_symbols(enum conf_def_mode mode)
sym->def[S_DEF_USER].tri = no;
break;
case def_random:
- cnt = sym_get_type(sym) == S_TRISTATE ? 3 : 2;
- sym->def[S_DEF_USER].tri = (tristate)(rand() % cnt);
+ sym->def[S_DEF_USER].tri = no;
+ cnt = rand() % 100;
+ if (sym->type == S_TRISTATE) {
+ if (cnt < pty)
+ sym->def[S_DEF_USER].tri = yes;
+ else if (cnt < (pty+ptm))
+ sym->def[S_DEF_USER].tri = mod;
+ } else if (cnt < pby)
+ sym->def[S_DEF_USER].tri = yes;
break;
default:
continue;
diff --git a/scripts/kconfig/list.h b/scripts/kconfig/list.h
index 0ae730be5f49..685d80e1bb0e 100644
--- a/scripts/kconfig/list.h
+++ b/scripts/kconfig/list.h
@@ -51,6 +51,19 @@ struct list_head {
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
@@ -88,4 +101,31 @@ static inline void list_add_tail(struct list_head *_new, struct list_head *head)
__list_add(_new, head->prev, head);
}
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = (struct list_head*)LIST_POISON1;
+ entry->prev = (struct list_head*)LIST_POISON2;
+}
#endif
diff --git a/scripts/kconfig/lxdialog/check-lxdialog.sh b/scripts/kconfig/lxdialog/check-lxdialog.sh
index 80788137c670..9d2a4c585ee1 100644
--- a/scripts/kconfig/lxdialog/check-lxdialog.sh
+++ b/scripts/kconfig/lxdialog/check-lxdialog.sh
@@ -4,6 +4,8 @@
# What library to link
ldflags()
{
+ pkg-config --libs ncursesw 2>/dev/null && exit
+ pkg-config --libs ncurses 2>/dev/null && exit
for ext in so a dll.a dylib ; do
for lib in ncursesw ncurses curses ; do
$cc -print-file-name=lib${lib}.${ext} | grep -q /
@@ -20,12 +22,12 @@ ldflags()
ccflags()
{
if [ -f /usr/include/ncursesw/curses.h ]; then
- echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncursesw/curses.h>"'
+ echo '-I/usr/include/ncursesw -DCURSES_LOC="<curses.h>"'
echo ' -DNCURSES_WIDECHAR=1'
elif [ -f /usr/include/ncurses/ncurses.h ]; then
echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
elif [ -f /usr/include/ncurses/curses.h ]; then
- echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"'
+ echo '-I/usr/include/ncurses -DCURSES_LOC="<curses.h>"'
elif [ -f /usr/include/ncurses.h ]; then
echo '-DCURSES_LOC="<ncurses.h>"'
else
diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h
index 307022a8beef..1099337079b6 100644
--- a/scripts/kconfig/lxdialog/dialog.h
+++ b/scripts/kconfig/lxdialog/dialog.h
@@ -106,8 +106,14 @@ struct dialog_color {
int hl; /* highlight this item */
};
+struct subtitle_list {
+ struct subtitle_list *next;
+ const char *text;
+};
+
struct dialog_info {
const char *backtitle;
+ struct subtitle_list *subtitles;
struct dialog_color screen;
struct dialog_color shadow;
struct dialog_color dialog;
@@ -196,6 +202,7 @@ int on_key_resize(void);
int init_dialog(const char *backtitle);
void set_dialog_backtitle(const char *backtitle);
+void set_dialog_subtitles(struct subtitle_list *subtitles);
void end_dialog(int x, int y);
void attr_clear(WINDOW * win, int height, int width, chtype attr);
void dialog_clear(void);
diff --git a/scripts/kconfig/lxdialog/util.c b/scripts/kconfig/lxdialog/util.c
index 109d53117d22..a0e97c299410 100644
--- a/scripts/kconfig/lxdialog/util.c
+++ b/scripts/kconfig/lxdialog/util.c
@@ -257,12 +257,48 @@ void dialog_clear(void)
attr_clear(stdscr, LINES, COLS, dlg.screen.atr);
/* Display background title if it exists ... - SLH */
if (dlg.backtitle != NULL) {
- int i;
+ int i, len = 0, skip = 0;
+ struct subtitle_list *pos;
wattrset(stdscr, dlg.screen.atr);
mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle);
+
+ for (pos = dlg.subtitles; pos != NULL; pos = pos->next) {
+ /* 3 is for the arrow and spaces */
+ len += strlen(pos->text) + 3;
+ }
+
wmove(stdscr, 1, 1);
- for (i = 1; i < COLS - 1; i++)
+ if (len > COLS - 2) {
+ const char *ellipsis = "[...] ";
+ waddstr(stdscr, ellipsis);
+ skip = len - (COLS - 2 - strlen(ellipsis));
+ }
+
+ for (pos = dlg.subtitles; pos != NULL; pos = pos->next) {
+ if (skip == 0)
+ waddch(stdscr, ACS_RARROW);
+ else
+ skip--;
+
+ if (skip == 0)
+ waddch(stdscr, ' ');
+ else
+ skip--;
+
+ if (skip < strlen(pos->text)) {
+ waddstr(stdscr, pos->text + skip);
+ skip = 0;
+ } else
+ skip -= strlen(pos->text);
+
+ if (skip == 0)
+ waddch(stdscr, ' ');
+ else
+ skip--;
+ }
+
+ for (i = len + 1; i < COLS - 1; i++)
waddch(stdscr, ACS_HLINE);
}
wnoutrefresh(stdscr);
@@ -302,6 +338,11 @@ void set_dialog_backtitle(const char *backtitle)
dlg.backtitle = backtitle;
}
+void set_dialog_subtitles(struct subtitle_list *subtitles)
+{
+ dlg.subtitles = subtitles;
+}
+
/*
* End using dialog functions.
*/
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index 566288a76370..387dc8daf7b2 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -311,6 +311,50 @@ static void set_config_filename(const char *config_filename)
filename[sizeof(filename)-1] = '\0';
}
+struct subtitle_part {
+ struct list_head entries;
+ const char *text;
+};
+static LIST_HEAD(trail);
+
+static struct subtitle_list *subtitles;
+static void set_subtitle(void)
+{
+ struct subtitle_part *sp;
+ struct subtitle_list *pos, *tmp;
+
+ for (pos = subtitles; pos != NULL; pos = tmp) {
+ tmp = pos->next;
+ free(pos);
+ }
+
+ subtitles = NULL;
+ list_for_each_entry(sp, &trail, entries) {
+ if (sp->text) {
+ if (pos) {
+ pos->next = xcalloc(sizeof(*pos), 1);
+ pos = pos->next;
+ } else {
+ subtitles = pos = xcalloc(sizeof(*pos), 1);
+ }
+ pos->text = sp->text;
+ }
+ }
+
+ set_dialog_subtitles(subtitles);
+}
+
+static void reset_subtitle(void)
+{
+ struct subtitle_list *pos, *tmp;
+
+ for (pos = subtitles; pos != NULL; pos = tmp) {
+ tmp = pos->next;
+ free(pos);
+ }
+ subtitles = NULL;
+ set_dialog_subtitles(subtitles);
+}
struct search_data {
struct list_head *head;
@@ -353,6 +397,8 @@ static void search_conf(void)
char *dialog_input;
int dres, vscroll = 0, hscroll = 0;
bool again;
+ struct gstr sttext;
+ struct subtitle_part stpart;
title = str_new();
str_printf( &title, _("Enter %s (sub)string to search for "
@@ -379,6 +425,11 @@ again:
if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
dialog_input += strlen(CONFIG_);
+ sttext = str_new();
+ str_printf(&sttext, "Search (%s)", dialog_input_result);
+ stpart.text = str_get(&sttext);
+ list_add_tail(&stpart.entries, &trail);
+
sym_arr = sym_re_search(dialog_input);
do {
LIST_HEAD(head);
@@ -389,8 +440,10 @@ again:
.targets = targets,
.keys = keys,
};
+ struct jump_key *pos, *tmp;
res = get_relations_str(sym_arr, &head);
+ set_subtitle();
dres = show_textbox_ext(_("Search Results"), (char *)
str_get(&res), 0, 0, keys, &vscroll,
&hscroll, &update_text, (void *)
@@ -402,9 +455,13 @@ again:
again = true;
}
str_free(&res);
+ list_for_each_entry_safe(pos, tmp, &head, entries)
+ free(pos);
} while (again);
free(sym_arr);
str_free(&title);
+ list_del(trail.prev);
+ str_free(&sttext);
}
static void build_conf(struct menu *menu)
@@ -589,16 +646,24 @@ static void conf(struct menu *menu, struct menu *active_menu)
{
struct menu *submenu;
const char *prompt = menu_get_prompt(menu);
+ struct subtitle_part stpart;
struct symbol *sym;
int res;
int s_scroll = 0;
+ if (menu != &rootmenu)
+ stpart.text = menu_get_prompt(menu);
+ else
+ stpart.text = NULL;
+ list_add_tail(&stpart.entries, &trail);
+
while (1) {
item_reset();
current_menu = menu;
build_conf(menu);
if (!child_count)
break;
+ set_subtitle();
dialog_clear();
res = dialog_menu(prompt ? _(prompt) : _("Main Menu"),
_(menu_instructions),
@@ -640,13 +705,17 @@ static void conf(struct menu *menu, struct menu *active_menu)
case 2:
if (sym)
show_help(submenu);
- else
+ else {
+ reset_subtitle();
show_helptext(_("README"), _(mconf_readme));
+ }
break;
case 3:
+ reset_subtitle();
conf_save();
break;
case 4:
+ reset_subtitle();
conf_load();
break;
case 5:
@@ -679,6 +748,8 @@ static void conf(struct menu *menu, struct menu *active_menu)
break;
}
}
+
+ list_del(trail.prev);
}
static int show_textbox_ext(const char *title, char *text, int r, int c, int
@@ -881,6 +952,7 @@ static int handle_exit(void)
int res;
save_and_exit = 1;
+ reset_subtitle();
dialog_clear();
if (conf_get_changed())
res = dialog_yesno(NULL,
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 826da662886d..b5c7d90df9df 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -600,14 +600,18 @@ void get_symbol_str(struct gstr *r, struct symbol *sym,
}
for_all_prompts(sym, prop)
get_prompt_str(r, prop, head);
+
prop = get_symbol_prop(sym);
- str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name,
- prop->menu->lineno);
- if (!expr_is_yes(prop->visible.expr)) {
- str_append(r, _(" Depends on: "));
- expr_gstr_print(prop->visible.expr, r);
- str_append(r, "\n");
+ if (prop) {
+ str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name,
+ prop->menu->lineno);
+ if (!expr_is_yes(prop->visible.expr)) {
+ str_append(r, _(" Depends on: "));
+ expr_gstr_print(prop->visible.expr, r);
+ str_append(r, "\n");
+ }
}
+
hit = false;
for_all_properties(sym, prop, P_SELECT) {
if (!hit) {
diff --git a/scripts/kconfig/merge_config.sh b/scripts/kconfig/merge_config.sh
index 05274fccb88e..81b0c61bb9e2 100755
--- a/scripts/kconfig/merge_config.sh
+++ b/scripts/kconfig/merge_config.sh
@@ -120,10 +120,18 @@ if [ "$MAKE" = "false" ]; then
exit
fi
+# If we have an output dir, setup the O= argument, otherwise leave
+# it blank, since O=. will create an unnecessary ./source softlink
+OUTPUT_ARG=""
+if [ "$OUTPUT" != "." ] ; then
+ OUTPUT_ARG="O=$OUTPUT"
+fi
+
+
# Use the merged file as the starting point for:
# alldefconfig: Fills in any missing symbols with Kconfig default
# allnoconfig: Fills in any missing symbols with # CONFIG_* is not set
-make KCONFIG_ALLCONFIG=$TMP_FILE O=$OUTPUT $ALLTARGET
+make KCONFIG_ALLCONFIG=$TMP_FILE $OUTPUT_ARG $ALLTARGET
# Check all specified config values took (might have missed-dependency issues)
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index 9415b5663364..75d59fcd48b8 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -37,6 +37,8 @@ scripts/mod/devicetable-offsets.s: scripts/mod/devicetable-offsets.c FORCE
$(obj)/$(devicetable-offsets-file): scripts/mod/devicetable-offsets.s
$(call cmd,offsets)
+targets += $(devicetable-offsets-file)
+
# dependencies on generated files need to be listed explicitly
$(obj)/modpost.o $(obj)/file2alias.o $(obj)/sumversion.o: $(obj)/elfconfig.h
diff --git a/scripts/package/Makefile b/scripts/package/Makefile
index 87bf08076b11..84a406070f6f 100644
--- a/scripts/package/Makefile
+++ b/scripts/package/Makefile
@@ -27,53 +27,44 @@ RPM := $(shell if [ -x "/usr/bin/rpmbuild" ]; then echo rpmbuild; \
# Remove hyphens since they have special meaning in RPM filenames
KERNELPATH := kernel-$(subst -,_,$(KERNELRELEASE))
+# Include only those top-level files that are needed by make, plus the GPL copy
+TAR_CONTENT := $(KBUILD_ALLDIRS) kernel.spec .config .scmversion Makefile \
+ Kbuild Kconfig COPYING $(wildcard localversion*)
+TAR_CONTENT := $(addprefix $(KERNELPATH)/,$(TAR_CONTENT))
MKSPEC := $(srctree)/scripts/package/mkspec
-PREV := set -e; cd -P ..;
# rpm-pkg
# ---------------------------------------------------------------------------
-$(objtree)/kernel.spec: $(MKSPEC) $(srctree)/Makefile
- $(CONFIG_SHELL) $(MKSPEC) > $@
-
-rpm-pkg rpm: $(objtree)/kernel.spec FORCE
- @if test -n "$(KBUILD_OUTPUT)"; then \
+rpm-pkg rpm: FORCE
+ @if test "$(objtree)" != "$(srctree)"; then \
echo "Building source + binary RPM is not possible outside the"; \
echo "kernel source tree. Don't set KBUILD_OUTPUT, or use the"; \
echo "binrpm-pkg target instead."; \
false; \
fi
$(MAKE) clean
- $(PREV) ln -sf $(srctree) $(KERNELPATH)
+ ln -sf $(srctree) $(KERNELPATH)
+ $(CONFIG_SHELL) $(MKSPEC) >$(objtree)/kernel.spec
$(CONFIG_SHELL) $(srctree)/scripts/setlocalversion --save-scmversion
- $(PREV) tar -cz $(RCS_TAR_IGNORE) -f $(KERNELPATH).tar.gz $(KERNELPATH)/.
- $(PREV) rm $(KERNELPATH)
+ tar -cz $(RCS_TAR_IGNORE) -f $(KERNELPATH).tar.gz $(TAR_CONTENT)
+ rm $(KERNELPATH)
rm -f $(objtree)/.scmversion
- set -e; \
$(CONFIG_SHELL) $(srctree)/scripts/mkversion > $(objtree)/.tmp_version
- set -e; \
mv -f $(objtree)/.tmp_version $(objtree)/.version
-
- $(RPM) $(RPMOPTS) --target $(UTS_MACHINE) -ta ../$(KERNELPATH).tar.gz
- rm ../$(KERNELPATH).tar.gz
-
-clean-files := $(objtree)/kernel.spec
+ $(RPM) $(RPMOPTS) --target $(UTS_MACHINE) -ta $(KERNELPATH).tar.gz
+ rm $(KERNELPATH).tar.gz kernel.spec
# binrpm-pkg
# ---------------------------------------------------------------------------
-$(objtree)/binkernel.spec: $(MKSPEC) $(srctree)/Makefile
- $(CONFIG_SHELL) $(MKSPEC) prebuilt > $@
-
-binrpm-pkg: $(objtree)/binkernel.spec FORCE
+binrpm-pkg: FORCE
$(MAKE) KBUILD_SRC=
- set -e; \
+ $(CONFIG_SHELL) $(MKSPEC) prebuilt > $(objtree)/binkernel.spec
$(CONFIG_SHELL) $(srctree)/scripts/mkversion > $(objtree)/.tmp_version
- set -e; \
mv -f $(objtree)/.tmp_version $(objtree)/.version
$(RPM) $(RPMOPTS) --define "_builddir $(objtree)" --target \
$(UTS_MACHINE) -bb $<
-
-clean-files += $(objtree)/binkernel.spec
+ rm binkernel.spec
# Deb target
# ---------------------------------------------------------------------------
diff --git a/scripts/package/buildtar b/scripts/package/buildtar
index 62d8234f8787..cdd9bb909bcd 100644
--- a/scripts/package/buildtar
+++ b/scripts/package/buildtar
@@ -16,7 +16,7 @@ set -e
# Some variables and settings used throughout the script
#
tmpdir="${objtree}/tar-install"
-tarball="${objtree}/linux-${KERNELRELEASE}.tar"
+tarball="${objtree}/linux-${KERNELRELEASE}-${ARCH}.tar"
#
diff --git a/scripts/tags.sh b/scripts/tags.sh
index 26a87e68afed..74f02e4dddd2 100755
--- a/scripts/tags.sh
+++ b/scripts/tags.sh
@@ -199,7 +199,9 @@ exuberant()
--regex-c='/DEFINE_PER_CPU_SHARED_ALIGNED\(([^,]*,\s*)(\w*).*\)/\2/v/' \
--regex-c='/DECLARE_WAIT_QUEUE_HEAD\((\w*)/\1/v/' \
--regex-c='/DECLARE_(TASKLET|WORK|DELAYED_WORK)\((\w*)/\2/v/' \
- --regex-c='/DEFINE_PCI_DEVICE_TABLE\((\w*)/\1/v/'
+ --regex-c='/DEFINE_PCI_DEVICE_TABLE\((\w*)/\1/v/' \
+ --regex-c='/(^\s)OFFSET\((\w*)/\2/v/' \
+ --regex-c='/(^\s)DEFINE\((\w*)/\2/v/'
all_kconfigs | xargs $1 -a \
--langdef=kconfig --language-force=kconfig \
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 8bbefc3b55d4..d4f1468b9b50 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -16,6 +16,8 @@
#include <linux/key-type.h>
#include <linux/task_work.h>
+struct iovec;
+
#ifdef __KDEBUG
#define kenter(FMT, ...) \
printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__)
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 4b5c948eb414..33cfd27b4de2 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -22,6 +22,7 @@
#include <linux/err.h>
#include <linux/vmalloc.h>
#include <linux/security.h>
+#include <linux/uio.h>
#include <asm/uaccess.h>
#include "internal.h"
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 23e3c46cd0a4..ccfa383f1fda 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -25,7 +25,7 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/pm_qos.h>
-#include <linux/uio.h>
+#include <linux/aio.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/control.h>