summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPraneeth Bajjuri <praneeth@ti.com>2015-01-05 16:10:32 -0600
committerPraneeth Bajjuri <praneeth@ti.com>2015-01-05 16:10:32 -0600
commit32ce742951f7078bbecd1251e0854b31acc16bfd (patch)
treea2a168c6ad1640f84f6ee22cf6e464ee658fb626
parent33c641a3122514422d8618390557f470d313dce4 (diff)
parent4311f726041145161621593e5d67f106913c0f28 (diff)
downloadomap-omapzoom-p-ti-linux-3.14.y.tar.gz
Merge branch 'ti-linux-3.14.y' of git://git.ti.com/ti-linux-kernel/ti-linux-kernel into p-ti-linux-3.14.yp-ti-linux-3.14.y
* 'ti-linux-3.14.y' of git://git.ti.com/ti-linux-kernel/ti-linux-kernel: (487 commits) ARM: dra7xx: Fix counter frequency drift for AM572x errata i856 ARM: omap5/dra7xx: Fix frequency typos. ARM: OMAP2: pm33xx: Bump firmware version requirement to 0x190 ASoC: tlv320aic31xx: Do not ignore errors in aic31xx_device_init() ARM: OMAP2+: hwmod: Fix _wait_target_ready() for hwmods without sysc video/logo: prevent use of logos after they have been freed drm/omap: fix race conditon in DMM drm/omap: fix race condition with dev->obj_list remoteproc: wkup_m3_rproc: Modify wkup_m3_ping to not use interrupts remoteproc: wkup_m3: Defer probe until wkup_m3_pm_ops are populated ARM: OMAP2+: pm33xx: Only pass i2c volt scale offsets for DeepSleep ARM: dts: AM4372: Add ti,mbox-send-noirq to wkup_m3 mailbox ARM: dts: AM33XX: Add ti,mbox-send-noirq to wkup_m3 mailbox mailbox/omap: Add ti,mbox-send-noirq quirk to fix AM33xx CPU Idle ARM: dts: dra7: make usb1 of dra7-evm and dra72-evm as OTG ARM: dts: am437x-gp-evm: Move pinctrl data to dwc3_omap wrapper from usb_phy usb: dwc3: dwc3-omap: USB DRVVBUS pinctrl state should be set from wrapper usb: host: xhci: Call suspend/resume depending on DRD state usb: dwc3: gadget: Mark freed EPs as NULL usb: dwc3: dwc3-omap: Fix disable IRQ ... Conflicts: drivers/usb/dwc3/gadget.c Change-Id: Ia079ca3ec0c0fbf3c86c0fd53bc69026e58e5e2d Signed-off-by: Praneeth Bajjuri <praneeth@ti.com>
-rw-r--r--Documentation/devicetree/bindings/ata/sata_rcar.txt3
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/interrupts.txt4
-rw-r--r--Documentation/devicetree/bindings/mailbox/omap-mailbox.txt8
-rw-r--r--Documentation/devicetree/bindings/mmc/mmc.txt9
-rw-r--r--Documentation/devicetree/bindings/net/can/c_can.txt12
-rw-r--r--Documentation/devicetree/bindings/remoteproc/ti,pruss-remoteproc.txt1
-rw-r--r--Documentation/devicetree/bindings/usb/usb-xhci.txt8
-rw-r--r--Makefile2
-rw-r--r--arch/arm/boot/compressed/head.S20
-rw-r--r--arch/arm/boot/dts/am335x-evm.dts12
-rw-r--r--arch/arm/boot/dts/am33xx.dtsi26
-rw-r--r--arch/arm/boot/dts/am4372.dtsi36
-rw-r--r--arch/arm/boot/dts/am437x-gp-evm.dts91
-rw-r--r--arch/arm/boot/dts/am437x-sk-evm.dts2
-rw-r--r--arch/arm/boot/dts/am43x-epos-evm.dts42
-rw-r--r--arch/arm/boot/dts/am57xx-beagle-x15.dts127
-rw-r--r--arch/arm/boot/dts/dra7-evm.dts41
-rw-r--r--arch/arm/boot/dts/dra7.dtsi134
-rw-r--r--arch/arm/boot/dts/dra72-evm.dts46
-rw-r--r--arch/arm/boot/dts/dra74x.dtsi26
-rw-r--r--arch/arm/boot/dts/dra7xx-clocks.dtsi8
-rw-r--r--arch/arm/boot/dts/exynos5250.dtsi7
-rw-r--r--arch/arm/boot/dts/omap3-beagle-xm.dts1
-rw-r--r--arch/arm/boot/dts/omap3-beagle.dts1
-rw-r--r--arch/arm/boot/dts/omap3-devkit8000.dts1
-rw-r--r--arch/arm/boot/dts/omap3-igep.dtsi1
-rw-r--r--arch/arm/boot/dts/omap4-panda-common.dtsi1
-rw-r--r--arch/arm/boot/dts/omap4-sdp.dts1
-rw-r--r--arch/arm/boot/dts/omap4.dtsi2
-rw-r--r--arch/arm/boot/dts/omap44xx-clocks.dtsi8
-rw-r--r--arch/arm/boot/dts/omap5-uevm.dts1
-rw-r--r--arch/arm/boot/dts/omap5.dtsi7
-rw-r--r--arch/arm/include/asm/thread_info.h11
-rw-r--r--arch/arm/kernel/kprobes-common.c19
-rw-r--r--arch/arm/kernel/kprobes-thumb.c21
-rw-r--r--arch/arm/kernel/kprobes.c9
-rw-r--r--arch/arm/kernel/traps.c31
-rw-r--r--arch/arm/mach-omap2/clockdomains7xx_data.c4
-rw-r--r--arch/arm/mach-omap2/control.h4
-rw-r--r--arch/arm/mach-omap2/id.c8
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.c34
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.h8
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_44xx_data.c25
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_54xx_data.c5
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_7xx_data.c56
-rw-r--r--arch/arm/mach-omap2/pm33xx.c5
-rw-r--r--arch/arm/mach-omap2/pm33xx.h3
-rw-r--r--arch/arm/mach-omap2/soc.h1
-rw-r--r--arch/arm/mach-omap2/timer.c44
-rw-r--r--arch/arm/mm/Kconfig1
-rw-r--r--arch/arm/mm/proc-v7.S2
-rw-r--r--arch/arm/mm/proc-xscale.S4
-rw-r--r--arch/arm64/kernel/insn.c5
-rw-r--r--arch/arm64/lib/clear_user.S2
-rw-r--r--arch/mips/loongson/common/Makefile3
-rw-r--r--arch/mips/oprofile/backtrace.c2
-rw-r--r--arch/parisc/include/uapi/asm/shmbuf.h25
-rw-r--r--arch/parisc/kernel/syscall_table.S8
-rw-r--r--arch/powerpc/platforms/powernv/pci-ioda.c3
-rw-r--r--arch/powerpc/platforms/powernv/pci.c4
-rw-r--r--arch/powerpc/platforms/pseries/msi.c2
-rw-r--r--arch/powerpc/xmon/xmon.c6
-rw-r--r--arch/sparc/include/asm/atomic_32.h2
-rw-r--r--arch/sparc/include/asm/cmpxchg_32.h12
-rw-r--r--arch/sparc/include/asm/vio.h14
-rw-r--r--arch/sparc/include/uapi/asm/swab.h12
-rw-r--r--arch/sparc/kernel/pci_schizo.c6
-rw-r--r--arch/sparc/kernel/smp_64.c4
-rw-r--r--arch/sparc/lib/atomic32.c27
-rw-r--r--arch/x86/boot/compressed/Makefile4
-rw-r--r--arch/x86/boot/compressed/head_32.S5
-rw-r--r--arch/x86/boot/compressed/head_64.S5
-rw-r--r--arch/x86/boot/compressed/misc.c13
-rw-r--r--arch/x86/boot/compressed/mkpiggy.c9
-rw-r--r--arch/x86/include/asm/cpufeature.h1
-rw-r--r--arch/x86/include/asm/kvm_para.h10
-rw-r--r--arch/x86/include/asm/page_32_types.h1
-rw-r--r--arch/x86/include/asm/page_64_types.h11
-rw-r--r--arch/x86/include/asm/thread_info.h2
-rw-r--r--arch/x86/include/asm/traps.h1
-rw-r--r--arch/x86/kernel/cpu/amd.c7
-rw-r--r--arch/x86/kernel/cpu/common.c2
-rw-r--r--arch/x86/kernel/cpu/microcode/amd_early.c33
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel.c3
-rw-r--r--arch/x86/kernel/dumpstack_64.c1
-rw-r--r--arch/x86/kernel/entry_64.S81
-rw-r--r--arch/x86/kernel/ptrace.c11
-rw-r--r--arch/x86/kernel/traps.c71
-rw-r--r--arch/x86/kvm/x86.c2
-rw-r--r--arch/x86/mm/init_64.c11
-rw-r--r--arch/x86/mm/pgtable.c21
-rw-r--r--arch/x86/tools/calc_run_size.pl39
-rw-r--r--arch/xtensa/include/uapi/asm/unistd.h3
-rw-r--r--drivers/ata/ahci.c19
-rw-r--r--drivers/ata/sata_rcar.c10
-rw-r--r--drivers/base/power/runtime.c45
-rw-r--r--drivers/base/regmap/regmap.c6
-rw-r--r--drivers/block/sunvdc.c176
-rw-r--r--drivers/block/zram/zram_drv.c3
-rw-r--r--drivers/char/hw_random/pseries-rng.c11
-rw-r--r--drivers/clocksource/sun4i_timer.c12
-rw-r--r--drivers/crypto/caam/caamhash.c22
-rw-r--r--drivers/crypto/caam/key_gen.c29
-rw-r--r--drivers/crypto/caam/sg_sw_sec4.h54
-rw-r--r--drivers/dma/cppi41.c11
-rw-r--r--drivers/firewire/core-cdev.c3
-rw-r--r--drivers/gpio/gpio-pcf857x.c9
-rw-r--r--drivers/gpu/drm/drm_irq.c44
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c35
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_priv.h2
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_tiler.c15
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c42
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.h3
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.c10
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem.c5
-rw-r--r--drivers/gpu/drm/omapdrm/omap_irq.c2
-rw-r--r--drivers/gpu/drm/omapdrm/omap_plane.c3
-rw-r--r--drivers/gpu/drm/radeon/cik.c7
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c4
-rw-r--r--drivers/gpu/drm/radeon/r100.c3
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq_kms.c10
-rw-r--r--drivers/gpu/drm/radeon/rs600.c3
-rw-r--r--drivers/gpu/drm/radeon/rs690.c3
-rw-r--r--drivers/gpu/drm/radeon/rv515.c3
-rw-r--r--drivers/gpu/drm/radeon/si.c3
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c2
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c51
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c8
-rw-r--r--drivers/input/joystick/xpad.c16
-rw-r--r--drivers/input/mouse/alps.c28
-rw-r--r--drivers/input/mouse/synaptics.c9
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c3
-rw-r--r--drivers/mailbox/omap-mailbox.c54
-rw-r--r--drivers/md/dm-bufio.c12
-rw-r--r--drivers/md/dm-raid.c11
-rw-r--r--drivers/md/dm-thin.c16
-rw-r--r--drivers/md/md.c4
-rw-r--r--drivers/md/persistent-data/dm-btree-internal.h6
-rw-r--r--drivers/md/persistent-data/dm-btree-spine.c2
-rw-r--r--drivers/md/persistent-data/dm-btree.c24
-rw-r--r--drivers/media/platform/ti-vpe/Makefile7
-rw-r--r--drivers/media/platform/ti-vpe/vip.c235
-rw-r--r--drivers/media/platform/ti-vpe/vip.h14
-rw-r--r--drivers/media/platform/ti-vpe/vpdma.c119
-rw-r--r--drivers/media/platform/ti-vpe/vpdma.h43
-rw-r--r--drivers/media/platform/ti-vpe/vpdma_priv.h36
-rw-r--r--drivers/media/platform/ti-vpe/vpe.c234
-rw-r--r--drivers/media/usb/ttusb-dec/ttusbdecfe.c3
-rw-r--r--drivers/misc/ti-st/st_kim.c18
-rw-r--r--drivers/mmc/core/host.c18
-rw-r--r--drivers/mmc/host/omap_hsmmc.c560
-rw-r--r--drivers/mtd/tests/oobtest.c80
-rw-r--r--drivers/net/bonding/bond_main.c4
-rw-r--r--drivers/net/can/c_can/c_can.c36
-rw-r--r--drivers/net/can/c_can/c_can.h36
-rw-r--r--drivers/net/can/c_can/c_can_pci.c27
-rw-r--r--drivers/net/can/c_can/c_can_platform.c330
-rw-r--r--drivers/net/can/dev.c2
-rw-r--r--drivers/net/can/usb/esd_usb2.c1
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c46
-rw-r--r--drivers/net/ethernet/sun/sunvnet.c4
-rw-r--r--drivers/net/ethernet/ti/Kconfig8
-rw-r--r--drivers/net/ethernet/ti/cpsw.c135
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.c30
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.h2
-rw-r--r--drivers/net/ethernet/ti/davinci_cpdma.c9
-rw-r--r--drivers/net/ieee802154/fakehard.c13
-rw-r--r--drivers/net/macvtap.c15
-rw-r--r--drivers/net/ppp/pptp.c4
-rw-r--r--drivers/net/tun.c35
-rw-r--r--drivers/net/usb/qmi_wwan.c1
-rw-r--r--drivers/net/virtio_net.c24
-rw-r--r--drivers/net/vxlan.c28
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c13
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c13
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-power.h35
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h1
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c9
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c1
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c16
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c4
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c50
-rw-r--r--drivers/of/address.c19
-rw-r--r--drivers/pci/msi.c26
-rw-r--r--drivers/pci/probe.c29
-rw-r--r--drivers/phy/phy-omap-control.c7
-rw-r--r--drivers/phy/phy-omap-usb2.c6
-rw-r--r--drivers/phy/phy-ti-pipe3.c10
-rw-r--r--drivers/platform/x86/dell-wmi.c12
-rw-r--r--drivers/power/bq2415x_charger.c23
-rw-r--r--drivers/power/charger-manager.c163
-rw-r--r--drivers/remoteproc/Kconfig2
-rw-r--r--drivers/remoteproc/pruss_remoteproc.c134
-rw-r--r--drivers/remoteproc/remoteproc_core.c2
-rw-r--r--drivers/remoteproc/wkup_m3_rproc.c12
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c7
-rw-r--r--drivers/scsi/scsi_devinfo.c1
-rw-r--r--drivers/scsi/scsi_error.c4
-rw-r--r--drivers/spi/spi-dw.c6
-rw-r--r--drivers/staging/rtl8188eu/os_dep/usb_intf.c1
-rw-r--r--drivers/target/target_core_transport.c2
-rw-r--r--drivers/tty/serial/omap-serial.c21
-rw-r--r--drivers/usb/Kconfig15
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/core/hub.c24
-rw-r--r--drivers/usb/core/quirks.c3
-rw-r--r--drivers/usb/drd-lib.c346
-rw-r--r--drivers/usb/dwc3/Kconfig7
-rw-r--r--drivers/usb/dwc3/Makefile4
-rw-r--r--drivers/usb/dwc3/core.c33
-rw-r--r--drivers/usb/dwc3/core.h104
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c40
-rw-r--r--drivers/usb/dwc3/ep0.c31
-rw-r--r--drivers/usb/dwc3/gadget.c260
-rw-r--r--drivers/usb/dwc3/gadget.h1
-rw-r--r--drivers/usb/dwc3/host.c16
-rw-r--r--drivers/usb/dwc3/otg.c144
-rw-r--r--drivers/usb/host/Kconfig16
-rw-r--r--drivers/usb/host/Makefile6
-rw-r--r--drivers/usb/host/xhci-hub.c56
-rw-r--r--drivers/usb/host/xhci-mem.c230
-rw-r--r--drivers/usb/host/xhci-mvebu.c72
-rw-r--r--drivers/usb/host/xhci-mvebu.h21
-rw-r--r--drivers/usb/host/xhci-pci.c30
-rw-r--r--drivers/usb/host/xhci-plat.c140
-rw-r--r--drivers/usb/host/xhci-rcar.c148
-rw-r--r--drivers/usb/host/xhci-rcar.h27
-rw-r--r--drivers/usb/host/xhci-ring.c837
-rw-r--r--drivers/usb/host/xhci.c532
-rw-r--r--drivers/usb/host/xhci.h60
-rw-r--r--drivers/usb/musb/musb_core.c13
-rw-r--r--drivers/usb/musb/musb_cppi41.c10
-rw-r--r--drivers/usb/musb/musb_host.c1
-rw-r--r--drivers/usb/serial/cp210x.c1
-rw-r--r--drivers/usb/serial/ftdi_sio.c33
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h39
-rw-r--r--drivers/usb/serial/keyspan.c97
-rw-r--r--drivers/usb/serial/ssu100.c11
-rw-r--r--drivers/vhost/scsi.c24
-rw-r--r--drivers/video/fbdev/omap2/dss/dispc.c15
-rw-r--r--drivers/video/fbdev/omap2/dss/dss.c7
-rw-r--r--drivers/video/logo/logo.c17
-rw-r--r--drivers/virtio/virtio.c1
-rw-r--r--fs/aio.c21
-rw-r--r--fs/btrfs/compression.c2
-rw-r--r--fs/btrfs/file.c5
-rw-r--r--fs/cramfs/inode.c3
-rw-r--r--fs/fuse/file.c2
-rw-r--r--fs/gfs2/meta_io.c5
-rw-r--r--fs/gfs2/meta_io.h3
-rw-r--r--fs/gfs2/ops_fstype.c2
-rw-r--r--fs/ioprio.c14
-rw-r--r--fs/jffs2/fs.c2
-rw-r--r--fs/locks.c24
-rw-r--r--fs/nfs/blocklayout/blocklayout.c2
-rw-r--r--fs/nfs/delegation.c25
-rw-r--r--fs/nfs/delegation.h1
-rw-r--r--fs/nfs/direct.c1
-rw-r--r--fs/nfs/inode.c2
-rw-r--r--fs/nfs/nfs4proc.c68
-rw-r--r--fs/nfs/pagelist.c2
-rw-r--r--fs/nfsd/nfs4callback.c8
-rw-r--r--fs/nfsd/nfscache.c17
-rw-r--r--fs/nfsd/nfsd.h9
-rw-r--r--fs/super.c16
-rw-r--r--include/drm/drmP.h2
-rw-r--r--include/dt-bindings/pinctrl/dra.h4
-rw-r--r--include/linux/bitops.h7
-rw-r--r--include/linux/clocksource.h2
-rw-r--r--include/linux/compaction.h4
-rw-r--r--include/linux/crash_dump.h15
-rw-r--r--include/linux/iio/events.h2
-rw-r--r--include/linux/inetdevice.h2
-rw-r--r--include/linux/kgdb.h2
-rw-r--r--include/linux/memory.h2
-rw-r--r--include/linux/migrate.h11
-rw-r--r--include/linux/mm.h11
-rw-r--r--include/linux/mmzone.h5
-rw-r--r--include/linux/nfs_xdr.h11
-rw-r--r--include/linux/pagemap.h30
-rw-r--r--include/linux/pagevec.h5
-rw-r--r--include/linux/pci.h1
-rw-r--r--include/linux/phy/omap_control_phy.h6
-rw-r--r--include/linux/power/charger-manager.h3
-rw-r--r--include/linux/radix-tree.h5
-rw-r--r--include/linux/shmem_fs.h1
-rw-r--r--include/linux/usb/drd.h77
-rw-r--r--include/linux/usb/hcd.h1
-rw-r--r--include/linux/usb/xhci_pdriver.h29
-rw-r--r--include/net/sctp/sctp.h5
-rw-r--r--include/net/sctp/sm.h6
-rw-r--r--include/sound/soc-dpcm.h2
-rw-r--r--include/trace/events/compaction.h25
-rw-r--r--include/uapi/linux/Kbuild2
-rw-r--r--include/uapi/linux/net_switch_config.h8
-rw-r--r--include/uapi/linux/netfilter/xt_bpf.h2
-rw-r--r--include/uapi/linux/rpmsg_socket.h8
-rw-r--r--ipc/ipc_sysctl.c3
-rw-r--r--kernel/audit.c4
-rw-r--r--kernel/audit_tree.c1
-rw-r--r--kernel/events/core.c23
-rw-r--r--kernel/events/uprobes.c1
-rw-r--r--kernel/rcu/tree.c22
-rw-r--r--lib/radix-tree.c106
-rw-r--r--mm/compaction.c249
-rw-r--r--mm/filemap.c341
-rw-r--r--mm/internal.h22
-rw-r--r--mm/madvise.c2
-rw-r--r--mm/memory-failure.c4
-rw-r--r--mm/memory_hotplug.c2
-rw-r--r--mm/mempolicy.c4
-rw-r--r--mm/migrate.c56
-rw-r--r--mm/mincore.c20
-rw-r--r--mm/page_alloc.c62
-rw-r--r--mm/readahead.c27
-rw-r--r--mm/shmem.c122
-rw-r--r--mm/swap.c51
-rw-r--r--mm/truncate.c74
-rw-r--r--mm/vmscan.c36
-rw-r--r--net/batman-adv/hard-interface.c2
-rw-r--r--net/ceph/crypto.c169
-rw-r--r--net/ipv4/fib_rules.c4
-rw-r--r--net/ipv4/ping.c2
-rw-r--r--net/ipv6/ip6_gre.c5
-rw-r--r--net/ipv6/ip6_tunnel.c10
-rw-r--r--net/ipv6/ip6_vti.c11
-rw-r--r--net/ipv6/sit.c15
-rw-r--r--net/ipx/af_ipx.c6
-rw-r--r--net/mac80211/ibss.c2
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/iface.c7
-rw-r--r--net/mac80211/mesh.c2
-rw-r--r--net/mac80211/mlme.c5
-rw-r--r--net/mac80211/rx.c14
-rw-r--r--net/mac80211/spectmgmt.c18
-rw-r--r--net/netfilter/ipset/ip_set_core.c2
-rw-r--r--net/netfilter/nfnetlink_log.c31
-rw-r--r--net/netfilter/nft_compat.c2
-rw-r--r--net/rpmsg/rpmsg_proto.c135
-rw-r--r--net/sctp/associola.c2
-rw-r--r--net/sctp/auth.c2
-rw-r--r--net/sctp/inqueue.c33
-rw-r--r--net/sctp/sm_make_chunk.c102
-rw-r--r--net/sctp/sm_statefuns.c21
-rw-r--r--sound/soc/codecs/sgtl5000.c3
-rw-r--r--sound/soc/codecs/sgtl5000.h2
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c9
-rw-r--r--sound/soc/codecs/wm_adsp.c1
-rw-r--r--sound/soc/omap/omap-hdmi-audio.c8
-rw-r--r--sound/soc/sh/fsi.c3
-rw-r--r--sound/soc/sh/rcar/core.c3
-rw-r--r--sound/soc/soc-pcm.c72
-rw-r--r--sound/usb/mixer_quirks.c6
-rw-r--r--sound/usb/quirks.c14
-rw-r--r--ti_config_fragments/all_valid_socs.cfg25
-rw-r--r--ti_config_fragments/connectivity.cfg9
-rw-r--r--ti_config_fragments/defconfig_fragment1
-rw-r--r--ti_config_fragments/dra7_only.cfg24
-rw-r--r--ti_config_fragments/system_test.cfg6
-rw-r--r--ti_config_fragments/system_test_defconfig_fragment1
-rw-r--r--ti_config_fragments/wlan.cfg8
363 files changed, 8002 insertions, 3144 deletions
diff --git a/Documentation/devicetree/bindings/ata/sata_rcar.txt b/Documentation/devicetree/bindings/ata/sata_rcar.txt
index 1e6111333fa8..7dd32d321a34 100644
--- a/Documentation/devicetree/bindings/ata/sata_rcar.txt
+++ b/Documentation/devicetree/bindings/ata/sata_rcar.txt
@@ -3,7 +3,8 @@
Required properties:
- compatible : should contain one of the following:
- "renesas,sata-r8a7779" for R-Car H1
- - "renesas,sata-r8a7790" for R-Car H2
+ - "renesas,sata-r8a7790-es1" for R-Car H2 ES1
+ - "renesas,sata-r8a7790" for R-Car H2 other than ES1
- "renesas,sata-r8a7791" for R-Car M2
- reg : address and length of the SATA registers;
- interrupts : must consist of one interrupt specifier.
diff --git a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
index ce6a1a072028..8a3c40829899 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
@@ -30,10 +30,6 @@ should only be used when a device has multiple interrupt parents.
Example:
interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;
-A device node may contain either "interrupts" or "interrupts-extended", but not
-both. If both properties are present, then the operating system should log an
-error and use only the data in "interrupts".
-
2) Interrupt controller nodes
-----------------------------
diff --git a/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt b/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
index 1c6aa0d1c07c..35d9cfc08111 100644
--- a/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
+++ b/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
@@ -75,6 +75,14 @@ data that represent the following:
Cell #3 (usr_id) - mailbox user id for identifying the interrupt line
associated with generating a tx/rx fifo interrupt.
+Optional Properties:
+--------------------
+- ti,mbox-send-noirq: Quirk flag to allow the client user of this sub-mailbox
+ to send messages without triggering a Tx ready interrupt,
+ and to control the Tx ticker. Should be used only on
+ sub-mailboxes used to communicate with WkupM3 remote
+ processor on AM33xx/AM43xx SoCs.
+
Mailbox Users:
==============
A device needing to communicate with a target processor device should specify
diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
index 458b57f199af..9dce540771fb 100644
--- a/Documentation/devicetree/bindings/mmc/mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/mmc.txt
@@ -26,9 +26,18 @@ Optional properties:
this system, even if the controller claims it is.
- cap-sd-highspeed: SD high-speed timing is supported
- cap-mmc-highspeed: MMC high-speed timing is supported
+- sd-uhs-sdr12: SD UHS SDR12 speed is supported
+- sd-uhs-sdr25: SD UHS SDR25 speed is supported
+- sd-uhs-sdr50: SD UHS SDR50 speed is supported
+- sd-uhs-sdr104: SD UHS SDR104 speed is supported
+- sd-uhs-ddr50: SD UHS DDR50 speed is supported
- cap-power-off-card: powering off the card is safe
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
- full-pwr-cycle: full power cycle of the card is supported
+- mmc-highspeed-ddr-1_8v: eMMC high-speed DDR mode(1.8V I/O) is supported
+- mmc-highspeed-ddr-1_2v: eMMC high-speed DDR mode(1.2V I/O) is supported
+- mmc-hs200-1_8v: eMMC HS200 mode(1.8V I/O) is supported
+- mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted"
diff --git a/Documentation/devicetree/bindings/net/can/c_can.txt b/Documentation/devicetree/bindings/net/can/c_can.txt
index 2f691821b097..5a1d8b0c39e9 100644
--- a/Documentation/devicetree/bindings/net/can/c_can.txt
+++ b/Documentation/devicetree/bindings/net/can/c_can.txt
@@ -4,6 +4,8 @@ Bosch C_CAN/D_CAN controller Device Tree Bindings
Required properties:
- compatible : Should be "bosch,c_can" for C_CAN controllers and
"bosch,d_can" for D_CAN controllers.
+ Can be "ti,dra7-d_can", "ti,am3352-d_can" or
+ "ti,am4372-d_can".
- reg : physical base address and size of the C_CAN/D_CAN
registers map
- interrupts : property with a value describing the interrupt
@@ -12,13 +14,9 @@ Required properties:
Optional properties:
- ti,hwmods : Must be "d_can<n>" or "c_can<n>", n being the
instance number
-
-- syscon : Handle to system control region that contains the
- RAMINIT register. If specified, the second memory resource
- in the reg property must index into the RAMINIT
- register within the syscon region
-- raminit-start-bit : Bit posistion of START bit in the RAMINIT register
-- raminit-done-bit : Bit position of DONE bit in the RAMINIT register
+- syscon-raminit : Handle to system control region that contains the
+ RAMINIT register, register offset to the RAMINIT
+ register and the CAN instance number (0 offset).
Note: "ti,hwmods" field is used to fetch the base address and irq
resources from TI, omap hwmod data base during device registration.
diff --git a/Documentation/devicetree/bindings/remoteproc/ti,pruss-remoteproc.txt b/Documentation/devicetree/bindings/remoteproc/ti,pruss-remoteproc.txt
index 6956b176e7ef..7572cc511632 100644
--- a/Documentation/devicetree/bindings/remoteproc/ti,pruss-remoteproc.txt
+++ b/Documentation/devicetree/bindings/remoteproc/ti,pruss-remoteproc.txt
@@ -29,6 +29,7 @@ Required Properties:
- compatible : should be one of,
"ti,am335x-pruss" for AM335x family of SoCs
"ti,am4372-pruss" for AM437x family of SoCs
+ "ti,am5728-pruss" for AM572x family of SoCs
- ti,hwmods : name of the hwmod associated with the PRUSS instance
- reg : base address and size for each of the sub-modules as
mentioned in reg-names, and in the same order as the
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index 5752df0e17a2..93f2b6bde8f6 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -1,14 +1,18 @@
USB xHCI controllers
Required properties:
- - compatible: should be "xhci-platform".
+ - compatible: should be "generic-xhci" (deprecated: "xhci-platform").
- reg: should contain address and length of the standard XHCI
register set for the device.
- interrupts: one XHCI interrupt should be described here.
+Optional properties:
+ - clocks: reference to a clock
+ - usb3-lpm-capable: determines if platform is USB3 LPM capable
+
Example:
usb@f0931000 {
- compatible = "xhci-platform";
+ compatible = "generic-xhci";
reg = <0xf0931000 0x8c8>;
interrupts = <0x0 0x4e 0x0>;
};
diff --git a/Makefile b/Makefile
index 8fd06101c482..63a5ee858cc3 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 3
PATCHLEVEL = 14
-SUBLEVEL = 24
+SUBLEVEL = 26
EXTRAVERSION =
NAME = Remembering Coco
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 2a2dff9daa36..a3565154aded 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -400,8 +400,7 @@ dtb_check_done:
add sp, sp, r6
#endif
- tst r4, #1
- bleq cache_clean_flush
+ bl cache_clean_flush
adr r0, BSYM(restart)
add r0, r0, r6
@@ -1052,6 +1051,8 @@ cache_clean_flush:
b call_cache_fn
__armv4_mpu_cache_flush:
+ tst r4, #1
+ movne pc, lr
mov r2, #1
mov r3, #0
mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
@@ -1069,6 +1070,8 @@ __armv4_mpu_cache_flush:
mov pc, lr
__fa526_cache_flush:
+ tst r4, #1
+ movne pc, lr
mov r1, #0
mcr p15, 0, r1, c7, c14, 0 @ clean and invalidate D cache
mcr p15, 0, r1, c7, c5, 0 @ flush I cache
@@ -1077,13 +1080,16 @@ __fa526_cache_flush:
__armv6_mmu_cache_flush:
mov r1, #0
- mcr p15, 0, r1, c7, c14, 0 @ clean+invalidate D
+ tst r4, #1
+ mcreq p15, 0, r1, c7, c14, 0 @ clean+invalidate D
mcr p15, 0, r1, c7, c5, 0 @ invalidate I+BTB
- mcr p15, 0, r1, c7, c15, 0 @ clean+invalidate unified
+ mcreq p15, 0, r1, c7, c15, 0 @ clean+invalidate unified
mcr p15, 0, r1, c7, c10, 4 @ drain WB
mov pc, lr
__armv7_mmu_cache_flush:
+ tst r4, #1
+ bne iflush
mrc p15, 0, r10, c0, c1, 5 @ read ID_MMFR1
tst r10, #0xf << 16 @ hierarchical cache (ARMv7)
mov r10, #0
@@ -1144,6 +1150,8 @@ iflush:
mov pc, lr
__armv5tej_mmu_cache_flush:
+ tst r4, #1
+ movne pc, lr
1: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate D cache
bne 1b
mcr p15, 0, r0, c7, c5, 0 @ flush I cache
@@ -1151,6 +1159,8 @@ __armv5tej_mmu_cache_flush:
mov pc, lr
__armv4_mmu_cache_flush:
+ tst r4, #1
+ movne pc, lr
mov r2, #64*1024 @ default: 32K dcache size (*2)
mov r11, #32 @ default: 32 byte line size
mrc p15, 0, r3, c0, c0, 1 @ read cache type
@@ -1184,6 +1194,8 @@ no_cache_id:
__armv3_mmu_cache_flush:
__armv3_mpu_cache_flush:
+ tst r4, #1
+ movne pc, lr
mov r1, #0
mcr p15, 0, r1, c7, c0, 0 @ invalidate whole cache v3
mov pc, lr
diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts
index 3c5cda3537fa..0f8dd0f9f40e 100644
--- a/arch/arm/boot/dts/am335x-evm.dts
+++ b/arch/arm/boot/dts/am335x-evm.dts
@@ -496,6 +496,12 @@
>;
};
+ dcan1_pins_default: dcan1_pins_default {
+ pinctrl-single,pins = <
+ 0x168 (PIN_OUTPUT | MUX_MODE2) /* uart0_ctsn.d_can1_tx */
+ 0x16c (PIN_INPUT_PULLDOWN | MUX_MODE2) /* uart0_rtsn.d_can1_rx */
+ >;
+ };
};
&uart0 {
@@ -906,3 +912,9 @@
&wkup_m3 {
ti,scale-data-fw = "am335x-evm-scale-data.bin";
};
+
+&dcan1 {
+ status = "disabled"; /* Enable only if Profile 1 is selected */
+ pinctrl-names = "default";
+ pinctrl-0 = <&dcan1_pins_default>;
+};
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index d9880ff49a4f..9ee8029dbd99 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -71,6 +71,11 @@
};
};
+ am33xx_control_module: control_module@4a002000 {
+ compatible = "syscon";
+ reg = <0x44e10000 0x7fc>;
+ };
+
am33xx_pinmux: pinmux@44e10800 {
compatible = "pinctrl-single";
reg = <0x44e10800 0x0238>;
@@ -323,20 +328,24 @@
interrupts = <91>;
};
- dcan0: d_can@481cc000 {
- compatible = "bosch,d_can";
+ dcan0: can@481cc000 {
+ compatible = "ti,am3352-d_can";
ti,hwmods = "d_can0";
- reg = <0x481cc000 0x2000
- 0x44e10644 0x4>;
+ clocks = <&dcan0_fck>;
+ clock-names = "fck";
+ reg = <0x481cc000 0x2000>;
+ syscon-raminit = <&am33xx_control_module 0x644 0>;
interrupts = <52>;
status = "disabled";
};
- dcan1: d_can@481d0000 {
- compatible = "bosch,d_can";
+ dcan1: can@481d0000 {
+ compatible = "ti,am3352-d_can";
ti,hwmods = "d_can1";
- reg = <0x481d0000 0x2000
- 0x44e10644 0x4>;
+ clocks = <&dcan1_fck>;
+ clock-names = "fck";
+ reg = <0x481d0000 0x2000>;
+ syscon-raminit = <&am33xx_control_module 0x644 1>;
interrupts = <55>;
status = "disabled";
};
@@ -350,6 +359,7 @@
ti,mbox-num-users = <4>;
ti,mbox-num-fifos = <8>;
mbox_wkupm3: wkup_m3 {
+ ti,mbox-send-noirq;
ti,mbox-tx = <0 0 0>;
ti,mbox-rx = <0 0 3>;
};
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index eb61b36de837..236729d966f7 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -66,6 +66,11 @@
cache-level = <2>;
};
+ am43xx_control_module: control_module@4a002000 {
+ compatible = "syscon";
+ reg = <0x44e10000 0x7f4>;
+ };
+
am43xx_pinmux: pinmux@44e10800 {
compatible = "ti,am437-padconf", "pinctrl-single";
reg = <0x44e10800 0x31c>;
@@ -188,6 +193,7 @@
ti,mbox-num-fifos = <8>;
mbox_wkupm3: wkup_m3 {
+ ti,mbox-send-noirq;
ti,mbox-tx = <0 0 0>;
ti,mbox-rx = <0 0 3>;
};
@@ -933,8 +939,13 @@
usb1: usb@48390000 {
compatible = "synopsys,dwc3";
- reg = <0x48390000 0x10000>;
- interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0x48390000 0x17000>;
+ interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy1>;
phy-names = "usb2-phy";
maximum-speed = "high-speed";
@@ -955,8 +966,13 @@
usb2: usb@483d0000 {
compatible = "synopsys,dwc3";
- reg = <0x483d0000 0x10000>;
- interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0x483d0000 0x17000>;
+ interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy2>;
phy-names = "usb2-phy";
maximum-speed = "high-speed";
@@ -1020,23 +1036,23 @@
};
dcan0: can@481cc000 {
- compatible = "bosch,d_can";
+ compatible = "ti,am4372-d_can", "ti,am3352-d_can";
ti,hwmods = "d_can0";
clocks = <&dcan0_fck>;
clock-names = "fck";
- reg = <0x481cc000 0x2000
- 0x44e10644 0x4>;
+ reg = <0x481cc000 0x2000>;
+ syscon-raminit = <&am43xx_control_module 0x644 0>;
interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
dcan1: can@481d0000 {
- compatible = "bosch,d_can";
+ compatible = "ti,am4372-d_can", "ti,am3352-d_can";
ti,hwmods = "d_can1";
clocks = <&dcan1_fck>;
clock-names = "fck";
- reg = <0x481d0000 0x2000
- 0x44e10644 0x4>;
+ reg = <0x481d0000 0x2000>;
+ syscon-raminit = <&am43xx_control_module 0x644 1>;
interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
index 88c0537f2f44..96d7a4c13a3a 100644
--- a/arch/arm/boot/dts/am437x-gp-evm.dts
+++ b/arch/arm/boot/dts/am437x-gp-evm.dts
@@ -355,7 +355,7 @@
>;
};
- dss_pins: dss_pins {
+ dss_pinctrl_default: dss_pinctrl_default {
pinctrl-single,pins = <
0x020 (PIN_OUTPUT_PULLUP | MUX_MODE1) /*gpmc ad 8 -> DSS DATA 23 */
0x024 (PIN_OUTPUT_PULLUP | MUX_MODE1)
@@ -389,6 +389,39 @@
>;
};
+ dss_pinctrl_sleep: dss_pinctrl_sleep {
+ pinctrl-single,pins = <
+ 0x020 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x024 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x028 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x02C (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x030 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x034 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x038 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x03C (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x0A0 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0A4 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0A8 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0AC (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0B0 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0B4 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0B8 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0BC (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x0C0 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x0C4 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0C8 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x0CC (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x0D0 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0D4 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0D8 (DS0_PULL_UP_DOWN_EN | INPUT_EN | PULL_DISABLE | MUX_MODE7)
+ 0x0DC (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x0E0 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x0E4 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x0E8 (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x0EC (DS0_PULL_UP_DOWN_EN | PIN_INPUT_PULLDOWN | MUX_MODE7)
+ >;
+ };
+
lcd_pins: lcd_pins {
pinctrl-single,pins = <
/* GPIO 5_8 to select LCD / HDMI */
@@ -450,13 +483,6 @@
>;
};
- dcan0_sleep: dcan0_sleep_pins {
- pinctrl-single,pins = <
- 0x178 (PIN_INPUT_PULLDOWN | MUX_MODE7)
- 0x17c (PIN_INPUT_PULLDOWN | MUX_MODE7)
- >;
- };
-
dcan1_default: dcan1_default_pins {
pinctrl-single,pins = <
0x180 (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* uart1_rxd.d_can1_tx */
@@ -464,13 +490,6 @@
>;
};
- dcan1_sleep: dcan1_sleep_pins {
- pinctrl-single,pins = <
- 0x180 (PIN_INPUT_PULLDOWN | MUX_MODE7)
- 0x184 (PIN_INPUT_PULLDOWN | MUX_MODE7)
- >;
- };
-
mcasp1_pins: mcasp1_pins {
pinctrl-single,pins = <
0x108 (PIN_OUTPUT_PULLDOWN | MUX_MODE4) /* mii1_col.mcasp1_axr2 */
@@ -639,6 +658,12 @@
unused_pins: unused_pins {
pinctrl-single,pins = <
+ 0x54 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x58 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x60 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x64 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x68 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+ 0x6C (PIN_INPUT_PULLDOWN | MUX_MODE7)
0x150 (PIN_INPUT_PULLDOWN | MUX_MODE7)
0x154 (PIN_INPUT_PULLDOWN | MUX_MODE0)
0x190 (PIN_INPUT_PULLDOWN | MUX_MODE7)
@@ -876,8 +901,8 @@
status = "okay";
/* these are on the crossbar and are outlined in the
xbar-event-map element */
- dmas = <&edma 32
- &edma 33>;
+ dmas = <&edma 30
+ &edma 31>;
dma-names = "tx", "rx";
vmmc-supply = <&vmmcwl_fixed>;
bus-width = <4>;
@@ -899,8 +924,8 @@
};
&edma {
- ti,edma-xbar-event-map = /bits/ 16 <1 32
- 2 33>;
+ ti,edma-xbar-event-map = /bits/ 16 <1 30
+ 2 31>;
};
&uart3 {
@@ -909,25 +934,32 @@
pinctrl-0 = <&uart3_pins>;
};
-&usb2_phy1 {
- status = "okay";
+&dwc3_1 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&usb2_phy1_default>;
pinctrl-1 = <&usb2_phy1_sleep>;
+
};
-&usb1 {
- dr_mode = "peripheral";
+&usb2_phy1 {
status = "okay";
};
-&usb2_phy2 {
+&usb1 {
+ dr_mode = "otg";
status = "okay";
+};
+
+&dwc3_2 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&usb2_phy2_default>;
pinctrl-1 = <&usb2_phy2_sleep>;
};
+&usb2_phy2 {
+ status = "okay";
+};
+
&usb2 {
dr_mode = "host";
status = "okay";
@@ -1049,8 +1081,9 @@
&dss {
status = "ok";
- pinctrl-names = "default";
- pinctrl-0 = <&dss_pins>;
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&dss_pinctrl_default>;
+ pinctrl-1 = <&dss_pinctrl_sleep>;
port {
dpi_out: endpoint@0 {
@@ -1061,16 +1094,14 @@
};
&dcan0 {
- pinctrl-names = "default", "sleep";
+ pinctrl-names = "default";
pinctrl-0 = <&dcan0_default>;
- pinctrl-1 = <&dcan0_sleep>;
status = "okay";
};
&dcan1 {
- pinctrl-names = "default", "sleep";
+ pinctrl-names = "default";
pinctrl-0 = <&dcan1_default>;
- pinctrl-1 = <&dcan1_sleep>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/am437x-sk-evm.dts b/arch/arm/boot/dts/am437x-sk-evm.dts
index f4af8ca0cd66..39866da8aa1a 100644
--- a/arch/arm/boot/dts/am437x-sk-evm.dts
+++ b/arch/arm/boot/dts/am437x-sk-evm.dts
@@ -549,7 +549,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
status = "okay";
};
diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts
index 08a8d1a705a4..634d09a3def7 100644
--- a/arch/arm/boot/dts/am43x-epos-evm.dts
+++ b/arch/arm/boot/dts/am43x-epos-evm.dts
@@ -298,7 +298,7 @@
>;
};
- dss_pins: dss_pins {
+ dss_pinctrl_default: dss_pinctrl_default {
pinctrl-single,pins = <
0x020 (PIN_OUTPUT_PULLUP | MUX_MODE1) /*gpmc ad 8 -> DSS DATA 23 */
0x024 (PIN_OUTPUT_PULLUP | MUX_MODE1)
@@ -331,6 +331,39 @@
>;
};
+ dss_pinctrl_sleep: dss_pinctrl_sleep {
+ pinctrl-single,pins = <
+ 0x020 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE2)
+ 0x024 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE2)
+ 0x028 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE2)
+ 0x02C (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE2)
+ 0x030 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE0)
+ 0x034 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE0)
+ 0x038 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE0)
+ 0x03C (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE0)
+ 0x0A0 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0A4 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0A8 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0AC (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0B0 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0B4 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0B8 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0BC (DS0_PULL_UP_DOWN_EN | PULL_DISABLE | INPUT_EN | MUX_MODE7)
+ 0x0C0 (DS0_PULL_UP_DOWN_EN | PULL_DISABLE | INPUT_EN | MUX_MODE7)
+ 0x0C4 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0C8 (DS0_PULL_UP_DOWN_EN | PULL_DISABLE | INPUT_EN | MUX_MODE7)
+ 0x0CC (DS0_PULL_UP_DOWN_EN | PULL_DISABLE | INPUT_EN | MUX_MODE7)
+ 0x0D0 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0D4 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0D8 (DS0_PULL_UP_DOWN_EN | PULL_DISABLE | INPUT_EN | MUX_MODE7)
+ 0x0DC (DS0_PULL_UP_DOWN_EN | PULL_DISABLE | INPUT_EN | MUX_MODE7)
+ 0x0E0 (DS0_PULL_UP_DOWN_EN | PULL_DISABLE | INPUT_EN | MUX_MODE7)
+ 0x0E4 (DS0_PULL_UP_DOWN_EN | PULL_DISABLE | INPUT_EN | MUX_MODE7)
+ 0x0E8 (DS0_PULL_UP_DOWN_EN | INPUT_EN | MUX_MODE7)
+ 0x0EC (DS0_PULL_UP_DOWN_EN | PULL_DISABLE | INPUT_EN | MUX_MODE7)
+ >;
+ };
+
lcd_pins: lcd_pins {
pinctrl-single,pins = <
/* GPMC CLK -> GPIO 2_1 to select LCD / HDMI */
@@ -923,7 +956,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
status = "okay";
};
@@ -1002,8 +1035,9 @@
&dss {
status = "ok";
- pinctrl-names = "default";
- pinctrl-0 = <&dss_pins>;
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&dss_pinctrl_default>;
+ pinctrl-1 = <&dss_pinctrl_sleep>;
port {
dpi_out: endpoint@0 {
diff --git a/arch/arm/boot/dts/am57xx-beagle-x15.dts b/arch/arm/boot/dts/am57xx-beagle-x15.dts
index 1351f24e5e35..e9e2d95e637b 100644
--- a/arch/arm/boot/dts/am57xx-beagle-x15.dts
+++ b/arch/arm/boot/dts/am57xx-beagle-x15.dts
@@ -8,7 +8,6 @@
/dts-v1/;
#include "dra74x.dtsi"
-#include <dt-bindings/clk/ti-dra7-atl.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
@@ -70,6 +69,14 @@
regulator-max-microvolt = <3300000>;
};
+ aic_dvdd: fixedregulator-aic_dvdd {
+ compatible = "regulator-fixed";
+ regulator-name = "aic_dvdd_fixed";
+ vin-supply = <&vdd_3v3>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
vtt_fixed: fixedregulator-vtt {
/* TPS51200 */
compatible = "regulator-fixed";
@@ -168,6 +175,20 @@
default-state = "off";
};
};
+
+ sound {
+ compatible = "ti,da830-evm-audio";
+ ti,model = "BeagleBoard-X15";
+ ti,audio-codec = <&tlv320aic3104>;
+ ti,mcasp-controller = <&mcasp3>;
+ clocks = <&clkout2_clk>;
+ clock-names = "mclk";
+ ti,audio-routing =
+ "Line Out", "LLOUT",
+ "Line Out", "RLOUT",
+ "MIC3L", "Line In", /* Mic2L/LINE2L on aic3104 */
+ "MIC3R", "Line In"; /* Mic2R/LINE2R on aic3104 */
+ };
};
&dra7_pmx_core {
@@ -422,6 +443,35 @@
>;
};
+ clkout2_pins_default: clkout2_pins_default {
+ pinctrl-single,pins = <
+ 0x294 (PIN_OUTPUT_PULLDOWN | MUX_MODE9) /* xref_clk0.clkout2 */
+ >;
+ };
+
+ clkout2_pins_sleep: clkout2_pins_sleep {
+ pinctrl-single,pins = <
+ 0x294 (PIN_INPUT | MUX_MODE15) /* xref_clk0.clkout2 */
+ >;
+ };
+
+ mcasp3_pins_default: mcasp3_pins_default {
+ pinctrl-single,pins = <
+ 0x324 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* mcasp3_aclkx.mcasp3_aclkx */
+ 0x328 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* mcasp3_fsx.mcasp3_fsx */
+ 0x32c (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mcasp3_axr0.mcasp3_axr0 */
+ 0x330 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* mcasp3_axr1.mcasp3_axr1 */
+ >;
+ };
+
+ mcasp3_pins_sleep: mcasp3_pins_sleep {
+ pinctrl-single,pins = <
+ 0x324 (PIN_INPUT | MUX_MODE15)
+ 0x328 (PIN_INPUT | MUX_MODE15)
+ 0x32c (PIN_INPUT | MUX_MODE15)
+ 0x330 (PIN_INPUT | MUX_MODE15)
+ >;
+ };
};
&i2c1 {
@@ -584,6 +634,21 @@
interrupt-parent = <&gpio7>;
interrupts = <16 IRQ_TYPE_LEVEL_LOW>;
};
+
+ tlv320aic3104: tlv320aic3104@18 {
+ compatible = "ti,tlv320aic3106";
+ reg = <0x18>;
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&clkout2_pins_default>;
+ pinctrl-1 = <&clkout2_pins_sleep>;
+ status = "okay";
+ adc-settle-ms = <40>;
+
+ AVDD-supply = <&vdd_3v3>;
+ IOVDD-supply = <&vdd_3v3>;
+ DRVDD-supply = <&vdd_3v3>;
+ DVDD-supply = <&aic_dvdd>;
+ };
};
&i2c3 {
@@ -719,6 +784,26 @@
};
};
+&mailbox3 {
+ status = "okay";
+ mbox_pru1_0: mbox_pru1_0 {
+ status = "okay";
+ };
+ mbox_pru1_1: mbox_pru1_1 {
+ status = "okay";
+ };
+};
+
+&mailbox4 {
+ status = "okay";
+ mbox_pru2_0: mbox_pru2_0 {
+ status = "okay";
+ };
+ mbox_pru2_1: mbox_pru2_1 {
+ status = "okay";
+ };
+};
+
&mailbox5 {
status = "okay";
mbox_ipu1_legacy: mbox_ipu1_legacy {
@@ -811,3 +896,43 @@
pinctrl-names = "default";
pinctrl-0 = <&usb2_pins>;
};
+
+&mcasp3 {
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&mcasp3_pins_default>;
+ pinctrl-1 = <&mcasp3_pins_sleep>;
+ status = "okay";
+
+ op-mode = <0>; /* MCASP_IIS_MODE */
+ tdm-slots = <2>;
+ /* 4 serializers */
+ serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
+ 1 2 0 0
+ >;
+};
+
+&pruss1 {
+ status = "okay";
+ pru1_0: pru@4b234000 {
+ mboxes = <&mailbox3 &mbox_pru1_0>;
+ status = "okay";
+ };
+
+ pru1_1: pru@4b238000 {
+ mboxes = <&mailbox3 &mbox_pru1_1>;
+ status = "okay";
+ };
+};
+
+&pruss2 {
+ status = "okay";
+ pru2_0: pru@4b2b4000 {
+ mboxes = <&mailbox4 &mbox_pru2_0>;
+ status = "okay";
+ };
+
+ pru2_1: pru@4b2b8000 {
+ mboxes = <&mailbox4 &mbox_pru2_1>;
+ status = "okay";
+ };
+};
diff --git a/arch/arm/boot/dts/dra7-evm.dts b/arch/arm/boot/dts/dra7-evm.dts
index 129e36696421..b02e0888ff73 100644
--- a/arch/arm/boot/dts/dra7-evm.dts
+++ b/arch/arm/boot/dts/dra7-evm.dts
@@ -67,6 +67,15 @@
cable-name = "USB-HOST";
};
+ evm_3v3_sd: fixedregulator-sd {
+ compatible = "regulator-fixed";
+ regulator-name = "evm_3v3_sd";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ enable-active-high;
+ gpio = <&pcf_gpio_21 5 0>;
+ };
+
evm_3v3_sw: fixedregulator-evm_3v3_sw {
compatible = "regulator-fixed";
regulator-name = "evm_3v3_sw";
@@ -456,6 +465,20 @@
>;
};
+ dcan1_pins_default: dcan1_pins_default {
+ pinctrl-single,pins = <
+ 0x3d0 (PIN_OUTPUT | MUX_MODE0) /* dcan1_tx */
+ 0x418 (PULL_DIS | MUX_MODE1) /* wakeup0.dcan1_rx */
+ >;
+ };
+
+ dcan1_pins_sleep: dcan1_pins_sleep {
+ pinctrl-single,pins = <
+ 0x3d0 (MUX_MODE15) /* dcan1_tx.off */
+ 0x418 (MUX_MODE15) /* wakeup0.off */
+ >;
+ };
+
atl_pins: pinmux_atl_pins {
pinctrl-single,pins = <
0x298 (PIN_OUTPUT | MUX_MODE5) /* xref_clk1.atl_clk1 */
@@ -531,7 +554,7 @@
/* VDD_GPU - over VDD_SMPS6 */
regulator-name = "smps6";
regulator-min-microvolt = <850000>;
- regulator-max-microvolt = <12500000>;
+ regulator-max-microvolt = <1250000>;
regulator-boot-on;
};
@@ -539,7 +562,7 @@
/* CORE_VDD */
regulator-name = "smps7";
regulator-min-microvolt = <850000>;
- regulator-max-microvolt = <1030000>;
+ regulator-max-microvolt = <1060000>;
regulator-always-on;
regulator-boot-on;
};
@@ -686,11 +709,14 @@
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&bt_uart3_pins>;
+ gpios = <&pcf_gpio_21 14 GPIO_ACTIVE_LOW>;
};
&mmc1 {
status = "okay";
- vmmc-supply = <&ldo1_reg>;
+ pbias-supply = <&pbias_mmc_reg>;
+ vmmc-supply = <&evm_3v3_sd>;
+ vmmc_aux-supply = <&ldo1_reg>;
bus-width = <4>;
/*
* SDCD signal is not being used here - using the fact that GPIO mode
@@ -824,7 +850,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
pinctrl-names = "default";
pinctrl-0 = <&usb1_pins>;
};
@@ -967,6 +993,13 @@
};
};
+&dcan1 {
+ status = "ok";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&dcan1_pins_default>;
+ pinctrl-1 = <&dcan1_pins_sleep>;
+};
+
&mailbox5 {
status = "okay";
mbox_ipu1_legacy: mbox_ipu1_legacy {
diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index 6d046736ebe1..9992d058fd65 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -968,6 +968,88 @@
status = "disabled";
};
+ pruss1: pruss@4b200000 {
+ compatible = "ti,am5728-pruss";
+ ti,hwmods = "pruss1";
+ reg = <0x4b200000 0x2000>,
+ <0x4b202000 0x2000>,
+ <0x4b210000 0x10000>,
+ <0x4b220000 0x2000>,
+ <0x4b226000 0x2000>;
+ reg-names = "dram0", "dram1", "shrdram2", "intc", "cfg";
+ interrupts = <GIC_SPI 186 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 193 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ status = "disabled";
+
+ pru1_0: pru@4b234000 {
+ compatible = "ti,pru-rproc";
+ reg = <0x4b234000 0x3000>,
+ <0x4b222000 0x400>,
+ <0x4b222400 0x100>;
+ reg-names = "iram", "control", "debug";
+ status = "disabled";
+ };
+
+ pru1_1: pru@4b238000 {
+ compatible = "ti,pru-rproc";
+ reg = <0x4b238000 0x3000>,
+ <0x4b224000 0x400>,
+ <0x4b224400 0x100>;
+ reg-names = "iram", "control", "debug";
+ status = "disabled";
+ };
+ };
+
+ pruss2: pruss@4b280000 {
+ compatible = "ti,am5728-pruss";
+ ti,hwmods = "pruss2";
+ reg = <0x4b280000 0x2000>,
+ <0x4b282000 0x2000>,
+ <0x4b290000 0x8000>,
+ <0x4b2a0000 0x2000>,
+ <0x4b2a6000 0x2000>;
+ reg-names = "dram0", "dram1", "shrdram2", "intc", "cfg";
+ interrupts = <GIC_SPI 196 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 197 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 198 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 199 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 200 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 201 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 202 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 203 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ status = "disabled";
+
+ pru2_0: pru@4b2b4000 {
+ compatible = "ti,pru-rproc";
+ reg = <0x4b2b4000 0x3000>,
+ <0x4b2a2000 0x400>,
+ <0x4b2a2400 0x100>;
+ reg-names = "iram", "control", "debug";
+ status = "disabled";
+ };
+
+ pru2_1: pru@4b2b8000 {
+ compatible = "ti,pru-rproc";
+ reg = <0x4b2b8000 0x3000>,
+ <0x4b2a4000 0x400>,
+ <0x4b2a4400 0x100>;
+ reg-names = "iram", "control", "debug";
+ status = "disabled";
+ };
+ };
+
abb_mpu: regulator-abb-mpu {
compatible = "ti,abb-v3";
regulator-name = "abb_mpu";
@@ -1286,7 +1368,6 @@
"wkupclk", "refclk",
"div-clk", "phy-div";
#phy-cells = <0>;
- id = <1>;
ti,hwmods = "pcie1-phy";
};
@@ -1307,7 +1388,6 @@
"div-clk", "phy-div";
#phy-cells = <0>;
ti,hwmods = "pcie2-phy";
- id = <2>;
status = "disabled";
};
};
@@ -1426,7 +1506,12 @@
usb1: usb@48890000 {
compatible = "snps,dwc3";
reg = <0x48890000 0x17000>;
- interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy1>, <&usb3_phy1>;
phy-names = "usb2-phy", "usb3-phy";
tx-fifo-resize;
@@ -1447,7 +1532,12 @@
usb2: usb@488d0000 {
compatible = "snps,dwc3";
reg = <0x488d0000 0x17000>;
- interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy2>;
phy-names = "usb2-phy";
tx-fifo-resize;
@@ -1470,7 +1560,12 @@
usb3: usb@48910000 {
compatible = "snps,dwc3";
reg = <0x48910000 0x17000>;
- interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 344 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
tx-fifo-resize;
maximum-speed = "high-speed";
dr_mode = "otg";
@@ -1490,7 +1585,12 @@
usb4: usb@48950000 {
compatible = "snps,dwc3";
reg = <0x48950000 0x17000>;
- interrupts = <GIC_SPI 345 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 345 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 345 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 346 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
tx-fifo-resize;
maximum-speed = "high-speed";
dr_mode = "otg";
@@ -1712,27 +1812,21 @@
clock-names = "fck";
};
- dcan1: d_can@481cc000 {
- compatible = "bosch,d_can";
+ dcan1: can@481cc000 {
+ compatible = "ti,dra7-d_can";
ti,hwmods = "dcan1";
- reg = <0x4ae3c000 0x2000>,
- <0x558 0x4>; /* index to RAMINIT reg within syscon */
- syscon = <&dra7_ctrl_core>;
- raminit-start-bit = <3>;
- raminit-done-bit = <1>;
+ reg = <0x4ae3c000 0x2000>;
+ syscon-raminit = <&dra7_ctrl_core 0x558 0>;
interrupts = <GIC_SPI 222 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&dcan1_sys_clk_mux>;
status = "disabled";
};
- dcan2: d_can@481d0000 {
- compatible = "bosch,d_can";
+ dcan2: can@481d0000 {
+ compatible = "ti,dra7-d_can";
ti,hwmods = "dcan2";
- reg = <0x48480000 0x2000>,
- <0x558 0x4>; /* index to RAMINIT reg within syscon */
- syscon = <&dra7_ctrl_core>;
- raminit-start-bit = <5>;
- raminit-done-bit = <2>;
+ reg = <0x48480000 0x2000>;
+ syscon-raminit = <&dra7_ctrl_core 0x558 1>;
interrupts = <GIC_SPI 225 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&sys_clkin1>;
status = "disabled";
diff --git a/arch/arm/boot/dts/dra72-evm.dts b/arch/arm/boot/dts/dra72-evm.dts
index 522be33582f1..9e8044230111 100644
--- a/arch/arm/boot/dts/dra72-evm.dts
+++ b/arch/arm/boot/dts/dra72-evm.dts
@@ -110,6 +110,15 @@
cable-name = "USB-HOST";
};
+ evm_3v3_sd: fixedregulator-sd {
+ compatible = "regulator-fixed";
+ regulator-name = "evm_3v3_sd";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ enable-active-high;
+ gpio = <&pcf_gpio_21 5 0>;
+ };
+
evm_3v3: fixedregulator-evm_3v3 {
compatible = "regulator-fixed";
regulator-name = "evm_3v3";
@@ -349,6 +358,20 @@
0xb8 (PIN_OUTPUT | MUX_MODE1) /* gpmc_cs2.qspi1_cs0 */
>;
};
+
+ dcan1_pins_default: dcan1_pins_default {
+ pinctrl-single,pins = <
+ 0x3d0 (PIN_OUTPUT | MUX_MODE0) /* dcan1_tx */
+ 0x418 (PULL_DIS | MUX_MODE1) /* wakeup0.dcan1_rx */
+ >;
+ };
+
+ dcan1_pins_sleep: dcan1_pins_sleep {
+ pinctrl-single,pins = <
+ 0x3d0 (MUX_MODE15) /* dcan1_tx.off */
+ 0x418 (MUX_MODE15) /* wakeup0.off */
+ >;
+ };
};
&i2c1 {
@@ -388,7 +411,7 @@
/* VDD_CORE */
regulator-name = "smps2";
regulator-min-microvolt = <850000>;
- regulator-max-microvolt = <1030000>;
+ regulator-max-microvolt = <1060000>;
regulator-boot-on;
regulator-always-on;
};
@@ -548,13 +571,21 @@
&mmc1 {
/* Using default configured pins */
status = "okay";
- vmmc-supply = <&ldo1_reg>;
+ pbias-supply = <&pbias_mmc_reg>;
+ vmmc-supply = <&evm_3v3_sd>;
+ vmmc_aux-supply = <&ldo1_reg>;
bus-width = <4>;
/*
* SDCD signal is not being used here - using the fact that GPIO mode
* is always hardwired.
*/
cd-gpios = <&gpio6 27 0>;
+ sd-uhs-sdr104;
+ sd-uhs-sdr50;
+ sd-uhs-ddr50;
+ sd-uhs-sdr25;
+ sd-uhs-sdr12;
+ max-frequency = <192000000>;
};
&mmc2 {
@@ -563,6 +594,8 @@
vmmc-supply = <&evm_3v3>;
bus-width = <8>;
ti,non-removable;
+ mmc-hs200-1_8v;
+ max-frequency = <192000000>;
};
&mac {
@@ -816,7 +849,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
pinctrl-names = "default";
pinctrl-0 = <&usb1_pins>;
};
@@ -891,3 +924,10 @@
};
};
};
+
+&dcan1 {
+ status = "ok";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&dcan1_pins_default>;
+ pinctrl-1 = <&dcan1_pins_sleep>;
+};
diff --git a/arch/arm/boot/dts/dra74x.dtsi b/arch/arm/boot/dts/dra74x.dtsi
index 2e0c0e34b1d3..8a36afcd9250 100644
--- a/arch/arm/boot/dts/dra74x.dtsi
+++ b/arch/arm/boot/dts/dra74x.dtsi
@@ -136,6 +136,32 @@
};
};
+&mailbox3 {
+ mbox_pru1_0: mbox_pru1_0 {
+ ti,mbox-tx = <0 0 0>;
+ ti,mbox-rx = <1 0 0>;
+ status = "disabled";
+ };
+ mbox_pru1_1: mbox_pru1_1 {
+ ti,mbox-tx = <2 0 0>;
+ ti,mbox-rx = <3 0 0>;
+ status = "disabled";
+ };
+};
+
+&mailbox4 {
+ mbox_pru2_0: mbox_pru2_0 {
+ ti,mbox-tx = <0 0 0>;
+ ti,mbox-rx = <1 0 0>;
+ status = "disabled";
+ };
+ mbox_pru2_1: mbox_pru2_1 {
+ ti,mbox-tx = <2 0 0>;
+ ti,mbox-rx = <3 0 0>;
+ status = "disabled";
+ };
+};
+
&mailbox5 {
mbox_ipu1_legacy: mbox_ipu1_legacy {
ti,mbox-tx = <6 2 2>;
diff --git a/arch/arm/boot/dts/dra7xx-clocks.dtsi b/arch/arm/boot/dts/dra7xx-clocks.dtsi
index 3d713ac352e7..8d5486cf7217 100644
--- a/arch/arm/boot/dts/dra7xx-clocks.dtsi
+++ b/arch/arm/boot/dts/dra7xx-clocks.dtsi
@@ -1421,6 +1421,14 @@
ti,dividers = <1>, <8>;
};
+ clkout2_clk: clkout2_clk {
+ #clock-cells = <0>;
+ compatible = "ti,gate-clock";
+ clocks = <&clkoutmux2_clk_mux>;
+ ti,bit-shift = <8>;
+ reg = <0x06b0>;
+ };
+
l3init_960m_gfclk: l3init_960m_gfclk {
#clock-cells = <0>;
compatible = "ti,gate-clock";
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index b7dec41e32af..ebdb4157e199 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -511,7 +511,12 @@
dwc3 {
compatible = "synopsys,dwc3";
reg = <0x12000000 0x10000>;
- interrupts = <0 72 0>;
+ interrupts = <0 72 0>,
+ <0 72 0>,
+ <0 72 0>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
usb-phy = <&usb2_phy &usb3_phy>;
};
};
diff --git a/arch/arm/boot/dts/omap3-beagle-xm.dts b/arch/arm/boot/dts/omap3-beagle-xm.dts
index cf0be662297e..d0ff998238bc 100644
--- a/arch/arm/boot/dts/omap3-beagle-xm.dts
+++ b/arch/arm/boot/dts/omap3-beagle-xm.dts
@@ -268,6 +268,7 @@
&mmc1 {
vmmc-supply = <&vmmc1>;
vmmc_aux-supply = <&vsim>;
+ pbias-supply = <&pbias_mmc_reg>;
bus-width = <8>;
};
diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts
index 3c3e6da1deac..82d32567511e 100644
--- a/arch/arm/boot/dts/omap3-beagle.dts
+++ b/arch/arm/boot/dts/omap3-beagle.dts
@@ -258,6 +258,7 @@
&mmc1 {
vmmc-supply = <&vmmc1>;
vmmc_aux-supply = <&vsim>;
+ pbias-supply = <&pbias_mmc_reg>;
bus-width = <8>;
};
diff --git a/arch/arm/boot/dts/omap3-devkit8000.dts b/arch/arm/boot/dts/omap3-devkit8000.dts
index bf5a515a3247..015676edcbb3 100644
--- a/arch/arm/boot/dts/omap3-devkit8000.dts
+++ b/arch/arm/boot/dts/omap3-devkit8000.dts
@@ -86,6 +86,7 @@
&mmc1 {
vmmc-supply = <&vmmc1>;
vmmc_aux-supply = <&vsim>;
+ pbias-supply = <&pbias_mmc_reg>;
bus-width = <8>;
};
diff --git a/arch/arm/boot/dts/omap3-igep.dtsi b/arch/arm/boot/dts/omap3-igep.dtsi
index b97736d98a64..d3ab32440222 100644
--- a/arch/arm/boot/dts/omap3-igep.dtsi
+++ b/arch/arm/boot/dts/omap3-igep.dtsi
@@ -178,6 +178,7 @@
pinctrl-0 = <&mmc1_pins>;
vmmc-supply = <&vmmc1>;
vmmc_aux-supply = <&vsim>;
+ pbias-supply = <&pbias_mmc_reg>;
bus-width = <4>;
};
diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi
index bad058d159f5..32e5242051d8 100644
--- a/arch/arm/boot/dts/omap4-panda-common.dtsi
+++ b/arch/arm/boot/dts/omap4-panda-common.dtsi
@@ -451,6 +451,7 @@
&mmc1 {
vmmc-supply = <&vmmc>;
+ pbias-supply = <&pbias_mmc_reg>;
bus-width = <8>;
};
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index 3e1da43068f6..c4c42db884be 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -463,6 +463,7 @@
&mmc1 {
vmmc-supply = <&vmmc>;
+ pbias-supply = <&pbias_mmc_reg>;
bus-width = <8>;
};
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index b4dbcf48cf61..f6b7e4f850b3 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -902,7 +902,7 @@
reg = <0x58002000 0x1000>;
status = "disabled";
ti,hwmods = "dss_rfbi";
- clocks = <&dss_dss_clk>, <&dss_fck>;
+ clocks = <&dss_dss_clk>, <&l3_div_ck>;
clock-names = "fck", "ick";
};
diff --git a/arch/arm/boot/dts/omap44xx-clocks.dtsi b/arch/arm/boot/dts/omap44xx-clocks.dtsi
index c821ff5e9b8d..f2c48f09824e 100644
--- a/arch/arm/boot/dts/omap44xx-clocks.dtsi
+++ b/arch/arm/boot/dts/omap44xx-clocks.dtsi
@@ -1018,14 +1018,6 @@
reg = <0x1120>;
};
- dss_fck: dss_fck {
- #clock-cells = <0>;
- compatible = "ti,gate-clock";
- clocks = <&l3_div_ck>;
- ti,bit-shift = <1>;
- reg = <0x1120>;
- };
-
fdif_fck: fdif_fck {
#clock-cells = <0>;
compatible = "ti,divider-clock";
diff --git a/arch/arm/boot/dts/omap5-uevm.dts b/arch/arm/boot/dts/omap5-uevm.dts
index 93658cfb27d5..28c8c972c7a3 100644
--- a/arch/arm/boot/dts/omap5-uevm.dts
+++ b/arch/arm/boot/dts/omap5-uevm.dts
@@ -282,6 +282,7 @@
&mmc1 {
vmmc-supply = <&ldo9_reg>;
+ pbias-supply = <&pbias_mmc_reg>;
bus-width = <4>;
};
diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi
index 131c5afcb84b..a432d245541d 100644
--- a/arch/arm/boot/dts/omap5.dtsi
+++ b/arch/arm/boot/dts/omap5.dtsi
@@ -830,7 +830,12 @@
dwc3@4a030000 {
compatible = "snps,dwc3";
reg = <0x4a030000 0x10000>;
- interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "peripheral",
+ "host",
+ "otg";
phys = <&usb2_phy>, <&usb3_phy>;
phy-names = "usb2-phy", "usb3-phy";
dr_mode = "peripheral";
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 71a06b293489..3e635eed9c6c 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -43,16 +43,6 @@ struct cpu_context_save {
__u32 extra[2]; /* Xscale 'acc' register, etc */
};
-struct arm_restart_block {
- union {
- /* For user cache flushing */
- struct {
- unsigned long start;
- unsigned long end;
- } cache;
- };
-};
-
/*
* low level task data that entry.S needs immediate access to.
* __switch_to() assumes cpu_context follows immediately after cpu_domain.
@@ -78,7 +68,6 @@ struct thread_info {
unsigned long thumbee_state; /* ThumbEE Handler Base register */
#endif
struct restart_block restart_block;
- struct arm_restart_block arm_restart_block;
};
#define INIT_THREAD_INFO(tsk) \
diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
index 18a76282970e..380c20fb9c85 100644
--- a/arch/arm/kernel/kprobes-common.c
+++ b/arch/arm/kernel/kprobes-common.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <asm/system_info.h>
+#include <asm/opcodes.h>
#include "kprobes.h"
@@ -305,7 +306,8 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
if (handler) {
/* We can emulate the instruction in (possibly) modified form */
- asi->insn[0] = (insn & 0xfff00000) | (rn << 16) | reglist;
+ asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) |
+ (rn << 16) | reglist);
asi->insn_handler = handler;
return INSN_GOOD;
}
@@ -334,13 +336,14 @@ prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
#ifdef CONFIG_THUMB2_KERNEL
if (thumb) {
u16 *thumb_insn = (u16 *)asi->insn;
- thumb_insn[1] = 0x4770; /* Thumb bx lr */
- thumb_insn[2] = 0x4770; /* Thumb bx lr */
+ /* Thumb bx lr */
+ thumb_insn[1] = __opcode_to_mem_thumb16(0x4770);
+ thumb_insn[2] = __opcode_to_mem_thumb16(0x4770);
return insn;
}
- asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
+ asi->insn[1] = __opcode_to_mem_arm(0xe12fff1e); /* ARM bx lr */
#else
- asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
+ asi->insn[1] = __opcode_to_mem_arm(0xe1a0f00e); /* mov pc, lr */
#endif
/* Make an ARM instruction unconditional */
if (insn < 0xe0000000)
@@ -360,12 +363,12 @@ set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
if (thumb) {
u16 *ip = (u16 *)asi->insn;
if (is_wide_instruction(insn))
- *ip++ = insn >> 16;
- *ip++ = insn;
+ *ip++ = __opcode_to_mem_thumb16(insn >> 16);
+ *ip++ = __opcode_to_mem_thumb16(insn);
return;
}
#endif
- asi->insn[0] = insn;
+ asi->insn[0] = __opcode_to_mem_arm(insn);
}
/*
diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
index 6123daf397a7..241222c66a13 100644
--- a/arch/arm/kernel/kprobes-thumb.c
+++ b/arch/arm/kernel/kprobes-thumb.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/module.h>
+#include <asm/opcodes.h>
#include "kprobes.h"
@@ -163,9 +164,9 @@ t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi);
/* Fixup modified instruction to have halfwords in correct order...*/
- insn = asi->insn[0];
- ((u16 *)asi->insn)[0] = insn >> 16;
- ((u16 *)asi->insn)[1] = insn & 0xffff;
+ insn = __mem_to_opcode_arm(asi->insn[0]);
+ ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16);
+ ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff);
return ret;
}
@@ -1153,7 +1154,7 @@ t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
insn &= ~0x00ff;
insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
- ((u16 *)asi->insn)[0] = insn;
+ ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn);
asi->insn_handler = t16_emulate_hiregs;
return INSN_GOOD;
}
@@ -1182,8 +1183,10 @@ t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi)
* and call it with R9=SP and LR in the register list represented
* by R8.
*/
- ((u16 *)asi->insn)[0] = 0xe929; /* 1st half STMDB R9!,{} */
- ((u16 *)asi->insn)[1] = insn & 0x1ff; /* 2nd half (register list) */
+ /* 1st half STMDB R9!,{} */
+ ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929);
+ /* 2nd half (register list) */
+ ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
asi->insn_handler = t16_emulate_push;
return INSN_GOOD;
}
@@ -1232,8 +1235,10 @@ t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi)
* and call it with R9=SP and PC in the register list represented
* by R8.
*/
- ((u16 *)asi->insn)[0] = 0xe8b9; /* 1st half LDMIA R9!,{} */
- ((u16 *)asi->insn)[1] = insn & 0x1ff; /* 2nd half (register list) */
+ /* 1st half LDMIA R9!,{} */
+ ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9);
+ /* 2nd half (register list) */
+ ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc
: t16_emulate_pop_nopc;
return INSN_GOOD;
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
index a7b621ece23d..49a87b6d0bf3 100644
--- a/arch/arm/kernel/kprobes.c
+++ b/arch/arm/kernel/kprobes.c
@@ -26,6 +26,7 @@
#include <linux/stop_machine.h>
#include <linux/stringify.h>
#include <asm/traps.h>
+#include <asm/opcodes.h>
#include <asm/cacheflush.h>
#include "kprobes.h"
@@ -62,10 +63,10 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
#ifdef CONFIG_THUMB2_KERNEL
thumb = true;
addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
- insn = ((u16 *)addr)[0];
+ insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]);
if (is_wide_instruction(insn)) {
- insn <<= 16;
- insn |= ((u16 *)addr)[1];
+ u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]);
+ insn = __opcode_thumb32_compose(insn, inst2);
decode_insn = thumb32_kprobe_decode_insn;
} else
decode_insn = thumb16_kprobe_decode_insn;
@@ -73,7 +74,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
thumb = false;
if (addr & 0x3)
return -EINVAL;
- insn = *p->addr;
+ insn = __mem_to_opcode_arm(*p->addr);
decode_insn = arm_kprobe_decode_insn;
#endif
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 9265b8bb529a..3f314433d653 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -510,8 +510,6 @@ static int bad_syscall(int n, struct pt_regs *regs)
return regs->ARM_r0;
}
-static long do_cache_op_restart(struct restart_block *);
-
static inline int
__do_cache_op(unsigned long start, unsigned long end)
{
@@ -520,24 +518,8 @@ __do_cache_op(unsigned long start, unsigned long end)
do {
unsigned long chunk = min(PAGE_SIZE, end - start);
- if (signal_pending(current)) {
- struct thread_info *ti = current_thread_info();
-
- ti->restart_block = (struct restart_block) {
- .fn = do_cache_op_restart,
- };
-
- ti->arm_restart_block = (struct arm_restart_block) {
- {
- .cache = {
- .start = start,
- .end = end,
- },
- },
- };
-
- return -ERESTART_RESTARTBLOCK;
- }
+ if (fatal_signal_pending(current))
+ return 0;
ret = flush_cache_user_range(start, start + chunk);
if (ret)
@@ -550,15 +532,6 @@ __do_cache_op(unsigned long start, unsigned long end)
return 0;
}
-static long do_cache_op_restart(struct restart_block *unused)
-{
- struct arm_restart_block *restart_block;
-
- restart_block = &current_thread_info()->arm_restart_block;
- return __do_cache_op(restart_block->cache.start,
- restart_block->cache.end);
-}
-
static inline int
do_cache_op(unsigned long start, unsigned long end, int flags)
{
diff --git a/arch/arm/mach-omap2/clockdomains7xx_data.c b/arch/arm/mach-omap2/clockdomains7xx_data.c
index bc9905cfe29f..6e571fbd6ad1 100644
--- a/arch/arm/mach-omap2/clockdomains7xx_data.c
+++ b/arch/arm/mach-omap2/clockdomains7xx_data.c
@@ -492,7 +492,7 @@ static struct clockdomain l4cfg_7xx_clkdm = {
.cm_inst = DRA7XX_CM_CORE_CORE_INST,
.clkdm_offs = DRA7XX_CM_CORE_CORE_L4CFG_CDOFFS,
.dep_bit = DRA7XX_L4CFG_STATDEP_SHIFT,
- .flags = CLKDM_CAN_HWSUP,
+ .flags = CLKDM_CAN_DISABLE_AUTO,
};
static struct clockdomain dma_7xx_clkdm = {
@@ -677,7 +677,7 @@ static struct clockdomain wkupaon_7xx_clkdm = {
.cm_inst = DRA7XX_PRM_WKUPAON_CM_INST,
.clkdm_offs = DRA7XX_PRM_WKUPAON_CM_WKUPAON_CDOFFS,
.dep_bit = DRA7XX_WKUPAON_STATDEP_SHIFT,
- .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP,
+ .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_DISABLE_AUTO,
};
static struct clockdomain eve1_7xx_clkdm = {
diff --git a/arch/arm/mach-omap2/control.h b/arch/arm/mach-omap2/control.h
index 8acafadd29c3..4ec6218193f5 100644
--- a/arch/arm/mach-omap2/control.h
+++ b/arch/arm/mach-omap2/control.h
@@ -263,6 +263,10 @@
#define DRA7XX_CTRL_CORE_DSP_RST_VECT_MASK (0x3FFFFF << 0)
#define DRA7XX_CTRL_CORE_DSP_RST_VECT_SHIFT 10
+/* DRA7XX CONTROL CORE BOOTSTRAP */
+#define DRA7_CTRL_CORE_BOOTSTRAP 0x6c4
+#define DRA7_SPEEDSELECT_MASK (0x3 << 8)
+
/*
* REVISIT: This list of registers is not comprehensive - there are more
* that should be added.
diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c
index 801244ad82ec..2fc4912f7b75 100644
--- a/arch/arm/mach-omap2/id.c
+++ b/arch/arm/mach-omap2/id.c
@@ -471,11 +471,15 @@ void __init omap3xxx_check_revision(void)
cpu_rev = "1.0";
break;
case 1:
- /* FALLTHROUGH */
- default:
omap_revision = AM437X_REV_ES1_1;
cpu_rev = "1.1";
break;
+ case 2:
+ /* FALLTHROUGH */
+ default:
+ omap_revision = AM437X_REV_ES1_2;
+ cpu_rev = "1.2";
+ break;
}
break;
case 0xb8f2:
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index ecf07f6acf3a..f22b4dea0aa3 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -2749,11 +2749,33 @@ static int __init _setup(struct omap_hwmod *oh, void *data)
if (oh->_state != _HWMOD_STATE_INITIALIZED)
return 0;
+ if (oh->parent_hwmod) {
+ int r;
+
+ r = _enable(oh->parent_hwmod);
+ WARN(r, "hwmod: %s: setup: failed to enable parent hwmod %s\n",
+ oh->name, oh->parent_hwmod->name);
+ }
+
_setup_iclk_autoidle(oh);
if (!_setup_reset(oh))
_setup_postsetup(oh);
+ if (oh->parent_hwmod) {
+ u8 postsetup_state;
+
+ postsetup_state = oh->parent_hwmod->_postsetup_state;
+
+ if (postsetup_state == _HWMOD_STATE_IDLE)
+ _idle(oh->parent_hwmod);
+ else if (postsetup_state == _HWMOD_STATE_DISABLED)
+ _shutdown(oh->parent_hwmod);
+ else if (postsetup_state != _HWMOD_STATE_ENABLED)
+ WARN(1, "hwmod: %s: unknown postsetup state %d! defaulting to enabled\n",
+ oh->parent_hwmod->name, postsetup_state);
+ }
+
return 0;
}
@@ -2973,9 +2995,6 @@ static int _omap2xxx_wait_target_ready(struct omap_hwmod *oh)
if (oh->flags & HWMOD_NO_IDLEST)
return 0;
- if (!_find_mpu_rt_port(oh))
- return 0;
-
/* XXX check module SIDLEMODE, hardreset status, enabled clocks */
return omap2xxx_cm_wait_module_ready(oh->prcm.omap2.module_offs,
@@ -3000,9 +3019,6 @@ static int _omap3xxx_wait_target_ready(struct omap_hwmod *oh)
if (oh->flags & HWMOD_NO_IDLEST)
return 0;
- if (!_find_mpu_rt_port(oh))
- return 0;
-
/* XXX check module SIDLEMODE, hardreset status, enabled clocks */
return omap3xxx_cm_wait_module_ready(oh->prcm.omap2.module_offs,
@@ -3027,9 +3043,6 @@ static int _omap4_wait_target_ready(struct omap_hwmod *oh)
if (oh->flags & HWMOD_NO_IDLEST || !oh->clkdm)
return 0;
- if (!_find_mpu_rt_port(oh))
- return 0;
-
/* XXX check module SIDLEMODE, hardreset status */
return omap4_cminst_wait_module_ready(oh->clkdm->prcm_partition,
@@ -3055,9 +3068,6 @@ static int _am33xx_wait_target_ready(struct omap_hwmod *oh)
if (oh->flags & HWMOD_NO_IDLEST)
return 0;
- if (!_find_mpu_rt_port(oh))
- return 0;
-
/* XXX check module SIDLEMODE, hardreset status */
return am33xx_cm_wait_module_ready(oh->clkdm->cm_inst,
diff --git a/arch/arm/mach-omap2/omap_hwmod.h b/arch/arm/mach-omap2/omap_hwmod.h
index 7cf13c8fa432..cc1a121dc357 100644
--- a/arch/arm/mach-omap2/omap_hwmod.h
+++ b/arch/arm/mach-omap2/omap_hwmod.h
@@ -639,6 +639,7 @@ struct omap_hwmod_link {
* @flags: hwmod flags (documented below)
* @_lock: spinlock serializing operations on this hwmod
* @node: list node for hwmod list (internal use)
+ * @parent_hwmod: (temporary) a pointer to the hierarchical parent of this hwmod
*
* @main_clk refers to this module's "main clock," which for our
* purposes is defined as "the functional clock needed for register
@@ -649,6 +650,12 @@ struct omap_hwmod_link {
* the omap_hwmod code and should not be set during initialization.
*
* @masters and @slaves are now deprecated.
+ *
+ * @parent_hwmod is temporary; there should be no need for it, as this
+ * information should already be expressed in the OCP interface
+ * structures. @parent_hwmod is present as a workaround until we improve
+ * handling for hwmods with multiple parents (e.g., OMAP4+ DSS with
+ * multiple register targets across different interconnects).
*/
struct omap_hwmod {
const char *name;
@@ -686,6 +693,7 @@ struct omap_hwmod {
u8 _int_flags;
u8 _state;
u8 _postsetup_state;
+ struct omap_hwmod *parent_hwmod;
};
/*
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index dab6085bac9b..d587f54e02b4 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -588,6 +588,7 @@ static struct omap_hwmod omap44xx_dss_hwmod = {
.omap4 = {
.clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET,
.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
+ .modulemode = MODULEMODE_SWCTRL,
},
},
.opt_clks = dss_opt_clks,
@@ -646,7 +647,8 @@ static struct omap_hwmod omap44xx_dss_dispc_hwmod = {
.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
},
},
- .dev_attr = &omap44xx_dss_dispc_dev_attr
+ .dev_attr = &omap44xx_dss_dispc_dev_attr,
+ .parent_hwmod = &omap44xx_dss_hwmod,
};
/*
@@ -700,6 +702,7 @@ static struct omap_hwmod omap44xx_dss_dsi1_hwmod = {
},
.opt_clks = dss_dsi1_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_dsi1_opt_clks),
+ .parent_hwmod = &omap44xx_dss_hwmod,
};
/* dss_dsi2 */
@@ -732,6 +735,7 @@ static struct omap_hwmod omap44xx_dss_dsi2_hwmod = {
},
.opt_clks = dss_dsi2_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_dsi2_opt_clks),
+ .parent_hwmod = &omap44xx_dss_hwmod,
};
/*
@@ -789,6 +793,7 @@ static struct omap_hwmod omap44xx_dss_hdmi_hwmod = {
},
.opt_clks = dss_hdmi_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_hdmi_opt_clks),
+ .parent_hwmod = &omap44xx_dss_hwmod,
};
/*
@@ -818,7 +823,7 @@ static struct omap_hwmod_dma_info omap44xx_dss_rfbi_sdma_reqs[] = {
};
static struct omap_hwmod_opt_clk dss_rfbi_opt_clks[] = {
- { .role = "ick", .clk = "dss_fck" },
+ { .role = "ick", .clk = "l3_div_ck" },
};
static struct omap_hwmod omap44xx_dss_rfbi_hwmod = {
@@ -835,6 +840,7 @@ static struct omap_hwmod omap44xx_dss_rfbi_hwmod = {
},
.opt_clks = dss_rfbi_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_rfbi_opt_clks),
+ .parent_hwmod = &omap44xx_dss_hwmod,
};
/*
@@ -858,6 +864,7 @@ static struct omap_hwmod omap44xx_dss_venc_hwmod = {
.context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET,
},
},
+ .parent_hwmod = &omap44xx_dss_hwmod,
};
/*
@@ -3673,7 +3680,7 @@ static struct omap_hwmod_addr_space omap44xx_dss_dma_addrs[] = {
static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss = {
.master = &omap44xx_l3_main_2_hwmod,
.slave = &omap44xx_dss_hwmod,
- .clk = "dss_fck",
+ .clk = "l3_div_ck",
.addr = omap44xx_dss_dma_addrs,
.user = OCP_USER_SDMA,
};
@@ -3709,7 +3716,7 @@ static struct omap_hwmod_addr_space omap44xx_dss_dispc_dma_addrs[] = {
static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_dispc = {
.master = &omap44xx_l3_main_2_hwmod,
.slave = &omap44xx_dss_dispc_hwmod,
- .clk = "dss_fck",
+ .clk = "l3_div_ck",
.addr = omap44xx_dss_dispc_dma_addrs,
.user = OCP_USER_SDMA,
};
@@ -3745,7 +3752,7 @@ static struct omap_hwmod_addr_space omap44xx_dss_dsi1_dma_addrs[] = {
static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_dsi1 = {
.master = &omap44xx_l3_main_2_hwmod,
.slave = &omap44xx_dss_dsi1_hwmod,
- .clk = "dss_fck",
+ .clk = "l3_div_ck",
.addr = omap44xx_dss_dsi1_dma_addrs,
.user = OCP_USER_SDMA,
};
@@ -3781,7 +3788,7 @@ static struct omap_hwmod_addr_space omap44xx_dss_dsi2_dma_addrs[] = {
static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_dsi2 = {
.master = &omap44xx_l3_main_2_hwmod,
.slave = &omap44xx_dss_dsi2_hwmod,
- .clk = "dss_fck",
+ .clk = "l3_div_ck",
.addr = omap44xx_dss_dsi2_dma_addrs,
.user = OCP_USER_SDMA,
};
@@ -3817,7 +3824,7 @@ static struct omap_hwmod_addr_space omap44xx_dss_hdmi_dma_addrs[] = {
static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_hdmi = {
.master = &omap44xx_l3_main_2_hwmod,
.slave = &omap44xx_dss_hdmi_hwmod,
- .clk = "dss_fck",
+ .clk = "l3_div_ck",
.addr = omap44xx_dss_hdmi_dma_addrs,
.user = OCP_USER_SDMA,
};
@@ -3853,7 +3860,7 @@ static struct omap_hwmod_addr_space omap44xx_dss_rfbi_dma_addrs[] = {
static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_rfbi = {
.master = &omap44xx_l3_main_2_hwmod,
.slave = &omap44xx_dss_rfbi_hwmod,
- .clk = "dss_fck",
+ .clk = "l3_div_ck",
.addr = omap44xx_dss_rfbi_dma_addrs,
.user = OCP_USER_SDMA,
};
@@ -3889,7 +3896,7 @@ static struct omap_hwmod_addr_space omap44xx_dss_venc_dma_addrs[] = {
static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_venc = {
.master = &omap44xx_l3_main_2_hwmod,
.slave = &omap44xx_dss_venc_hwmod,
- .clk = "dss_fck",
+ .clk = "l3_div_ck",
.addr = omap44xx_dss_venc_dma_addrs,
.user = OCP_USER_SDMA,
};
diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
index a09f9dabc568..b8f2c10f0c74 100644
--- a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
@@ -451,6 +451,7 @@ static struct omap_hwmod omap54xx_dss_dispc_hwmod = {
.opt_clks = dss_dispc_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_dispc_opt_clks),
.dev_attr = &dss_dispc_dev_attr,
+ .parent_hwmod = &omap54xx_dss_hwmod,
};
/*
@@ -492,6 +493,7 @@ static struct omap_hwmod omap54xx_dss_dsi1_a_hwmod = {
},
.opt_clks = dss_dsi1_a_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_dsi1_a_opt_clks),
+ .parent_hwmod = &omap54xx_dss_hwmod,
};
/* dss_dsi1_c */
@@ -512,6 +514,7 @@ static struct omap_hwmod omap54xx_dss_dsi1_c_hwmod = {
},
.opt_clks = dss_dsi1_c_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_dsi1_c_opt_clks),
+ .parent_hwmod = &omap54xx_dss_hwmod,
};
/*
@@ -551,6 +554,7 @@ static struct omap_hwmod omap54xx_dss_hdmi_hwmod = {
},
.opt_clks = dss_hdmi_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_hdmi_opt_clks),
+ .parent_hwmod = &omap54xx_dss_hwmod,
};
/*
@@ -590,6 +594,7 @@ static struct omap_hwmod omap54xx_dss_rfbi_hwmod = {
},
.opt_clks = dss_rfbi_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_rfbi_opt_clks),
+ .parent_hwmod = &omap54xx_dss_hwmod,
};
/*
diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c
index 7e95fc04341b..01bf78dddb08 100644
--- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c
@@ -723,6 +723,7 @@ static struct omap_hwmod dra7xx_dss_dispc_hwmod = {
},
},
.dev_attr = &dss_dispc_dev_attr,
+ .parent_hwmod = &dra7xx_dss_hwmod,
};
/*
@@ -764,6 +765,7 @@ static struct omap_hwmod dra7xx_dss_hdmi_hwmod = {
},
.opt_clks = dss_hdmi_opt_clks,
.opt_clks_cnt = ARRAY_SIZE(dss_hdmi_opt_clks),
+ .parent_hwmod = &dra7xx_dss_hwmod,
};
/* AES (the 'P' (public) device) */
@@ -2093,6 +2095,42 @@ static struct omap_hwmod dra7xx_pcie2_phy_hwmod = {
};
/*
+ * 'pru-icss' class
+ * Programmable Real-Time Unit and Industrial Communication Subsystem
+ */
+static struct omap_hwmod_class dra7xx_pruss_hwmod_class = {
+ .name = "pruss",
+};
+
+/* pru-icss1 */
+static struct omap_hwmod dra7xx_pruss1_hwmod = {
+ .name = "pruss1",
+ .class = &dra7xx_pruss_hwmod_class,
+ .clkdm_name = "l4per2_clkdm",
+ .prcm = {
+ .omap4 = {
+ .clkctrl_offs = DRA7XX_CM_L4PER2_PRUSS1_CLKCTRL_OFFSET,
+ .context_offs = DRA7XX_RM_L4PER2_PRUSS1_CONTEXT_OFFSET,
+ .modulemode = MODULEMODE_SWCTRL,
+ },
+ },
+};
+
+/* pru-icss2 */
+static struct omap_hwmod dra7xx_pruss2_hwmod = {
+ .name = "pruss2",
+ .class = &dra7xx_pruss_hwmod_class,
+ .clkdm_name = "l4per2_clkdm",
+ .prcm = {
+ .omap4 = {
+ .clkctrl_offs = DRA7XX_CM_L4PER2_PRUSS2_CLKCTRL_OFFSET,
+ .context_offs = DRA7XX_RM_L4PER2_PRUSS2_CONTEXT_OFFSET,
+ .modulemode = MODULEMODE_SWCTRL,
+ },
+ },
+};
+
+/*
* 'qspi' class
*
*/
@@ -3710,6 +3748,22 @@ static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pcie2_phy = {
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
+/* l4_cfg -> pruss1 */
+static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pruss1 = {
+ .master = &dra7xx_l4_cfg_hwmod,
+ .slave = &dra7xx_pruss1_hwmod,
+ .clk = "dpll_gmac_h13x2_ck",
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_cfg -> pruss2 */
+static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pruss2 = {
+ .master = &dra7xx_l4_cfg_hwmod,
+ .slave = &dra7xx_pruss2_hwmod,
+ .clk = "dpll_gmac_h13x2_ck",
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
static struct omap_hwmod_addr_space dra7xx_qspi_addrs[] = {
{
.pa_start = 0x4b300000,
@@ -4186,6 +4240,8 @@ static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
&dra7xx_l4_cfg__pcie2,
&dra7xx_l4_cfg__pcie1_phy,
&dra7xx_l4_cfg__pcie2_phy,
+ &dra7xx_l4_cfg__pruss1, /* AM57xx only */
+ &dra7xx_l4_cfg__pruss2, /* AM57xx only */
&dra7xx_l3_main_1__qspi,
&dra7xx_l4_per3__rtcss,
&dra7xx_l4_cfg__sata,
diff --git a/arch/arm/mach-omap2/pm33xx.c b/arch/arm/mach-omap2/pm33xx.c
index 15a285154de7..95aef0e3443d 100644
--- a/arch/arm/mach-omap2/pm33xx.c
+++ b/arch/arm/mach-omap2/pm33xx.c
@@ -115,6 +115,7 @@ int am33xx_do_sram_cpuidle(u32 wfi_flags, u32 m3_flags)
am33xx_pm->ipc.reg1 = IPC_CMD_IDLE;
am33xx_pm->ipc.reg2 = DS_IPC_DEFAULT;
am33xx_pm->ipc.reg3 = m3_flags;
+ am33xx_pm->ipc.reg5 = DS_IPC_DEFAULT;
wkup_m3_set_cmd(&am33xx_pm->ipc);
ret = wkup_m3_ping();
if (ret < 0)
@@ -341,9 +342,11 @@ static int am33xx_pm_begin(suspend_state_t state)
switch (state) {
case PM_SUSPEND_MEM:
am33xx_pm->ipc.reg1 = IPC_CMD_DS0;
+ am33xx_pm->ipc.reg5 = am33xx_pm->m3_i2c_sequence_offsets;
break;
case PM_SUSPEND_STANDBY:
am33xx_pm->ipc.reg1 = IPC_CMD_STANDBY;
+ am33xx_pm->ipc.reg5 = DS_IPC_DEFAULT;
break;
}
@@ -422,7 +425,7 @@ static void am33xx_scale_data_fw_cb(const struct firmware *fw, void *context)
val = (aux_base + hdr.sleep_offset);
val |= ((aux_base + hdr.wake_offset) << 16);
- am33xx_pm->ipc.reg5 = val;
+ am33xx_pm->m3_i2c_sequence_offsets = val;
release_sd_fw:
release_firmware(fw);
diff --git a/arch/arm/mach-omap2/pm33xx.h b/arch/arm/mach-omap2/pm33xx.h
index ca46244926e0..d8a3c3b667dc 100644
--- a/arch/arm/mach-omap2/pm33xx.h
+++ b/arch/arm/mach-omap2/pm33xx.h
@@ -35,6 +35,7 @@ struct am33xx_pm_context {
struct am33xx_pm_ops *ops;
u8 state;
u32 ver;
+ u32 m3_i2c_sequence_offsets;
const char *sd_fw_name;
};
@@ -69,7 +70,7 @@ void __iomem *omap_rtc_get_base_addr(void);
#define IPC_CMD_RESET 0xe
#define DS_IPC_DEFAULT 0xffffffff
#define M3_VERSION_UNKNOWN 0x0000ffff
-#define M3_BASELINE_VERSION 0x189
+#define M3_BASELINE_VERSION 0x190
#define M3_STATE_UNKNOWN 0
#define M3_STATE_RESET 1
diff --git a/arch/arm/mach-omap2/soc.h b/arch/arm/mach-omap2/soc.h
index 4376f59626d1..c1a3b4416311 100644
--- a/arch/arm/mach-omap2/soc.h
+++ b/arch/arm/mach-omap2/soc.h
@@ -446,6 +446,7 @@ IS_OMAP_TYPE(3430, 0x3430)
#define AM437X_CLASS 0x43700000
#define AM437X_REV_ES1_0 (AM437X_CLASS | (0x10 << 8))
#define AM437X_REV_ES1_1 (AM437X_CLASS | (0x11 << 8))
+#define AM437X_REV_ES1_2 (AM437X_CLASS | (0x12 << 8))
#define OMAP443X_CLASS 0x44300044
#define OMAP4430_REV_ES1_0 (OMAP443X_CLASS | (0x10 << 8))
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index 64b16e5834b8..c2b3b1fad6fc 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -54,6 +54,7 @@
#include "soc.h"
#include "common.h"
+#include "control.h"
#include "powerdomain.h"
#include "pm.h"
#include "omap-secure.h"
@@ -587,7 +588,8 @@ static void __init realtime_counter_init(void)
void __iomem *base;
static struct clk *sys_clk;
unsigned long rate;
- unsigned int reg, num, den;
+ unsigned int reg;
+ unsigned long long num, den;
base = ioremap(REALTIME_COUNTER_BASE, SZ_32);
if (!base) {
@@ -602,13 +604,42 @@ static void __init realtime_counter_init(void)
}
rate = clk_get_rate(sys_clk);
+
+ if (soc_is_dra7xx()) {
+ /*
+ * Errata i856 says the 32.768KHz crystal does not start at
+ * power on, so the CPU falls back to an emulated 32KHz clock
+ * based on sysclk / 610 instead. This causes the master counter
+ * frequency to not be 6.144MHz but at sysclk / 610 * 375 / 2
+ * (OR sysclk * 75 / 244)
+ *
+ * This affects at least the DRA7/AM572x 1.0, 1.1 revisions.
+ * Of course any board built without a populated 32.768KHz
+ * crystal would also need this fix even if the CPU is fixed
+ * later.
+ *
+ * Either case can be detected by using the two speedselect bits
+ * If they are not 0, then the 32.768KHz clock driving the
+ * coarse counter that corrects the fine counter every time it
+ * ticks is actually rate/610 rather than 32.768KHz and we
+ * should compensate to avoid the 570ppm (at 20MHz, much worse
+ * at other rates) too fast system time.
+ */
+ reg = omap_ctrl_readl(DRA7_CTRL_CORE_BOOTSTRAP);
+ if (reg & DRA7_SPEEDSELECT_MASK) {
+ num = 75;
+ den = 244;
+ goto sysclk1_based;
+ }
+ }
+
/* Numerator/denumerator values refer TRM Realtime Counter section */
switch (rate) {
- case 1200000:
+ case 12000000:
num = 64;
den = 125;
break;
- case 1300000:
+ case 13000000:
num = 768;
den = 1625;
break;
@@ -620,11 +651,11 @@ static void __init realtime_counter_init(void)
num = 192;
den = 625;
break;
- case 2600000:
+ case 26000000:
num = 384;
den = 1625;
break;
- case 2700000:
+ case 27000000:
num = 256;
den = 1125;
break;
@@ -636,6 +667,7 @@ static void __init realtime_counter_init(void)
break;
}
+sysclk1_based:
/* Program numerator and denumerator registers */
reg = __raw_readl(base + INCREMENTER_NUMERATOR_OFFSET) &
NUMERATOR_DENUMERATOR_MASK;
@@ -647,7 +679,7 @@ static void __init realtime_counter_init(void)
reg |= den;
__raw_writel(reg, base + INCREMENTER_DENUMERATOR_RELOAD_OFFSET);
- arch_timer_freq = (rate / den) * num;
+ arch_timer_freq = DIV_ROUND_UP_ULL(rate * num, den);
set_cntfreq();
iounmap(base);
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index ca8ecdee47d8..e9c290c21744 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -798,6 +798,7 @@ config NEED_KUSER_HELPERS
config KUSER_HELPERS
bool "Enable kuser helpers in vector page" if !NEED_KUSER_HELPERS
+ depends on MMU
default y
help
Warning: disabling this option may break user programs.
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index 74f6033e76dd..fdedc31e0f40 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -211,7 +211,6 @@ __v7_pj4b_setup:
/* Auxiliary Debug Modes Control 1 Register */
#define PJ4B_STATIC_BP (1 << 2) /* Enable Static BP */
#define PJ4B_INTER_PARITY (1 << 8) /* Disable Internal Parity Handling */
-#define PJ4B_BCK_OFF_STREX (1 << 5) /* Enable the back off of STREX instr */
#define PJ4B_CLEAN_LINE (1 << 16) /* Disable data transfer for clean line */
/* Auxiliary Debug Modes Control 2 Register */
@@ -234,7 +233,6 @@ __v7_pj4b_setup:
/* Auxiliary Debug Modes Control 1 Register */
mrc p15, 1, r0, c15, c1, 1
orr r0, r0, #PJ4B_CLEAN_LINE
- orr r0, r0, #PJ4B_BCK_OFF_STREX
orr r0, r0, #PJ4B_INTER_PARITY
bic r0, r0, #PJ4B_STATIC_BP
mcr p15, 1, r0, c15, c1, 1
diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S
index d19b1cfcad91..b34b95f45cb3 100644
--- a/arch/arm/mm/proc-xscale.S
+++ b/arch/arm/mm/proc-xscale.S
@@ -535,7 +535,7 @@ ENTRY(cpu_xscale_do_suspend)
mrc p15, 0, r5, c15, c1, 0 @ CP access reg
mrc p15, 0, r6, c13, c0, 0 @ PID
mrc p15, 0, r7, c3, c0, 0 @ domain ID
- mrc p15, 0, r8, c1, c1, 0 @ auxiliary control reg
+ mrc p15, 0, r8, c1, c0, 1 @ auxiliary control reg
mrc p15, 0, r9, c1, c0, 0 @ control reg
bic r4, r4, #2 @ clear frequency change bit
stmia r0, {r4 - r9} @ store cp regs
@@ -552,7 +552,7 @@ ENTRY(cpu_xscale_do_resume)
mcr p15, 0, r6, c13, c0, 0 @ PID
mcr p15, 0, r7, c3, c0, 0 @ domain ID
mcr p15, 0, r1, c2, c0, 0 @ translation table base addr
- mcr p15, 0, r8, c1, c1, 0 @ auxiliary control reg
+ mcr p15, 0, r8, c1, c0, 1 @ auxiliary control reg
mov r0, r9 @ control register
b cpu_resume_mmu
ENDPROC(cpu_xscale_do_resume)
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 92f36835486b..565e26f23f31 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -156,9 +156,10 @@ static int __kprobes aarch64_insn_patch_text_cb(void *arg)
* which ends with "dsb; isb" pair guaranteeing global
* visibility.
*/
- atomic_set(&pp->cpu_count, -1);
+ /* Notify other processors with an additional increment. */
+ atomic_inc(&pp->cpu_count);
} else {
- while (atomic_read(&pp->cpu_count) != -1)
+ while (atomic_read(&pp->cpu_count) <= num_online_cpus())
cpu_relax();
isb();
}
diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S
index 6e0ed93d51fe..c17967fdf5f6 100644
--- a/arch/arm64/lib/clear_user.S
+++ b/arch/arm64/lib/clear_user.S
@@ -46,7 +46,7 @@ USER(9f, strh wzr, [x0], #2 )
sub x1, x1, #2
4: adds x1, x1, #1
b.mi 5f
- strb wzr, [x0]
+USER(9f, strb wzr, [x0] )
5: mov x0, #0
ret
ENDPROC(__clear_user)
diff --git a/arch/mips/loongson/common/Makefile b/arch/mips/loongson/common/Makefile
index 9e4484ccbb03..9005a8d60969 100644
--- a/arch/mips/loongson/common/Makefile
+++ b/arch/mips/loongson/common/Makefile
@@ -11,7 +11,8 @@ obj-$(CONFIG_PCI) += pci.o
# Serial port support
#
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
-obj-$(CONFIG_SERIAL_8250) += serial.o
+loongson-serial-$(CONFIG_SERIAL_8250) := serial.o
+obj-y += $(loongson-serial-m) $(loongson-serial-y)
obj-$(CONFIG_LOONGSON_UART_BASE) += uart_base.o
obj-$(CONFIG_LOONGSON_MC146818) += rtc.o
diff --git a/arch/mips/oprofile/backtrace.c b/arch/mips/oprofile/backtrace.c
index 6854ed5097d2..83a1dfd8f0e3 100644
--- a/arch/mips/oprofile/backtrace.c
+++ b/arch/mips/oprofile/backtrace.c
@@ -92,7 +92,7 @@ static inline int unwind_user_frame(struct stackframe *old_frame,
/* This marks the end of the previous function,
which means we overran. */
break;
- stack_size = (unsigned) stack_adjustment;
+ stack_size = (unsigned long) stack_adjustment;
} else if (is_ra_save_ins(&ip)) {
int ra_slot = ip.i_format.simmediate;
if (ra_slot < 0)
diff --git a/arch/parisc/include/uapi/asm/shmbuf.h b/arch/parisc/include/uapi/asm/shmbuf.h
index 0a3eada1863b..f395cde7b593 100644
--- a/arch/parisc/include/uapi/asm/shmbuf.h
+++ b/arch/parisc/include/uapi/asm/shmbuf.h
@@ -36,23 +36,16 @@ struct shmid64_ds {
unsigned int __unused2;
};
-#ifdef CONFIG_64BIT
-/* The 'unsigned int' (formerly 'unsigned long') data types below will
- * ensure that a 32-bit app calling shmctl(*,IPC_INFO,*) will work on
- * a wide kernel, but if some of these values are meant to contain pointers
- * they may need to be 'long long' instead. -PB XXX FIXME
- */
-#endif
struct shminfo64 {
- unsigned int shmmax;
- unsigned int shmmin;
- unsigned int shmmni;
- unsigned int shmseg;
- unsigned int shmall;
- unsigned int __unused1;
- unsigned int __unused2;
- unsigned int __unused3;
- unsigned int __unused4;
+ unsigned long shmmax;
+ unsigned long shmmin;
+ unsigned long shmmni;
+ unsigned long shmseg;
+ unsigned long shmall;
+ unsigned long __unused1;
+ unsigned long __unused2;
+ unsigned long __unused3;
+ unsigned long __unused4;
};
#endif /* _PARISC_SHMBUF_H */
diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S
index 7dd8a3b22147..fc77d53e2ca5 100644
--- a/arch/parisc/kernel/syscall_table.S
+++ b/arch/parisc/kernel/syscall_table.S
@@ -286,11 +286,11 @@
ENTRY_COMP(msgsnd)
ENTRY_COMP(msgrcv)
ENTRY_SAME(msgget) /* 190 */
- ENTRY_SAME(msgctl)
- ENTRY_SAME(shmat)
+ ENTRY_COMP(msgctl)
+ ENTRY_COMP(shmat)
ENTRY_SAME(shmdt)
ENTRY_SAME(shmget)
- ENTRY_SAME(shmctl) /* 195 */
+ ENTRY_COMP(shmctl) /* 195 */
ENTRY_SAME(ni_syscall) /* streams1 */
ENTRY_SAME(ni_syscall) /* streams2 */
ENTRY_SAME(lstat64)
@@ -323,7 +323,7 @@
ENTRY_SAME(epoll_ctl) /* 225 */
ENTRY_SAME(epoll_wait)
ENTRY_SAME(remap_file_pages)
- ENTRY_SAME(semtimedop)
+ ENTRY_COMP(semtimedop)
ENTRY_COMP(mq_open)
ENTRY_SAME(mq_unlink) /* 230 */
ENTRY_COMP(mq_timedsend)
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index beedaf0c5e75..d558b8595e6f 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -902,7 +902,6 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
unsigned int is_64, struct msi_msg *msg)
{
struct pnv_ioda_pe *pe = pnv_ioda_get_pe(dev);
- struct pci_dn *pdn = pci_get_pdn(dev);
struct irq_data *idata;
struct irq_chip *ichip;
unsigned int xive_num = hwirq - phb->msi_base;
@@ -918,7 +917,7 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
return -ENXIO;
/* Force 32-bit MSI on some broken devices */
- if (pdn && pdn->force_32bit_msi)
+ if (dev->no_64bit_msi)
is_64 = 0;
/* Assign XIVE to PE */
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index 8518817dcdfd..52c1162bcee3 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -1,3 +1,4 @@
+
/*
* Support PCI/PCIe on PowerNV platforms
*
@@ -50,9 +51,8 @@ static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type)
{
struct pci_controller *hose = pci_bus_to_host(pdev->bus);
struct pnv_phb *phb = hose->private_data;
- struct pci_dn *pdn = pci_get_pdn(pdev);
- if (pdn && pdn->force_32bit_msi && !phb->msi32_support)
+ if (pdev->no_64bit_msi && !phb->msi32_support)
return -ENODEV;
return (phb && phb->msi_bmp.bitmap) ? 0 : -ENODEV;
diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c
index 0c882e83c4ce..6849d85ea0d5 100644
--- a/arch/powerpc/platforms/pseries/msi.c
+++ b/arch/powerpc/platforms/pseries/msi.c
@@ -428,7 +428,7 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
*/
again:
if (type == PCI_CAP_ID_MSI) {
- if (pdn->force_32bit_msi) {
+ if (pdev->no_64bit_msi) {
rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec);
if (rc < 0) {
/*
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index b07909850f77..bc5fbc201bcb 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -288,10 +288,10 @@ static inline void disable_surveillance(void)
args.token = rtas_token("set-indicator");
if (args.token == RTAS_UNKNOWN_SERVICE)
return;
- args.nargs = 3;
- args.nret = 1;
+ args.nargs = cpu_to_be32(3);
+ args.nret = cpu_to_be32(1);
args.rets = &args.args[3];
- args.args[0] = SURVEILLANCE_TOKEN;
+ args.args[0] = cpu_to_be32(SURVEILLANCE_TOKEN);
args.args[1] = 0;
args.args[2] = 0;
enter_rtas(__pa(&args));
diff --git a/arch/sparc/include/asm/atomic_32.h b/arch/sparc/include/asm/atomic_32.h
index 905832aa9e9e..a0ed182ae73c 100644
--- a/arch/sparc/include/asm/atomic_32.h
+++ b/arch/sparc/include/asm/atomic_32.h
@@ -21,7 +21,7 @@
extern int __atomic_add_return(int, atomic_t *);
extern int atomic_cmpxchg(atomic_t *, int, int);
-#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
+extern int atomic_xchg(atomic_t *, int);
extern int __atomic_add_unless(atomic_t *, int, int);
extern void atomic_set(atomic_t *, int);
diff --git a/arch/sparc/include/asm/cmpxchg_32.h b/arch/sparc/include/asm/cmpxchg_32.h
index 1fae1a02e3c2..ae0f9a7a314d 100644
--- a/arch/sparc/include/asm/cmpxchg_32.h
+++ b/arch/sparc/include/asm/cmpxchg_32.h
@@ -11,22 +11,14 @@
#ifndef __ARCH_SPARC_CMPXCHG__
#define __ARCH_SPARC_CMPXCHG__
-static inline unsigned long xchg_u32(__volatile__ unsigned long *m, unsigned long val)
-{
- __asm__ __volatile__("swap [%2], %0"
- : "=&r" (val)
- : "0" (val), "r" (m)
- : "memory");
- return val;
-}
-
+extern unsigned long __xchg_u32(volatile u32 *m, u32 new);
extern void __xchg_called_with_bad_pointer(void);
static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int size)
{
switch (size) {
case 4:
- return xchg_u32(ptr, x);
+ return __xchg_u32(ptr, x);
}
__xchg_called_with_bad_pointer();
return x;
diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h
index 432afa838861..55841c184e6d 100644
--- a/arch/sparc/include/asm/vio.h
+++ b/arch/sparc/include/asm/vio.h
@@ -118,12 +118,18 @@ struct vio_disk_attr_info {
u8 vdisk_type;
#define VD_DISK_TYPE_SLICE 0x01 /* Slice in block device */
#define VD_DISK_TYPE_DISK 0x02 /* Entire block device */
- u16 resv1;
+ u8 vdisk_mtype; /* v1.1 */
+#define VD_MEDIA_TYPE_FIXED 0x01 /* Fixed device */
+#define VD_MEDIA_TYPE_CD 0x02 /* CD Device */
+#define VD_MEDIA_TYPE_DVD 0x03 /* DVD Device */
+ u8 resv1;
u32 vdisk_block_size;
u64 operations;
- u64 vdisk_size;
+ u64 vdisk_size; /* v1.1 */
u64 max_xfer_size;
- u64 resv2[2];
+ u32 phys_block_size; /* v1.2 */
+ u32 resv2;
+ u64 resv3[1];
};
struct vio_disk_desc {
@@ -259,7 +265,7 @@ static inline u32 vio_dring_avail(struct vio_dring_state *dr,
unsigned int ring_size)
{
return (dr->pending -
- ((dr->prod - dr->cons) & (ring_size - 1)));
+ ((dr->prod - dr->cons) & (ring_size - 1)) - 1);
}
#define VIO_MAX_TYPE_LEN 32
diff --git a/arch/sparc/include/uapi/asm/swab.h b/arch/sparc/include/uapi/asm/swab.h
index a34ad079487e..4c7c12d69bea 100644
--- a/arch/sparc/include/uapi/asm/swab.h
+++ b/arch/sparc/include/uapi/asm/swab.h
@@ -9,9 +9,9 @@ static inline __u16 __arch_swab16p(const __u16 *addr)
{
__u16 ret;
- __asm__ __volatile__ ("lduha [%1] %2, %0"
+ __asm__ __volatile__ ("lduha [%2] %3, %0"
: "=r" (ret)
- : "r" (addr), "i" (ASI_PL));
+ : "m" (*addr), "r" (addr), "i" (ASI_PL));
return ret;
}
#define __arch_swab16p __arch_swab16p
@@ -20,9 +20,9 @@ static inline __u32 __arch_swab32p(const __u32 *addr)
{
__u32 ret;
- __asm__ __volatile__ ("lduwa [%1] %2, %0"
+ __asm__ __volatile__ ("lduwa [%2] %3, %0"
: "=r" (ret)
- : "r" (addr), "i" (ASI_PL));
+ : "m" (*addr), "r" (addr), "i" (ASI_PL));
return ret;
}
#define __arch_swab32p __arch_swab32p
@@ -31,9 +31,9 @@ static inline __u64 __arch_swab64p(const __u64 *addr)
{
__u64 ret;
- __asm__ __volatile__ ("ldxa [%1] %2, %0"
+ __asm__ __volatile__ ("ldxa [%2] %3, %0"
: "=r" (ret)
- : "r" (addr), "i" (ASI_PL));
+ : "m" (*addr), "r" (addr), "i" (ASI_PL));
return ret;
}
#define __arch_swab64p __arch_swab64p
diff --git a/arch/sparc/kernel/pci_schizo.c b/arch/sparc/kernel/pci_schizo.c
index 8f76f23dac38..f9c6813c132d 100644
--- a/arch/sparc/kernel/pci_schizo.c
+++ b/arch/sparc/kernel/pci_schizo.c
@@ -581,7 +581,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
{
unsigned long csr_reg, csr, csr_error_bits;
irqreturn_t ret = IRQ_NONE;
- u16 stat;
+ u32 stat;
csr_reg = pbm->pbm_regs + SCHIZO_PCI_CTRL;
csr = upa_readq(csr_reg);
@@ -617,7 +617,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
pbm->name);
ret = IRQ_HANDLED;
}
- pci_read_config_word(pbm->pci_bus->self, PCI_STATUS, &stat);
+ pbm->pci_ops->read(pbm->pci_bus, 0, PCI_STATUS, 2, &stat);
if (stat & (PCI_STATUS_PARITY |
PCI_STATUS_SIG_TARGET_ABORT |
PCI_STATUS_REC_TARGET_ABORT |
@@ -625,7 +625,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
PCI_STATUS_SIG_SYSTEM_ERROR)) {
printk("%s: PCI bus error, PCI_STATUS[%04x]\n",
pbm->name, stat);
- pci_write_config_word(pbm->pci_bus->self, PCI_STATUS, 0xffff);
+ pbm->pci_ops->write(pbm->pci_bus, 0, PCI_STATUS, 2, 0xffff);
ret = IRQ_HANDLED;
}
return ret;
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index 50c3dd03be31..9af0a5dbb36d 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -823,13 +823,17 @@ void arch_send_call_function_single_ipi(int cpu)
void __irq_entry smp_call_function_client(int irq, struct pt_regs *regs)
{
clear_softint(1 << irq);
+ irq_enter();
generic_smp_call_function_interrupt();
+ irq_exit();
}
void __irq_entry smp_call_function_single_client(int irq, struct pt_regs *regs)
{
clear_softint(1 << irq);
+ irq_enter();
generic_smp_call_function_single_interrupt();
+ irq_exit();
}
static void tsb_sync(void *info)
diff --git a/arch/sparc/lib/atomic32.c b/arch/sparc/lib/atomic32.c
index 1d32b54089aa..8f2f94d53434 100644
--- a/arch/sparc/lib/atomic32.c
+++ b/arch/sparc/lib/atomic32.c
@@ -40,6 +40,19 @@ int __atomic_add_return(int i, atomic_t *v)
}
EXPORT_SYMBOL(__atomic_add_return);
+int atomic_xchg(atomic_t *v, int new)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(ATOMIC_HASH(v), flags);
+ ret = v->counter;
+ v->counter = new;
+ spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
+ return ret;
+}
+EXPORT_SYMBOL(atomic_xchg);
+
int atomic_cmpxchg(atomic_t *v, int old, int new)
{
int ret;
@@ -132,3 +145,17 @@ unsigned long __cmpxchg_u32(volatile u32 *ptr, u32 old, u32 new)
return (unsigned long)prev;
}
EXPORT_SYMBOL(__cmpxchg_u32);
+
+unsigned long __xchg_u32(volatile u32 *ptr, u32 new)
+{
+ unsigned long flags;
+ u32 prev;
+
+ spin_lock_irqsave(ATOMIC_HASH(ptr), flags);
+ prev = *ptr;
+ *ptr = new;
+ spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags);
+
+ return (unsigned long)prev;
+}
+EXPORT_SYMBOL(__xchg_u32);
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 0fcd9133790c..14fe7cba21d1 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -75,8 +75,10 @@ suffix-$(CONFIG_KERNEL_XZ) := xz
suffix-$(CONFIG_KERNEL_LZO) := lzo
suffix-$(CONFIG_KERNEL_LZ4) := lz4
+RUN_SIZE = $(shell objdump -h vmlinux | \
+ perl $(srctree)/arch/x86/tools/calc_run_size.pl)
quiet_cmd_mkpiggy = MKPIGGY $@
- cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
+ cmd_mkpiggy = $(obj)/mkpiggy $< $(RUN_SIZE) > $@ || ( rm -f $@ ; false )
targets += piggy.S
$(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index f45ab7a36fb6..c5b56ed10aff 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -186,7 +186,8 @@ relocated:
* Do the decompression, and jump to the new kernel..
*/
/* push arguments for decompress_kernel: */
- pushl $z_output_len /* decompressed length */
+ pushl $z_run_size /* size of kernel with .bss and .brk */
+ pushl $z_output_len /* decompressed length, end of relocs */
leal z_extract_offset_negative(%ebx), %ebp
pushl %ebp /* output address */
pushl $z_input_len /* input_len */
@@ -196,7 +197,7 @@ relocated:
pushl %eax /* heap area */
pushl %esi /* real mode pointer */
call decompress_kernel /* returns kernel location in %eax */
- addl $24, %esp
+ addl $28, %esp
/*
* Jump to the decompressed kernel.
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index b10fa66a2540..34bbc0911b7c 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -334,13 +334,16 @@ relocated:
* Do the decompression, and jump to the new kernel..
*/
pushq %rsi /* Save the real mode argument */
+ movq $z_run_size, %r9 /* size of kernel with .bss and .brk */
+ pushq %r9
movq %rsi, %rdi /* real mode address */
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
leaq input_data(%rip), %rdx /* input_data */
movl $z_input_len, %ecx /* input_len */
movq %rbp, %r8 /* output target address */
- movq $z_output_len, %r9 /* decompressed length */
+ movq $z_output_len, %r9 /* decompressed length, end of relocs */
call decompress_kernel /* returns kernel location in %rax */
+ popq %r9
popq %rsi
/*
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index 196eaf373a06..eb25ca1eb6da 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -393,7 +393,8 @@ asmlinkage void *decompress_kernel(void *rmode, memptr heap,
unsigned char *input_data,
unsigned long input_len,
unsigned char *output,
- unsigned long output_len)
+ unsigned long output_len,
+ unsigned long run_size)
{
real_mode = rmode;
@@ -416,8 +417,14 @@ asmlinkage void *decompress_kernel(void *rmode, memptr heap,
free_mem_ptr = heap; /* Heap */
free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
- output = choose_kernel_location(input_data, input_len,
- output, output_len);
+ /*
+ * The memory hole needed for the kernel is the larger of either
+ * the entire decompressed kernel plus relocation table, or the
+ * entire decompressed kernel plus .bss and .brk sections.
+ */
+ output = choose_kernel_location(input_data, input_len, output,
+ output_len > run_size ? output_len
+ : run_size);
/* Validate memory location choices. */
if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
index b669ab65bf6c..d8222f213182 100644
--- a/arch/x86/boot/compressed/mkpiggy.c
+++ b/arch/x86/boot/compressed/mkpiggy.c
@@ -36,11 +36,13 @@ int main(int argc, char *argv[])
uint32_t olen;
long ilen;
unsigned long offs;
+ unsigned long run_size;
FILE *f = NULL;
int retval = 1;
- if (argc < 2) {
- fprintf(stderr, "Usage: %s compressed_file\n", argv[0]);
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s compressed_file run_size\n",
+ argv[0]);
goto bail;
}
@@ -74,6 +76,7 @@ int main(int argc, char *argv[])
offs += olen >> 12; /* Add 8 bytes for each 32K block */
offs += 64*1024 + 128; /* Add 64K + 128 bytes slack */
offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
+ run_size = atoi(argv[2]);
printf(".section \".rodata..compressed\",\"a\",@progbits\n");
printf(".globl z_input_len\n");
@@ -85,6 +88,8 @@ int main(int argc, char *argv[])
/* z_extract_offset_negative allows simplification of head_32.S */
printf(".globl z_extract_offset_negative\n");
printf("z_extract_offset_negative = -0x%lx\n", offs);
+ printf(".globl z_run_size\n");
+ printf("z_run_size = %lu\n", run_size);
printf(".globl input_data, input_data_end\n");
printf("input_data:\n");
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index 5f1296872aed..1717156f4dd1 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -203,6 +203,7 @@
#define X86_FEATURE_DECODEASSISTS (8*32+12) /* AMD Decode Assists support */
#define X86_FEATURE_PAUSEFILTER (8*32+13) /* AMD filtered pause intercept */
#define X86_FEATURE_PFTHRESHOLD (8*32+14) /* AMD pause filter threshold */
+#define X86_FEATURE_VMMCALL (8*32+15) /* Prefer vmmcall to vmcall */
/* Intel-defined CPU features, CPUID level 0x00000007:0 (ebx), word 9 */
diff --git a/arch/x86/include/asm/kvm_para.h b/arch/x86/include/asm/kvm_para.h
index c7678e43465b..e62cf897f781 100644
--- a/arch/x86/include/asm/kvm_para.h
+++ b/arch/x86/include/asm/kvm_para.h
@@ -2,6 +2,7 @@
#define _ASM_X86_KVM_PARA_H
#include <asm/processor.h>
+#include <asm/alternative.h>
#include <uapi/asm/kvm_para.h>
extern void kvmclock_init(void);
@@ -16,10 +17,15 @@ static inline bool kvm_check_and_clear_guest_paused(void)
}
#endif /* CONFIG_KVM_GUEST */
-/* This instruction is vmcall. On non-VT architectures, it will generate a
- * trap that we will then rewrite to the appropriate instruction.
+#ifdef CONFIG_DEBUG_RODATA
+#define KVM_HYPERCALL \
+ ALTERNATIVE(".byte 0x0f,0x01,0xc1", ".byte 0x0f,0x01,0xd9", X86_FEATURE_VMMCALL)
+#else
+/* On AMD processors, vmcall will generate a trap that we will
+ * then rewrite to the appropriate instruction.
*/
#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1"
+#endif
/* For KVM hypercalls, a three-byte sequence of either the vmcall or the vmmcall
* instruction. The hypervisor may replace it with something else but only the
diff --git a/arch/x86/include/asm/page_32_types.h b/arch/x86/include/asm/page_32_types.h
index f48b17df4224..3a52ee0e726d 100644
--- a/arch/x86/include/asm/page_32_types.h
+++ b/arch/x86/include/asm/page_32_types.h
@@ -20,7 +20,6 @@
#define THREAD_SIZE_ORDER 1
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
-#define STACKFAULT_STACK 0
#define DOUBLEFAULT_STACK 1
#define NMI_STACK 0
#define DEBUG_STACK 0
diff --git a/arch/x86/include/asm/page_64_types.h b/arch/x86/include/asm/page_64_types.h
index 8de6d9cf3b95..d54d1eebeffe 100644
--- a/arch/x86/include/asm/page_64_types.h
+++ b/arch/x86/include/asm/page_64_types.h
@@ -14,12 +14,11 @@
#define IRQ_STACK_ORDER 2
#define IRQ_STACK_SIZE (PAGE_SIZE << IRQ_STACK_ORDER)
-#define STACKFAULT_STACK 1
-#define DOUBLEFAULT_STACK 2
-#define NMI_STACK 3
-#define DEBUG_STACK 4
-#define MCE_STACK 5
-#define N_EXCEPTION_STACKS 5 /* hw limit: 7 */
+#define DOUBLEFAULT_STACK 1
+#define NMI_STACK 2
+#define DEBUG_STACK 3
+#define MCE_STACK 4
+#define N_EXCEPTION_STACKS 4 /* hw limit: 7 */
#define PUD_PAGE_SIZE (_AC(1, UL) << PUD_SHIFT)
#define PUD_PAGE_MASK (~(PUD_PAGE_SIZE-1))
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index e1940c06ed02..e870ea9232c3 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -144,7 +144,7 @@ struct thread_info {
/* Only used for 64 bit */
#define _TIF_DO_NOTIFY_MASK \
(_TIF_SIGPENDING | _TIF_MCE_NOTIFY | _TIF_NOTIFY_RESUME | \
- _TIF_USER_RETURN_NOTIFY)
+ _TIF_USER_RETURN_NOTIFY | _TIF_UPROBE)
/* flags to check in __switch_to() */
#define _TIF_WORK_CTXSW \
diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h
index 58d66fe06b61..b409b17efb48 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -39,6 +39,7 @@ asmlinkage void simd_coprocessor_error(void);
#ifdef CONFIG_TRACING
asmlinkage void trace_page_fault(void);
+#define trace_stack_segment stack_segment
#define trace_divide_error divide_error
#define trace_bounds bounds
#define trace_invalid_op invalid_op
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index c67ffa686064..c005fdd52529 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -508,6 +508,13 @@ static void early_init_amd(struct cpuinfo_x86 *c)
}
#endif
+ /*
+ * This is only needed to tell the kernel whether to use VMCALL
+ * and VMMCALL. VMMCALL is never executed except under virt, so
+ * we can set it unconditionally.
+ */
+ set_cpu_cap(c, X86_FEATURE_VMMCALL);
+
/* F16h erratum 793, CVE-2013-6885 */
if (c->x86 == 0x16 && c->x86_model <= 0xf) {
u64 val;
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 3f27f5fd0847..e6bddd5b9da3 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -144,6 +144,8 @@ EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
static int __init x86_xsave_setup(char *s)
{
+ if (strlen(s))
+ return 0;
setup_clear_cpu_cap(X86_FEATURE_XSAVE);
setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
setup_clear_cpu_cap(X86_FEATURE_AVX);
diff --git a/arch/x86/kernel/cpu/microcode/amd_early.c b/arch/x86/kernel/cpu/microcode/amd_early.c
index 617a9e284245..b63773ba1646 100644
--- a/arch/x86/kernel/cpu/microcode/amd_early.c
+++ b/arch/x86/kernel/cpu/microcode/amd_early.c
@@ -108,12 +108,13 @@ static size_t compute_container_size(u8 *data, u32 total_size)
* load_microcode_amd() to save equivalent cpu table and microcode patches in
* kernel heap memory.
*/
-static void apply_ucode_in_initrd(void *ucode, size_t size)
+static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
{
struct equiv_cpu_entry *eq;
size_t *cont_sz;
u32 *header;
u8 *data, **cont;
+ u8 (*patch)[PATCH_MAX_SIZE];
u16 eq_id = 0;
int offset, left;
u32 rev, eax, ebx, ecx, edx;
@@ -123,10 +124,12 @@ static void apply_ucode_in_initrd(void *ucode, size_t size)
new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);
cont_sz = (size_t *)__pa_nodebug(&container_size);
cont = (u8 **)__pa_nodebug(&container);
+ patch = (u8 (*)[PATCH_MAX_SIZE])__pa_nodebug(&amd_ucode_patch);
#else
new_rev = &ucode_new_rev;
cont_sz = &container_size;
cont = &container;
+ patch = &amd_ucode_patch;
#endif
data = ucode;
@@ -213,9 +216,9 @@ static void apply_ucode_in_initrd(void *ucode, size_t size)
rev = mc->hdr.patch_id;
*new_rev = rev;
- /* save ucode patch */
- memcpy(amd_ucode_patch, mc,
- min_t(u32, header[1], PATCH_MAX_SIZE));
+ if (save_patch)
+ memcpy(patch, mc,
+ min_t(u32, header[1], PATCH_MAX_SIZE));
}
}
@@ -246,7 +249,7 @@ void __init load_ucode_amd_bsp(void)
*data = cp.data;
*size = cp.size;
- apply_ucode_in_initrd(cp.data, cp.size);
+ apply_ucode_in_initrd(cp.data, cp.size, true);
}
#ifdef CONFIG_X86_32
@@ -263,7 +266,7 @@ void load_ucode_amd_ap(void)
size_t *usize;
void **ucode;
- mc = (struct microcode_amd *)__pa(amd_ucode_patch);
+ mc = (struct microcode_amd *)__pa_nodebug(amd_ucode_patch);
if (mc->hdr.patch_id && mc->hdr.processor_rev_id) {
__apply_microcode_amd(mc);
return;
@@ -275,7 +278,7 @@ void load_ucode_amd_ap(void)
if (!*ucode || !*usize)
return;
- apply_ucode_in_initrd(*ucode, *usize);
+ apply_ucode_in_initrd(*ucode, *usize, false);
}
static void __init collect_cpu_sig_on_bsp(void *arg)
@@ -339,7 +342,7 @@ void load_ucode_amd_ap(void)
* AP has a different equivalence ID than BSP, looks like
* mixed-steppings silicon so go through the ucode blob anew.
*/
- apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size);
+ apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size, false);
}
}
#endif
@@ -347,7 +350,9 @@ void load_ucode_amd_ap(void)
int __init save_microcode_in_initrd_amd(void)
{
unsigned long cont;
+ int retval = 0;
enum ucode_state ret;
+ u8 *cont_va;
u32 eax;
if (!container)
@@ -355,13 +360,15 @@ int __init save_microcode_in_initrd_amd(void)
#ifdef CONFIG_X86_32
get_bsp_sig();
- cont = (unsigned long)container;
+ cont = (unsigned long)container;
+ cont_va = __va(container);
#else
/*
* We need the physical address of the container for both bitness since
* boot_params.hdr.ramdisk_image is a physical address.
*/
- cont = __pa(container);
+ cont = __pa(container);
+ cont_va = container;
#endif
/*
@@ -372,6 +379,8 @@ int __init save_microcode_in_initrd_amd(void)
if (relocated_ramdisk)
container = (u8 *)(__va(relocated_ramdisk) +
(cont - boot_params.hdr.ramdisk_image));
+ else
+ container = cont_va;
if (ucode_new_rev)
pr_info("microcode: updated early to new patch_level=0x%08x\n",
@@ -382,7 +391,7 @@ int __init save_microcode_in_initrd_amd(void)
ret = load_microcode_amd(eax, container, container_size);
if (ret != UCODE_OK)
- return -EINVAL;
+ retval = -EINVAL;
/*
* This will be freed any msec now, stash patches for the current
@@ -391,5 +400,5 @@ int __init save_microcode_in_initrd_amd(void)
container = NULL;
container_size = 0;
- return 0;
+ return retval;
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index 1340ebfcb467..5ee8064bd1d2 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -2475,6 +2475,9 @@ __init int intel_pmu_init(void)
case 62: /* IvyBridge EP */
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
sizeof(hw_cache_event_ids));
+ /* dTLB-load-misses on IVB is different than SNB */
+ hw_cache_event_ids[C(DTLB)][C(OP_READ)][C(RESULT_MISS)] = 0x8108; /* DTLB_LOAD_MISSES.DEMAND_LD_MISS_CAUSES_A_WALK */
+
memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs,
sizeof(hw_cache_extra_regs));
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c
index addb207dab92..66e274a3d968 100644
--- a/arch/x86/kernel/dumpstack_64.c
+++ b/arch/x86/kernel/dumpstack_64.c
@@ -24,7 +24,6 @@ static char x86_stack_ids[][8] = {
[ DEBUG_STACK-1 ] = "#DB",
[ NMI_STACK-1 ] = "NMI",
[ DOUBLEFAULT_STACK-1 ] = "#DF",
- [ STACKFAULT_STACK-1 ] = "#SS",
[ MCE_STACK-1 ] = "#MC",
#if DEBUG_STKSZ > EXCEPTION_STKSZ
[ N_EXCEPTION_STACKS ...
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
index 03cd2a8f6009..02553d6d183d 100644
--- a/arch/x86/kernel/entry_64.S
+++ b/arch/x86/kernel/entry_64.S
@@ -1053,9 +1053,15 @@ ENTRY(native_iret)
jnz native_irq_return_ldt
#endif
+.global native_irq_return_iret
native_irq_return_iret:
+ /*
+ * This may fault. Non-paranoid faults on return to userspace are
+ * handled by fixup_bad_iret. These include #SS, #GP, and #NP.
+ * Double-faults due to espfix64 are handled in do_double_fault.
+ * Other faults here are fatal.
+ */
iretq
- _ASM_EXTABLE(native_irq_return_iret, bad_iret)
#ifdef CONFIG_X86_ESPFIX64
native_irq_return_ldt:
@@ -1083,25 +1089,6 @@ native_irq_return_ldt:
jmp native_irq_return_iret
#endif
- .section .fixup,"ax"
-bad_iret:
- /*
- * The iret traps when the %cs or %ss being restored is bogus.
- * We've lost the original trap vector and error code.
- * #GPF is the most likely one to get for an invalid selector.
- * So pretend we completed the iret and took the #GPF in user mode.
- *
- * We are now running with the kernel GS after exception recovery.
- * But error_entry expects us to have user GS to match the user %cs,
- * so swap back.
- */
- pushq $0
-
- SWAPGS
- jmp general_protection
-
- .previous
-
/* edi: workmask, edx: work */
retint_careful:
CFI_RESTORE_STATE
@@ -1147,37 +1134,6 @@ ENTRY(retint_kernel)
CFI_ENDPROC
END(common_interrupt)
- /*
- * If IRET takes a fault on the espfix stack, then we
- * end up promoting it to a doublefault. In that case,
- * modify the stack to make it look like we just entered
- * the #GP handler from user space, similar to bad_iret.
- */
-#ifdef CONFIG_X86_ESPFIX64
- ALIGN
-__do_double_fault:
- XCPT_FRAME 1 RDI+8
- movq RSP(%rdi),%rax /* Trap on the espfix stack? */
- sarq $PGDIR_SHIFT,%rax
- cmpl $ESPFIX_PGD_ENTRY,%eax
- jne do_double_fault /* No, just deliver the fault */
- cmpl $__KERNEL_CS,CS(%rdi)
- jne do_double_fault
- movq RIP(%rdi),%rax
- cmpq $native_irq_return_iret,%rax
- jne do_double_fault /* This shouldn't happen... */
- movq PER_CPU_VAR(kernel_stack),%rax
- subq $(6*8-KERNEL_STACK_OFFSET),%rax /* Reset to original stack */
- movq %rax,RSP(%rdi)
- movq $0,(%rax) /* Missing (lost) #GP error code */
- movq $general_protection,RIP(%rdi)
- retq
- CFI_ENDPROC
-END(__do_double_fault)
-#else
-# define __do_double_fault do_double_fault
-#endif
-
/*
* End of kprobes section
*/
@@ -1379,7 +1335,7 @@ zeroentry overflow do_overflow
zeroentry bounds do_bounds
zeroentry invalid_op do_invalid_op
zeroentry device_not_available do_device_not_available
-paranoiderrorentry double_fault __do_double_fault
+paranoiderrorentry double_fault do_double_fault
zeroentry coprocessor_segment_overrun do_coprocessor_segment_overrun
errorentry invalid_TSS do_invalid_TSS
errorentry segment_not_present do_segment_not_present
@@ -1549,7 +1505,7 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
paranoidzeroentry_ist debug do_debug DEBUG_STACK
paranoidzeroentry_ist int3 do_int3 DEBUG_STACK
-paranoiderrorentry stack_segment do_stack_segment
+errorentry stack_segment do_stack_segment
#ifdef CONFIG_XEN
zeroentry xen_debug do_debug
zeroentry xen_int3 do_int3
@@ -1659,16 +1615,15 @@ error_sti:
/*
* There are two places in the kernel that can potentially fault with
- * usergs. Handle them here. The exception handlers after iret run with
- * kernel gs again, so don't set the user space flag. B stepping K8s
- * sometimes report an truncated RIP for IRET exceptions returning to
- * compat mode. Check for these here too.
+ * usergs. Handle them here. B stepping K8s sometimes report a
+ * truncated RIP for IRET exceptions returning to compat mode. Check
+ * for these here too.
*/
error_kernelspace:
incl %ebx
leaq native_irq_return_iret(%rip),%rcx
cmpq %rcx,RIP+8(%rsp)
- je error_swapgs
+ je error_bad_iret
movl %ecx,%eax /* zero extend */
cmpq %rax,RIP+8(%rsp)
je bstep_iret
@@ -1679,7 +1634,15 @@ error_kernelspace:
bstep_iret:
/* Fix truncated RIP */
movq %rcx,RIP+8(%rsp)
- jmp error_swapgs
+ /* fall through */
+
+error_bad_iret:
+ SWAPGS
+ mov %rsp,%rdi
+ call fixup_bad_iret
+ mov %rax,%rsp
+ decl %ebx /* Return to usergs */
+ jmp error_sti
CFI_ENDPROC
END(error_entry)
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 7461f50d5bb1..0686fe313b3b 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -1441,15 +1441,6 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
force_sig_info(SIGTRAP, &info, tsk);
}
-
-#ifdef CONFIG_X86_32
-# define IS_IA32 1
-#elif defined CONFIG_IA32_EMULATION
-# define IS_IA32 is_compat_task()
-#else
-# define IS_IA32 0
-#endif
-
/*
* We must return the syscall number to actually look up in the table.
* This can be -1L to skip running any syscall at all.
@@ -1487,7 +1478,7 @@ long syscall_trace_enter(struct pt_regs *regs)
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->orig_ax);
- if (IS_IA32)
+ if (is_ia32_task())
audit_syscall_entry(AUDIT_ARCH_I386,
regs->orig_ax,
regs->bx, regs->cx,
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 57409f6b8c62..f9d976e0ae67 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -218,32 +218,40 @@ DO_ERROR_INFO(X86_TRAP_UD, SIGILL, "invalid opcode", invalid_op, ILL
DO_ERROR (X86_TRAP_OLD_MF, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun )
DO_ERROR (X86_TRAP_TS, SIGSEGV, "invalid TSS", invalid_TSS )
DO_ERROR (X86_TRAP_NP, SIGBUS, "segment not present", segment_not_present )
-#ifdef CONFIG_X86_32
DO_ERROR (X86_TRAP_SS, SIGBUS, "stack segment", stack_segment )
-#endif
DO_ERROR_INFO(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0 )
#ifdef CONFIG_X86_64
/* Runs on IST stack */
-dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code)
-{
- enum ctx_state prev_state;
-
- prev_state = exception_enter();
- if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
- X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) {
- preempt_conditional_sti(regs);
- do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL);
- preempt_conditional_cli(regs);
- }
- exception_exit(prev_state);
-}
-
dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
{
static const char str[] = "double fault";
struct task_struct *tsk = current;
+#ifdef CONFIG_X86_ESPFIX64
+ extern unsigned char native_irq_return_iret[];
+
+ /*
+ * If IRET takes a non-IST fault on the espfix64 stack, then we
+ * end up promoting it to a doublefault. In that case, modify
+ * the stack to make it look like we just entered the #GP
+ * handler from user space, similar to bad_iret.
+ */
+ if (((long)regs->sp >> PGDIR_SHIFT) == ESPFIX_PGD_ENTRY &&
+ regs->cs == __KERNEL_CS &&
+ regs->ip == (unsigned long)native_irq_return_iret)
+ {
+ struct pt_regs *normal_regs = task_pt_regs(current);
+
+ /* Fake a #GP(0) from userspace. */
+ memmove(&normal_regs->ip, (void *)regs->sp, 5*8);
+ normal_regs->orig_ax = 0; /* Missing (lost) #GP error code */
+ regs->ip = (unsigned long)general_protection;
+ regs->sp = (unsigned long)&normal_regs->orig_ax;
+ return;
+ }
+#endif
+
exception_enter();
/* Return not checked because double check cannot be ignored */
notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
@@ -376,6 +384,35 @@ asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
*regs = *eregs;
return regs;
}
+
+struct bad_iret_stack {
+ void *error_entry_ret;
+ struct pt_regs regs;
+};
+
+asmlinkage __visible
+struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
+{
+ /*
+ * This is called from entry_64.S early in handling a fault
+ * caused by a bad iret to user mode. To handle the fault
+ * correctly, we want move our stack frame to task_pt_regs
+ * and we want to pretend that the exception came from the
+ * iret target.
+ */
+ struct bad_iret_stack *new_stack =
+ container_of(task_pt_regs(current),
+ struct bad_iret_stack, regs);
+
+ /* Copy the IRET target to the new stack. */
+ memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8);
+
+ /* Copy the remainder of the stack from the current stack. */
+ memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip));
+
+ BUG_ON(!user_mode_vm(&new_stack->regs));
+ return new_stack;
+}
#endif
/*
@@ -748,7 +785,7 @@ void __init trap_init(void)
set_intr_gate(X86_TRAP_OLD_MF, coprocessor_segment_overrun);
set_intr_gate(X86_TRAP_TS, invalid_TSS);
set_intr_gate(X86_TRAP_NP, segment_not_present);
- set_intr_gate_ist(X86_TRAP_SS, &stack_segment, STACKFAULT_STACK);
+ set_intr_gate(X86_TRAP_SS, stack_segment);
set_intr_gate(X86_TRAP_GP, general_protection);
set_intr_gate(X86_TRAP_SPURIOUS, spurious_interrupt_bug);
set_intr_gate(X86_TRAP_MF, coprocessor_error);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index aa21c23e010e..f127b83b5e0c 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4911,7 +4911,7 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu)
++vcpu->stat.insn_emulation_fail;
trace_kvm_emulate_insn_failed(vcpu);
- if (!is_guest_mode(vcpu)) {
+ if (!is_guest_mode(vcpu) && kvm_x86_ops->get_cpl(vcpu) == 0) {
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
vcpu->run->internal.ndata = 0;
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index f35c66c5959a..2308a401a1c5 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -1110,7 +1110,7 @@ void mark_rodata_ro(void)
unsigned long end = (unsigned long) &__end_rodata_hpage_align;
unsigned long text_end = PFN_ALIGN(&__stop___ex_table);
unsigned long rodata_end = PFN_ALIGN(&__end_rodata);
- unsigned long all_end = PFN_ALIGN(&_end);
+ unsigned long all_end;
printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n",
(end - start) >> 10);
@@ -1121,7 +1121,16 @@ void mark_rodata_ro(void)
/*
* The rodata/data/bss/brk section (but not the kernel text!)
* should also be not-executable.
+ *
+ * We align all_end to PMD_SIZE because the existing mapping
+ * is a full PMD. If we would align _brk_end to PAGE_SIZE we
+ * split the PMD and the reminder between _brk_end and the end
+ * of the PMD will remain mapped executable.
+ *
+ * Any PMD which was setup after the one which covers _brk_end
+ * has been zapped already via cleanup_highmem().
*/
+ all_end = roundup((unsigned long)_brk_end, PMD_SIZE);
set_memory_nx(rodata_start, (all_end - rodata_start) >> PAGE_SHIFT);
rodata_test();
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index c96314abd144..0004ac72dbdd 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -399,13 +399,20 @@ int pmdp_test_and_clear_young(struct vm_area_struct *vma,
int ptep_clear_flush_young(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep)
{
- int young;
-
- young = ptep_test_and_clear_young(vma, address, ptep);
- if (young)
- flush_tlb_page(vma, address);
-
- return young;
+ /*
+ * On x86 CPUs, clearing the accessed bit without a TLB flush
+ * doesn't cause data corruption. [ It could cause incorrect
+ * page aging and the (mistaken) reclaim of hot pages, but the
+ * chance of that should be relatively low. ]
+ *
+ * So as a performance optimization don't flush the TLB when
+ * clearing the accessed bit, it will eventually be flushed by
+ * a context switch or a VM operation anyway. [ In the rare
+ * event of it not getting flushed for a long time the delay
+ * shouldn't really matter because there's no real memory
+ * pressure for swapout to react to. ]
+ */
+ return ptep_test_and_clear_young(vma, address, ptep);
}
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
diff --git a/arch/x86/tools/calc_run_size.pl b/arch/x86/tools/calc_run_size.pl
new file mode 100644
index 000000000000..23210baade2d
--- /dev/null
+++ b/arch/x86/tools/calc_run_size.pl
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+#
+# Calculate the amount of space needed to run the kernel, including room for
+# the .bss and .brk sections.
+#
+# Usage:
+# objdump -h a.out | perl calc_run_size.pl
+use strict;
+
+my $mem_size = 0;
+my $file_offset = 0;
+
+my $sections=" *[0-9]+ \.(?:bss|brk) +";
+while (<>) {
+ if (/^$sections([0-9a-f]+) +(?:[0-9a-f]+ +){2}([0-9a-f]+)/) {
+ my $size = hex($1);
+ my $offset = hex($2);
+ $mem_size += $size;
+ if ($file_offset == 0) {
+ $file_offset = $offset;
+ } elsif ($file_offset != $offset) {
+ # BFD linker shows the same file offset in ELF.
+ # Gold linker shows them as consecutive.
+ next if ($file_offset + $mem_size == $offset + $size);
+
+ printf STDERR "file_offset: 0x%lx\n", $file_offset;
+ printf STDERR "mem_size: 0x%lx\n", $mem_size;
+ printf STDERR "offset: 0x%lx\n", $offset;
+ printf STDERR "size: 0x%lx\n", $size;
+
+ die ".bss and .brk are non-contiguous\n";
+ }
+ }
+}
+
+if ($file_offset == 0) {
+ die "Never found .bss or .brk file offset\n";
+}
+printf("%d\n", $mem_size + $file_offset);
diff --git a/arch/xtensa/include/uapi/asm/unistd.h b/arch/xtensa/include/uapi/asm/unistd.h
index b9395529f02d..50084f7c01c8 100644
--- a/arch/xtensa/include/uapi/asm/unistd.h
+++ b/arch/xtensa/include/uapi/asm/unistd.h
@@ -384,7 +384,8 @@ __SYSCALL(174, sys_chroot, 1)
#define __NR_pivot_root 175
__SYSCALL(175, sys_pivot_root, 2)
#define __NR_umount 176
-__SYSCALL(176, sys_umount, 2)
+__SYSCALL(176, sys_oldumount, 1)
+#define __ARCH_WANT_SYS_OLDUMOUNT
#define __NR_swapoff 177
__SYSCALL(177, sys_swapoff, 1)
#define __NR_sync 178
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 4f458b53f99d..82bfb0c81c9c 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -60,6 +60,7 @@ enum board_ids {
/* board IDs by feature in alphabetical order */
board_ahci,
board_ahci_ign_iferr,
+ board_ahci_nomsi,
board_ahci_noncq,
board_ahci_nosntf,
board_ahci_yes_fbs,
@@ -121,6 +122,13 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
+ [board_ahci_nomsi] = {
+ AHCI_HFLAGS (AHCI_HFLAG_NO_MSI),
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_ops,
+ },
[board_ahci_noncq] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ),
.flags = AHCI_FLAG_COMMON,
@@ -313,6 +321,11 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */
+ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */
+ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
@@ -475,10 +488,10 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */
/*
- * Samsung SSDs found on some macbooks. NCQ times out.
- * https://bugzilla.kernel.org/show_bug.cgi?id=60731
+ * Samsung SSDs found on some macbooks. NCQ times out if MSI is
+ * enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731
*/
- { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_noncq },
+ { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi },
/* Enmotus */
{ PCI_DEVICE(0x1c44, 0x8000), board_ahci },
diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c
index 2b25bd83fc9d..c1ea780fca07 100644
--- a/drivers/ata/sata_rcar.c
+++ b/drivers/ata/sata_rcar.c
@@ -146,6 +146,7 @@
enum sata_rcar_type {
RCAR_GEN1_SATA,
RCAR_GEN2_SATA,
+ RCAR_R8A7790_ES1_SATA,
};
struct sata_rcar_priv {
@@ -763,6 +764,9 @@ static void sata_rcar_setup_port(struct ata_host *host)
ap->udma_mask = ATA_UDMA6;
ap->flags |= ATA_FLAG_SATA;
+ if (priv->type == RCAR_R8A7790_ES1_SATA)
+ ap->flags |= ATA_FLAG_NO_DIPM;
+
ioaddr->cmd_addr = base + SDATA_REG;
ioaddr->ctl_addr = base + SSDEVCON_REG;
ioaddr->scr_addr = base + SCRSSTS_REG;
@@ -792,6 +796,7 @@ static void sata_rcar_init_controller(struct ata_host *host)
sata_rcar_gen1_phy_init(priv);
break;
case RCAR_GEN2_SATA:
+ case RCAR_R8A7790_ES1_SATA:
sata_rcar_gen2_phy_init(priv);
break;
default:
@@ -838,6 +843,10 @@ static struct of_device_id sata_rcar_match[] = {
.data = (void *)RCAR_GEN2_SATA
},
{
+ .compatible = "renesas,sata-r8a7790-es1",
+ .data = (void *)RCAR_R8A7790_ES1_SATA
+ },
+ {
.compatible = "renesas,sata-r8a7791",
.data = (void *)RCAR_GEN2_SATA
},
@@ -849,6 +858,7 @@ static const struct platform_device_id sata_rcar_id_table[] = {
{ "sata_rcar", RCAR_GEN1_SATA }, /* Deprecated by "sata-r8a7779" */
{ "sata-r8a7779", RCAR_GEN1_SATA },
{ "sata-r8a7790", RCAR_GEN2_SATA },
+ { "sata-r8a7790-es1", RCAR_R8A7790_ES1_SATA },
{ "sata-r8a7791", RCAR_GEN2_SATA },
{ },
};
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 72e00e66ecc5..16164bd42d1d 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -381,13 +381,14 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
* or wait for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC
* flags. If the RPM_ASYNC flag is set then queue a suspend request;
* otherwise run the ->runtime_suspend() callback directly. When
- * ->runtime_suspend succeeded, if a deferred resume was requested while
- * the callback was running then carry it out, otherwise send an idle
- * notification for its parent (if the suspend succeeded and both
- * ignore_children of parent->power and irq_safe of dev->power are not set).
- * If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO
- * flag is set and the next autosuspend-delay expiration time is in the
- * future, schedule another autosuspend attempt.
+ * ->runtime_suspend succeeded, if a deferred resume was requested
+ * while the callback was running then carry it out, otherwise send an
+ * idle notification for its parent (if the suspend succeeded,
+ * ignore_children of parent->power is not set and irq_safe of
+ * parent->power is set or irq_safe of dev->power is not set). If
+ * ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the
+ * RPM_AUTO flag is set and the next autosuspend-delay expiration time
+ * is in the future, schedule another autosuspend attempt.
*
* This function must be called under dev->power.lock with interrupts disabled.
*/
@@ -528,7 +529,8 @@ static int rpm_suspend(struct device *dev, int rpmflags)
}
/* Maybe the parent is now able to suspend. */
- if (parent && !parent->power.ignore_children && !dev->power.irq_safe) {
+ if (parent && !parent->power.ignore_children &&
+ (!dev->power.irq_safe || parent->power.irq_safe)) {
spin_unlock(&dev->power.lock);
spin_lock(&parent->power.lock);
@@ -688,12 +690,13 @@ static int rpm_resume(struct device *dev, int rpmflags)
if (!parent && dev->parent) {
/*
- * Increment the parent's usage counter and resume it if
- * necessary. Not needed if dev is irq-safe; then the
- * parent is permanently resumed.
+ * Increment the parent's usage counter and resume it
+ * if necessary. Not needed if dev is irq-safe and
+ * its paret is not; then the parent is permanently
+ * resumed.
*/
parent = dev->parent;
- if (dev->power.irq_safe)
+ if (dev->power.irq_safe && !parent->power.irq_safe)
goto skip_parent;
spin_unlock(&dev->power.lock);
@@ -754,7 +757,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
rpm_idle(dev, RPM_ASYNC);
out:
- if (parent && !dev->power.irq_safe) {
+ if (parent && (!dev->power.irq_safe || parent->power.irq_safe)) {
spin_unlock_irq(&dev->power.lock);
pm_runtime_put(parent);
@@ -1260,15 +1263,17 @@ EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks);
* @dev: Device to handle
*
* Set the power.irq_safe flag, which tells the PM core that the
- * ->runtime_suspend() and ->runtime_resume() callbacks for this device should
- * always be invoked with the spinlock held and interrupts disabled. It also
- * causes the parent's usage counter to be permanently incremented, preventing
- * the parent from runtime suspending -- otherwise an irq-safe child might have
- * to wait for a non-irq-safe parent.
+ * ->runtime_suspend() and ->runtime_resume() callbacks for this
+ * device should always be invoked with the spinlock held and
+ * interrupts disabled. It also causes the parent's usage counter to
+ * be permanently incremented if the parent's power.irq_safe flag is
+ * not set, preventing a non-irq-safe parent from runtime suspending
+ * -- otherwise an irq-safe child might have to wait for a
+ * non-irq-safe parent.
*/
void pm_runtime_irq_safe(struct device *dev)
{
- if (dev->parent)
+ if (dev->parent && !dev->parent->power.irq_safe)
pm_runtime_get_sync(dev->parent);
spin_lock_irq(&dev->power.lock);
dev->power.irq_safe = 1;
@@ -1398,6 +1403,6 @@ void pm_runtime_remove(struct device *dev)
/* Change the status back to 'suspended' to match the initial status. */
if (dev->power.runtime_status == RPM_ACTIVE)
pm_runtime_set_suspended(dev);
- if (dev->power.irq_safe && dev->parent)
+ if (dev->power.irq_safe && dev->parent && !dev->parent->power.irq_safe)
pm_runtime_put(dev->parent);
}
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index f6cff3be0ed7..2f9a3d8ecbbf 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1557,8 +1557,10 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
} else {
void *wval;
- if (!val_count)
- return -EINVAL;
+ if (!val_count) {
+ ret = -EINVAL;
+ goto out;
+ }
wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
if (!wval) {
diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c
index 5814deb6963d..0ebadf93b6c5 100644
--- a/drivers/block/sunvdc.c
+++ b/drivers/block/sunvdc.c
@@ -9,6 +9,7 @@
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
+#include <linux/cdrom.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
@@ -22,8 +23,8 @@
#define DRV_MODULE_NAME "sunvdc"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "1.0"
-#define DRV_MODULE_RELDATE "June 25, 2007"
+#define DRV_MODULE_VERSION "1.1"
+#define DRV_MODULE_RELDATE "February 13, 2013"
static char version[] =
DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
@@ -32,7 +33,7 @@ MODULE_DESCRIPTION("Sun LDOM virtual disk client driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
-#define VDC_TX_RING_SIZE 256
+#define VDC_TX_RING_SIZE 512
#define WAITING_FOR_LINK_UP 0x01
#define WAITING_FOR_TX_SPACE 0x02
@@ -65,11 +66,9 @@ struct vdc_port {
u64 operations;
u32 vdisk_size;
u8 vdisk_type;
+ u8 vdisk_mtype;
char disk_name[32];
-
- struct vio_disk_geom geom;
- struct vio_disk_vtoc label;
};
static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio)
@@ -79,9 +78,16 @@ static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio)
/* Ordered from largest major to lowest */
static struct vio_version vdc_versions[] = {
+ { .major = 1, .minor = 1 },
{ .major = 1, .minor = 0 },
};
+static inline int vdc_version_supported(struct vdc_port *port,
+ u16 major, u16 minor)
+{
+ return port->vio.ver.major == major && port->vio.ver.minor >= minor;
+}
+
#define VDCBLK_NAME "vdisk"
static int vdc_major;
#define PARTITION_SHIFT 3
@@ -94,18 +100,54 @@ static inline u32 vdc_tx_dring_avail(struct vio_dring_state *dr)
static int vdc_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
struct gendisk *disk = bdev->bd_disk;
- struct vdc_port *port = disk->private_data;
+ sector_t nsect = get_capacity(disk);
+ sector_t cylinders = nsect;
- geo->heads = (u8) port->geom.num_hd;
- geo->sectors = (u8) port->geom.num_sec;
- geo->cylinders = port->geom.num_cyl;
+ geo->heads = 0xff;
+ geo->sectors = 0x3f;
+ sector_div(cylinders, geo->heads * geo->sectors);
+ geo->cylinders = cylinders;
+ if ((sector_t)(geo->cylinders + 1) * geo->heads * geo->sectors < nsect)
+ geo->cylinders = 0xffff;
return 0;
}
+/* Add ioctl/CDROM_GET_CAPABILITY to support cdrom_id in udev
+ * when vdisk_mtype is VD_MEDIA_TYPE_CD or VD_MEDIA_TYPE_DVD.
+ * Needed to be able to install inside an ldom from an iso image.
+ */
+static int vdc_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned command, unsigned long argument)
+{
+ int i;
+ struct gendisk *disk;
+
+ switch (command) {
+ case CDROMMULTISESSION:
+ pr_debug(PFX "Multisession CDs not supported\n");
+ for (i = 0; i < sizeof(struct cdrom_multisession); i++)
+ if (put_user(0, (char __user *)(argument + i)))
+ return -EFAULT;
+ return 0;
+
+ case CDROM_GET_CAPABILITY:
+ disk = bdev->bd_disk;
+
+ if (bdev->bd_disk && (disk->flags & GENHD_FL_CD))
+ return 0;
+ return -EINVAL;
+
+ default:
+ pr_debug(PFX "ioctl %08x not supported\n", command);
+ return -EINVAL;
+ }
+}
+
static const struct block_device_operations vdc_fops = {
.owner = THIS_MODULE,
.getgeo = vdc_getgeo,
+ .ioctl = vdc_ioctl,
};
static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for)
@@ -165,9 +207,9 @@ static int vdc_handle_attr(struct vio_driver_state *vio, void *arg)
struct vio_disk_attr_info *pkt = arg;
viodbg(HS, "GOT ATTR stype[0x%x] ops[%llx] disk_size[%llu] disk_type[%x] "
- "xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n",
+ "mtype[0x%x] xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n",
pkt->tag.stype, pkt->operations,
- pkt->vdisk_size, pkt->vdisk_type,
+ pkt->vdisk_size, pkt->vdisk_type, pkt->vdisk_mtype,
pkt->xfer_mode, pkt->vdisk_block_size,
pkt->max_xfer_size);
@@ -192,8 +234,11 @@ static int vdc_handle_attr(struct vio_driver_state *vio, void *arg)
}
port->operations = pkt->operations;
- port->vdisk_size = pkt->vdisk_size;
port->vdisk_type = pkt->vdisk_type;
+ if (vdc_version_supported(port, 1, 1)) {
+ port->vdisk_size = pkt->vdisk_size;
+ port->vdisk_mtype = pkt->vdisk_mtype;
+ }
if (pkt->max_xfer_size < port->max_xfer_size)
port->max_xfer_size = pkt->max_xfer_size;
port->vdisk_block_size = pkt->vdisk_block_size;
@@ -236,7 +281,9 @@ static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr,
__blk_end_request(req, (desc->status ? -EIO : 0), desc->size);
- if (blk_queue_stopped(port->disk->queue))
+ /* restart blk queue when ring is half emptied */
+ if (blk_queue_stopped(port->disk->queue) &&
+ vdc_tx_dring_avail(dr) * 100 / VDC_TX_RING_SIZE >= 50)
blk_start_queue(port->disk->queue);
}
@@ -388,12 +435,6 @@ static int __send_request(struct request *req)
for (i = 0; i < nsg; i++)
len += sg[i].length;
- if (unlikely(vdc_tx_dring_avail(dr) < 1)) {
- blk_stop_queue(port->disk->queue);
- err = -ENOMEM;
- goto out;
- }
-
desc = vio_dring_cur(dr);
err = ldc_map_sg(port->vio.lp, sg, nsg,
@@ -433,21 +474,32 @@ static int __send_request(struct request *req)
port->req_id++;
dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1);
}
-out:
return err;
}
-static void do_vdc_request(struct request_queue *q)
+static void do_vdc_request(struct request_queue *rq)
{
- while (1) {
- struct request *req = blk_fetch_request(q);
+ struct request *req;
- if (!req)
- break;
+ while ((req = blk_peek_request(rq)) != NULL) {
+ struct vdc_port *port;
+ struct vio_dring_state *dr;
- if (__send_request(req) < 0)
- __blk_end_request_all(req, -EIO);
+ port = req->rq_disk->private_data;
+ dr = &port->vio.drings[VIO_DRIVER_TX_RING];
+ if (unlikely(vdc_tx_dring_avail(dr) < 1))
+ goto wait;
+
+ blk_start_request(req);
+
+ if (__send_request(req) < 0) {
+ blk_requeue_request(rq, req);
+wait:
+ /* Avoid pointless unplugs. */
+ blk_stop_queue(rq);
+ break;
+ }
}
}
@@ -656,25 +708,27 @@ static int probe_disk(struct vdc_port *port)
if (comp.err)
return comp.err;
- err = generic_request(port, VD_OP_GET_VTOC,
- &port->label, sizeof(port->label));
- if (err < 0) {
- printk(KERN_ERR PFX "VD_OP_GET_VTOC returns error %d\n", err);
- return err;
- }
-
- err = generic_request(port, VD_OP_GET_DISKGEOM,
- &port->geom, sizeof(port->geom));
- if (err < 0) {
- printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "
- "error %d\n", err);
- return err;
+ if (vdc_version_supported(port, 1, 1)) {
+ /* vdisk_size should be set during the handshake, if it wasn't
+ * then the underlying disk is reserved by another system
+ */
+ if (port->vdisk_size == -1)
+ return -ENODEV;
+ } else {
+ struct vio_disk_geom geom;
+
+ err = generic_request(port, VD_OP_GET_DISKGEOM,
+ &geom, sizeof(geom));
+ if (err < 0) {
+ printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "
+ "error %d\n", err);
+ return err;
+ }
+ port->vdisk_size = ((u64)geom.num_cyl *
+ (u64)geom.num_hd *
+ (u64)geom.num_sec);
}
- port->vdisk_size = ((u64)port->geom.num_cyl *
- (u64)port->geom.num_hd *
- (u64)port->geom.num_sec);
-
q = blk_init_queue(do_vdc_request, &port->vio.lock);
if (!q) {
printk(KERN_ERR PFX "%s: Could not allocate queue.\n",
@@ -691,6 +745,10 @@ static int probe_disk(struct vdc_port *port)
port->disk = g;
+ /* Each segment in a request is up to an aligned page in size. */
+ blk_queue_segment_boundary(q, PAGE_SIZE - 1);
+ blk_queue_max_segment_size(q, PAGE_SIZE);
+
blk_queue_max_segments(q, port->ring_cookies);
blk_queue_max_hw_sectors(q, port->max_xfer_size);
g->major = vdc_major;
@@ -704,9 +762,32 @@ static int probe_disk(struct vdc_port *port)
set_capacity(g, port->vdisk_size);
- printk(KERN_INFO PFX "%s: %u sectors (%u MB)\n",
+ if (vdc_version_supported(port, 1, 1)) {
+ switch (port->vdisk_mtype) {
+ case VD_MEDIA_TYPE_CD:
+ pr_info(PFX "Virtual CDROM %s\n", port->disk_name);
+ g->flags |= GENHD_FL_CD;
+ g->flags |= GENHD_FL_REMOVABLE;
+ set_disk_ro(g, 1);
+ break;
+
+ case VD_MEDIA_TYPE_DVD:
+ pr_info(PFX "Virtual DVD %s\n", port->disk_name);
+ g->flags |= GENHD_FL_CD;
+ g->flags |= GENHD_FL_REMOVABLE;
+ set_disk_ro(g, 1);
+ break;
+
+ case VD_MEDIA_TYPE_FIXED:
+ pr_info(PFX "Virtual Hard disk %s\n", port->disk_name);
+ break;
+ }
+ }
+
+ pr_info(PFX "%s: %u sectors (%u MB) protocol %d.%d\n",
g->disk_name,
- port->vdisk_size, (port->vdisk_size >> (20 - 9)));
+ port->vdisk_size, (port->vdisk_size >> (20 - 9)),
+ port->vio.ver.major, port->vio.ver.minor);
add_disk(g);
@@ -765,6 +846,7 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
else
snprintf(port->disk_name, sizeof(port->disk_name),
VDCBLK_NAME "%c", 'a' + ((int)vdev->dev_no % 26));
+ port->vdisk_size = -1;
err = vio_driver_init(&port->vio, vdev, VDEV_DISK,
vdc_versions, ARRAY_SIZE(vdc_versions),
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 51c557cfd92b..d8ddb8e2adc1 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -447,7 +447,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
}
if (page_zero_filled(uncmem)) {
- kunmap_atomic(user_mem);
+ if (user_mem)
+ kunmap_atomic(user_mem);
/* Free memory associated with this sector now. */
write_lock(&zram->meta->tb_lock);
zram_free_page(zram, index);
diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c
index ab7ffdec0ec3..f38f2c13e79c 100644
--- a/drivers/char/hw_random/pseries-rng.c
+++ b/drivers/char/hw_random/pseries-rng.c
@@ -25,18 +25,21 @@
#include <asm/vio.h>
-static int pseries_rng_data_read(struct hwrng *rng, u32 *data)
+static int pseries_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
+ u64 buffer[PLPAR_HCALL_BUFSIZE];
+ size_t size = max < 8 ? max : 8;
int rc;
- rc = plpar_hcall(H_RANDOM, (unsigned long *)data);
+ rc = plpar_hcall(H_RANDOM, (unsigned long *)buffer);
if (rc != H_SUCCESS) {
pr_err_ratelimited("H_RANDOM call failed %d\n", rc);
return -EIO;
}
+ memcpy(data, buffer, size);
/* The hypervisor interface returns 64 bits */
- return 8;
+ return size;
}
/**
@@ -55,7 +58,7 @@ static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev)
static struct hwrng pseries_rng = {
.name = KBUILD_MODNAME,
- .data_read = pseries_rng_data_read,
+ .read = pseries_rng_read,
};
static int __init pseries_rng_probe(struct vio_dev *dev,
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index bf497afba9ad..7d19f86012f2 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -182,6 +182,12 @@ static void __init sun4i_timer_init(struct device_node *node)
/* Make sure timer is stopped before playing with interrupts */
sun4i_clkevt_time_stop(0);
+ sun4i_clockevent.cpumask = cpu_possible_mask;
+ sun4i_clockevent.irq = irq;
+
+ clockevents_config_and_register(&sun4i_clockevent, rate,
+ TIMER_SYNC_TICKS, 0xffffffff);
+
ret = setup_irq(irq, &sun4i_timer_irq);
if (ret)
pr_warn("failed to setup irq %d\n", irq);
@@ -189,12 +195,6 @@ static void __init sun4i_timer_init(struct device_node *node)
/* Enable timer0 interrupt */
val = readl(timer_base + TIMER_IRQ_EN_REG);
writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG);
-
- sun4i_clockevent.cpumask = cpu_possible_mask;
- sun4i_clockevent.irq = irq;
-
- clockevents_config_and_register(&sun4i_clockevent, rate,
- TIMER_SYNC_TICKS, 0xffffffff);
}
CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer",
sun4i_timer_init);
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index a4127453baae..d97a03dbf42c 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -835,8 +835,9 @@ static int ahash_update_ctx(struct ahash_request *req)
edesc->sec4_sg + sec4_sg_src_index,
chained);
if (*next_buflen) {
- sg_copy_part(next_buf, req->src, to_hash -
- *buflen, req->nbytes);
+ scatterwalk_map_and_copy(next_buf, req->src,
+ to_hash - *buflen,
+ *next_buflen, 0);
state->current_buf = !state->current_buf;
}
} else {
@@ -869,7 +870,8 @@ static int ahash_update_ctx(struct ahash_request *req)
kfree(edesc);
}
} else if (*next_buflen) {
- sg_copy(buf + *buflen, req->src, req->nbytes);
+ scatterwalk_map_and_copy(buf + *buflen, req->src, 0,
+ req->nbytes, 0);
*buflen = *next_buflen;
*next_buflen = last_buflen;
}
@@ -1216,8 +1218,9 @@ static int ahash_update_no_ctx(struct ahash_request *req)
src_map_to_sec4_sg(jrdev, req->src, src_nents,
edesc->sec4_sg + 1, chained);
if (*next_buflen) {
- sg_copy_part(next_buf, req->src, to_hash - *buflen,
- req->nbytes);
+ scatterwalk_map_and_copy(next_buf, req->src,
+ to_hash - *buflen,
+ *next_buflen, 0);
state->current_buf = !state->current_buf;
}
@@ -1248,7 +1251,8 @@ static int ahash_update_no_ctx(struct ahash_request *req)
kfree(edesc);
}
} else if (*next_buflen) {
- sg_copy(buf + *buflen, req->src, req->nbytes);
+ scatterwalk_map_and_copy(buf + *buflen, req->src, 0,
+ req->nbytes, 0);
*buflen = *next_buflen;
*next_buflen = 0;
}
@@ -1405,7 +1409,8 @@ static int ahash_update_first(struct ahash_request *req)
}
if (*next_buflen)
- sg_copy_part(next_buf, req->src, to_hash, req->nbytes);
+ scatterwalk_map_and_copy(next_buf, req->src, to_hash,
+ *next_buflen, 0);
sh_len = desc_len(sh_desc);
desc = edesc->hw_desc;
@@ -1438,7 +1443,8 @@ static int ahash_update_first(struct ahash_request *req)
state->update = ahash_update_no_ctx;
state->finup = ahash_finup_no_ctx;
state->final = ahash_final_no_ctx;
- sg_copy(next_buf, req->src, req->nbytes);
+ scatterwalk_map_and_copy(next_buf, req->src, 0,
+ req->nbytes, 0);
}
#ifdef DEBUG
print_hex_dump(KERN_ERR, "next buf@"__stringify(__LINE__)": ",
diff --git a/drivers/crypto/caam/key_gen.c b/drivers/crypto/caam/key_gen.c
index ea2e406610eb..b872eed2957b 100644
--- a/drivers/crypto/caam/key_gen.c
+++ b/drivers/crypto/caam/key_gen.c
@@ -51,23 +51,29 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
u32 *desc;
struct split_key_result result;
dma_addr_t dma_addr_in, dma_addr_out;
- int ret = 0;
+ int ret = -ENOMEM;
desc = kmalloc(CAAM_CMD_SZ * 6 + CAAM_PTR_SZ * 2, GFP_KERNEL | GFP_DMA);
if (!desc) {
dev_err(jrdev, "unable to allocate key input memory\n");
- return -ENOMEM;
+ return ret;
}
- init_job_desc(desc, 0);
-
dma_addr_in = dma_map_single(jrdev, (void *)key_in, keylen,
DMA_TO_DEVICE);
if (dma_mapping_error(jrdev, dma_addr_in)) {
dev_err(jrdev, "unable to map key input memory\n");
- kfree(desc);
- return -ENOMEM;
+ goto out_free;
}
+
+ dma_addr_out = dma_map_single(jrdev, key_out, split_key_pad_len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(jrdev, dma_addr_out)) {
+ dev_err(jrdev, "unable to map key output memory\n");
+ goto out_unmap_in;
+ }
+
+ init_job_desc(desc, 0);
append_key(desc, dma_addr_in, keylen, CLASS_2 | KEY_DEST_CLASS_REG);
/* Sets MDHA up into an HMAC-INIT */
@@ -84,13 +90,6 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
* FIFO_STORE with the explicit split-key content store
* (0x26 output type)
*/
- dma_addr_out = dma_map_single(jrdev, key_out, split_key_pad_len,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(jrdev, dma_addr_out)) {
- dev_err(jrdev, "unable to map key output memory\n");
- kfree(desc);
- return -ENOMEM;
- }
append_fifo_store(desc, dma_addr_out, split_key_len,
LDST_CLASS_2_CCB | FIFOST_TYPE_SPLIT_KEK);
@@ -118,10 +117,10 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
dma_unmap_single(jrdev, dma_addr_out, split_key_pad_len,
DMA_FROM_DEVICE);
+out_unmap_in:
dma_unmap_single(jrdev, dma_addr_in, keylen, DMA_TO_DEVICE);
-
+out_free:
kfree(desc);
-
return ret;
}
EXPORT_SYMBOL(gen_split_key);
diff --git a/drivers/crypto/caam/sg_sw_sec4.h b/drivers/crypto/caam/sg_sw_sec4.h
index b12ff85f4241..ce28a563effc 100644
--- a/drivers/crypto/caam/sg_sw_sec4.h
+++ b/drivers/crypto/caam/sg_sw_sec4.h
@@ -116,57 +116,3 @@ static int dma_unmap_sg_chained(struct device *dev, struct scatterlist *sg,
}
return nents;
}
-
-/* Map SG page in kernel virtual address space and copy */
-static inline void sg_map_copy(u8 *dest, struct scatterlist *sg,
- int len, int offset)
-{
- u8 *mapped_addr;
-
- /*
- * Page here can be user-space pinned using get_user_pages
- * Same must be kmapped before use and kunmapped subsequently
- */
- mapped_addr = kmap_atomic(sg_page(sg));
- memcpy(dest, mapped_addr + offset, len);
- kunmap_atomic(mapped_addr);
-}
-
-/* Copy from len bytes of sg to dest, starting from beginning */
-static inline void sg_copy(u8 *dest, struct scatterlist *sg, unsigned int len)
-{
- struct scatterlist *current_sg = sg;
- int cpy_index = 0, next_cpy_index = current_sg->length;
-
- while (next_cpy_index < len) {
- sg_map_copy(dest + cpy_index, current_sg, current_sg->length,
- current_sg->offset);
- current_sg = scatterwalk_sg_next(current_sg);
- cpy_index = next_cpy_index;
- next_cpy_index += current_sg->length;
- }
- if (cpy_index < len)
- sg_map_copy(dest + cpy_index, current_sg, len-cpy_index,
- current_sg->offset);
-}
-
-/* Copy sg data, from to_skip to end, to dest */
-static inline void sg_copy_part(u8 *dest, struct scatterlist *sg,
- int to_skip, unsigned int end)
-{
- struct scatterlist *current_sg = sg;
- int sg_index, cpy_index, offset;
-
- sg_index = current_sg->length;
- while (sg_index <= to_skip) {
- current_sg = scatterwalk_sg_next(current_sg);
- sg_index += current_sg->length;
- }
- cpy_index = sg_index - to_skip;
- offset = current_sg->offset + current_sg->length - cpy_index;
- sg_map_copy(dest, current_sg, cpy_index, offset);
- if (end - sg_index) {
- current_sg = scatterwalk_sg_next(current_sg);
- sg_copy(dest + cpy_index, current_sg, end - sg_index);
- }
-}
diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c
index 8f8b0b608875..617fd0c86b07 100644
--- a/drivers/dma/cppi41.c
+++ b/drivers/dma/cppi41.c
@@ -1,3 +1,4 @@
+#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
@@ -567,7 +568,7 @@ static int cppi41_tear_down_chan(struct cppi41_channel *c)
reg |= GCR_TEARDOWN;
cppi_writel(reg, c->gcr_reg);
c->td_queued = 1;
- c->td_retry = 100;
+ c->td_retry = 500;
}
if (!c->td_seen || !c->td_desc_seen) {
@@ -603,12 +604,16 @@ static int cppi41_tear_down_chan(struct cppi41_channel *c)
* descriptor before the TD we fetch it from enqueue, it has to be
* there waiting for us.
*/
- if (!c->td_seen && c->td_retry)
+ if (!c->td_seen && c->td_retry) {
+ udelay(1);
return -EAGAIN;
-
+ }
WARN_ON(!c->td_retry);
+
if (!c->td_desc_seen) {
desc_phys = cppi41_pop_desc(cdd, c->q_num);
+ if (!desc_phys)
+ desc_phys = cppi41_pop_desc(cdd, c->q_comp_num);
WARN_ON(!desc_phys);
}
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index d7d5c8af92b9..6d4456898007 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -1637,8 +1637,7 @@ static int dispatch_ioctl(struct client *client,
_IOC_SIZE(cmd) > sizeof(buffer))
return -ENOTTY;
- if (_IOC_DIR(cmd) == _IOC_READ)
- memset(&buffer, 0, _IOC_SIZE(cmd));
+ memset(&buffer, 0, sizeof(buffer));
if (_IOC_DIR(cmd) & _IOC_WRITE)
if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 27b46751ea7e..e98c9527136e 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -450,6 +450,14 @@ static int pcf857x_remove(struct i2c_client *client)
return status;
}
+static void pcf857x_shutdown(struct i2c_client *client)
+{
+ struct pcf857x *gpio = i2c_get_clientdata(client);
+
+ /* Drive all the I/O lines high */
+ gpio->write(gpio->client, BIT(gpio->chip.ngpio) - 1);
+}
+
static struct i2c_driver pcf857x_driver = {
.driver = {
.name = "pcf857x",
@@ -458,6 +466,7 @@ static struct i2c_driver pcf857x_driver = {
},
.probe = pcf857x_probe,
.remove = pcf857x_remove,
+ .shutdown = pcf857x_shutdown,
.id_table = pcf857x_id,
};
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index c2676b5908d9..8c6f17d7782c 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -940,6 +940,50 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
EXPORT_SYMBOL(drm_vblank_put);
/**
+ * drm_wait_one_vblank - wait for one vblank
+ * @dev: DRM device
+ * @crtc: crtc index
+ *
+ * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_wait_one_vblank(struct drm_device *dev, int crtc)
+{
+ int ret;
+ u32 last;
+
+ ret = drm_vblank_get(dev, crtc);
+ if (WARN_ON(ret))
+ return;
+
+ last = drm_vblank_count(dev, crtc);
+
+ ret = wait_event_timeout(dev->vblank[crtc].queue,
+ last != drm_vblank_count(dev, crtc),
+ msecs_to_jiffies(100));
+
+ WARN_ON(ret == 0);
+
+ drm_vblank_put(dev, crtc);
+}
+EXPORT_SYMBOL(drm_wait_one_vblank);
+
+/**
+ * drm_crtc_wait_one_vblank - wait for one vblank
+ * @crtc: DRM crtc
+ *
+ * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
+{
+ drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
+
+/**
* drm_vblank_off - disable vblank events on a CRTC
* @dev: DRM device
* @crtc: CRTC in question
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index c7778899c1f3..5c47a9d1d132 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -76,6 +76,8 @@ struct omap_crtc {
* XXX maybe fold into apply_work??
*/
struct work_struct page_flip_work;
+
+ bool ignore_digit_sync_lost;
};
uint32_t pipe2vbl(struct drm_crtc *crtc)
@@ -434,10 +436,14 @@ static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
struct omap_crtc *omap_crtc =
container_of(irq, struct omap_crtc, error_irq);
- struct drm_crtc *crtc = &omap_crtc->base;
- DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus);
- /* avoid getting in a flood, unregister the irq until next vblank */
- __omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
+
+ if (omap_crtc->ignore_digit_sync_lost) {
+ irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
+ if (!irqstatus)
+ return;
+ }
+
+ DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
}
static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
@@ -446,9 +452,6 @@ static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
container_of(irq, struct omap_crtc, apply_irq);
struct drm_crtc *crtc = &omap_crtc->base;
- if (!omap_crtc->error_irq.registered)
- __omap_irq_register(crtc->dev, &omap_crtc->error_irq);
-
/* make sure we see the most recent 'go_bit_set' */
rmb();
if (omap_crtc->go_bit_set && !dispc_mgr_go_busy(omap_crtc->channel)) {
@@ -570,11 +573,13 @@ static void set_enabled(struct drm_crtc *crtc, bool enable)
if (dispc_mgr_is_enabled(channel) == enable)
return;
- /*
- * Digit output produces some sync lost interrupts during the first
- * frame when enabling, so we need to ignore those.
- */
- omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
+ if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
+ /*
+ * Digit output produces some sync lost interrupts during the
+ * first frame when enabling, so we need to ignore those.
+ */
+ omap_crtc->ignore_digit_sync_lost = true;
+ }
framedone_irq = dispc_mgr_get_framedone_irq(channel);
vsync_irq = dispc_mgr_get_vsync_irq(channel);
@@ -605,7 +610,11 @@ static void set_enabled(struct drm_crtc *crtc, bool enable)
omap_crtc->name, enable ? "enable" : "disable");
}
- omap_irq_register(crtc->dev, &omap_crtc->error_irq);
+ if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
+ omap_crtc->ignore_digit_sync_lost = false;
+ /* make sure the irq handler sees the value above */
+ mb();
+ }
}
static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
index d96660573076..9f32a83ca507 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
@@ -148,7 +148,7 @@ struct refill_engine {
bool async;
- wait_queue_head_t wait_for_refill;
+ struct completion compl;
struct list_head idle_node;
};
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index c4563af55a82..ccf1bf06ceb7 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -29,6 +29,7 @@
#include <linux/mm.h>
#include <linux/time.h>
#include <linux/list.h>
+#include <linux/completion.h>
#include "omap_dmm_tiler.h"
#include "omap_dmm_priv.h"
@@ -146,10 +147,10 @@ static irqreturn_t omap_dmm_irq_handler(int irq, void *arg)
for (i = 0; i < dmm->num_engines; i++) {
if (status & DMM_IRQSTAT_LST) {
- wake_up_interruptible(&dmm->engines[i].wait_for_refill);
-
if (dmm->engines[i].async)
release_engine(&dmm->engines[i]);
+
+ complete(&dmm->engines[i].compl);
}
status >>= 8;
@@ -273,7 +274,8 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait)
/* mark whether it is async to denote list management in IRQ handler */
engine->async = wait ? false : true;
- /* verify that the irq handler sees the 'async' value */
+ reinit_completion(&engine->compl);
+ /* verify that the irq handler sees the 'async' and completion value */
smp_mb();
/* kick reload */
@@ -281,9 +283,8 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait)
dmm->base + reg[PAT_DESCR][engine->id]);
if (wait) {
- if (wait_event_interruptible_timeout(engine->wait_for_refill,
- wait_status(engine, DMM_PATSTATUS_READY) == 0,
- msecs_to_jiffies(1)) <= 0) {
+ if (!wait_for_completion_timeout(&engine->compl,
+ msecs_to_jiffies(1))) {
dev_err(dmm->dev, "timed out waiting for done\n");
ret = -ETIMEDOUT;
}
@@ -719,7 +720,7 @@ static int omap_dmm_probe(struct platform_device *dev)
(REFILL_BUFFER_SIZE * i);
omap_dmm->engines[i].refill_pa = omap_dmm->refill_pa +
(REFILL_BUFFER_SIZE * i);
- init_waitqueue_head(&omap_dmm->engines[i].wait_for_refill);
+ init_completion(&omap_dmm->engines[i].compl);
list_add(&omap_dmm->engines[i].idle_node, &omap_dmm->idle_head);
}
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 67cbe9adae7f..7cb1c8f4c7a2 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -479,6 +479,7 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
priv->wq = alloc_ordered_workqueue("omapdrm", 0);
+ spin_lock_init(&priv->list_lock);
INIT_LIST_HEAD(&priv->obj_list);
omap_gem_init(dev);
@@ -683,23 +684,6 @@ static struct drm_driver omap_drm_driver = {
.patchlevel = DRIVER_PATCHLEVEL,
};
-static int pdev_suspend(struct platform_device *pDevice, pm_message_t state)
-{
- DBG("");
- return 0;
-}
-
-static int pdev_resume(struct platform_device *device)
-{
- DBG("");
- return 0;
-}
-
-static void pdev_shutdown(struct platform_device *device)
-{
- DBG("");
-}
-
static int pdev_probe(struct platform_device *device)
{
int r;
@@ -731,9 +715,28 @@ static int pdev_remove(struct platform_device *device)
return 0;
}
+static int omap_drm_suspend(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ drm_kms_helper_poll_disable(drm_dev);
+
+ return 0;
+}
+
+static int omap_drm_resume(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ drm_kms_helper_poll_enable(drm_dev);
+
+ return omap_gem_resume(dev);
+}
+
#ifdef CONFIG_PM
static const struct dev_pm_ops omapdrm_pm_ops = {
- .resume = omap_gem_resume,
+ .suspend = omap_drm_suspend,
+ .resume = omap_drm_resume,
};
#endif
@@ -747,9 +750,6 @@ static struct platform_driver pdev = {
},
.probe = pdev_probe,
.remove = pdev_remove,
- .suspend = pdev_suspend,
- .resume = pdev_resume,
- .shutdown = pdev_shutdown,
};
static int __init omap_drm_init(void)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h
index 2bd0c158990e..da2fafe3cc6f 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.h
+++ b/drivers/gpu/drm/omapdrm/omap_drv.h
@@ -104,6 +104,9 @@ struct omap_drm_private {
struct workqueue_struct *wq;
+ /* lock for obj_list below */
+ spinlock_t list_lock;
+
/* list of GEM objects: */
struct list_head obj_list;
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 3dfb6dab0158..92c2df894e72 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -25,8 +25,11 @@
#include <linux/suspend.h>
MODULE_PARM_DESC(ywrap, "Enable ywrap scrolling (omap44xx and later, default 'y')");
+MODULE_PARM_DESC(num_fbdev_backbuffers, "Number of buffers to allocate, 1=single buffered, 2=double buffers, 3=triple buffered...");
static bool ywrap_enabled = true;
+static int num_fbdev_backbuffers = 1;
module_param_named(ywrap, ywrap_enabled, bool, 0644);
+module_param(num_fbdev_backbuffers, int, 0444);
/*
* fbdev funcs, to implement legacy fbdev interface on top of drm driver
@@ -154,15 +157,16 @@ static int omap_fbdev_create(struct drm_fb_helper *helper,
sizes->surface_bpp = 32;
sizes->surface_depth = 32;
- DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width,
+ DBG("create fbdev: %dx%d@%d (%dx%d)x%d", sizes->surface_width,
sizes->surface_height, sizes->surface_bpp,
- sizes->fb_width, sizes->fb_height);
+ sizes->fb_width, sizes->fb_height,
+ num_fbdev_backbuffers);
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
mode_cmd.width = sizes->surface_width;
- mode_cmd.height = sizes->surface_height;
+ mode_cmd.height = sizes->surface_height * num_fbdev_backbuffers;
mode_cmd.pitches[0] = align_pitch(
mode_cmd.width * ((sizes->surface_bpp + 7) / 8),
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index d4a93e059211..01e68997c823 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -1192,13 +1192,16 @@ unlock:
void omap_gem_free_object(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
+ struct omap_drm_private *priv = dev->dev_private;
struct omap_gem_object *omap_obj = to_omap_bo(obj);
evict(obj);
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+ spin_lock(&priv->list_lock);
list_del(&omap_obj->mm_list);
+ spin_unlock(&priv->list_lock);
drm_gem_free_mmap_offset(obj);
@@ -1295,7 +1298,9 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
if (!omap_obj)
goto fail;
+ spin_lock(&priv->list_lock);
list_add(&omap_obj->mm_list, &priv->obj_list);
+ spin_unlock(&priv->list_lock);
obj = &omap_obj->base;
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c
index f035d2bceae7..3eb097efc488 100644
--- a/drivers/gpu/drm/omapdrm/omap_irq.c
+++ b/drivers/gpu/drm/omapdrm/omap_irq.c
@@ -34,7 +34,7 @@ static void omap_irq_update(struct drm_device *dev)
struct omap_drm_irq *irq;
uint32_t irqmask = priv->vblank_mask;
- BUG_ON(!spin_is_locked(&list_lock));
+ assert_spin_locked(&list_lock);
list_for_each_entry(irq, &priv->irq_list, node)
irqmask |= irq->irqmask;
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index 8f4fb34a4392..8f8b3041e611 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -389,7 +389,8 @@ static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
struct omap_plane *omap_plane =
container_of(irq, struct omap_plane, error_irq);
- DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus);
+ DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name,
+ irqstatus);
}
static const char *plane_names[] = {
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index ab5c26575622..ddf70d6c0270 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -3936,8 +3936,8 @@ static int cik_cp_gfx_start(struct radeon_device *rdev)
/* init the CE partitions. CE only used for gfx on CIK */
radeon_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2));
radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE));
- radeon_ring_write(ring, 0xc000);
- radeon_ring_write(ring, 0xc000);
+ radeon_ring_write(ring, 0x8000);
+ radeon_ring_write(ring, 0x8000);
/* setup clear context state */
radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
@@ -8893,6 +8893,9 @@ void dce8_bandwidth_update(struct radeon_device *rdev)
u32 num_heads = 0, lb_size;
int i;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) {
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 4b3c5f7ae63b..7138f3e31b7c 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -2362,6 +2362,9 @@ void evergreen_bandwidth_update(struct radeon_device *rdev)
u32 num_heads = 0, lb_size;
int i;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) {
@@ -2570,6 +2573,7 @@ void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *sav
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
tmp |= EVERGREEN_CRTC_BLANK_DATA_EN;
WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
+ WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
}
} else {
tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]);
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 3cc78bb66042..07620e198a6d 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -3219,6 +3219,9 @@ void r100_bandwidth_update(struct radeon_device *rdev)
uint32_t pixel_bytes1 = 0;
uint32_t pixel_bytes2 = 0;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled) {
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c
index 813db8de52b7..3334f916945b 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.c
+++ b/drivers/gpu/drm/radeon/r600_dpm.c
@@ -1209,7 +1209,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(ext_hdr->usPowerTuneTableOffset));
rdev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit =
- ppt->usMaximumPowerDeliveryLimit;
+ le16_to_cpu(ppt->usMaximumPowerDeliveryLimit);
pt = &ppt->power_tune_table;
} else {
ATOM_PPLIB_POWERTUNE_Table *ppt = (ATOM_PPLIB_POWERTUNE_Table *)
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 089c9ffb0aa9..b3f0293ba0d8 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -202,6 +202,16 @@ static bool radeon_msi_ok(struct radeon_device *rdev)
if (rdev->flags & RADEON_IS_AGP)
return false;
+ /*
+ * Older chips have a HW limitation, they can only generate 40 bits
+ * of address for "64-bit" MSIs which breaks on some platforms, notably
+ * IBM POWER servers, so we limit them
+ */
+ if (rdev->family < CHIP_BONAIRE) {
+ dev_info(rdev->dev, "radeon: MSI limited to 32-bit\n");
+ rdev->pdev->no_64bit_msi = 1;
+ }
+
/* force MSI on */
if (radeon_msi == 1)
return true;
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 95b693c11640..e5619d5e2a30 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -890,6 +890,9 @@ void rs600_bandwidth_update(struct radeon_device *rdev)
u32 d1mode_priority_a_cnt, d2mode_priority_a_cnt;
/* FIXME: implement full support */
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled)
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
index 3462b64369bf..0a2d36e81108 100644
--- a/drivers/gpu/drm/radeon/rs690.c
+++ b/drivers/gpu/drm/radeon/rs690.c
@@ -579,6 +579,9 @@ void rs690_bandwidth_update(struct radeon_device *rdev)
u32 d1mode_priority_a_cnt, d1mode_priority_b_cnt;
u32 d2mode_priority_a_cnt, d2mode_priority_b_cnt;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled)
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index 237dd29d9f1c..b49965a21a2d 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -1276,6 +1276,9 @@ void rv515_bandwidth_update(struct radeon_device *rdev)
struct drm_display_mode *mode0 = NULL;
struct drm_display_mode *mode1 = NULL;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled)
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index 559564c1dc97..52b64ad285d6 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -2227,6 +2227,9 @@ void dce6_bandwidth_update(struct radeon_device *rdev)
u32 num_heads = 0, lb_size;
int i;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) {
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index ea6203ee7bcc..23467a2abd62 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -2425,6 +2425,8 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
attr.grh.sgid_index = cmd.attr.grh.sgid_index;
attr.grh.hop_limit = cmd.attr.grh.hop_limit;
attr.grh.traffic_class = cmd.attr.grh.traffic_class;
+ attr.vlan_id = 0;
+ memset(&attr.dmac, 0, sizeof(attr.dmac));
memcpy(attr.grh.dgid.raw, cmd.attr.grh.dgid, 16);
ah = ib_create_ah(pd, &attr);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index c5c194c2e0b6..a96cfc31372e 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -112,9 +112,12 @@ isert_conn_setup_qp(struct isert_conn *isert_conn, struct rdma_cm_id *cma_id)
attr.cap.max_recv_wr = ISERT_QP_MAX_RECV_DTOS;
/*
* FIXME: Use devattr.max_sge - 2 for max_send_sge as
- * work-around for RDMA_READ..
+ * work-around for RDMA_READs with ConnectX-2.
+ *
+ * Also, still make sure to have at least two SGEs for
+ * outgoing control PDU responses.
*/
- attr.cap.max_send_sge = device->dev_attr.max_sge - 2;
+ attr.cap.max_send_sge = max(2, device->dev_attr.max_sge - 2);
isert_conn->max_sge = attr.cap.max_send_sge;
attr.cap.max_recv_sge = 1;
@@ -220,12 +223,16 @@ isert_create_device_ib_res(struct isert_device *device)
struct isert_cq_desc *cq_desc;
struct ib_device_attr *dev_attr;
int ret = 0, i, j;
+ int max_rx_cqe, max_tx_cqe;
dev_attr = &device->dev_attr;
ret = isert_query_device(ib_dev, dev_attr);
if (ret)
return ret;
+ max_rx_cqe = min(ISER_MAX_RX_CQ_LEN, dev_attr->max_cqe);
+ max_tx_cqe = min(ISER_MAX_TX_CQ_LEN, dev_attr->max_cqe);
+
/* asign function handlers */
if (dev_attr->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS) {
device->use_fastreg = 1;
@@ -261,7 +268,7 @@ isert_create_device_ib_res(struct isert_device *device)
isert_cq_rx_callback,
isert_cq_event_callback,
(void *)&cq_desc[i],
- ISER_MAX_RX_CQ_LEN, i);
+ max_rx_cqe, i);
if (IS_ERR(device->dev_rx_cq[i])) {
ret = PTR_ERR(device->dev_rx_cq[i]);
device->dev_rx_cq[i] = NULL;
@@ -273,7 +280,7 @@ isert_create_device_ib_res(struct isert_device *device)
isert_cq_tx_callback,
isert_cq_event_callback,
(void *)&cq_desc[i],
- ISER_MAX_TX_CQ_LEN, i);
+ max_tx_cqe, i);
if (IS_ERR(device->dev_tx_cq[i])) {
ret = PTR_ERR(device->dev_tx_cq[i]);
device->dev_tx_cq[i] = NULL;
@@ -718,14 +725,25 @@ wake_up:
complete(&isert_conn->conn_wait);
}
-static void
+static int
isert_disconnected_handler(struct rdma_cm_id *cma_id, bool disconnect)
{
- struct isert_conn *isert_conn = (struct isert_conn *)cma_id->context;
+ struct isert_conn *isert_conn;
+
+ if (!cma_id->qp) {
+ struct isert_np *isert_np = cma_id->context;
+
+ isert_np->np_cm_id = NULL;
+ return -1;
+ }
+
+ isert_conn = (struct isert_conn *)cma_id->context;
isert_conn->disconnect = disconnect;
INIT_WORK(&isert_conn->conn_logout_work, isert_disconnect_work);
schedule_work(&isert_conn->conn_logout_work);
+
+ return 0;
}
static int
@@ -740,6 +758,9 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST:
ret = isert_connect_request(cma_id, event);
+ if (ret)
+ pr_err("isert_cma_handler failed RDMA_CM_EVENT: 0x%08x %d\n",
+ event->event, ret);
break;
case RDMA_CM_EVENT_ESTABLISHED:
isert_connected_handler(cma_id);
@@ -749,7 +770,7 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
case RDMA_CM_EVENT_DEVICE_REMOVAL: /* FALLTHRU */
disconnect = true;
case RDMA_CM_EVENT_TIMEWAIT_EXIT: /* FALLTHRU */
- isert_disconnected_handler(cma_id, disconnect);
+ ret = isert_disconnected_handler(cma_id, disconnect);
break;
case RDMA_CM_EVENT_CONNECT_ERROR:
default:
@@ -757,12 +778,6 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
break;
}
- if (ret != 0) {
- pr_err("isert_cma_handler failed RDMA_CM_EVENT: 0x%08x %d\n",
- event->event, ret);
- dump_stack();
- }
-
return ret;
}
@@ -970,7 +985,8 @@ isert_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login,
}
if (!login->login_failed) {
if (login->login_complete) {
- if (isert_conn->conn_device->use_fastreg) {
+ if (!conn->sess->sess_ops->SessionType &&
+ isert_conn->conn_device->use_fastreg) {
ret = isert_conn_create_fastreg_pool(isert_conn);
if (ret) {
pr_err("Conn: %p failed to create"
@@ -1937,7 +1953,7 @@ isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
isert_cmd->tx_desc.num_sge = 2;
}
- isert_init_send_wr(isert_conn, isert_cmd, send_wr, true);
+ isert_init_send_wr(isert_conn, isert_cmd, send_wr, false);
pr_debug("Posting SCSI Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n");
@@ -2456,7 +2472,7 @@ isert_put_datain(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
&isert_cmd->tx_desc.iscsi_header);
isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc);
isert_init_send_wr(isert_conn, isert_cmd,
- &isert_cmd->tx_desc.send_wr, true);
+ &isert_cmd->tx_desc.send_wr, false);
atomic_add(wr->send_wr_num + 1, &isert_conn->post_send_buf_count);
@@ -2768,7 +2784,8 @@ isert_free_np(struct iscsi_np *np)
{
struct isert_np *isert_np = (struct isert_np *)np->np_context;
- rdma_destroy_id(isert_np->np_cm_id);
+ if (isert_np->np_cm_id)
+ rdma_destroy_id(isert_np->np_cm_id);
np->np_context = NULL;
kfree(isert_np);
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index d1078ce73095..0097b8dae5bc 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -2091,6 +2091,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
if (!qp_init)
goto out;
+retry:
ch->cq = ib_create_cq(sdev->device, srpt_completion, NULL, ch,
ch->rq_size + srp_sq_size, 0);
if (IS_ERR(ch->cq)) {
@@ -2114,6 +2115,13 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
ch->qp = ib_create_qp(sdev->pd, qp_init);
if (IS_ERR(ch->qp)) {
ret = PTR_ERR(ch->qp);
+ if (ret == -ENOMEM) {
+ srp_sq_size /= 2;
+ if (srp_sq_size >= MIN_SRPT_SQ_SIZE) {
+ ib_destroy_cq(ch->cq);
+ goto retry;
+ }
+ }
printk(KERN_ERR "failed to create_qp ret= %d\n", ret);
goto err_destroy_cq;
}
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 603fe0dd3682..517829f6a58b 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -1003,9 +1003,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
}
ep_irq_in = &intf->cur_altsetting->endpoint[1].desc;
- usb_fill_bulk_urb(xpad->bulk_out, udev,
- usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress),
- xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad);
+ if (usb_endpoint_is_bulk_out(ep_irq_in)) {
+ usb_fill_bulk_urb(xpad->bulk_out, udev,
+ usb_sndbulkpipe(udev,
+ ep_irq_in->bEndpointAddress),
+ xpad->bdata, XPAD_PKT_LEN,
+ xpad_bulk_out, xpad);
+ } else {
+ usb_fill_int_urb(xpad->bulk_out, udev,
+ usb_sndintpipe(udev,
+ ep_irq_in->bEndpointAddress),
+ xpad->bdata, XPAD_PKT_LEN,
+ xpad_bulk_out, xpad, 0);
+ }
/*
* Submit the int URB immediately rather than waiting for open
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index fb15c64ffb95..4979b00fbf04 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -1047,7 +1047,13 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
- if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
+ /*
+ * Check if we are dealing with a bare PS/2 packet, presumably from
+ * a device connected to the external PS/2 port. Because bare PS/2
+ * protocol does not have enough constant bits to self-synchronize
+ * properly we only do this if the device is fully synchronized.
+ */
+ if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
if (psmouse->pktcnt == 3) {
alps_report_bare_ps2_packet(psmouse, psmouse->packet,
true);
@@ -1071,12 +1077,27 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
}
/* Bytes 2 - pktsize should have 0 in the highest bit */
- if ((priv->proto_version < ALPS_PROTO_V5) &&
+ if (priv->proto_version < ALPS_PROTO_V5 &&
psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&
(psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
psmouse->pktcnt - 1,
psmouse->packet[psmouse->pktcnt - 1]);
+
+ if (priv->proto_version == ALPS_PROTO_V3 &&
+ psmouse->pktcnt == psmouse->pktsize) {
+ /*
+ * Some Dell boxes, such as Latitude E6440 or E7440
+ * with closed lid, quite often smash last byte of
+ * otherwise valid packet with 0xff. Given that the
+ * next packet is very likely to be valid let's
+ * report PSMOUSE_FULL_PACKET but not process data,
+ * rather than reporting PSMOUSE_BAD_DATA and
+ * filling the logs.
+ */
+ return PSMOUSE_FULL_PACKET;
+ }
+
return PSMOUSE_BAD_DATA;
}
@@ -2148,6 +2169,9 @@ int alps_init(struct psmouse *psmouse)
/* We are having trouble resyncing ALPS touchpads so disable it for now */
psmouse->resync_time = 0;
+ /* Allow 2 invalid packets without resetting device */
+ psmouse->resetafter = psmouse->pktsize * 2;
+
return 0;
init_fail:
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index a50a2a7a43f7..a3769cf84381 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -132,14 +132,18 @@ static const struct min_max_quirk min_max_pnpid_table[] = {
1232, 5710, 1156, 4696
},
{
- (const char * const []){"LEN0034", "LEN0036", "LEN2002",
- "LEN2004", NULL},
+ (const char * const []){"LEN0034", "LEN0036", "LEN0039",
+ "LEN2002", "LEN2004", NULL},
1024, 5112, 2024, 4832
},
{
(const char * const []){"LEN2001", NULL},
1024, 5022, 2508, 4832
},
+ {
+ (const char * const []){"LEN2006", NULL},
+ 1264, 5675, 1171, 4688
+ },
{ }
};
@@ -160,6 +164,7 @@ static const char * const topbuttonpad_pnp_ids[] = {
"LEN0036", /* T440 */
"LEN0037",
"LEN0038",
+ "LEN0039", /* T440s */
"LEN0041",
"LEN0042", /* Yoga */
"LEN0045",
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index d4f33992ad8c..de9ae8612cb9 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -1149,6 +1149,9 @@ static struct i2c_driver edt_ft5x06_ts_driver = {
module_i2c_driver(edt_ft5x06_ts_driver);
+MODULE_ALIAS("i2c:edt-ft5206");
+MODULE_ALIAS("i2c:edt-ft5306");
+MODULE_ALIAS("i2c:edt-ft5406");
MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>");
MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c
index 66b83ca94dcf..2a657e715ab5 100644
--- a/drivers/mailbox/omap-mailbox.c
+++ b/drivers/mailbox/omap-mailbox.c
@@ -67,6 +67,13 @@
#define MBOX_NR_REGS (MBOX_REG_SIZE / sizeof(u32))
#define OMAP4_MBOX_NR_REGS (OMAP4_MBOX_REG_SIZE / sizeof(u32))
+/*
+ * We need this for special case handling where controller indicates done
+ * state with IRQ but a specific channel needs to use manually ACK (used
+ * by wkup_m3 on AM33xx/AM43xx).
+ */
+#define MBOX_TXDONE_BY_ACK (1 << 2)
+
struct omap_mbox_fifo {
unsigned long msg;
unsigned long fifo_stat;
@@ -106,6 +113,7 @@ struct omap_mbox_fifo_info {
int rx_irq;
const char *name;
+ bool send_no_irq;
};
struct omap_mbox {
@@ -119,6 +127,7 @@ struct omap_mbox {
u32 ctx[OMAP4_MBOX_NR_REGS];
u32 intr_type;
struct mbox_chan *chan;
+ bool send_no_irq;
};
/* global variables for the mailbox devices */
@@ -418,6 +427,9 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
goto fail_request_irq;
}
+ if (mbox->send_no_irq)
+ mbox->chan->txdone_method = MBOX_TXDONE_BY_ACK;
+
_omap_mbox_enable_irq(mbox, IRQ_RX);
return 0;
@@ -586,13 +598,27 @@ static void omap_mbox_chan_shutdown(struct mbox_chan *chan)
mutex_unlock(&mdev->cfg_lock);
}
-static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
+static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, void *data)
{
- struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
int ret = -EBUSY;
- if (!mbox)
- return -EINVAL;
+ if (!mbox_fifo_full(mbox)) {
+ _omap_mbox_enable_irq(mbox, IRQ_RX);
+ mbox_fifo_write(mbox, (mbox_msg_t)data);
+ ret = 0;
+ _omap_mbox_disable_irq(mbox, IRQ_RX);
+
+ /* we must read and ack the interrupt directly from here */
+ mbox_fifo_read(mbox);
+ ack_mbox_irq(mbox, IRQ_RX);
+ }
+
+ return ret;
+}
+
+static int omap_mbox_chan_send(struct omap_mbox *mbox, void *data)
+{
+ int ret = -EBUSY;
if (!mbox_fifo_full(mbox)) {
mbox_fifo_write(mbox, (mbox_msg_t)data);
@@ -604,6 +630,22 @@ static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
return ret;
}
+static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
+{
+ struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
+ int ret;
+
+ if (!mbox)
+ return -EINVAL;
+
+ if (mbox->send_no_irq)
+ ret = omap_mbox_chan_send_noirq(mbox, data);
+ else
+ ret = omap_mbox_chan_send(mbox, data);
+
+ return ret;
+}
+
static struct mbox_chan_ops omap_mbox_chan_ops = {
.startup = omap_mbox_chan_startup,
.send_data = omap_mbox_chan_send_data,
@@ -732,6 +774,9 @@ static int omap_mbox_probe(struct platform_device *pdev)
finfo->rx_usr = tmp[2];
finfo->name = child->name;
+
+ if (of_find_property(child, "ti,mbox-send-noirq", NULL))
+ finfo->send_no_irq = true;
} else {
finfo->tx_id = info->tx_id;
finfo->rx_id = info->rx_id;
@@ -791,6 +836,7 @@ static int omap_mbox_probe(struct platform_device *pdev)
fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr);
fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr);
+ mbox->send_no_irq = finfo->send_no_irq;
mbox->intr_type = intr_type;
mbox->parent = mdev;
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index ca1621b49453..a1cebf745b22 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -1448,9 +1448,9 @@ static void drop_buffers(struct dm_bufio_client *c)
/*
* Test if the buffer is unused and too old, and commit it.
- * At if noio is set, we must not do any I/O because we hold
- * dm_bufio_clients_lock and we would risk deadlock if the I/O gets rerouted to
- * different bufio client.
+ * And if GFP_NOFS is used, we must not do any I/O because we hold
+ * dm_bufio_clients_lock and we would risk deadlock if the I/O gets
+ * rerouted to different bufio client.
*/
static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
unsigned long max_jiffies)
@@ -1458,7 +1458,7 @@ static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
if (jiffies - b->last_accessed < max_jiffies)
return 0;
- if (!(gfp & __GFP_IO)) {
+ if (!(gfp & __GFP_FS)) {
if (test_bit(B_READING, &b->state) ||
test_bit(B_WRITING, &b->state) ||
test_bit(B_DIRTY, &b->state))
@@ -1500,7 +1500,7 @@ dm_bufio_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
unsigned long freed;
c = container_of(shrink, struct dm_bufio_client, shrinker);
- if (sc->gfp_mask & __GFP_IO)
+ if (sc->gfp_mask & __GFP_FS)
dm_bufio_lock(c);
else if (!dm_bufio_trylock(c))
return SHRINK_STOP;
@@ -1517,7 +1517,7 @@ dm_bufio_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
unsigned long count;
c = container_of(shrink, struct dm_bufio_client, shrinker);
- if (sc->gfp_mask & __GFP_IO)
+ if (sc->gfp_mask & __GFP_FS)
dm_bufio_lock(c);
else if (!dm_bufio_trylock(c))
return 0;
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 4880b69e2e9e..59715389b3cf 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -785,8 +785,7 @@ struct dm_raid_superblock {
__le32 layout;
__le32 stripe_sectors;
- __u8 pad[452]; /* Round struct to 512 bytes. */
- /* Always set to 0 when writing. */
+ /* Remainder of a logical block is zero-filled when writing (see super_sync()). */
} __packed;
static int read_disk_sb(struct md_rdev *rdev, int size)
@@ -823,7 +822,7 @@ static void super_sync(struct mddev *mddev, struct md_rdev *rdev)
test_bit(Faulty, &(rs->dev[i].rdev.flags)))
failed_devices |= (1ULL << i);
- memset(sb, 0, sizeof(*sb));
+ memset(sb + 1, 0, rdev->sb_size - sizeof(*sb));
sb->magic = cpu_to_le32(DM_RAID_MAGIC);
sb->features = cpu_to_le32(0); /* No features yet */
@@ -858,7 +857,11 @@ static int super_load(struct md_rdev *rdev, struct md_rdev *refdev)
uint64_t events_sb, events_refsb;
rdev->sb_start = 0;
- rdev->sb_size = sizeof(*sb);
+ rdev->sb_size = bdev_logical_block_size(rdev->meta_bdev);
+ if (rdev->sb_size < sizeof(*sb) || rdev->sb_size > PAGE_SIZE) {
+ DMERR("superblock size of a logical block is no longer valid");
+ return -EINVAL;
+ }
ret = read_disk_sb(rdev, rdev->sb_size);
if (ret)
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 359af3a519b5..37f2648c112b 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -1704,6 +1704,14 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
return DM_MAPIO_SUBMITTED;
}
+ /*
+ * We must hold the virtual cell before doing the lookup, otherwise
+ * there's a race with discard.
+ */
+ build_virtual_key(tc->td, block, &key);
+ if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result))
+ return DM_MAPIO_SUBMITTED;
+
r = dm_thin_find_block(td, block, 0, &result);
/*
@@ -1727,13 +1735,10 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
* shared flag will be set in their case.
*/
thin_defer_bio(tc, bio);
+ cell_defer_no_holder_no_free(tc, &cell1);
return DM_MAPIO_SUBMITTED;
}
- build_virtual_key(tc->td, block, &key);
- if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result))
- return DM_MAPIO_SUBMITTED;
-
build_data_key(tc->td, result.block, &key);
if (dm_bio_detain(tc->pool->prison, &key, bio, &cell2, &cell_result)) {
cell_defer_no_holder_no_free(tc, &cell1);
@@ -1754,6 +1759,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
* of doing so.
*/
handle_unserviceable_bio(tc->pool, bio);
+ cell_defer_no_holder_no_free(tc, &cell1);
return DM_MAPIO_SUBMITTED;
}
/* fall through */
@@ -1764,6 +1770,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
* provide the hint to load the metadata into cache.
*/
thin_defer_bio(tc, bio);
+ cell_defer_no_holder_no_free(tc, &cell1);
return DM_MAPIO_SUBMITTED;
default:
@@ -1773,6 +1780,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
* pool is switched to fail-io mode.
*/
bio_io_error(bio);
+ cell_defer_no_holder_no_free(tc, &cell1);
return DM_MAPIO_SUBMITTED;
}
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 73aedcb639c0..40959ee73583 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -5333,6 +5333,7 @@ static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
printk("md: %s still in use.\n",mdname(mddev));
if (did_freeze) {
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
}
err = -EBUSY;
@@ -5347,6 +5348,8 @@ static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
mddev->ro = 1;
set_disk_ro(mddev->gendisk, 1);
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+ md_wakeup_thread(mddev->thread);
sysfs_notify_dirent_safe(mddev->sysfs_state);
err = 0;
}
@@ -5390,6 +5393,7 @@ static int do_md_stop(struct mddev * mddev, int mode,
mutex_unlock(&mddev->open_mutex);
if (did_freeze) {
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
}
return -EBUSY;
diff --git a/drivers/md/persistent-data/dm-btree-internal.h b/drivers/md/persistent-data/dm-btree-internal.h
index 37d367bb9aa8..bf2b80d5c470 100644
--- a/drivers/md/persistent-data/dm-btree-internal.h
+++ b/drivers/md/persistent-data/dm-btree-internal.h
@@ -42,6 +42,12 @@ struct btree_node {
} __packed;
+/*
+ * Locks a block using the btree node validator.
+ */
+int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
+ struct dm_block **result);
+
void inc_children(struct dm_transaction_manager *tm, struct btree_node *n,
struct dm_btree_value_type *vt);
diff --git a/drivers/md/persistent-data/dm-btree-spine.c b/drivers/md/persistent-data/dm-btree-spine.c
index cf9fd676ae44..1b5e13ec7f96 100644
--- a/drivers/md/persistent-data/dm-btree-spine.c
+++ b/drivers/md/persistent-data/dm-btree-spine.c
@@ -92,7 +92,7 @@ struct dm_block_validator btree_node_validator = {
/*----------------------------------------------------------------*/
-static int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
+int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
struct dm_block **result)
{
return dm_tm_read_lock(info->tm, b, &btree_node_validator, result);
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c
index 416060c25709..200ac12a1d40 100644
--- a/drivers/md/persistent-data/dm-btree.c
+++ b/drivers/md/persistent-data/dm-btree.c
@@ -847,22 +847,26 @@ EXPORT_SYMBOL_GPL(dm_btree_find_lowest_key);
* FIXME: We shouldn't use a recursive algorithm when we have limited stack
* space. Also this only works for single level trees.
*/
-static int walk_node(struct ro_spine *s, dm_block_t block,
+static int walk_node(struct dm_btree_info *info, dm_block_t block,
int (*fn)(void *context, uint64_t *keys, void *leaf),
void *context)
{
int r;
unsigned i, nr;
+ struct dm_block *node;
struct btree_node *n;
uint64_t keys;
- r = ro_step(s, block);
- n = ro_node(s);
+ r = bn_read_lock(info, block, &node);
+ if (r)
+ return r;
+
+ n = dm_block_data(node);
nr = le32_to_cpu(n->header.nr_entries);
for (i = 0; i < nr; i++) {
if (le32_to_cpu(n->header.flags) & INTERNAL_NODE) {
- r = walk_node(s, value64(n, i), fn, context);
+ r = walk_node(info, value64(n, i), fn, context);
if (r)
goto out;
} else {
@@ -874,7 +878,7 @@ static int walk_node(struct ro_spine *s, dm_block_t block,
}
out:
- ro_pop(s);
+ dm_tm_unlock(info->tm, node);
return r;
}
@@ -882,15 +886,7 @@ int dm_btree_walk(struct dm_btree_info *info, dm_block_t root,
int (*fn)(void *context, uint64_t *keys, void *leaf),
void *context)
{
- int r;
- struct ro_spine spine;
-
BUG_ON(info->levels > 1);
-
- init_ro_spine(&spine, info);
- r = walk_node(&spine, root, fn, context);
- exit_ro_spine(&spine);
-
- return r;
+ return walk_node(info, root, fn, context);
}
EXPORT_SYMBOL_GPL(dm_btree_walk);
diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile
index 98d8e63da1d3..3ac9c207889d 100644
--- a/drivers/media/platform/ti-vpe/Makefile
+++ b/drivers/media/platform/ti-vpe/Makefile
@@ -2,8 +2,13 @@ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o
obj-$(CONFIG_VIDEO_TI_VIP) += ti-vip.o
obj-$(CONFIG_VIDEO_TI_VPDMA_HELPER) += ti-vpdma.o
+ifdef CONFIG_VIDEO_TI_VPE_DEBUG
+CFLAGS_vpe.o := -DDEBUG
+CFLAGS_csc.o := -DDEBUG
+CFLAGS_sc.o := -DDEBUG
+endif
+
ti-vpe-y := vpe.o sc.o csc.o
ti-vip-y := vip.o
ti-vpdma-y := vpdma.o
-ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
diff --git a/drivers/media/platform/ti-vpe/vip.c b/drivers/media/platform/ti-vpe/vip.c
index 648427fe5fd5..3382ceff51e9 100644
--- a/drivers/media/platform/ti-vpe/vip.c
+++ b/drivers/media/platform/ti-vpe/vip.c
@@ -2,10 +2,10 @@
* TI VIP capture driver
*
* Copyright (C) 2013 - 2014 Texas Instruments, Inc.
- * Benoit Parrot, <bparrot@ti.com>
- *
- * Based on original work by Dale Farnsworth.
+ * David Griego, <dagriego@biglakesoftware.com>
* Dale Farnsworth, <dale@farnsworth.org>
+ * Nikhil Devshatwar, <nikhil.nd@ti.com>
+ * Benoit Parrot, <bparrot@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
@@ -27,6 +27,8 @@
#include <linux/pm_runtime.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of_device.h>
@@ -363,13 +365,23 @@ static void vip_set_clock_enable(struct vip_dev *dev, bool on)
{
u32 val = 0;
+ val = read_vreg(dev, VIP_CLK_ENABLE);
if (on) {
+ val |= VIP_VPDMA_CLK_ENABLE;
if (dev->slice_id == VIP_SLICE1)
- val = VIP_VIP1_DATA_PATH_CLK_ENABLE |
- VIP_VPDMA_CLK_ENABLE;
+ val |= VIP_VIP1_DATA_PATH_CLK_ENABLE;
else
- val = VIP_VIP2_DATA_PATH_CLK_ENABLE |
- VIP_VPDMA_CLK_ENABLE;
+ val |= VIP_VIP2_DATA_PATH_CLK_ENABLE;
+ } else {
+ if (dev->slice_id == VIP_SLICE1)
+ val &= ~VIP_VIP1_DATA_PATH_CLK_ENABLE;
+ else
+ val &= ~VIP_VIP2_DATA_PATH_CLK_ENABLE;
+
+ /* Both VIP are disabled then shutdown VPDMA also */
+ if (!(val & (VIP_VIP1_DATA_PATH_CLK_ENABLE|
+ VIP_VIP2_DATA_PATH_CLK_ENABLE)))
+ val = 0;
}
write_vreg(dev, VIP_CLK_ENABLE, val);
@@ -509,7 +521,29 @@ static void vip_set_discrete_basic_mode(struct vip_port *port)
static void vip_set_pclk_polarity(struct vip_port *port, int polarity)
{
- u32 val;
+ u32 val, ret, offset;
+
+ if (polarity == 0 && port->dev->syscon) {
+
+ /*
+ * When the VIP parser is configured to so that the pixel clock
+ * is to be sampled at falling edge, the pixel clock needs to be
+ * inverted before it is given to the VIP module. This is done
+ * by setting a bit in the CTRL_CORE_SMA_SW1 register.
+ */
+
+ if (port->dev->instance_id == VIP_INSTANCE1)
+ offset = 0 + 2 * port->port_id + port->dev->slice_id;
+ else if (port->dev->instance_id == VIP_INSTANCE2)
+ offset = 4 + 2 * port->port_id + port->dev->slice_id;
+ else if (port->dev->instance_id == VIP_INSTANCE3)
+ offset = 10 - port->dev->slice_id;
+ else
+ BUG();
+
+ ret = regmap_update_bits(port->dev->syscon,
+ 0, 1 << offset, 1 << offset);
+ }
if (port->port_id == 0 && port->dev->slice_id == VIP_SLICE1) {
val = read_vreg(port->dev, VIP1_PARSER_REG_OFFSET +
@@ -908,21 +942,6 @@ static void vip_set_slice_path(struct vip_dev *dev,
}
}
-/* This are unused function for now.
- * Just declare them to quiet down the compiler.
- */
-struct dfn_holder {
- void (*reset_port)(struct vip_port *port);
- void (*xtra_set_repack_sel)(struct vip_port *port, int repack_mode);
- void (*set_actvid_hsync_n)(struct vip_port *port, int enable);
-};
-
-static const struct dfn_holder s_dfn = {
- .reset_port = vip_reset_port,
- .xtra_set_repack_sel = vip_xtra_set_repack_sel,
- .set_actvid_hsync_n = vip_set_actvid_hsync_n,
-};
-
/*
* Return the vip_stream structure for a given struct file
*/
@@ -944,6 +963,7 @@ static int add_out_dtd(struct vip_stream *stream, int srce_type)
struct v4l2_rect *c_rect = &port->c_rect;
struct vip_fmt *fmt = port->fmt;
int channel, plane = 0;
+ int max_width, max_height;
dma_addr_t dma_addr;
u32 flags;
@@ -981,9 +1001,33 @@ static int add_out_dtd(struct vip_stream *stream, int srce_type)
*/
dma_addr = (dma_addr_t)NULL;
- vpdma_vip_set_max_size(dev->shared->vpdma, 1);
+ /*
+ * Use VPDMA_MAX_SIZE1 or VPDMA_MAX_SIZE2 register for slice0/1
+ */
+
+ if (dev->slice_id == VIP_SLICE1) {
+ vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE1,
+ stream->width, stream->height);
+
+ max_width = MAX_OUT_WIDTH_REG1;
+ max_height = MAX_OUT_HEIGHT_REG1;
+ } else {
+ vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE2,
+ stream->width, stream->height);
+
+ max_width = MAX_OUT_WIDTH_REG2;
+ max_height = MAX_OUT_HEIGHT_REG2;
+ }
+
+ /* Mark this channel to be cleared while cleaning up resources
+ * This will make sure that an abort descriptor for this channel
+ * would be submitted to VPDMA causing any ongoing transaction to be
+ * aborted and cleanup the VPDMA FSM for this channel */
+ dev->vpdma_channels[channel] = 1;
+
vpdma_rawchan_add_out_dtd(&dev->desc_list, c_rect->width, c_rect,
- fmt->vpdma_fmt[plane], dma_addr, channel, flags);
+ fmt->vpdma_fmt[plane], dma_addr, max_width, max_height,
+ channel, flags);
return 0;
}
@@ -1035,6 +1079,16 @@ static void disable_irqs(struct vip_dev *dev, int irq_num)
irq_num, list_num, false);
}
+static void clear_irqs(struct vip_dev *dev, int irq_num)
+{
+ u32 reg_addr = VIP_INT0_STATUS0_CLR +
+ VIP_INTC_INTX_OFFSET * irq_num;
+
+ write_sreg(dev->shared, reg_addr, 0xffffffff);
+
+ vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id);
+}
+
static void populate_desc_list(struct vip_stream *stream)
{
struct vip_port *port = stream->port;
@@ -1186,7 +1240,8 @@ static irqreturn_t vip_irq(int irq_vip, void *data)
vip_dbg(8, dev, "IRQ %d VIP_INT%d_STATUS0 0x%x\n",
irq_vip, irq_num, irqst);
if (irqst) {
- vpdma_clear_list_stat(dev->shared->vpdma, list_num);
+ vpdma_clear_list_stat(dev->shared->vpdma, irq_num, list_num);
+
reg_addr = VIP_INT0_STATUS0_CLR +
VIP_INTC_INTX_OFFSET * irq_num;
write_sreg(dev->shared, reg_addr, irqst);
@@ -1254,6 +1309,62 @@ static int vip_enuminput(struct file *file, void *priv,
return 0;
}
+static int vip_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vip_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vip_querystd(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_dev *dev = stream->port->dev;
+
+ v4l2_subdev_call(dev->sensor, video, querystd, std);
+ return 0;
+}
+
+static int vip_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_dev *dev = stream->port->dev;
+
+ *std = 0;
+ v4l2_subdev_call(dev->sensor, video, g_std_output, std);
+ return 0;
+}
+
+static int vip_s_std(struct file *file, void *fh, v4l2_std_id std)
+{
+ struct vip_stream *stream = file2stream(file);
+ struct vip_dev *dev = stream->port->dev;
+
+ v4l2_subdev_call(dev->sensor, video, s_std_output, std);
+ return 0;
+}
+
+static int vip_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a)
+{
+ return -EINVAL;
+}
+
+static int vip_g_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ return 0;
+}
+
+static int vip_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ return 0;
+}
+
static int vip_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
@@ -1343,8 +1454,8 @@ static int vip_enum_frameintervals(struct file *file, void *priv,
}
f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- f->discrete.numerator = 30;
- f->discrete.denominator = 1;
+ f->discrete.numerator = 1;
+ f->discrete.denominator = 30;
return 0;
}
@@ -1355,8 +1466,8 @@ static int vip_s_parm(struct file *file, void *priv,
if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- parm->parm.capture.timeperframe.numerator = 30;
- parm->parm.capture.timeperframe.denominator = 1;
+ parm->parm.capture.timeperframe.numerator = 1;
+ parm->parm.capture.timeperframe.denominator = 30;
return 0;
}
@@ -1644,6 +1755,17 @@ static long vip_ioctl_default(struct file *file, void *fh, bool valid_prio,
static const struct v4l2_ioctl_ops vip_ioctl_ops = {
.vidioc_querycap = vip_querycap,
+ .vidioc_enum_input = vip_enuminput,
+ .vidioc_g_input = vip_g_input,
+ .vidioc_s_input = vip_s_input,
+
+ .vidioc_querystd = vip_querystd,
+ .vidioc_g_std = vip_g_std,
+ .vidioc_s_std = vip_s_std,
+
+ .vidioc_queryctrl = vip_queryctrl,
+ .vidioc_g_ctrl = vip_g_ctrl,
+ .vidioc_s_ctrl = vip_s_ctrl,
.vidioc_enum_fmt_vid_cap = vip_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vip_g_fmt_vid_cap,
@@ -1782,16 +1904,9 @@ static int vip_stop_streaming(struct vb2_queue *vq)
struct vip_port *port = stream->port;
struct vip_dev *dev = port->dev;
struct vip_buffer *buf;
- unsigned long flags;
-
- if (!vb2_is_streaming(vq))
- return 0;
-
- spin_lock_irqsave(&dev->slock, flags);
- vpdma_unmap_desc_buf(dev->shared->vpdma, &dev->desc_list.buf);
- vpdma_reset_desc_list(&dev->desc_list);
disable_irqs(dev, dev->slice_id);
+ clear_irqs(dev, dev->slice_id);
/* release all active buffers */
while (!list_empty(&dev->vip_bufs)) {
@@ -1807,7 +1922,13 @@ static int vip_stop_streaming(struct vb2_queue *vq)
list_del(&buf->list);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
- spin_unlock_irqrestore(&dev->slock, flags);
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ vpdma_unmap_desc_buf(dev->shared->vpdma, &dev->desc_list.buf);
+ vpdma_reset_desc_list(&dev->desc_list);
+
return 0;
}
@@ -1884,6 +2005,7 @@ static int vip_setup_parser(struct vip_port *port)
int sync_type;
unsigned int flags;
+ vip_reset_port(port);
vip_set_port_enable(port, 1);
if (endpoint->bus_type == V4L2_MBUS_BT656) {
@@ -1941,6 +2063,8 @@ static int vip_setup_parser(struct vip_port *port)
vip_set_pclk_polarity(port,
flags & V4L2_MBUS_PCLK_SAMPLE_RISING ? 1 : 0);
+ vip_xtra_set_repack_sel(port, 0);
+ vip_set_actvid_hsync_n(port, 0);
vip_set_actvid_polarity(port, 1);
vip_set_discrete_basic_mode(port);
@@ -1977,6 +2101,24 @@ done:
static void vip_release_port(struct vip_port *port)
{
+ struct vip_dev *dev = port->dev;
+ int ch, size = 0;
+
+ /* Create a list of channels to be cleared */
+ for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++) {
+ if (dev->vpdma_channels[ch] == 1) {
+ dev->vpdma_channels[size++] = ch;
+ vip_dbg(2, dev, "Clear channel no: %d\n", ch);
+ }
+ }
+
+ /* Clear all the used channels for the list */
+ vpdma_list_cleanup(dev->shared->vpdma, dev->slice_id,
+ dev->vpdma_channels, size);
+
+ for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++)
+ dev->vpdma_channels[ch] = 0;
+
if (--port->num_streams == 0)
vip_release_dev(port->dev);
}
@@ -2408,6 +2550,7 @@ static int vip_of_probe(struct platform_device *pdev, struct vip_dev *dev)
{
struct device_node *ep_node = NULL, *port, *remote_ep,
*sensor_node, *parent;
+ struct device_node *syscon_np;
struct v4l2_of_endpoint *endpoint;
struct v4l2_async_subdev *asd;
u32 regval = 0;
@@ -2415,6 +2558,10 @@ static int vip_of_probe(struct platform_device *pdev, struct vip_dev *dev)
parent = pdev->dev.of_node;
+ syscon_np = of_parse_phandle(pdev->dev.of_node, "syscon-smasw", 0);
+ dev->syscon = syscon_node_to_regmap(syscon_np);
+ of_node_put(syscon_np);
+
dev->config = kzalloc(sizeof(struct vip_config), GFP_KERNEL);
if (!dev->config)
return -ENOMEM;
@@ -2628,10 +2775,10 @@ static int vip_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&dev->vip_bufs);
- dev->vip_name = (const char *)of_dev_id->data;
+ dev->instance_id = (int)of_dev_id->data;
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
- "%s-%d", dev->vip_name, slice);
+ "%s%d-%d", VIP_MODULE_NAME, dev->instance_id, slice);
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret)
goto err_runtime_get;
@@ -2702,15 +2849,15 @@ static int vip_remove(struct platform_device *pdev)
#if defined(CONFIG_OF)
static const struct of_device_id vip_of_match[] = {
{
- .compatible = "ti,vip1", .data = "vip1",
+ .compatible = "ti,vip1", .data = (void *) VIP_INSTANCE1,
},
{
- .compatible = "ti,vip2", .data = "vip2",
+ .compatible = "ti,vip2", .data = (void *) VIP_INSTANCE2,
},
{
- .compatible = "ti,vip3", .data = "vip3",
+ .compatible = "ti,vip3", .data = (void *) VIP_INSTANCE3,
},
{},
};
diff --git a/drivers/media/platform/ti-vpe/vip.h b/drivers/media/platform/ti-vpe/vip.h
index 4cf39c3ff1ca..ee610cb7de54 100644
--- a/drivers/media/platform/ti-vpe/vip.h
+++ b/drivers/media/platform/ti-vpe/vip.h
@@ -2,10 +2,10 @@
* TI VIP capture driver
*
* Copyright (C) 2013 - 2014 Texas Instruments, Inc.
- * Benoit Parrot, <bparrot@ti.com>
- *
- * Based on original work by Dale Farnsworth.
+ * David Griego, <dagriego@biglakesoftware.com>
* Dale Farnsworth, <dale@farnsworth.org>
+ * Nikhil Devshatwar, <nikhil.nd@ti.com>
+ * Benoit Parrot, <bparrot@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
@@ -32,6 +32,10 @@
#include "vpdma.h"
#include "vpdma_priv.h"
+#define VIP_INSTANCE1 1
+#define VIP_INSTANCE2 2
+#define VIP_INSTANCE3 3
+
#define VIP_SLICE1 0
#define VIP_SLICE2 1
#define VIP_NUM_SLICES 2
@@ -114,6 +118,8 @@ struct vip_dev {
struct platform_device *pdev;
struct vip_shared *shared;
struct resource *res;
+ struct regmap *syscon;
+ int instance_id;
int slice_id;
int num_ports; /* count of open ports */
struct mutex mutex;
@@ -127,6 +133,8 @@ struct vip_dev {
struct vpdma_desc_list desc_list; /* DMA descriptor list */
struct vpdma_dtd *write_desc;
void *desc_next; /* next unused desc_list addr */
+ /* Maintain a list of used channels - Needed for VPDMA cleanup */
+ int vpdma_channels[VPDMA_MAX_CHANNELS];
struct list_head vip_bufs; /* vip_bufs to be DMAed */
struct vb2_alloc_ctx *alloc_ctx;
struct vip_port *ports[VIP_NUM_PORTS];
diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c
index 6c6461843c11..94947a65b752 100644
--- a/drivers/media/platform/ti-vpe/vpdma.c
+++ b/drivers/media/platform/ti-vpe/vpdma.c
@@ -385,6 +385,56 @@ void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf)
EXPORT_SYMBOL(vpdma_unmap_desc_buf);
/*
+ * Cleanup all pending descriptors of a list
+ * First, stop the current list being processed.
+ * If the VPDMA was busy, this step makes vpdma to accept post lists.
+ * To cleanup the internal FSM, post abort list descriptor for all the
+ * channels from @channels array of size @size.
+ */
+int vpdma_list_cleanup(struct vpdma_data *vpdma, int list_num,
+ int *channels, int size)
+{
+ struct vpdma_desc_list abort_list;
+ int i, ret, timeout = 500;
+
+ write_reg(vpdma, VPDMA_LIST_ATTR,
+ (list_num << VPDMA_LIST_NUM_SHFT) |
+ (1 << VPDMA_LIST_STOP_SHFT));
+
+ if (size <= 0 || !channels)
+ return 0;
+
+ ret = vpdma_create_desc_list(&abort_list,
+ size * sizeof(struct vpdma_dtd), VPDMA_LIST_TYPE_NORMAL);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < size; i++)
+ vpdma_add_abort_channel_ctd(&abort_list, channels[i]);
+
+ ret = vpdma_map_desc_buf(vpdma, &abort_list.buf);
+ if (ret)
+ return ret;
+ ret = vpdma_submit_descs(vpdma, &abort_list, list_num);
+ if (ret)
+ return ret;
+
+ while (vpdma_list_busy(vpdma, list_num) && timeout--)
+ ;
+
+ if (timeout == 0) {
+ dev_err(&vpdma->pdev->dev, "Timed out cleaning up VPDMA list\n");
+ return -EBUSY;
+ }
+
+ vpdma_unmap_desc_buf(vpdma, &abort_list.buf);
+ vpdma_free_desc_buf(&abort_list.buf);
+
+ return 0;
+}
+EXPORT_SYMBOL(vpdma_list_cleanup);
+
+/*
* create a descriptor list, the user of this list will append configuration,
* control and data descriptors to this list, this list will be submitted to
* VPDMA. VPDMA's list parser will go through each descriptor and perform the
@@ -441,6 +491,7 @@ int vpdma_submit_descs(struct vpdma_data *vpdma,
struct vpdma_desc_list *list, int list_num)
{
int list_size;
+ unsigned long flags;
if (vpdma_list_busy(vpdma, list_num))
return -EBUSY;
@@ -448,12 +499,14 @@ int vpdma_submit_descs(struct vpdma_data *vpdma,
/* 16-byte granularity */
list_size = (list->next - list->buf.addr) >> 4;
+ spin_lock_irqsave(&vpdma->lock, flags);
write_reg(vpdma, VPDMA_LIST_ADDR, (u32) list->buf.dma_addr);
write_reg(vpdma, VPDMA_LIST_ATTR,
(list_num << VPDMA_LIST_NUM_SHFT) |
(list->type << VPDMA_LIST_TYPE_SHFT) |
list_size);
+ spin_unlock_irqrestore(&vpdma->lock, flags);
return 0;
}
@@ -492,18 +545,21 @@ void vpdma_update_dma_addr(struct vpdma_data *vpdma,
}
EXPORT_SYMBOL(vpdma_update_dma_addr);
-void vpdma_vip_set_max_size(struct vpdma_data *vpdma, int vip_num)
+void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr,
+ u32 width, u32 height)
{
- if (vip_num == 1) {
- write_field_reg(vpdma, VPDMA_MAX_SIZE1, 1919,
- VPDMA_MAX_SIZE_WIDTH_MASK,
- VPDMA_MAX_SIZE_WIDTH_SHFT);
- write_field_reg(vpdma, VPDMA_MAX_SIZE1, 1079,
- VPDMA_MAX_SIZE_HEIGHT_MASK,
- VPDMA_MAX_SIZE_HEIGHT_SHFT);
- }
+ if (reg_addr != VPDMA_MAX_SIZE1 && reg_addr != VPDMA_MAX_SIZE2
+ && reg_addr != VPDMA_MAX_SIZE3)
+ reg_addr = VPDMA_MAX_SIZE1;
+
+ write_field_reg(vpdma, reg_addr, width - 1 ,
+ VPDMA_MAX_SIZE_WIDTH_MASK, VPDMA_MAX_SIZE_WIDTH_SHFT);
+
+ write_field_reg(vpdma, reg_addr, height - 1 ,
+ VPDMA_MAX_SIZE_HEIGHT_MASK, VPDMA_MAX_SIZE_HEIGHT_SHFT);
+
}
-EXPORT_SYMBOL(vpdma_vip_set_max_size);
+EXPORT_SYMBOL(vpdma_set_max_size);
static void dump_cfd(struct vpdma_cfd *cfd)
{
@@ -625,6 +681,31 @@ void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list,
}
EXPORT_SYMBOL(vpdma_add_sync_on_channel_ctd);
+/*
+ * append an 'abort_channel' type control descriptor to the given descriptor
+ * list, this descriptor aborts any DMA transaction happening using the
+ * specified channel
+ */
+void vpdma_add_abort_channel_ctd(struct vpdma_desc_list *list,
+ int chan_num)
+{
+ struct vpdma_ctd *ctd;
+
+ ctd = list->next;
+ WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size));
+
+ ctd->w0 = 0;
+ ctd->w1 = 0;
+ ctd->w2 = 0;
+ ctd->type_source_ctl = ctd_type_source_ctl(chan_num,
+ CTD_TYPE_ABORT_CHANNEL);
+
+ list->next = ctd + 1;
+
+ dump_ctd(ctd);
+}
+EXPORT_SYMBOL(vpdma_add_abort_channel_ctd);
+
static void dump_dtd(struct vpdma_dtd *dtd)
{
int dir, chan;
@@ -681,23 +762,25 @@ static void dump_dtd(struct vpdma_dtd *dtd)
* @c_rect: compose params of output image
* @fmt: vpdma data format of the buffer
* dma_addr: dma address as seen by VPDMA
+ * max_width: enum for maximum width of data transfer
+ * max_height: enum for maximum height of data transfer
* chan: VPDMA channel
* flags: VPDMA flags to configure some descriptor fileds
*/
void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width,
const struct v4l2_rect *c_rect,
const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
- enum vpdma_channel chan, u32 flags)
+ int max_w, int max_h, enum vpdma_channel chan, u32 flags)
{
vpdma_rawchan_add_out_dtd(list, width, c_rect, fmt, dma_addr,
- chan_info[chan].num, flags);
+ max_w, max_h, chan_info[chan].num, flags);
}
EXPORT_SYMBOL(vpdma_add_out_dtd);
void vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width,
const struct v4l2_rect *c_rect,
const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
- int raw_vpdma_chan, u32 flags)
+ int max_w, int max_h, int raw_vpdma_chan, u32 flags)
{
int priority = 0;
int field = 0;
@@ -736,8 +819,7 @@ void vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width,
dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED),
DTD_DIR_OUT, channel, priority, next_chan);
dtd->desc_write_addr = dtd_desc_write_addr(0, 0, 0, 0);
- dtd->max_width_height = dtd_max_width_height(MAX_OUT_WIDTH_1920,
- MAX_OUT_HEIGHT_1080);
+ dtd->max_width_height = dtd_max_width_height(max_w, max_h);
dtd->client_attr0 = 0;
dtd->client_attr1 = 0;
@@ -870,11 +952,11 @@ unsigned int vpdma_get_list_mask(struct vpdma_data *vpdma, int irq_num)
EXPORT_SYMBOL(vpdma_get_list_mask);
/* clear previosuly occured list intterupts in the LIST_STAT register */
-void vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num)
+void vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num,
+ int list_num)
{
u32 reg_addr = VPDMA_INT_LIST0_STAT + VPDMA_INTX_OFFSET * irq_num;
- write_reg(vpdma, reg_addr,
- read_reg(vpdma, reg_addr));
+ write_reg(vpdma, reg_addr, 3 << (list_num * 2));
}
EXPORT_SYMBOL(vpdma_clear_list_stat);
@@ -1009,6 +1091,7 @@ struct vpdma_data *vpdma_create(struct platform_device *pdev,
vpdma->pdev = pdev;
vpdma->cb = cb;
+ spin_lock_init(&vpdma->lock);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma");
if (res == NULL) {
diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h
index d591ca5267e5..c448c8a71b39 100644
--- a/drivers/media/platform/ti-vpe/vpdma.h
+++ b/drivers/media/platform/ti-vpe/vpdma.h
@@ -35,6 +35,7 @@ struct vpdma_data {
struct platform_device *pdev;
+ spinlock_t lock;
/* callback to VPE driver when the firmware is loaded */
void (*cb)(struct platform_device *pdev);
};
@@ -117,6 +118,30 @@ enum vpdma_frame_start_event {
VPDMA_FSEVENT_CHANNEL_ACTIVE,
};
+/* max width configurations */
+enum vpdma_max_width {
+ MAX_OUT_WIDTH_UNLIMITED = 0,
+ MAX_OUT_WIDTH_REG1,
+ MAX_OUT_WIDTH_REG2,
+ MAX_OUT_WIDTH_REG3,
+ MAX_OUT_WIDTH_352,
+ MAX_OUT_WIDTH_768,
+ MAX_OUT_WIDTH_1280,
+ MAX_OUT_WIDTH_1920,
+};
+
+/* max height configurations */
+enum vpdma_max_height {
+ MAX_OUT_HEIGHT_UNLIMITED = 0,
+ MAX_OUT_HEIGHT_REG1,
+ MAX_OUT_HEIGHT_REG2,
+ MAX_OUT_HEIGHT_REG3,
+ MAX_OUT_HEIGHT_288,
+ MAX_OUT_HEIGHT_576,
+ MAX_OUT_HEIGHT_720,
+ MAX_OUT_HEIGHT_1080,
+};
+
/*
* VPDMA channel numbers
*/
@@ -139,6 +164,8 @@ enum vpdma_channel {
#define VIP_CHAN_YUV_PORTB_OFFSET 2
#define VIP_CHAN_RGB_PORTB_OFFSET 1
+#define VPDMA_MAX_CHANNELS 256
+
/* flags for VPDMA data descriptors */
#define VPDMA_DATA_ODD_LINE_SKIP (1 << 0)
#define VPDMA_DATA_EVEN_LINE_SKIP (1 << 1)
@@ -195,24 +222,30 @@ void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client,
struct vpdma_buf *adb);
void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list,
enum vpdma_channel chan);
+void vpdma_add_abort_channel_ctd(struct vpdma_desc_list *list,
+ int chan_num);
void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width,
const struct v4l2_rect *c_rect,
const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
- enum vpdma_channel chan, u32 flags);
+ int max_w, int max_h, enum vpdma_channel chan, u32 flags);
void vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width,
const struct v4l2_rect *c_rect,
const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
- int raw_vpdma_chan, u32 flags);
+ int max_w, int max_h, int raw_vpdma_chan, u32 flags);
+
void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width,
const struct v4l2_rect *c_rect,
const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
enum vpdma_channel chan, int field, u32 flags, int frame_width,
int frame_height, int start_h, int start_v);
+int vpdma_list_cleanup(struct vpdma_data *vpdma, int list_num,
+ int *channels, int size);
/* vpdma list interrupt management */
void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int irq_num,
int list_num, bool enable);
-void vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num);
+void vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num,
+ int list_num);
unsigned int vpdma_get_list_stat(struct vpdma_data *vpdma, int irq_num);
unsigned int vpdma_get_list_mask(struct vpdma_data *vpdma, int irq_num);
@@ -221,7 +254,9 @@ void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode,
enum vpdma_channel chan);
void vpdma_set_frame_start_event(struct vpdma_data *vpdma,
enum vpdma_frame_start_event fs_event, enum vpdma_channel chan);
-void vpdma_vip_set_max_size(struct vpdma_data *vpdma, int vip_num);
+void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr,
+ u32 width, u32 height);
+
void vpdma_set_bg_color(struct vpdma_data *vpdma,
struct vpdma_data_format *fmt, u32 color);
void vpdma_dump_regs(struct vpdma_data *vpdma);
diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h
index 762b3fec4919..269051a68618 100644
--- a/drivers/media/platform/ti-vpe/vpdma_priv.h
+++ b/drivers/media/platform/ti-vpe/vpdma_priv.h
@@ -231,42 +231,6 @@ struct vpdma_dtd {
#define DTD_MAX_HEIGHT_MASK 0x07
#define DTD_MAX_HEIGHT_SHFT 0
-/* max width configurations */
- /* unlimited width */
-#define MAX_OUT_WIDTH_UNLIMITED 0
-/* as specified in max_size1 reg */
-#define MAX_OUT_WIDTH_REG1 1
-/* as specified in max_size2 reg */
-#define MAX_OUT_WIDTH_REG2 2
-/* as specified in max_size3 reg */
-#define MAX_OUT_WIDTH_REG3 3
-/* maximum of 352 pixels as width */
-#define MAX_OUT_WIDTH_352 4
-/* maximum of 768 pixels as width */
-#define MAX_OUT_WIDTH_768 5
-/* maximum of 1280 pixels width */
-#define MAX_OUT_WIDTH_1280 6
-/* maximum of 1920 pixels as width */
-#define MAX_OUT_WIDTH_1920 7
-
-/* max height configurations */
- /* unlimited height */
-#define MAX_OUT_HEIGHT_UNLIMITED 0
-/* as specified in max_size1 reg */
-#define MAX_OUT_HEIGHT_REG1 1
-/* as specified in max_size2 reg */
-#define MAX_OUT_HEIGHT_REG2 2
-/* as specified in max_size3 reg */
-#define MAX_OUT_HEIGHT_REG3 3
-/* maximum of 288 lines as height */
-#define MAX_OUT_HEIGHT_288 4
-/* maximum of 576 lines as height */
-#define MAX_OUT_HEIGHT_576 5
-/* maximum of 720 lines as height */
-#define MAX_OUT_HEIGHT_720 6
-/* maximum of 1080 lines as height */
-#define MAX_OUT_HEIGHT_1080 7
-
static inline u32 dtd_type_ctl_stride(int type, bool notify, int field,
bool one_d, bool even_line_skip, bool odd_line_skip,
int line_stride)
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index 0ae19eef85ac..cc87a798f8b5 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -51,8 +51,8 @@
/* minimum and maximum frame sizes */
#define MIN_W 32
#define MIN_H 32
-#define MAX_W 1920
-#define MAX_H 1080
+#define MAX_W 2048
+#define MAX_H 1184
/* required alignments */
#define S_ALIGN 0 /* multiple of 1 */
@@ -139,7 +139,7 @@ struct vpe_dei_regs {
*/
static const struct vpe_dei_regs dei_regs = {
0x020C0804u,
- 0x0118100Fu,
+ 0x0118100Cu,
0x08040200u,
0x1010100Cu,
0x10101010u,
@@ -318,9 +318,13 @@ struct vpe_q_data {
};
/* vpe_q_data flag bits */
-#define Q_DATA_FRAME_1D (1 << 0)
-#define Q_DATA_MODE_TILED (1 << 1)
-#define Q_DATA_INTERLACED (1 << 2)
+#define Q_DATA_FRAME_1D (1 << 0)
+#define Q_DATA_MODE_TILED (1 << 1)
+#define Q_DATA_INTERLACED_ALTERNATE (1 << 2)
+#define Q_DATA_INTERLACED_SEQ_TB (1 << 3)
+
+#define Q_IS_INTERLACED (Q_DATA_INTERLACED_ALTERNATE | \
+ Q_DATA_INTERLACED_SEQ_TB)
enum {
Q_DATA_SRC = 0,
@@ -416,7 +420,7 @@ static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx,
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return &ctx->q_data[Q_DATA_DST];
default:
- BUG();
+ return NULL;
}
return NULL;
}
@@ -495,6 +499,14 @@ struct vpe_mmr_adb {
#define VPE_SET_MMR_ADB_HDR(ctx, hdr, regs, offset_a) \
VPDMA_SET_MMR_ADB_HDR(ctx->mmr_adb, vpe_mmr_adb, hdr, regs, offset_a)
+
+static inline dma_addr_t vb2_dma_addr_plus_data_offset(struct vb2_buffer *vb,
+ unsigned int plane_no)
+{
+ return vb2_dma_contig_plane_dma_addr(vb, plane_no) +
+ vb->v4l2_planes[plane_no].data_offset;
+}
+
/*
* Set the headers for all of the address/data block structures.
*/
@@ -638,7 +650,7 @@ static void set_us_coefficients(struct vpe_ctx *ctx)
cp = &us_coeffs[0].anchor_fid0_c0;
- if (s_q_data->flags & Q_DATA_INTERLACED) /* interlaced */
+ if (s_q_data->flags & Q_IS_INTERLACED) /* interlaced */
cp += sizeof(us_coeffs[0]) / sizeof(*cp);
end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp);
@@ -655,14 +667,13 @@ static void set_us_coefficients(struct vpe_ctx *ctx)
/*
* Set the upsampler config mode and the VPDMA line mode in the shadow MMRs.
*/
-static void set_cfg_and_line_modes(struct vpe_ctx *ctx)
+static void set_cfg_modes(struct vpe_ctx *ctx)
{
struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt;
struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
u32 *us1_reg0 = &mmr_adb->us1_regs[0];
u32 *us2_reg0 = &mmr_adb->us2_regs[0];
u32 *us3_reg0 = &mmr_adb->us3_regs[0];
- int line_mode = 1;
int cfg_mode = 1;
/*
@@ -670,15 +681,24 @@ static void set_cfg_and_line_modes(struct vpe_ctx *ctx)
* Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing.
*/
- if (fmt->fourcc == V4L2_PIX_FMT_NV12) {
+ if (fmt->fourcc == V4L2_PIX_FMT_NV12)
cfg_mode = 0;
- line_mode = 0; /* double lines to line buffer */
- }
write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
write_field(us2_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
write_field(us3_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
+ ctx->load_mmrs = true;
+}
+
+static void set_line_modes(struct vpe_ctx *ctx)
+{
+ struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt;
+ int line_mode = 1;
+
+ if (fmt->fourcc == V4L2_PIX_FMT_NV12)
+ line_mode = 0; /* double lines to line buffer */
+
/* regs for now */
vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA1_IN);
vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA2_IN);
@@ -703,8 +723,6 @@ static void set_cfg_and_line_modes(struct vpe_ctx *ctx)
/* frame start for MV in client */
vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
VPE_CHAN_MV_IN);
-
- ctx->load_mmrs = true;
}
/*
@@ -765,8 +783,7 @@ static void set_dei_regs(struct vpe_ctx *ctx)
* for both progressive and interlace content in interlace bypass mode.
* It has been recommended not to use progressive bypass mode.
*/
- if ((!ctx->deinterlacing && (s_q_data->flags & Q_DATA_INTERLACED)) ||
- !(s_q_data->flags & Q_DATA_INTERLACED)) {
+ if (!(s_q_data->flags & Q_IS_INTERLACED) || !ctx->deinterlacing) {
deinterlace = false;
val = VPE_DEI_INTERLACE_BYPASS;
}
@@ -798,6 +815,23 @@ static void set_dei_shadow_registers(struct vpe_ctx *ctx)
ctx->load_mmrs = true;
}
+static void config_edi_input_mode(struct vpe_ctx *ctx, int mode)
+{
+ struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+ u32 *edi_config_reg = &mmr_adb->dei_regs[3];
+
+ if (mode & 0x2)
+ write_field(edi_config_reg, 1, 1, 2); /* EDI_ENABLE_3D */
+
+ if (mode & 0x3)
+ write_field(edi_config_reg, 1, 1, 3); /* EDI_CHROMA_3D */
+
+ write_field(edi_config_reg, mode, VPE_EDI_INP_MODE_MASK,
+ VPE_EDI_INP_MODE_SHIFT);
+
+ ctx->load_mmrs = true;
+}
+
/*
* Set the shadow registers whose values are modified when either the
* source or destination format is changed.
@@ -817,8 +851,8 @@ static int set_srcdst_params(struct vpe_ctx *ctx)
ctx->sequence = 0;
ctx->field = V4L2_FIELD_TOP;
- if ((s_q_data->flags & Q_DATA_INTERLACED) &&
- !(d_q_data->flags & Q_DATA_INTERLACED)) {
+ if ((s_q_data->flags & Q_IS_INTERLACED) &&
+ !(d_q_data->flags & Q_IS_INTERLACED)) {
int bytes_per_line;
const struct vpdma_data_format *mv =
&vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
@@ -847,7 +881,7 @@ static int set_srcdst_params(struct vpe_ctx *ctx)
if (ret)
return ret;
- set_cfg_and_line_modes(ctx);
+ set_cfg_modes(ctx);
set_dei_regs(ctx);
csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0],
@@ -881,15 +915,14 @@ static struct vpe_ctx *file2ctx(struct file *file)
static int job_ready(void *priv)
{
struct vpe_ctx *ctx = priv;
- int needed = ctx->bufs_per_job;
-
- if (ctx->deinterlacing && ctx->src_vbs[2] == NULL)
- needed += 2; /* need additional two most recent fields */
- if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed)
- return 0;
-
- if (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < needed)
+ /*
+ * This check is needed as this might be called directly from driver
+ * When called by m2m framework, this will always satisy, but when
+ * called from vpe_irq, this might fail. (src stream with zero buffers)
+ */
+ if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) <= 0 ||
+ v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) <= 0)
return 0;
return 1;
@@ -1002,7 +1035,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
int plane = fmt->coplanar ? p_data->vb_part : 0;
vpdma_fmt = fmt->vpdma_fmt[plane];
- dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
+ dma_addr = vb2_dma_addr_plus_data_offset(vb, plane);
if (!dma_addr) {
vpe_err(ctx->dev,
"acquiring output buffer(%d) dma_addr failed\n",
@@ -1017,7 +1050,8 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
flags |= VPDMA_DATA_MODE_TILED;
vpdma_add_out_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect,
- vpdma_fmt, dma_addr, p_data->channel, flags);
+ vpdma_fmt, dma_addr, MAX_OUT_WIDTH_1920, MAX_OUT_HEIGHT_1080,
+ p_data->channel, flags);
}
static void add_in_dtd(struct vpe_ctx *ctx, int port)
@@ -1042,7 +1076,28 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
vpdma_fmt = fmt->vpdma_fmt[plane];
- dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
+ dma_addr = vb2_dma_addr_plus_data_offset(vb, plane);
+
+ if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB) {
+ /*
+ * Use top or bottom field from same vb alternately
+ * f,f-1,f-2 = TBT when seq is even
+ * f,f-1,f-2 = BTB when seq is odd
+ */
+ field = (p_data->vb_index + (ctx->sequence % 2)) % 2;
+
+ if (field) {
+ /* bottom field of a SEQ_TB buffer
+ * Skip the top field data by */
+ int height = q_data->height / 2;
+ int bpp = fmt->fourcc == V4L2_PIX_FMT_NV12 ?
+ 1 : (vpdma_fmt->depth >> 3);
+ if (plane)
+ height /= 2;
+ dma_addr += q_data->width * height * bpp;
+ }
+ }
+
if (!dma_addr) {
vpe_err(ctx->dev,
"acquiring input buffer(%d) dma_addr failed\n",
@@ -1097,23 +1152,49 @@ static void device_run(void *priv)
struct vpe_ctx *ctx = priv;
struct sc_data *sc = ctx->dev->sc;
struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
+ struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
- if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) {
- ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- WARN_ON(ctx->src_vbs[2] == NULL);
- ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- WARN_ON(ctx->src_vbs[1] == NULL);
+ if (ctx->deinterlacing && s_q_data->flags & Q_DATA_INTERLACED_SEQ_TB &&
+ ctx->sequence % 2 == 0) {
+ /* When using SEQ_TB buffers, When using it first time,
+ * No need to remove the buffer as the next field is present
+ * in the same buffer. (so that job_ready won't fail)
+ * It will be removed when using bottom field
+ */
+ ctx->src_vbs[0] = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ WARN_ON(ctx->src_vbs[0] == NULL);
+ } else {
+ ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ WARN_ON(ctx->src_vbs[0] == NULL);
}
- ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- WARN_ON(ctx->src_vbs[0] == NULL);
ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
WARN_ON(ctx->dst_vb == NULL);
+ if (ctx->deinterlacing) {
+
+ if (ctx->src_vbs[2] == NULL) {
+ ctx->src_vbs[2] = ctx->src_vbs[0];
+ WARN_ON(ctx->src_vbs[2] == NULL);
+ ctx->src_vbs[1] = ctx->src_vbs[0];
+ WARN_ON(ctx->src_vbs[1] == NULL);
+ }
+
+ /*
+ * we have output the first 2 frames through line average, we
+ * now switch to EDI de-interlacer
+ */
+ if (ctx->sequence == 2)
+ config_edi_input_mode(ctx, 0x3); /* EDI (Y + UV) */
+ }
+
/* config descriptors */
if (ctx->dev->loaded_mmrs != ctx->mmr_adb.dma_addr || ctx->load_mmrs) {
vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->mmr_adb);
vpdma_add_cfd_adb(&ctx->desc_list, CFD_MMR_CLIENT, &ctx->mmr_adb);
+
+ set_line_modes(ctx);
+
ctx->dev->loaded_mmrs = ctx->mmr_adb.dma_addr;
ctx->load_mmrs = false;
}
@@ -1221,6 +1302,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
struct vpe_dev *dev = (struct vpe_dev *)data;
struct vpe_ctx *ctx;
struct vpe_q_data *d_q_data;
+ struct vpe_q_data *s_q_data;
struct vb2_buffer *s_vb, *d_vb;
struct v4l2_buffer *s_buf, *d_buf;
unsigned long flags;
@@ -1257,7 +1339,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
if (irqst0) {
if (irqst0 & VPE_INT0_LIST0_COMPLETE)
- vpdma_clear_list_stat(ctx->dev->vpdma, 0);
+ vpdma_clear_list_stat(ctx->dev->vpdma, 0, 0);
irqst0 &= ~(VPE_INT0_LIST0_COMPLETE);
}
@@ -1297,7 +1379,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
d_buf->sequence = ctx->sequence;
d_q_data = &ctx->q_data[Q_DATA_DST];
- if (d_q_data->flags & Q_DATA_INTERLACED) {
+ if (d_q_data->flags & Q_IS_INTERLACED) {
d_buf->field = ctx->field;
if (ctx->field == V4L2_FIELD_BOTTOM) {
ctx->sequence++;
@@ -1311,12 +1393,28 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
ctx->sequence++;
}
- if (ctx->deinterlacing)
- s_vb = ctx->src_vbs[2];
+ s_q_data = &ctx->q_data[Q_DATA_SRC];
+
+ if (ctx->deinterlacing) {
+ /* Allow source buffer to be dequeued only if it won't be used
+ * in the next iteration. All vbs are initialized to first
+ * buffer and we are shifting buffers every iteration, for the
+ * first two iterations, no buffer will be dequeued.
+ * This ensures that driver will keep (n-2)th (n-1)th and (n)th
+ * field when deinterlacing is enabled */
+ if (ctx->src_vbs[2] != ctx->src_vbs[1])
+ s_vb = ctx->src_vbs[2];
+ else
+ s_vb = NULL;
+ }
spin_lock_irqsave(&dev->lock, flags);
- v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE);
+
+ if (s_vb)
+ v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE);
+
v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE);
+
spin_unlock_irqrestore(&dev->lock, flags);
if (ctx->deinterlacing) {
@@ -1325,7 +1423,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
}
ctx->bufs_completed++;
- if (ctx->bufs_completed < ctx->bufs_per_job) {
+ if (ctx->bufs_completed < ctx->bufs_per_job && job_ready(ctx)) {
device_run(ctx);
goto handled;
}
@@ -1432,7 +1530,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
struct v4l2_plane_pix_format *plane_fmt;
unsigned int w_align;
- int i, depth, depth_bytes;
+ int i, depth, depth_bytes, height;
if (!fmt || !(fmt->types & type)) {
vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
@@ -1440,7 +1538,8 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
return -EINVAL;
}
- if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE)
+ if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE
+ && pix->field != V4L2_FIELD_SEQ_TB)
pix->field = V4L2_FIELD_NONE;
depth = fmt->vpdma_fmt[VPE_LUMA]->depth;
@@ -1474,6 +1573,14 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
pix->num_planes = fmt->coplanar ? 2 : 1;
pix->pixelformat = fmt->fourcc;
+ /* for the actual image parameters, we need to consider the field height
+ * of the image for SEQ_TB buffers.
+ */
+ if (pix->field == V4L2_FIELD_SEQ_TB)
+ height = pix->height / 2;
+ else
+ height = pix->height;
+
if (!pix->colorspace) {
if (fmt->fourcc == V4L2_PIX_FMT_RGB24 ||
fmt->fourcc == V4L2_PIX_FMT_BGR24 ||
@@ -1481,7 +1588,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
fmt->fourcc == V4L2_PIX_FMT_BGR32) {
pix->colorspace = V4L2_COLORSPACE_SRGB;
} else {
- if (pix->height > 1280) /* HD */
+ if (height > 1280) /* HD */
pix->colorspace = V4L2_COLORSPACE_REC709;
else /* SD */
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
@@ -1558,9 +1665,15 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f)
q_data->c_rect.height = q_data->height;
if (q_data->field == V4L2_FIELD_ALTERNATE)
- q_data->flags |= Q_DATA_INTERLACED;
+ q_data->flags |= Q_DATA_INTERLACED_ALTERNATE;
+ else if (q_data->field == V4L2_FIELD_SEQ_TB)
+ q_data->flags |= Q_DATA_INTERLACED_SEQ_TB;
else
- q_data->flags &= ~Q_DATA_INTERLACED;
+ q_data->flags &= ~Q_IS_INTERLACED;
+
+ /* the crop height is halved for the case of SEQ_TB buffers */
+ if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB)
+ q_data->c_rect.height /= 2;
vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d",
f->type, q_data->width, q_data->height, q_data->fmt->fourcc,
@@ -1596,6 +1709,7 @@ static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
{
struct vpe_q_data *q_data;
+ int height;
if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
(s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
@@ -1630,13 +1744,22 @@ static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
return -EINVAL;
}
+ /*
+ * For SEQ_TB buffers, crop height should be less than the height of
+ * the field height, not the buffer height
+ */
+ if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB)
+ height = q_data->height / 2;
+ else
+ height = q_data->height;
+
if (s->r.top < 0 || s->r.left < 0) {
vpe_err(ctx->dev, "negative values for top and left\n");
s->r.top = s->r.left = 0;
}
v4l_bound_align_image(&s->r.width, MIN_W, q_data->width, 1,
- &s->r.height, MIN_H, q_data->height, H_ALIGN, S_ALIGN);
+ &s->r.height, MIN_H, height, H_ALIGN, S_ALIGN);
/* adjust left/top if cropping rectangle is out of bounds */
if (s->r.left + s->r.width > q_data->width)
@@ -1771,6 +1894,9 @@ static int vpe_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
{
struct vpe_ctx *ctx = file2ctx(file);
+ if (ctx->deinterlacing)
+ config_edi_input_mode(ctx, 0x0);
+
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
}
@@ -1881,11 +2007,12 @@ static int vpe_buf_prepare(struct vb2_buffer *vb)
num_planes = q_data->fmt->coplanar ? 2 : 1;
if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- if (!(q_data->flags & Q_DATA_INTERLACED)) {
+ if (!(q_data->flags & Q_IS_INTERLACED)) {
vb->v4l2_buf.field = V4L2_FIELD_NONE;
} else {
if (vb->v4l2_buf.field != V4L2_FIELD_TOP &&
- vb->v4l2_buf.field != V4L2_FIELD_BOTTOM)
+ vb->v4l2_buf.field != V4L2_FIELD_BOTTOM &&
+ vb->v4l2_buf.field != V4L2_FIELD_SEQ_TB)
return -EINVAL;
}
}
@@ -2112,6 +2239,9 @@ static int vpe_release(struct file *file)
vpdma_free_desc_list(&ctx->desc_list);
vpdma_free_desc_buf(&ctx->mmr_adb);
+ vpdma_free_desc_buf(&ctx->sc_coeff_v);
+ vpdma_free_desc_buf(&ctx->sc_coeff_h);
+
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
v4l2_ctrl_handler_free(&ctx->hdl);
diff --git a/drivers/media/usb/ttusb-dec/ttusbdecfe.c b/drivers/media/usb/ttusb-dec/ttusbdecfe.c
index 5c45c9d0712d..9c29552aedec 100644
--- a/drivers/media/usb/ttusb-dec/ttusbdecfe.c
+++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.c
@@ -156,6 +156,9 @@ static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struc
0x00, 0x00, 0x00, 0x00,
0x00, 0x00 };
+ if (cmd->msg_len > sizeof(b) - 4)
+ return -EINVAL;
+
memcpy(&b[4], cmd->msg, cmd->msg_len);
state->config->send_command(fe, 0x72,
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index 8a27dac5fd85..f6c8944abd97 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -690,12 +690,16 @@ void st_kim_ref(struct st_data_s **core_data, int id)
struct kim_data_s *kim_gdata;
/* get kim_gdata reference from platform device */
pdev = st_get_plat_device(id);
- if (!pdev) {
- *core_data = NULL;
- return;
- }
+ if (!pdev)
+ goto err;
kim_gdata = platform_get_drvdata(pdev);
+ if (!kim_gdata)
+ goto err;
+
*core_data = kim_gdata->core_data;
+ return;
+err:
+ *core_data = NULL;
}
static int kim_version_open(struct inode *i, struct file *f)
@@ -835,8 +839,7 @@ static int kim_probe(struct platform_device *pdev)
kim_debugfs_dir = debugfs_create_dir("ti-st", NULL);
if (IS_ERR(kim_debugfs_dir)) {
pr_err(" debugfs entries creation failed ");
- err = -EIO;
- goto err_debugfs_dir;
+ return 0;
}
debugfs_create_file("version", S_IRUGO, kim_debugfs_dir,
@@ -846,9 +849,6 @@ static int kim_probe(struct platform_device *pdev)
pr_info(" debugfs entries created ");
return 0;
-err_debugfs_dir:
- sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp);
-
err_sysfs_group:
st_core_exit(kim_gdata->core_data);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 2c886ae34198..08ccfb318df6 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -419,6 +419,16 @@ int mmc_of_parse(struct mmc_host *host)
host->caps |= MMC_CAP_SD_HIGHSPEED;
if (of_find_property(np, "cap-mmc-highspeed", &len))
host->caps |= MMC_CAP_MMC_HIGHSPEED;
+ if (of_find_property(np, "sd-uhs-sdr12", &len))
+ host->caps |= MMC_CAP_UHS_SDR12;
+ if (of_find_property(np, "sd-uhs-sdr25", &len))
+ host->caps |= MMC_CAP_UHS_SDR25;
+ if (of_find_property(np, "sd-uhs-sdr50", &len))
+ host->caps |= MMC_CAP_UHS_SDR50;
+ if (of_find_property(np, "sd-uhs-sdr104", &len))
+ host->caps |= MMC_CAP_UHS_SDR104;
+ if (of_find_property(np, "sd-uhs-ddr50", &len))
+ host->caps |= MMC_CAP_UHS_DDR50;
if (of_find_property(np, "cap-power-off-card", &len))
host->caps |= MMC_CAP_POWER_OFF_CARD;
if (of_find_property(np, "cap-sdio-irq", &len))
@@ -429,6 +439,14 @@ int mmc_of_parse(struct mmc_host *host)
host->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", &len))
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+ if (of_find_property(np, "mmc-ddr-1_8v", &len))
+ host->caps |= MMC_CAP_1_8V_DDR;
+ if (of_find_property(np, "mmc-ddr-1_2v", &len))
+ host->caps |= MMC_CAP_1_2V_DDR;
+ if (of_find_property(np, "mmc-hs200-1_8v", &len))
+ host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+ if (of_find_property(np, "mmc-hs200-1_2v", &len))
+ host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
return 0;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 4691593aa9cd..a458396617d4 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -22,6 +22,7 @@
#include <linux/dmaengine.h>
#include <linux/seq_file.h>
#include <linux/sizes.h>
+#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -45,6 +46,7 @@
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C
+#define OMAP_HSMMC_DLL 0x0034
#define OMAP_HSMMC_SDMASA 0x0100
#define OMAP_HSMMC_BLK 0x0104
#define OMAP_HSMMC_ARG 0x0108
@@ -54,6 +56,7 @@
#define OMAP_HSMMC_RSP54 0x0118
#define OMAP_HSMMC_RSP76 0x011C
#define OMAP_HSMMC_DATA 0x0120
+#define OMAP_HSMMC_PSTATE 0x0124
#define OMAP_HSMMC_HCTL 0x0128
#define OMAP_HSMMC_SYSCTL 0x012C
#define OMAP_HSMMC_STAT 0x0130
@@ -61,6 +64,7 @@
#define OMAP_HSMMC_ISE 0x0138
#define OMAP_HSMMC_AC12 0x013C
#define OMAP_HSMMC_CAPA 0x0140
+#define OMAP_HSMMC_CAPA2 0x0144
#define VS18 (1 << 26)
#define VS30 (1 << 25)
@@ -92,7 +96,12 @@
#define FOUR_BIT (1 << 1)
#define HSPE (1 << 2)
#define DDR (1 << 19)
+#define CLKEXTFREE (1 << 16)
+#define PADEN (1 << 15)
+#define CLEV (1 << 24)
+#define DLEV (0xF << 20)
#define DW8 (1 << 5)
+#define BRR (1 << 5)
#define OD 0x1
#define STAT_CLEAR 0xFFFFFFFF
#define INIT_STREAM_CMD 0x00000000
@@ -118,6 +127,20 @@
#define CERR_EN (1 << 28)
#define BADA_EN (1 << 29)
+#define V1V8_SIGEN (1 << 19)
+#define AC12_SCLK_SEL (1 << 23)
+#define AC12_UHSMC_MASK (7 << 16)
+#define AC12_UHSMC_SDR50 (2 << 16)
+#define AC12_UHSMC_SDR104 (3 << 16)
+#define DLL_LOCK (1 << 0)
+#define DLL_CALIB (1 << 1)
+#define DLL_UNLOCK_STICKY (1 << 2)
+#define DLL_SWT (1 << 20)
+#define DLL_FORCE_SR_C_MASK (0x7F << 13)
+#define DLL_FORCE_SR_C_SHIFT 13
+#define DLL_FORCE_VALUE (1 << 12)
+#define DLL_RESET (1 << 31)
+
#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\
DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
BRR_EN | BWR_EN | TC_EN | CC_EN)
@@ -129,17 +152,22 @@
#define ACTO (1 << 1)
#define ACNE (1 << 0)
+#define CAPA2_TSDR50 (1 << 13)
#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20 /* 20 mSec */
#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */
#define OMAP_MMC_MIN_CLOCK 400000
#define OMAP_MMC_MAX_CLOCK 52000000
+#define MAX_PHASE_DELAY 0x7F
#define DRIVER_NAME "omap_hsmmc"
#define VDD_1V8 1800000 /* 180000 uV */
#define VDD_3V0 3000000 /* 300000 uV */
#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
+#define EMMC_HSDDR_SD_SDR25_MAX 52000000
+#define SD_SDR50_MAX_FREQ 104000000
+
#define AUTO_CMD23 (1 << 1) /* Auto CMD23 support */
/*
* One controller can have multiple slots, like on some omap boards using
@@ -184,6 +212,7 @@ struct omap_hsmmc_host {
void __iomem *base;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
+ struct completion buf_ready;
unsigned int dma_len;
unsigned int dma_sg_idx;
unsigned char bus_mode;
@@ -204,8 +233,15 @@ struct omap_hsmmc_host {
int reqs_blocked;
int use_reg;
int req_in_progress;
+ int regulator_enabled;
unsigned long clk_rate;
unsigned int flags;
+ u32 *tuning_data;
+ int tuning_size;
+ int tuning_done;
+ int tuning_fsrc;
+ u32 tuning_uhsmc;
+ u32 tuning_opcode;
struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata;
};
@@ -215,6 +251,50 @@ struct omap_mmc_of_data {
u8 controller_flags;
};
+static const u32 ref_tuning_4bits[] = {
+ 0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
+ 0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
+ 0xF0FFF0FF, 0x3CCCFC0F, 0xCFCC33CC, 0xEEFFEFFF,
+ 0xFDFFFDFF, 0xFFBFFFDF, 0xFFF7FFBB, 0xDE7B7FF7
+};
+
+static const u32 ref_tuning_8bits[] = {
+ 0xFF00FFFF, 0x0000FFFF, 0xCCCCFFFF, 0xCCCC33CC,
+ 0xCC3333CC, 0xFFFFCCCC, 0xFFFFEEFF, 0xFFEEEEFF,
+ 0xFFDDFFFF, 0xDDDDFFFF, 0xBBFFFFFF, 0xBBFFFFFF,
+ 0xFFFFFFBB, 0XFFFFFF77, 0x77FF7777, 0xFFEEDDBB,
+ 0x00FFFFFF, 0x00FFFFFF, 0xCCFFFF00, 0xCC33CCCC,
+ 0x3333CCCC, 0xFFCCCCCC, 0xFFEEFFFF, 0xEEEEFFFF,
+ 0xDDFFFFFF, 0xDDFFFFFF, 0xFFFFFFDD, 0XFFFFFFBB,
+ 0xFFFFBBBB, 0xFFFF77FF, 0xFF7777FF, 0xEEDDBB77
+};
+
+static inline int omap_hsmmc_set_dll(struct omap_hsmmc_host *host, int count)
+{
+ int i;
+ u32 dll;
+ unsigned int retries = 1000;
+
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+ dll &= ~(DLL_FORCE_SR_C_MASK);
+ dll &= ~DLL_CALIB;
+ dll |= (count << DLL_FORCE_SR_C_SHIFT);
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+ dll |= DLL_FORCE_VALUE;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+ dll |= DLL_CALIB;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+ for (i = 0; i < retries; i++) {
+ if (OMAP_HSMMC_READ(host->base, DLL) & DLL_CALIB)
+ break;
+ usleep_range(10, 20);
+ }
+ dll &= ~DLL_CALIB;
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+
+ return 0;
+}
+
static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
static int omap_hsmmc_card_detect(struct device *dev, int slot)
@@ -274,11 +354,13 @@ static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)
#ifdef CONFIG_REGULATOR
static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
- int vdd)
+ int vdd_iopower)
{
struct omap_hsmmc_host *host =
platform_get_drvdata(to_platform_device(dev));
+ struct mmc_ios *ios = &host->mmc->ios;
int ret = 0;
+ u32 ac12 = 0;
/*
* If we don't see a Vcc regulator, assume it's a fixed
@@ -288,7 +370,7 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
return 0;
if (mmc_slot(host).before_set_reg)
- mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
+ mmc_slot(host).before_set_reg(dev, slot, power_on, vdd_iopower);
if (host->pbias) {
if (host->pbias_enabled == 1) {
@@ -313,19 +395,57 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
* chips/cards need an interface voltage rail too.
*/
if (power_on) {
- if (host->vcc)
- ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
+ if (host->vcc) {
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc,
+ ios->vdd);
+ if (ret < 0)
+ return ret;
+ }
+
/* Enable interface voltage rail, if needed */
- if (ret == 0 && host->vcc_aux) {
+ if (host->vcc_aux) {
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+
+ if (vdd_iopower == VDD_165_195) {
+ if (host->regulator_enabled) {
+ ret = regulator_disable(host->vcc_aux);
+ if (ret < 0)
+ goto error_set_power;
+ host->regulator_enabled = 0;
+ }
+ ret = regulator_set_voltage(host->vcc_aux,
+ VDD_1V8, VDD_1V8);
+ if (ret < 0)
+ goto error_set_power;
+ ac12 |= V1V8_SIGEN;
+ } else {
+ ret = regulator_set_voltage(host->vcc_aux,
+ VDD_3V0, VDD_3V0);
+ if (ret < 0)
+ goto error_set_power;
+ ac12 &= ~V1V8_SIGEN;
+ }
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+ }
+ if (host->vcc_aux && !host->regulator_enabled) {
ret = regulator_enable(host->vcc_aux);
- if (ret < 0 && host->vcc)
- ret = mmc_regulator_set_ocr(host->mmc,
- host->vcc, 0);
+
+ if (!ret) {
+ host->regulator_enabled = 1;
+ } else {
+ mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
+ goto error_set_power;
+ }
}
} else {
/* Shut down the rail */
- if (host->vcc_aux)
+ if (host->vcc_aux && host->regulator_enabled) {
ret = regulator_disable(host->vcc_aux);
+
+ if (!ret)
+ host->regulator_enabled = 0;
+ }
+
if (host->vcc) {
/* Then proceed to shut down the local regulator */
ret = mmc_regulator_set_ocr(host->mmc,
@@ -334,7 +454,7 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
}
if (host->pbias) {
- if (vdd <= VDD_165_195)
+ if (vdd_iopower == VDD_165_195)
ret = regulator_set_voltage(host->pbias, VDD_1V8,
VDD_1V8);
else
@@ -351,7 +471,7 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
}
if (mmc_slot(host).after_set_reg)
- mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
+ mmc_slot(host).after_set_reg(dev, slot, power_on, vdd_iopower);
error_set_power:
return ret;
@@ -512,7 +632,10 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
{
unsigned int irq_mask;
- if (host->use_dma)
+ if ((cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
+ irq_mask = INT_EN_MASK | BRR_EN;
+ else if (host->use_dma)
irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN);
else
irq_mask = INT_EN_MASK;
@@ -547,6 +670,33 @@ static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios)
return dsor;
}
+static inline int omap_hsmmc_restore_dll(struct omap_hsmmc_host *host)
+{
+ u32 ac12;
+ u32 dll;
+
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ ac12 |= host->tuning_uhsmc;
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+ dll |= DLL_FORCE_VALUE;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+
+ if (omap_hsmmc_set_dll(host, host->tuning_fsrc))
+ return -EIO;
+ return 0;
+}
+
+static inline void omap_hsmmc_save_dll(struct omap_hsmmc_host *host)
+{
+ u32 ac12;
+
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ ac12 &= ~AC12_UHSMC_MASK;
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+}
+
static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
{
struct mmc_ios *ios = &host->mmc->ios;
@@ -602,7 +752,7 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
u32 con;
con = OMAP_HSMMC_READ(host->base, CON);
- if (ios->timing == MMC_TIMING_MMC_DDR52)
+ if (ios->timing == MMC_TIMING_UHS_DDR50)
con |= DDR; /* configure in DDR mode */
else
con &= ~DDR;
@@ -636,7 +786,6 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
}
#ifdef CONFIG_PM
-
/*
* Restore the MMC host context, if it was lost as result of a
* power state change.
@@ -645,6 +794,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
{
struct mmc_ios *ios = &host->mmc->ios;
u32 hctl, capa;
+ u32 value;
unsigned long timeout;
if (host->con == OMAP_HSMMC_READ(host->base, CON) &&
@@ -657,7 +807,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
if (host->power_mode != MMC_POWER_OFF &&
- (1 << ios->vdd) <= MMC_VDD_23_24)
+ ((1 << ios->vdd) <= MMC_VDD_23_24 ||
+ (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)))
hctl = SDVS18;
else
hctl = SDVS30;
@@ -693,6 +844,17 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
omap_hsmmc_set_bus_mode(host);
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ value = OMAP_HSMMC_READ(host->base, HCTL);
+ value &= ~SDVS_MASK;
+ value |= SDVS18;
+ OMAP_HSMMC_WRITE(host->base, HCTL, value);
+
+ value = OMAP_HSMMC_READ(host->base, AC12);
+ value |= V1V8_SIGEN;
+ OMAP_HSMMC_WRITE(host->base, AC12, value);
+ }
+
out:
dev_dbg(mmc_dev(host->mmc), "context is restored: restore count %d\n",
host->context_loss);
@@ -842,6 +1004,12 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
if (host->use_dma)
cmdreg |= DMAE;
+ /* Tuning command is special. Data Present Select should be set */
+ if ((cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)) {
+ cmdreg = (cmd->opcode << 24) | (resptype << 16) |
+ (cmdtype << 22) | DP_SELECT | DDIR;
+ }
host->req_in_progress = 1;
OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
@@ -929,6 +1097,9 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
return;
}
+ if (host->cmd->opcode == MMC_SEND_TUNING_BLOCK)
+ return;
+
host->cmd = NULL;
if (cmd->flags & MMC_RSP_PRESENT) {
@@ -1068,6 +1239,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
struct mmc_data *data;
int end_cmd = 0, end_trans = 0;
int error = 0;
+ int i = 0;
data = host->data;
dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
@@ -1103,6 +1275,15 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
}
OMAP_HSMMC_WRITE(host->base, STAT, status);
+
+ if (status & BRR_EN) {
+ for (i = 0; i < host->tuning_size/4; i++)
+ host->tuning_data[i] =
+ OMAP_HSMMC_READ(host->base, DATA);
+ complete(&host->buf_ready);
+ return;
+ }
+
if (end_cmd || ((status & CC_EN) && host->cmd))
omap_hsmmc_cmd_done(host, host->cmd);
if ((end_trans || (status & TC_EN)) && host->mrq)
@@ -1603,6 +1784,8 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
}
+ if (!ios->clock)
+ goto end_ios;
omap_hsmmc_set_clock(host);
if (do_send_init_stream)
@@ -1610,6 +1793,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
omap_hsmmc_set_bus_mode(host);
+end_ios:
pm_runtime_put_autosuspend(host->dev);
}
@@ -1681,6 +1865,304 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc)
return 0;
}
+static int omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct omap_hsmmc_host *host;
+ struct mmc_ios *ios = &mmc->ios;
+ const u32 *tuning_ref;
+ int phase_delay = 0;
+ int err = 0;
+ int count = 0;
+ int length = 0;
+ int note_index = 0xFF;
+ int max_index = 0;
+ int max_window = 0;
+ bool previous_match = false;
+ bool current_match;
+ u32 ac12, capa2, dll = 0;
+
+ host = mmc_priv(mmc);
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ tuning_ref = ref_tuning_8bits;
+ host->tuning_size = sizeof(ref_tuning_8bits);
+ break;
+ case MMC_BUS_WIDTH_4:
+ tuning_ref = ref_tuning_4bits;
+ host->tuning_size = sizeof(ref_tuning_4bits);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ host->tuning_data = kzalloc(host->tuning_size, GFP_KERNEL);
+ if (!host->tuning_data)
+ return -ENOMEM;
+
+ host->tuning_done = 0;
+ /* clock tuning is not needed for upto 52MHz */
+ if (ios->clock <= EMMC_HSDDR_SD_SDR25_MAX)
+ return 0;
+
+ omap_hsmmc_stop_clock(host);
+
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ capa2 = OMAP_HSMMC_READ(host->base, CAPA2);
+
+ ac12 &= ~AC12_UHSMC_MASK;
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+
+ /*
+ * Host Controller needs tuning only in case of SDR104 mode
+ * and for SDR50 mode when Use Tuning for SDR50 is set in
+ * Capabilities register.
+ */
+ if (ios->clock <= SD_SDR50_MAX_FREQ) {
+ if (!(capa2 & CAPA2_TSDR50))
+ return 0;
+ ac12 |= AC12_UHSMC_SDR50;
+ } else {
+ ac12 |= AC12_UHSMC_SDR104;
+ }
+
+ ac12 |= AC12_UHSMC_SDR104;
+ ac12 |= V1V8_SIGEN;
+
+ /* Enable SDR50/SDR104 mode */
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+ omap_hsmmc_start_clock(host);
+
+ /* Start software tuning Procedure */
+ dll |= DLL_SWT;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+
+ while (phase_delay < MAX_PHASE_DELAY) {
+ struct mmc_command cmd;
+ struct mmc_request mrq;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&mrq, 0, sizeof(struct mmc_request));
+
+ if (phase_delay > MAX_PHASE_DELAY)
+ break;
+
+ omap_hsmmc_set_dll(host, phase_delay);
+
+ cmd.opcode = opcode;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd.retries = 0;
+ cmd.data = NULL;
+ cmd.error = 0;
+
+ mrq.cmd = &cmd;
+ host->mrq = &mrq;
+
+ OMAP_HSMMC_WRITE(host->base, BLK, host->tuning_size);
+ set_data_timeout(host, 50000000, 0);
+ omap_hsmmc_start_command(host, &cmd, NULL);
+
+ host->cmd = NULL;
+ host->mrq = NULL;
+
+ /* Wait for Buffer Read Ready interrupt */
+ err = wait_for_completion_timeout(&host->buf_ready,
+ msecs_to_jiffies(5000));
+ omap_hsmmc_disable_irq(host);
+ host->req_in_progress = 0;
+
+ if (err == 0) {
+ dev_err(mmc_dev(host->mmc),
+ "Tuning BRR timeout. phase_delay=%x",
+ phase_delay);
+ err = -ETIMEDOUT;
+ goto tuning_error;
+ }
+
+ current_match = true;
+ if (memcmp(host->tuning_data, tuning_ref, host->tuning_size))
+ current_match = false;
+ else
+ current_match = true;
+
+ if (current_match == true) {
+ if (previous_match == false) {
+ /* new window */
+ note_index = count;
+ length = 1;
+ } else {
+ length++;
+ }
+ previous_match = true;
+ if (length > max_window) {
+ max_index = note_index;
+ max_window = length;
+ }
+ } else {
+ previous_match = false;
+ }
+ phase_delay += 4;
+ count++;
+ }
+
+ if (!max_window) {
+ dev_err(mmc_dev(host->mmc), "Unable to find match\n");
+ err = -EIO;
+ goto tuning_error;
+ }
+
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ if (!(ac12 & AC12_SCLK_SEL)) {
+ err = -EIO;
+ goto tuning_error;
+ }
+
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+ dll &= ~DLL_SWT;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+ count = 4 * (max_index + (max_window >> 1));
+ if (omap_hsmmc_set_dll(host, count)) {
+ err = -EIO;
+ goto tuning_error;
+ }
+ host->tuning_fsrc = count;
+ host->tuning_uhsmc = (OMAP_HSMMC_READ(host->base, AC12)
+ & AC12_UHSMC_MASK);
+ host->tuning_opcode = opcode;
+ host->tuning_done = 1;
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+
+ return 0;
+
+tuning_error:
+ dev_err(mmc_dev(host->mmc),
+ "Tuning failed. Using fixed sampling clock\n");
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ ac12 &= ~(AC12_UHSMC_MASK | AC12_SCLK_SEL);
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+ dll &= ~(DLL_FORCE_VALUE | DLL_SWT);
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+ return err;
+}
+
+static int omap_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct omap_hsmmc_host *host;
+ u32 value = 0;
+ int ret = 0;
+
+ if (!(mmc->caps & (MMC_CAP_UHS_SDR12 |
+ MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_DDR50)))
+ return 0;
+
+ host = mmc_priv(mmc);
+
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+ omap_hsmmc_conf_bus_power(host);
+
+ value = OMAP_HSMMC_READ(host->base, AC12);
+ value &= ~V1V8_SIGEN;
+ OMAP_HSMMC_WRITE(host->base, AC12, value);
+ dev_dbg(mmc_dev(host->mmc), " i/o voltage switch to 3V\n");
+ return 0;
+ }
+
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ value = OMAP_HSMMC_READ(host->base, HCTL);
+ value &= ~SDVS_MASK;
+ value |= SDVS18;
+ OMAP_HSMMC_WRITE(host->base, HCTL, value);
+ value |= SDBP;
+ OMAP_HSMMC_WRITE(host->base, HCTL, value);
+
+ ret = mmc_slot(host).set_power(host->dev, host->slot_id,
+ 1, VDD_165_195);
+ if (ret < 0) {
+ dev_dbg(mmc_dev(host->mmc), "failed to switch 1.8v\n");
+ goto voltage_switch_error;
+ }
+ }
+
+voltage_switch_error:
+ return ret;
+}
+
+static int omap_hsmmc_card_busy_low(struct omap_hsmmc_host *host)
+{
+ u32 value;
+ unsigned long timeout;
+
+ value = OMAP_HSMMC_READ(host->base, CON);
+ value &= ~CLKEXTFREE;
+ value |= PADEN;
+ OMAP_HSMMC_WRITE(host->base, CON, value);
+
+ timeout = jiffies + msecs_to_jiffies(1);
+ do {
+ value = OMAP_HSMMC_READ(host->base, PSTATE);
+ if (!(value & (CLEV | DLEV)))
+ return true;
+
+ usleep_range(100, 200);
+ } while (!time_after(jiffies, timeout));
+
+ dev_err(mmc_dev(host->mmc), "timeout : i/o low 0x%x\n", value);
+
+ return false;
+}
+
+static int omap_hsmmc_card_busy_high(struct omap_hsmmc_host *host)
+{
+ u32 value;
+ unsigned long timeout;
+
+ value = OMAP_HSMMC_READ(host->base, CON);
+ value |= CLKEXTFREE;
+ OMAP_HSMMC_WRITE(host->base, CON, value);
+
+ timeout = jiffies + msecs_to_jiffies(1);
+ do {
+ value = OMAP_HSMMC_READ(host->base, PSTATE);
+ if ((value & CLEV) && (value & DLEV)) {
+ value = OMAP_HSMMC_READ(host->base, CON);
+ value &= ~(CLKEXTFREE | PADEN);
+ OMAP_HSMMC_WRITE(host->base, CON,
+ (value & ~(CLKEXTFREE | PADEN)));
+ return false;
+ }
+
+ usleep_range(100, 200);
+ } while (!time_after(jiffies, timeout));
+
+ dev_dbg(mmc_dev(host->mmc), "timeout : i/o high 0x%x\n", value);
+
+ return true;
+}
+
+static int omap_hsmmc_card_busy(struct mmc_host *mmc)
+{
+ struct omap_hsmmc_host *host;
+ u32 value;
+ int ret;
+
+ host = mmc_priv(mmc);
+ value = OMAP_HSMMC_READ(host->base, AC12);
+
+ if (value & V1V8_SIGEN)
+ ret = omap_hsmmc_card_busy_high(host);
+ else
+ ret = omap_hsmmc_card_busy_low(host);
+
+ return ret;
+}
+
static const struct mmc_host_ops omap_hsmmc_ops = {
.enable = omap_hsmmc_enable_fclk,
.disable = omap_hsmmc_disable_fclk,
@@ -1691,6 +2173,9 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
.init_card = omap_hsmmc_init_card,
+ .start_signal_voltage_switch = omap_start_signal_voltage_switch,
+ .card_busy = omap_hsmmc_card_busy,
+ .execute_tuning = omap_execute_tuning,
/* NYET -- enable_sdio_irq */
};
@@ -1850,6 +2335,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
{
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
struct mmc_host *mmc;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
struct omap_hsmmc_host *host = NULL;
struct resource *res;
int ret, irq;
@@ -1916,6 +2403,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->power_mode = MMC_POWER_OFF;
host->next_data.cookie = 1;
host->pbias_enabled = 0;
+ host->regulator_enabled = 0;
platform_set_drvdata(pdev, host);
@@ -1923,12 +2411,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->f_min = OMAP_MMC_MIN_CLOCK;
- if (pdata->max_freq > 0)
- mmc->f_max = pdata->max_freq;
- else
- mmc->f_max = OMAP_MMC_MAX_CLOCK;
-
spin_lock_init(&host->irq_lock);
+ init_completion(&host->buf_ready);
host->fclk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(host->fclk)) {
@@ -1937,6 +2421,18 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
goto err1;
}
+ if (pdata->max_freq > 0) {
+ mmc->f_max = pdata->max_freq;
+ ret = clk_set_rate(host->fclk, pdata->max_freq);
+ if (ret) {
+ dev_err(dev, "failed to set clock to %d\n",
+ pdata->max_freq);
+ goto err1;
+ }
+ } else {
+ mmc->f_max = OMAP_MMC_MAX_CLOCK;
+ }
+
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
@@ -1981,6 +2477,22 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->pm_caps = mmc_slot(host).pm_caps;
+ if (of_property_read_bool(np, "sd-uhs-sdr12"))
+ mmc->caps |= MMC_CAP_UHS_SDR12;
+ if (of_property_read_bool(np, "sd-uhs-sdr25"))
+ mmc->caps |= MMC_CAP_UHS_SDR25;
+ if (of_property_read_bool(np, "sd-uhs-sdr50"))
+ mmc->caps |= MMC_CAP_UHS_SDR50;
+ if (of_property_read_bool(np, "sd-uhs-sdr104"))
+ mmc->caps |= MMC_CAP_UHS_SDR104;
+ if (of_property_read_bool(np, "sd-uhs-ddr50"))
+ mmc->caps |= MMC_CAP_UHS_DDR50;
+ if (of_property_read_bool(np, "mmc-hs200-1_8v"))
+ mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+
+ mmc->caps |= MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C |
+ MMC_CAP_DRIVER_TYPE_D;
+
omap_hsmmc_conf_bus_power(host);
if (!pdev->dev.of_node) {
@@ -2180,6 +2692,7 @@ static int omap_hsmmc_suspend(struct device *dev)
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
}
+ host->tuning_done = 0;
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
@@ -2230,6 +2743,9 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
struct omap_hsmmc_host *host;
host = platform_get_drvdata(to_platform_device(dev));
+ if (host->tuning_done)
+ omap_hsmmc_restore_dll(host);
+
omap_hsmmc_context_save(host);
dev_dbg(dev, "disabled\n");
@@ -2249,6 +2765,10 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
pinctrl_pm_select_default_state(host->dev);
omap_hsmmc_context_restore(host);
+
+ if (host->tuning_done)
+ omap_hsmmc_restore_dll(host);
+
dev_dbg(dev, "enabled\n");
return 0;
diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c
index 96042b17d584..b77f0e9311ae 100644
--- a/drivers/mtd/tests/oobtest.c
+++ b/drivers/mtd/tests/oobtest.c
@@ -119,27 +119,54 @@ static int write_whole_device(void)
}
/* Display the address, offset and data bytes at comparison failure */
-static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t count)
+static size_t memcmpshowoffset(loff_t addr, loff_t offset, const void *cs,
+ const void *ct, size_t count)
{
const unsigned char *su1, *su2;
int res;
- int ret = 0;
size_t i = 0;
size_t bitflips = 0;
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) {
res = *su1 ^ *su2;
if (res) {
- pr_info("error @addr[0x%lx:0x%x] 0x%x -> 0x%x diff 0x%x\n",
- (unsigned long)addr, i, *su1, *su2, res);
+ pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0x%x diff 0x%x\n",
+ (unsigned long)addr, (unsigned long)offset + i,
+ *su1, *su2, res);
bitflips += hweight8(res);
- ret = 1;
}
}
return bitflips;
}
+#define memcmpshow(addr, cs, ct, count) memcmpshowoffset((addr), 0, (cs), (ct),\
+ (count))
+
+/*
+ * Compare with 0xff and show the address, offset and data bytes at
+ * comparison failure
+ */
+static size_t memffshow(loff_t addr, loff_t offset, const void *cs,
+ size_t count)
+{
+ const unsigned char *su1;
+ int res;
+ size_t i = 0;
+ size_t bitflips = 0;
+
+ for (su1 = cs; 0 < count; ++su1, count--, i++) {
+ res = *su1 ^ 0xff;
+ if (res) {
+ pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0xff diff 0x%x\n",
+ (unsigned long)addr, (unsigned long)offset + i,
+ *su1, res);
+ bitflips += hweight8(res);
+ }
+ }
+
+ return bitflips;
+}
static int verify_eraseblock(int ebnum)
{
int i;
@@ -197,9 +224,19 @@ static int verify_eraseblock(int ebnum)
errcnt += 1;
return err ? err : -1;
}
- bitflips = memcmpshow(addr, readbuf + use_offset,
- writebuf + (use_len_max * i) + use_offset,
- use_len);
+ bitflips = memcmpshowoffset(addr, use_offset,
+ readbuf + use_offset,
+ writebuf + (use_len_max * i) + use_offset,
+ use_len);
+
+ /* verify pre-offset area for 0xff */
+ bitflips += memffshow(addr, 0, readbuf, use_offset);
+
+ /* verify post-(use_offset + use_len) area for 0xff */
+ k = use_offset + use_len;
+ bitflips += memffshow(addr, k, readbuf + k,
+ mtd->ecclayout->oobavail - k);
+
if (bitflips > bitflip_limit) {
pr_err("error: verify failed at %#llx\n",
(long long)addr);
@@ -209,33 +246,8 @@ static int verify_eraseblock(int ebnum)
return -1;
}
} else if (bitflips) {
- pr_info("ignoring error as within bitflip limit\n");
+ pr_info("ignoring errors as within bitflip limit\n");
}
- for (k = 0; k < use_offset; ++k)
- if (readbuf[k] != 0xff) {
- pr_err("error: verify 0xff "
- "failed at %#llx\n",
- (long long)addr);
- errcnt += 1;
- if (errcnt > 1000) {
- pr_err("error: too "
- "many errors\n");
- return -1;
- }
- }
- for (k = use_offset + use_len;
- k < mtd->ecclayout->oobavail; ++k)
- if (readbuf[k] != 0xff) {
- pr_err("error: verify 0xff "
- "failed at %#llx\n",
- (long long)addr);
- errcnt += 1;
- if (errcnt > 1000) {
- pr_err("error: too "
- "many errors\n");
- return -1;
- }
- }
}
if (vary_offset)
do_vary_offset();
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index cc38948cf65d..15379824d77d 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2450,9 +2450,9 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
if (!rtnl_trylock())
goto re_arm;
- if (slave_state_changed) {
+ if (slave_state_changed)
bond_slave_state_change(bond);
- } else if (do_failover) {
+ if (do_failover) {
/* the bond_select_active_slave must hold RTNL
* and curr_slave_lock for write.
*/
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c
index 951bfede8f3d..66431fac608b 100644
--- a/drivers/net/can/c_can/c_can.c
+++ b/drivers/net/can/c_can/c_can.c
@@ -35,6 +35,7 @@
#include <linux/list.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/can.h>
#include <linux/can/dev.h>
@@ -212,8 +213,10 @@ static const struct can_bittiming_const c_can_bittiming_const = {
static inline void c_can_pm_runtime_enable(const struct c_can_priv *priv)
{
- if (priv->device)
+ if (priv->device) {
pm_runtime_enable(priv->device);
+ pm_runtime_irq_safe(priv->device);
+ }
}
static inline void c_can_pm_runtime_disable(const struct c_can_priv *priv)
@@ -685,6 +688,9 @@ static void c_can_start(struct net_device *dev)
/* enable status change, error and module interrupts */
c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS);
+
+ /* activate pins */
+ c_can_pinctrl_select_state(dev, PINCTRL_STATE_DEFAULT);
}
static void c_can_stop(struct net_device *dev)
@@ -696,6 +702,9 @@ static void c_can_stop(struct net_device *dev)
/* set the state as STOPPED */
priv->can.state = CAN_STATE_STOPPED;
+
+ /* deactivate pins */
+ c_can_pinctrl_select_state(dev, PINCTRL_STATE_SLEEP);
}
static int c_can_set_mode(struct net_device *dev, enum can_mode mode)
@@ -1284,6 +1293,15 @@ int register_c_can_dev(struct net_device *dev)
struct c_can_priv *priv = netdev_priv(dev);
int err;
+ priv->pinctrl = devm_pinctrl_get(dev->dev.parent);
+
+ /* Deactivate pins to prevent DRA7 DCAN IP from being
+ * stuck in transition when module is disabled.
+ * Pins are activated in c_can_start() and deactivated
+ * in c_can_stop()
+ */
+ c_can_pinctrl_select_state(dev, PINCTRL_STATE_SLEEP);
+
c_can_pm_runtime_enable(priv);
dev->flags |= IFF_ECHO; /* we support local echo */
@@ -1309,6 +1327,22 @@ void unregister_c_can_dev(struct net_device *dev)
}
EXPORT_SYMBOL_GPL(unregister_c_can_dev);
+/* Selects the pinctrl state specified in the name. */
+void c_can_pinctrl_select_state(struct net_device *dev,
+ const char *name)
+{
+ struct c_can_priv *priv = netdev_priv(dev);
+
+ if (!IS_ERR(priv->pinctrl)) {
+ struct pinctrl_state *s;
+
+ s = pinctrl_lookup_state(priv->pinctrl, name);
+ if (!IS_ERR(s))
+ pinctrl_select_state(priv->pinctrl, s);
+ }
+}
+EXPORT_SYMBOL_GPL(c_can_pinctrl_select_state);
+
MODULE_AUTHOR("Bhupesh Sharma <bhupesh.sharma@st.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CAN bus driver for Bosch C_CAN controller");
diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h
index 896430c285a8..0d74c1d0049b 100644
--- a/drivers/net/can/c_can/c_can.h
+++ b/drivers/net/can/c_can/c_can.h
@@ -61,6 +61,7 @@ enum reg {
C_CAN_INTPND2_REG,
C_CAN_MSGVAL1_REG,
C_CAN_MSGVAL2_REG,
+ C_CAN_FUNCTION_REG,
};
static const u16 reg_map_c_can[] = {
@@ -112,6 +113,7 @@ static const u16 reg_map_d_can[] = {
[C_CAN_BRPEXT_REG] = 0x0E,
[C_CAN_INT_REG] = 0x10,
[C_CAN_TEST_REG] = 0x14,
+ [C_CAN_FUNCTION_REG] = 0x18,
[C_CAN_TXRQST1_REG] = 0x88,
[C_CAN_TXRQST2_REG] = 0x8A,
[C_CAN_NEWDAT1_REG] = 0x9C,
@@ -150,6 +152,25 @@ enum c_can_dev_id {
BOSCH_D_CAN,
};
+struct c_can_driver_data {
+ enum c_can_dev_id id;
+
+ /* RAMINIT register description. Optional. */
+ u8 num_can; /* Number of CAN instances on the SoC */
+ u8 *raminit_start_bits; /* Array of START bit positions */
+ u8 *raminit_done_bits; /* Array of DONE bit positions */
+ bool raminit_pulse; /* If set, sets and clears START bit (pulse) */
+};
+
+/* Out of band RAMINIT register access via syscon regmap */
+struct c_can_raminit {
+ struct regmap *syscon; /* for raminit ctrl. reg. access */
+ unsigned int reg; /* register index within syscon */
+ u8 start_bit;
+ u8 done_bit;
+ bool needs_pulse;
+};
+
/* c_can private data structure */
struct c_can_priv {
struct can_priv can; /* must be the first member */
@@ -159,8 +180,10 @@ struct c_can_priv {
int tx_object;
int current_status;
int last_status;
- u16 (*read_reg) (struct c_can_priv *priv, enum reg index);
- void (*write_reg) (struct c_can_priv *priv, enum reg index, u16 val);
+ u16 (*read_reg) (const struct c_can_priv *priv, enum reg index);
+ void (*write_reg) (const struct c_can_priv *priv, enum reg index, u16 val);
+ u32 (*read_reg32) (const struct c_can_priv *priv, enum reg index);
+ void (*write_reg32) (const struct c_can_priv *priv, enum reg index, u32 val);
void __iomem *base;
const u16 *regs;
unsigned long irq_flags; /* for request_irq() */
@@ -169,19 +192,16 @@ struct c_can_priv {
void *priv; /* for board-specific data */
u16 irqstatus;
enum c_can_dev_id type;
- u32 __iomem *raminit_ctrlreg;
- struct regmap *syscon; /* Alternative raminit reg. access */
- unsigned int raminit_idx; /* register index within syscon */
- u8 raminit_start_bit; /* START bit position in raminit reg. */
- u8 raminit_done_bit; /* DONE bit position in raminit reg. */
- unsigned int instance;
+ struct c_can_raminit raminit_sys; /* RAMINIT via syscon regmap */
void (*raminit) (const struct c_can_priv *priv, bool enable);
+ struct pinctrl *pinctrl;
};
struct net_device *alloc_c_can_dev(void);
void free_c_can_dev(struct net_device *dev);
int register_c_can_dev(struct net_device *dev);
void unregister_c_can_dev(struct net_device *dev);
+void c_can_pinctrl_select_state(struct net_device *dev, const char *name);
#ifdef CONFIG_PM
int c_can_power_up(struct net_device *dev);
diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c
index bce0be54c2f5..afb487ed8c35 100644
--- a/drivers/net/can/c_can/c_can_pci.c
+++ b/drivers/net/can/c_can/c_can_pci.c
@@ -39,30 +39,47 @@ struct c_can_pci_data {
* registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
* Handle the same by providing a common read/write interface.
*/
-static u16 c_can_pci_read_reg_aligned_to_16bit(struct c_can_priv *priv,
+static u16 c_can_pci_read_reg_aligned_to_16bit(const struct c_can_priv *priv,
enum reg index)
{
return readw(priv->base + priv->regs[index]);
}
-static void c_can_pci_write_reg_aligned_to_16bit(struct c_can_priv *priv,
+static void c_can_pci_write_reg_aligned_to_16bit(const struct c_can_priv *priv,
enum reg index, u16 val)
{
writew(val, priv->base + priv->regs[index]);
}
-static u16 c_can_pci_read_reg_aligned_to_32bit(struct c_can_priv *priv,
+static u16 c_can_pci_read_reg_aligned_to_32bit(const struct c_can_priv *priv,
enum reg index)
{
return readw(priv->base + 2 * priv->regs[index]);
}
-static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv,
+static void c_can_pci_write_reg_aligned_to_32bit(const struct c_can_priv *priv,
enum reg index, u16 val)
{
writew(val, priv->base + 2 * priv->regs[index]);
}
+static u32 c_can_pci_read_reg32(const struct c_can_priv *priv, enum reg index)
+{
+ u32 val;
+
+ val = priv->read_reg(priv, index);
+ val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
+
+ return val;
+}
+
+static void c_can_pci_write_reg32(const struct c_can_priv *priv, enum reg index,
+ u32 val)
+{
+ priv->write_reg(priv, index + 1, val >> 16);
+ priv->write_reg(priv, index, val);
+}
+
static int c_can_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -146,6 +163,8 @@ static int c_can_pci_probe(struct pci_dev *pdev,
ret = -EINVAL;
goto out_free_c_can;
}
+ priv->read_reg32 = c_can_pci_read_reg32;
+ priv->write_reg32 = c_can_pci_write_reg32;
ret = register_c_can_dev(dev);
if (ret) {
diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c
index a71ae213370c..71b9dfb46c61 100644
--- a/drivers/net/can/c_can/c_can_platform.c
+++ b/drivers/net/can/c_can/c_can_platform.c
@@ -32,94 +32,211 @@
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/can/dev.h>
#include "c_can.h"
-#define CAN_RAMINIT_START_MASK(i) (1 << (i))
-
+#define DCAN_RAM_INIT_BIT (1 << 3)
+static DEFINE_SPINLOCK(raminit_lock);
/*
* 16-bit c_can registers can be arranged differently in the memory
* architecture of different implementations. For example: 16-bit
* registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
* Handle the same by providing a common read/write interface.
*/
-static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv,
+static u16 c_can_plat_read_reg_aligned_to_16bit(const struct c_can_priv *priv,
enum reg index)
{
return readw(priv->base + priv->regs[index]);
}
-static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv,
+static void c_can_plat_write_reg_aligned_to_16bit(const struct c_can_priv *priv,
enum reg index, u16 val)
{
writew(val, priv->base + priv->regs[index]);
}
-static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv,
+static u16 c_can_plat_read_reg_aligned_to_32bit(const struct c_can_priv *priv,
enum reg index)
{
return readw(priv->base + 2 * priv->regs[index]);
}
-static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv,
+static void c_can_plat_write_reg_aligned_to_32bit(const struct c_can_priv *priv,
enum reg index, u16 val)
{
writew(val, priv->base + 2 * priv->regs[index]);
}
-static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
+static void c_can_hw_raminit_wait_syscon(const struct c_can_priv *priv,
+ u32 mask, u32 val)
+{
+ int timeout = 0;
+ const struct c_can_raminit *raminit = &priv->raminit_sys;
+ u32 ctrl;
+
+ /* We look only at the bits of our instance. */
+ val &= mask;
+ do {
+ udelay(1);
+ timeout++;
+
+ regmap_read(raminit->syscon, raminit->reg, &ctrl);
+ if (timeout == 1000) {
+ dev_err(&priv->dev->dev, "%s: time out\n", __func__);
+ break;
+ }
+ } while ((ctrl & mask) != val);
+}
+
+static void c_can_hw_raminit_syscon(const struct c_can_priv *priv, bool enable)
+{
+ u32 mask;
+ u32 ctrl;
+ const struct c_can_raminit *raminit = &priv->raminit_sys;
+ u8 start_bit, done_bit;
+
+ start_bit = raminit->start_bit;
+ done_bit = raminit->done_bit;
+
+ spin_lock(&raminit_lock);
+
+ mask = 1 << start_bit | 1 << done_bit;
+ regmap_read(raminit->syscon, raminit->reg, &ctrl);
+
+ /* We clear the done and start bit first. The start bit is
+ * looking at the 0 -> transition, but is not self clearing;
+ * And we clear the init done bit as well.
+ * NOTE: DONE must be written with 1 to clear it.
+ */
+ ctrl &= ~(1 << start_bit);
+ ctrl |= 1 << done_bit;
+ regmap_write(raminit->syscon, raminit->reg, ctrl);
+
+ ctrl &= ~(1 << done_bit);
+ c_can_hw_raminit_wait_syscon(priv, mask, ctrl);
+
+ if (enable) {
+ /* Set start bit and wait for the done bit. */
+ ctrl |= 1 << start_bit;
+ regmap_write(raminit->syscon, raminit->reg, ctrl);
+
+ /* clear START bit if start pulse is needed */
+ if (raminit->needs_pulse) {
+ ctrl &= ~(1 << start_bit);
+ regmap_write(raminit->syscon, raminit->reg, ctrl);
+ }
+
+ ctrl |= 1 << done_bit;
+ c_can_hw_raminit_wait_syscon(priv, mask, ctrl);
+ }
+ spin_unlock(&raminit_lock);
+}
+
+static u32 c_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index)
{
u32 val;
- val = readl(priv->raminit_ctrlreg);
- if (enable)
- val |= CAN_RAMINIT_START_MASK(priv->instance);
- else
- val &= ~CAN_RAMINIT_START_MASK(priv->instance);
- writel(val, priv->raminit_ctrlreg);
+ val = priv->read_reg(priv, index);
+ val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
+
+ return val;
}
-static void c_can_syscon_raminit(const struct c_can_priv *priv, bool enable)
+static void c_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index,
+ u32 val)
{
- u32 mask;
+ priv->write_reg(priv, index + 1, val >> 16);
+ priv->write_reg(priv, index, val);
+}
- if (IS_ERR(priv->syscon))
- return;
+static u32 d_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index)
+{
+ return readl(priv->base + priv->regs[index]);
+}
- mask = 1 << priv->raminit_start_bit;
+static void d_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index,
+ u32 val)
+{
+ writel(val, priv->base + priv->regs[index]);
+}
- if (enable)
- regmap_update_bits(priv->syscon, priv->raminit_idx,
- mask, mask);
- else
- regmap_update_bits(priv->syscon, priv->raminit_idx, mask, 0);
+static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask)
+{
+ while (priv->read_reg32(priv, C_CAN_FUNCTION_REG) & mask)
+ udelay(1);
}
+static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
+{
+ u32 ctrl;
+
+ ctrl = priv->read_reg32(priv, C_CAN_FUNCTION_REG);
+ ctrl &= ~DCAN_RAM_INIT_BIT;
+ priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
+ c_can_hw_raminit_wait(priv, ctrl);
+
+ if (enable) {
+ ctrl |= DCAN_RAM_INIT_BIT;
+ priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
+ c_can_hw_raminit_wait(priv, ctrl);
+ }
+}
+
+static struct c_can_driver_data c_can_drvdata = {
+ .id = BOSCH_C_CAN,
+};
+
+static struct c_can_driver_data d_can_drvdata = {
+ .id = BOSCH_D_CAN,
+};
+
+static u8 dra7_raminit_start_bits[] = {3, 5};
+static u8 dra7_raminit_done_bits[] = {1, 2};
+static struct c_can_driver_data dra7_dcan_drvdata = {
+ .id = BOSCH_D_CAN,
+ .num_can = 2,
+ .raminit_start_bits = dra7_raminit_start_bits,
+ .raminit_done_bits = dra7_raminit_done_bits,
+ .raminit_pulse = true,
+};
+
+static u8 am3352_raminit_start_bits[] = {0, 1};
+static u8 am3352_raminit_done_bits[] = {8, 9};
+static struct c_can_driver_data am3352_dcan_drvdata = {
+ .id = BOSCH_D_CAN,
+ .num_can = 2,
+ .raminit_start_bits = am3352_raminit_start_bits,
+ .raminit_done_bits = am3352_raminit_done_bits,
+};
+
static struct platform_device_id c_can_id_table[] = {
- [BOSCH_C_CAN_PLATFORM] = {
+ {
.name = KBUILD_MODNAME,
- .driver_data = BOSCH_C_CAN,
+ .driver_data = (kernel_ulong_t)&c_can_drvdata,
},
- [BOSCH_C_CAN] = {
+ {
.name = "c_can",
- .driver_data = BOSCH_C_CAN,
+ .driver_data = (kernel_ulong_t)&c_can_drvdata,
},
- [BOSCH_D_CAN] = {
+ {
.name = "d_can",
- .driver_data = BOSCH_D_CAN,
- }, {
- }
+ .driver_data = (kernel_ulong_t)&d_can_drvdata,
+ },
+ { /* sentinel */ },
};
MODULE_DEVICE_TABLE(platform, c_can_id_table);
static const struct of_device_id c_can_of_table[] = {
- { .compatible = "bosch,c_can", .data = &c_can_id_table[BOSCH_C_CAN] },
- { .compatible = "bosch,d_can", .data = &c_can_id_table[BOSCH_D_CAN] },
+ { .compatible = "bosch,c_can", .data = &c_can_drvdata },
+ { .compatible = "bosch,d_can", .data = &d_can_drvdata },
+ { .compatible = "ti,dra7-d_can", .data = &dra7_dcan_drvdata },
+ { .compatible = "ti,am3352-d_can", .data = &am3352_dcan_drvdata },
+ { .compatible = "ti,am4372-d_can", .data = &am3352_dcan_drvdata },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, c_can_of_table);
@@ -131,74 +248,67 @@ static int c_can_plat_probe(struct platform_device *pdev)
struct net_device *dev;
struct c_can_priv *priv;
const struct of_device_id *match;
- const struct platform_device_id *id;
- struct resource *mem, *res;
+ struct resource *mem;
int irq;
struct clk *clk;
+ const struct c_can_driver_data *drvdata;
struct device_node *np = pdev->dev.of_node;
- if (pdev->dev.of_node) {
- match = of_match_device(c_can_of_table, &pdev->dev);
- if (!match) {
- dev_err(&pdev->dev, "Failed to find matching dt id\n");
- ret = -EINVAL;
- goto exit;
- }
- id = match->data;
+ match = of_match_device(c_can_of_table, &pdev->dev);
+ if (match) {
+ drvdata = match->data;
+ } else if (pdev->id_entry->driver_data) {
+ drvdata = (struct c_can_driver_data *)
+ pdev->id_entry->driver_data;
} else {
- id = platform_get_device_id(pdev);
+ return -ENODEV;
}
/* get the appropriate clk */
- clk = clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "no clock defined\n");
- ret = -ENODEV;
+ ret = PTR_ERR(clk);
goto exit;
}
/* get the platform data */
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- if (!mem || irq <= 0) {
- ret = -ENODEV;
- goto exit_free_clk;
- }
-
- if (!request_mem_region(mem->start, resource_size(mem),
- KBUILD_MODNAME)) {
- dev_err(&pdev->dev, "resource unavailable\n");
+ if (irq <= 0) {
ret = -ENODEV;
- goto exit_free_clk;
+ goto exit;
}
- addr = ioremap(mem->start, resource_size(mem));
- if (!addr) {
- dev_err(&pdev->dev, "failed to map can port\n");
- ret = -ENOMEM;
- goto exit_release_mem;
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(addr)) {
+ ret = PTR_ERR(addr);
+ goto exit;
}
/* allocate the c_can device */
dev = alloc_c_can_dev();
if (!dev) {
ret = -ENOMEM;
- goto exit_iounmap;
+ goto exit;
}
priv = netdev_priv(dev);
- switch (id->driver_data) {
+ switch (drvdata->id) {
case BOSCH_C_CAN:
priv->regs = reg_map_c_can;
switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
case IORESOURCE_MEM_32BIT:
priv->read_reg = c_can_plat_read_reg_aligned_to_32bit;
priv->write_reg = c_can_plat_write_reg_aligned_to_32bit;
+ priv->read_reg32 = c_can_plat_read_reg32;
+ priv->write_reg32 = c_can_plat_write_reg32;
break;
case IORESOURCE_MEM_16BIT:
default:
priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
+ priv->read_reg32 = c_can_plat_read_reg32;
+ priv->write_reg32 = c_can_plat_write_reg32;
break;
}
break;
@@ -207,72 +317,52 @@ static int c_can_plat_probe(struct platform_device *pdev)
priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
+ priv->read_reg32 = d_can_plat_read_reg32;
+ priv->write_reg32 = d_can_plat_write_reg32;
- /* Try if syscon property exists */
- if (np)
- priv->syscon = syscon_regmap_lookup_by_phandle(np,
- "syscon");
- ret = -EINVAL;
- if (!IS_ERR(priv->syscon)) {
- u32 val;
+ /* Check if we need custom RAMINIT via syscon. Mostly for TI
+ * platforms. Only supported with DT boot.
+ */
+ if (np && of_property_read_bool(np, "syscon-raminit")) {
+ u32 id;
+ struct c_can_raminit *raminit = &priv->raminit_sys;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res) {
- dev_err(&pdev->dev,
- "missing memory resource 1\n");
- goto exit_free_device;
- }
- priv->raminit_idx = res->start;
-
- if (of_property_read_u32(np, "raminit-start-bit",
- &val)) {
- dev_err(&pdev->dev,
- "missing raminit-start-bit property\n");
+ ret = -EINVAL;
+ raminit->syscon = syscon_regmap_lookup_by_phandle(np,
+ "syscon-raminit");
+ if (IS_ERR(raminit->syscon)) {
+ ret = PTR_ERR(raminit->syscon);
goto exit_free_device;
}
- if (val > 31) {
+ if (of_property_read_u32_index(np, "syscon-raminit", 1,
+ &raminit->reg)) {
dev_err(&pdev->dev,
- "invalid raminit-start-bit property\n");
+ "couldn't get the RAMINIT reg. offset!\n");
goto exit_free_device;
}
- priv->raminit_start_bit = val & 0x1f;
-
- if (of_property_read_u32(np, "raminit-done-bit",
- &val)) {
+ if (of_property_read_u32_index(np, "syscon-raminit", 2,
+ &id)) {
dev_err(&pdev->dev,
- "missing raminit-done-bit property\n");
+ "couldn't get the CAN instance ID\n");
goto exit_free_device;
}
- if (val > 31) {
+ if (id >= drvdata->num_can) {
dev_err(&pdev->dev,
- "invalid raminit-done-bit property\n");
+ "Invalid CAN instance ID\n");
goto exit_free_device;
}
- priv->raminit_done_bit = val & 0x1f;
- priv->raminit = c_can_syscon_raminit;
- break; /* skip the non syscon method */
- }
+ raminit->start_bit = drvdata->raminit_start_bits[id];
+ raminit->done_bit = drvdata->raminit_done_bits[id];
+ raminit->needs_pulse = drvdata->raminit_pulse;
- /* non syscon method */
- if (pdev->dev.of_node)
- priv->instance = of_alias_get_id(pdev->dev.of_node, "d_can");
- else
- priv->instance = pdev->id;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res)
- priv->raminit_ctrlreg = devm_ioremap(&pdev->dev,
- res->start,
- resource_size(res));
-
- if (!priv->raminit_ctrlreg || (int)priv->instance < 0)
- dev_info(&pdev->dev, "control memory is not used for raminit\n");
- else
+ priv->raminit = c_can_hw_raminit_syscon;
+ } else {
priv->raminit = c_can_hw_raminit;
+ }
break;
default:
ret = -EINVAL;
@@ -284,7 +374,7 @@ static int c_can_plat_probe(struct platform_device *pdev)
priv->device = &pdev->dev;
priv->can.clock.freq = clk_get_rate(clk);
priv->priv = clk;
- priv->type = id->driver_data;
+ priv->type = drvdata->id;
platform_set_drvdata(pdev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);
@@ -302,12 +392,6 @@ static int c_can_plat_probe(struct platform_device *pdev)
exit_free_device:
free_c_can_dev(dev);
-exit_iounmap:
- iounmap(addr);
-exit_release_mem:
- release_mem_region(mem->start, resource_size(mem));
-exit_free_clk:
- clk_put(clk);
exit:
dev_err(&pdev->dev, "probe failed\n");
@@ -317,18 +401,10 @@ exit:
static int c_can_plat_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
- struct c_can_priv *priv = netdev_priv(dev);
- struct resource *mem;
unregister_c_can_dev(dev);
free_c_can_dev(dev);
- iounmap(priv->base);
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(mem->start, resource_size(mem));
-
- clk_put(priv->priv);
return 0;
}
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index fc59bc6f040b..cc11f7f5e91d 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -384,7 +384,7 @@ void can_free_echo_skb(struct net_device *dev, unsigned int idx)
BUG_ON(idx >= priv->echo_skb_max);
if (priv->echo_skb[idx]) {
- kfree_skb(priv->echo_skb[idx]);
+ dev_kfree_skb_any(priv->echo_skb[idx]);
priv->echo_skb[idx] = NULL;
}
}
diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c
index 7fbe85935f1d..f34f7fa1f901 100644
--- a/drivers/net/can/usb/esd_usb2.c
+++ b/drivers/net/can/usb/esd_usb2.c
@@ -1141,6 +1141,7 @@ static void esd_usb2_disconnect(struct usb_interface *intf)
}
}
unlink_all_urbs(dev);
+ kfree(dev);
}
}
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index 6382b7c416f4..e10f5ed26181 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -1341,6 +1341,42 @@ static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
spin_unlock(&pdata->mac_lock);
}
+static int smsc911x_phy_general_power_up(struct smsc911x_data *pdata)
+{
+ int rc = 0;
+
+ if (!pdata->phy_dev)
+ return rc;
+
+ /* If the internal PHY is in General Power-Down mode, all, except the
+ * management interface, is powered-down and stays in that condition as
+ * long as Phy register bit 0.11 is HIGH.
+ *
+ * In that case, clear the bit 0.11, so the PHY powers up and we can
+ * access to the phy registers.
+ */
+ rc = phy_read(pdata->phy_dev, MII_BMCR);
+ if (rc < 0) {
+ SMSC_WARN(pdata, drv, "Failed reading PHY control reg");
+ return rc;
+ }
+
+ /* If the PHY general power-down bit is not set is not necessary to
+ * disable the general power down-mode.
+ */
+ if (rc & BMCR_PDOWN) {
+ rc = phy_write(pdata->phy_dev, MII_BMCR, rc & ~BMCR_PDOWN);
+ if (rc < 0) {
+ SMSC_WARN(pdata, drv, "Failed writing PHY control reg");
+ return rc;
+ }
+
+ usleep_range(1000, 1500);
+ }
+
+ return 0;
+}
+
static int smsc911x_phy_disable_energy_detect(struct smsc911x_data *pdata)
{
int rc = 0;
@@ -1414,6 +1450,16 @@ static int smsc911x_soft_reset(struct smsc911x_data *pdata)
int ret;
/*
+ * Make sure to power-up the PHY chip before doing a reset, otherwise
+ * the reset fails.
+ */
+ ret = smsc911x_phy_general_power_up(pdata);
+ if (ret) {
+ SMSC_WARN(pdata, drv, "Failed to power-up the PHY chip");
+ return ret;
+ }
+
+ /*
* LAN9210/LAN9211/LAN9220/LAN9221 chips have an internal PHY that
* are initialized in a Energy Detect Power-Down mode that prevents
* the MAC chip to be software reseted. So we have to wakeup the PHY
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index fd411d6e19a2..03ae9def0e0c 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -656,7 +656,7 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_lock_irqsave(&port->vio.lock, flags);
dr = &port->vio.drings[VIO_DRIVER_TX_RING];
- if (unlikely(vnet_tx_dring_avail(dr) < 2)) {
+ if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
if (!netif_queue_stopped(dev)) {
netif_stop_queue(dev);
@@ -704,7 +704,7 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->stats.tx_bytes += skb->len;
dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1);
- if (unlikely(vnet_tx_dring_avail(dr) < 2)) {
+ if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
netif_stop_queue(dev);
if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr))
netif_wake_queue(dev);
diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig
index adfc87cfa5dc..1e275da6b25c 100644
--- a/drivers/net/ethernet/ti/Kconfig
+++ b/drivers/net/ethernet/ti/Kconfig
@@ -5,7 +5,7 @@
config NET_VENDOR_TI
bool "Texas Instruments (TI) devices"
default y
- depends on PCI || EISA || AR7 || (ARM && (ARCH_DAVINCI || ARCH_OMAP3 || SOC_AM33XX || SOC_AM43XX))
+ depends on PCI || EISA || AR7 || ARCH_DAVINCI || ARCH_OMAP2PLUS
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
@@ -32,7 +32,7 @@ config TI_DAVINCI_EMAC
config TI_DAVINCI_MDIO
tristate "TI DaVinci MDIO Support"
- depends on ARM && (ARCH_DAVINCI || ARCH_OMAP3 || SOC_AM33XX || SOC_AM43XX)
+ depends on ARCH_DAVINCI || ARCH_OMAP2PLUS
select PHYLIB
---help---
This driver supports TI's DaVinci MDIO module.
@@ -42,7 +42,7 @@ config TI_DAVINCI_MDIO
config TI_DAVINCI_CPDMA
tristate "TI DaVinci CPDMA Support"
- depends on ARM && (ARCH_DAVINCI || ARCH_OMAP3 || SOC_AM33XX || SOC_AM43XX)
+ depends on ARCH_DAVINCI || ARCH_OMAP2PLUS
---help---
This driver supports TI's DaVinci CPDMA dma engine.
@@ -58,7 +58,7 @@ config TI_CPSW_PHY_SEL
config TI_CPSW
tristate "TI CPSW Switch Support"
- depends on ARM && (ARCH_DAVINCI || SOC_AM33XX || SOC_AM43XX)
+ depends on ARCH_DAVINCI || ARCH_OMAP2PLUS
select TI_DAVINCI_CPDMA
select TI_DAVINCI_MDIO
select TI_CPSW_PHY_SEL
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 9be41d719025..85240ff50515 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -641,12 +641,16 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
if (ndev->flags & IFF_PROMISC) {
/* Enable promiscuous mode */
cpsw_set_promiscious(ndev, true);
+ cpsw_ale_set_allmulti(priv->ale, IFF_ALLMULTI);
return;
} else {
/* Disable promiscuous mode */
cpsw_set_promiscious(ndev, false);
}
+ /* Restore allmulti on vlans if necessary */
+ cpsw_ale_set_allmulti(priv->ale, priv->ndev->flags & IFF_ALLMULTI);
+
/* Clear all mcast from ALE */
cpsw_ale_flush_multicast(priv->ale, ALE_ALL_PORTS << priv->host_port);
@@ -916,7 +920,7 @@ static void _cpsw_adjust_link(struct cpsw_slave *slave,
/* disable forwarding */
cpsw_ale_control_set(priv->ale, slave_port,
ALE_PORT_STATE,
- priv->port_state[slave_port]);
+ ALE_PORT_STATE_DISABLE);
}
if (mac_control != slave->mac_control) {
@@ -1268,6 +1272,7 @@ static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
const int port = priv->host_port;
u32 reg;
int i;
+ int unreg_mcast_mask;
reg = (priv->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN :
CPSW2_PORT_VLAN;
@@ -1277,9 +1282,14 @@ static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
for (i = 0; i < priv->data.slaves; i++)
slave_write(priv->slaves + i, vlan, reg);
+ if (priv->ndev->flags & IFF_ALLMULTI)
+ unreg_mcast_mask = ALE_ALL_PORTS;
+ else
+ unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
+
cpsw_ale_add_vlan(priv->ale, vlan, ALE_ALL_PORTS << port,
ALE_ALL_PORTS << port, ALE_ALL_PORTS << port,
- (ALE_PORT_1 | ALE_PORT_2) << port);
+ unreg_mcast_mask << port);
}
static void cpsw_init_host_port(struct cpsw_priv *priv)
@@ -1833,6 +1843,109 @@ static int cpsw_switch_config_ioctl(struct net_device *ndev,
ret = -EINVAL;
}
break;
+ case CONFIG_SWITCH_GET_PORT_VLAN_CONFIG:
+ {
+ u32 __iomem *port_vlan_reg;
+ u32 port_vlan;
+
+ switch (config.port) {
+ case 0:
+ port_vlan_reg = &priv->host_port_regs->port_vlan;
+ port_vlan = readl(port_vlan_reg);
+ break;
+ case 1:
+ case 2:
+ {
+ int slave = config.port - 1;
+ if (priv->version == CPSW_VERSION_1) {
+ port_vlan = slave_read(priv->slaves + slave,
+ CPSW1_PORT_VLAN);
+ } else {
+ port_vlan = slave_read(priv->slaves + slave,
+ CPSW2_PORT_VLAN);
+ }
+ break;
+ }
+ default:
+ dev_err(priv->dev, "Invalid Arguments\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!ret) {
+ config.vid = port_vlan & 0xfff;
+ config.vlan_cfi = port_vlan & BIT(12) ? true : false;
+ config.prio = (port_vlan >> 13) & 0x7;
+ ret = copy_to_user(ifrq->ifr_data, &config,
+ sizeof(config));
+ }
+ break;
+ }
+ case CONFIG_SWITCH_SET_PORT_VLAN_CONFIG:
+ {
+ void __iomem *port_vlan_reg;
+ u32 port_vlan;
+
+ port_vlan = config.vid;
+ port_vlan |= config.vlan_cfi ? BIT(12) : 0;
+ port_vlan |= (config.prio & 0x7) << 13;
+
+ switch (config.port) {
+ case 0:
+ port_vlan_reg = &priv->host_port_regs->port_vlan;
+ writel(port_vlan, port_vlan_reg);
+ break;
+ case 1:
+ case 2:
+ {
+ int slave = config.port - 1;
+ if (priv->version == CPSW_VERSION_1) {
+ slave_write(priv->slaves + slave, port_vlan,
+ CPSW1_PORT_VLAN);
+ } else {
+ slave_write(priv->slaves + slave, port_vlan,
+ CPSW2_PORT_VLAN);
+ }
+ break;
+ }
+ default:
+ dev_err(priv->dev, "Invalid Arguments\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ break;
+ }
+ case CONFIG_SWITCH_RATELIMIT:
+ {
+ if (config.port > 2) {
+ dev_err(priv->dev, "Invalid Arguments\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = cpsw_ale_control_set(priv->ale, 0, ALE_RATE_LIMIT_TX,
+ !!config.direction);
+ if (ret) {
+ dev_err(priv->dev, "CPSW_ALE control set failed");
+ break;
+ }
+
+ ret = cpsw_ale_control_set(priv->ale, config.port,
+ ALE_PORT_BCAST_LIMIT,
+ config.bcast_rate_limit);
+ if (ret) {
+ dev_err(priv->dev, "CPSW_ALE control set failed");
+ break;
+ }
+
+ ret = cpsw_ale_control_set(priv->ale, config.port,
+ ALE_PORT_MCAST_LIMIT,
+ config.mcast_rate_limit);
+ if (ret)
+ dev_err(priv->dev, "CPSW_ALE control set failed");
+ break;
+ }
default:
ret = -EOPNOTSUPP;
@@ -1933,11 +2046,17 @@ static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv,
unsigned short vid)
{
int ret;
+ int unreg_mcast_mask;
+
+ if (priv->ndev->flags & IFF_ALLMULTI)
+ unreg_mcast_mask = ALE_ALL_PORTS;
+ else
+ unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
ret = cpsw_ale_add_vlan(priv->ale, vid,
ALE_ALL_PORTS << priv->host_port,
0, ALE_ALL_PORTS << priv->host_port,
- (ALE_PORT_1 | ALE_PORT_2) << priv->host_port);
+ unreg_mcast_mask << priv->host_port);
if (ret != 0)
return ret;
@@ -2646,6 +2765,15 @@ clean_ndev_ret:
return ret;
}
+static int cpsw_remove_child_device(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ of_device_unregister(pdev);
+
+ return 0;
+}
+
static int cpsw_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
@@ -2660,6 +2788,7 @@ static int cpsw_remove(struct platform_device *pdev)
cpdma_chan_destroy(priv->rxch);
cpdma_ctlr_destroy(priv->dma);
pm_runtime_disable(&pdev->dev);
+ device_for_each_child(&pdev->dev, NULL, cpsw_remove_child_device);
if (priv->data.dual_emac)
free_netdev(cpsw_get_slave_ndev(priv, 1));
free_netdev(ndev);
diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c
index 0579b2243bb6..097ebe7077ac 100644
--- a/drivers/net/ethernet/ti/cpsw_ale.c
+++ b/drivers/net/ethernet/ti/cpsw_ale.c
@@ -443,6 +443,35 @@ int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
return 0;
}
+void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti)
+{
+ u32 ale_entry[ALE_ENTRY_WORDS];
+ int type, idx;
+ int unreg_mcast = 0;
+
+ /* Only bother doing the work if the setting is actually changing */
+ if (ale->allmulti == allmulti)
+ return;
+
+ /* Remember the new setting to check against next time */
+ ale->allmulti = allmulti;
+
+ for (idx = 0; idx < ale->params.ale_entries; idx++) {
+ cpsw_ale_read(ale, idx, ale_entry);
+ type = cpsw_ale_get_entry_type(ale_entry);
+ if (type != ALE_TYPE_VLAN)
+ continue;
+
+ unreg_mcast = cpsw_ale_get_vlan_unreg_mcast(ale_entry);
+ if (allmulti)
+ unreg_mcast |= 1;
+ else
+ unreg_mcast &= ~1;
+ cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
+ cpsw_ale_write(ale, idx, ale_entry);
+ }
+}
+
struct ale_control_info {
const char *name;
int offset, port_offset;
@@ -756,7 +785,6 @@ int cpsw_ale_destroy(struct cpsw_ale *ale)
{
if (!ale)
return -EINVAL;
- cpsw_ale_stop(ale);
cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
kfree(ale);
return 0;
diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h
index 31cf43cab42e..c0d4127aa549 100644
--- a/drivers/net/ethernet/ti/cpsw_ale.h
+++ b/drivers/net/ethernet/ti/cpsw_ale.h
@@ -27,6 +27,7 @@ struct cpsw_ale {
struct cpsw_ale_params params;
struct timer_list timer;
unsigned long ageout;
+ int allmulti;
};
enum cpsw_ale_control {
@@ -103,6 +104,7 @@ int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
int reg_mcast, int unreg_mcast);
int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port);
+void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti);
int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
int cpsw_ale_control_set(struct cpsw_ale *ale, int port,
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index 539dbdecd310..657b65bf5cac 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -193,12 +193,9 @@ fail:
static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool)
{
- unsigned long flags;
-
if (!pool)
return;
- spin_lock_irqsave(&pool->lock, flags);
WARN_ON(pool->used_desc);
if (pool->cpumap) {
dma_free_coherent(pool->dev, pool->mem_size, pool->cpumap,
@@ -206,7 +203,6 @@ static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool)
} else {
iounmap(pool->iomap);
}
- spin_unlock_irqrestore(&pool->lock, flags);
}
static inline dma_addr_t desc_phys(struct cpdma_desc_pool *pool,
@@ -285,10 +281,8 @@ struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params)
ctlr->params.desc_hw_addr,
ctlr->params.desc_mem_size,
ctlr->params.desc_align);
- if (!ctlr->pool) {
- kfree(ctlr);
+ if (!ctlr->pool)
return NULL;
- }
if (WARN_ON(ctlr->num_chan > CPDMA_MAX_CHANNELS))
ctlr->num_chan = CPDMA_MAX_CHANNELS;
@@ -563,7 +557,6 @@ int cpdma_chan_destroy(struct cpdma_chan *chan)
cpdma_chan_stop(chan);
ctlr->channels[chan->chan_num] = NULL;
spin_unlock_irqrestore(&ctlr->lock, flags);
- kfree(chan);
return 0;
}
EXPORT_SYMBOL_GPL(cpdma_chan_destroy);
diff --git a/drivers/net/ieee802154/fakehard.c b/drivers/net/ieee802154/fakehard.c
index bf0d55e2dd63..6adbef89c4b0 100644
--- a/drivers/net/ieee802154/fakehard.c
+++ b/drivers/net/ieee802154/fakehard.c
@@ -376,17 +376,20 @@ static int ieee802154fake_probe(struct platform_device *pdev)
err = wpan_phy_register(phy);
if (err)
- goto out;
+ goto err_phy_reg;
err = register_netdev(dev);
- if (err < 0)
- goto out;
+ if (err)
+ goto err_netdev_reg;
dev_info(&pdev->dev, "Added ieee802154 HardMAC hardware\n");
return 0;
-out:
- unregister_netdev(dev);
+err_netdev_reg:
+ wpan_phy_unregister(phy);
+err_phy_reg:
+ free_netdev(dev);
+ wpan_phy_free(phy);
return err;
}
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index f30ceb17d5fc..07c942b6ae01 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -66,7 +66,7 @@ static struct cdev macvtap_cdev;
static const struct proto_ops macvtap_socket_ops;
#define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \
- NETIF_F_TSO6)
+ NETIF_F_TSO6 | NETIF_F_UFO)
#define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO)
#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG)
@@ -570,8 +570,6 @@ static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb,
gso_type = SKB_GSO_TCPV6;
break;
case VIRTIO_NET_HDR_GSO_UDP:
- pr_warn_once("macvtap: %s: using disabled UFO feature; please fix this program\n",
- current->comm);
gso_type = SKB_GSO_UDP;
if (skb->protocol == htons(ETH_P_IPV6))
ipv6_proxy_select_ident(skb);
@@ -619,6 +617,8 @@ static void macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
else if (sinfo->gso_type & SKB_GSO_TCPV6)
vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
+ else if (sinfo->gso_type & SKB_GSO_UDP)
+ vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP;
else
BUG();
if (sinfo->gso_type & SKB_GSO_TCP_ECN)
@@ -629,6 +629,8 @@ static void macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
if (skb->ip_summed == CHECKSUM_PARTIAL) {
vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
vnet_hdr->csum_start = skb_checksum_start_offset(skb);
+ if (vlan_tx_tag_present(skb))
+ vnet_hdr->csum_start += VLAN_HLEN;
vnet_hdr->csum_offset = skb->csum_offset;
} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
@@ -953,6 +955,9 @@ static int set_offload(struct macvtap_queue *q, unsigned long arg)
if (arg & TUN_F_TSO6)
feature_mask |= NETIF_F_TSO6;
}
+
+ if (arg & TUN_F_UFO)
+ feature_mask |= NETIF_F_UFO;
}
/* tun/tap driver inverts the usage for TSO offloads, where
@@ -963,7 +968,7 @@ static int set_offload(struct macvtap_queue *q, unsigned long arg)
* When user space turns off TSO, we turn off GSO/LRO so that
* user-space will not receive TSO frames.
*/
- if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6))
+ if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO))
features |= RX_OFFLOADS;
else
features &= ~RX_OFFLOADS;
@@ -1064,7 +1069,7 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
case TUNSETOFFLOAD:
/* let the user check for future flags */
if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
- TUN_F_TSO_ECN))
+ TUN_F_TSO_ECN | TUN_F_UFO))
return -EINVAL;
rtnl_lock();
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index 1aff970be33e..1dc628ffce2b 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -506,7 +506,9 @@ static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
int len = sizeof(struct sockaddr_pppox);
struct sockaddr_pppox sp;
- sp.sa_family = AF_PPPOX;
+ memset(&sp.sa_addr, 0, sizeof(sp.sa_addr));
+
+ sp.sa_family = AF_PPPOX;
sp.sa_protocol = PX_PROTO_PPTP;
sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index d25499c3ad0e..c789ec337c89 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -175,7 +175,7 @@ struct tun_struct {
struct net_device *dev;
netdev_features_t set_features;
#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \
- NETIF_F_TSO6)
+ NETIF_F_TSO6|NETIF_F_UFO)
int vnet_hdr_sz;
int sndbuf;
@@ -1153,20 +1153,10 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
break;
case VIRTIO_NET_HDR_GSO_UDP:
- {
- static bool warned;
-
- if (!warned) {
- warned = true;
- netdev_warn(tun->dev,
- "%s: using disabled UFO feature; please fix this program\n",
- current->comm);
- }
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
if (skb->protocol == htons(ETH_P_IPV6))
ipv6_proxy_select_ident(skb);
break;
- }
default:
tun->dev->stats.rx_frame_errors++;
kfree_skb(skb);
@@ -1236,6 +1226,10 @@ static ssize_t tun_put_user(struct tun_struct *tun,
struct tun_pi pi = { 0, skb->protocol };
ssize_t total = 0;
int vlan_offset = 0, copied;
+ int vlan_hlen = 0;
+
+ if (vlan_tx_tag_present(skb))
+ vlan_hlen = VLAN_HLEN;
if (!(tun->flags & TUN_NO_PI)) {
if ((len -= sizeof(pi)) < 0)
@@ -1266,6 +1260,8 @@ static ssize_t tun_put_user(struct tun_struct *tun,
gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
else if (sinfo->gso_type & SKB_GSO_TCPV6)
gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
+ else if (sinfo->gso_type & SKB_GSO_UDP)
+ gso.gso_type = VIRTIO_NET_HDR_GSO_UDP;
else {
pr_err("unexpected GSO type: "
"0x%x, gso_size %d, hdr_len %d\n",
@@ -1285,7 +1281,8 @@ static ssize_t tun_put_user(struct tun_struct *tun,
if (skb->ip_summed == CHECKSUM_PARTIAL) {
gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
- gso.csum_start = skb_checksum_start_offset(skb);
+ gso.csum_start = skb_checksum_start_offset(skb) +
+ vlan_hlen;
gso.csum_offset = skb->csum_offset;
} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
gso.flags = VIRTIO_NET_HDR_F_DATA_VALID;
@@ -1298,10 +1295,9 @@ static ssize_t tun_put_user(struct tun_struct *tun,
}
copied = total;
- total += skb->len;
- if (!vlan_tx_tag_present(skb)) {
- len = min_t(int, skb->len, len);
- } else {
+ len = min_t(int, skb->len + vlan_hlen, len);
+ total += skb->len + vlan_hlen;
+ if (vlan_hlen) {
int copy, ret;
struct {
__be16 h_vlan_proto;
@@ -1312,8 +1308,6 @@ static ssize_t tun_put_user(struct tun_struct *tun,
veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb));
vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
- len = min_t(int, skb->len + VLAN_HLEN, len);
- total += VLAN_HLEN;
copy = min_t(int, vlan_offset, len);
ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy);
@@ -1795,6 +1789,11 @@ static int set_offload(struct tun_struct *tun, unsigned long arg)
features |= NETIF_F_TSO6;
arg &= ~(TUN_F_TSO4|TUN_F_TSO6);
}
+
+ if (arg & TUN_F_UFO) {
+ features |= NETIF_F_UFO;
+ arg &= ~TUN_F_UFO;
+ }
}
/* This gives the user a way to test for new features in future by
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index d510f1d41bae..db21af8de9f6 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -769,6 +769,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
{QMI_FIXED_INTF(0x413c, 0x81a8, 8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
{QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
+ {QMI_FIXED_INTF(0x03f0, 0x581d, 4)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
/* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 07a3255fd3cc..841b60831df1 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -496,17 +496,8 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
break;
case VIRTIO_NET_HDR_GSO_UDP:
- {
- static bool warned;
-
- if (!warned) {
- warned = true;
- netdev_warn(dev,
- "host using disabled UFO feature; please fix it\n");
- }
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
break;
- }
case VIRTIO_NET_HDR_GSO_TCPV6:
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
break;
@@ -845,6 +836,8 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
+ else if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+ hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_UDP;
else
BUG();
if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_ECN)
@@ -1664,7 +1657,7 @@ static int virtnet_probe(struct virtio_device *vdev)
dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
if (virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) {
- dev->hw_features |= NETIF_F_TSO
+ dev->hw_features |= NETIF_F_TSO | NETIF_F_UFO
| NETIF_F_TSO_ECN | NETIF_F_TSO6;
}
/* Individual feature bits: what can host handle? */
@@ -1674,9 +1667,11 @@ static int virtnet_probe(struct virtio_device *vdev)
dev->hw_features |= NETIF_F_TSO6;
if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN))
dev->hw_features |= NETIF_F_TSO_ECN;
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO))
+ dev->hw_features |= NETIF_F_UFO;
if (gso)
- dev->features |= dev->hw_features & NETIF_F_ALL_TSO;
+ dev->features |= dev->hw_features & (NETIF_F_ALL_TSO|NETIF_F_UFO);
/* (!csum && gso) case will be fixed by register_netdev() */
}
if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_CSUM))
@@ -1716,7 +1711,8 @@ static int virtnet_probe(struct virtio_device *vdev)
/* If we can receive ANY GSO packets, we must allocate large ones. */
if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) ||
virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) ||
- virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
+ virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN) ||
+ virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_UFO))
vi->big_packets = true;
if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
@@ -1907,9 +1903,9 @@ static struct virtio_device_id id_table[] = {
static unsigned int features[] = {
VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM,
VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC,
- VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6,
+ VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6,
VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
- VIRTIO_NET_F_GUEST_ECN,
+ VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO,
VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ,
VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN,
VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ,
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 0704a0402897..5441b49ef89d 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -279,13 +279,15 @@ static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb)
return list_first_entry(&fdb->remotes, struct vxlan_rdst, list);
}
-/* Find VXLAN socket based on network namespace and UDP port */
-static struct vxlan_sock *vxlan_find_sock(struct net *net, __be16 port)
+/* Find VXLAN socket based on network namespace, address family and UDP port */
+static struct vxlan_sock *vxlan_find_sock(struct net *net,
+ sa_family_t family, __be16 port)
{
struct vxlan_sock *vs;
hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
- if (inet_sk(vs->sock->sk)->inet_sport == port)
+ if (inet_sk(vs->sock->sk)->inet_sport == port &&
+ inet_sk(vs->sock->sk)->sk.sk_family == family)
return vs;
}
return NULL;
@@ -304,11 +306,12 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, u32 id)
}
/* Look up VNI in a per net namespace table */
-static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, __be16 port)
+static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id,
+ sa_family_t family, __be16 port)
{
struct vxlan_sock *vs;
- vs = vxlan_find_sock(net, port);
+ vs = vxlan_find_sock(net, family, port);
if (!vs)
return NULL;
@@ -1872,7 +1875,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
struct vxlan_dev *dst_vxlan;
ip_rt_put(rt);
- dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
+ dst_vxlan = vxlan_find_vni(dev_net(dev), vni,
+ dst->sa.sa_family, dst_port);
if (!dst_vxlan)
goto tx_error;
vxlan_encap_bypass(skb, vxlan, dst_vxlan);
@@ -1925,7 +1929,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
struct vxlan_dev *dst_vxlan;
dst_release(ndst);
- dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
+ dst_vxlan = vxlan_find_vni(dev_net(dev), vni,
+ dst->sa.sa_family, dst_port);
if (!dst_vxlan)
goto tx_error;
vxlan_encap_bypass(skb, vxlan, dst_vxlan);
@@ -2083,6 +2088,7 @@ static int vxlan_init(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
+ bool ipv6 = vxlan->flags & VXLAN_F_IPV6;
struct vxlan_sock *vs;
int i;
@@ -2098,7 +2104,8 @@ static int vxlan_init(struct net_device *dev)
spin_lock(&vn->sock_lock);
- vs = vxlan_find_sock(dev_net(dev), vxlan->dst_port);
+ vs = vxlan_find_sock(dev_net(dev), ipv6 ? AF_INET6 : AF_INET,
+ vxlan->dst_port);
if (vs) {
/* If we have a socket with same port already, reuse it */
atomic_inc(&vs->refcnt);
@@ -2566,7 +2573,7 @@ struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
return vs;
spin_lock(&vn->sock_lock);
- vs = vxlan_find_sock(net, port);
+ vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port);
if (vs) {
if (vs->rcv == rcv)
atomic_inc(&vs->refcnt);
@@ -2712,7 +2719,8 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
if (data[IFLA_VXLAN_PORT])
vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);
- if (vxlan_find_vni(net, vni, vxlan->dst_port)) {
+ if (vxlan_find_vni(net, vni, use_ipv6 ? AF_INET6 : AF_INET,
+ vxlan->dst_port)) {
pr_info("duplicate VNI %u\n", vni);
return -EEXIST;
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 09facba1dc6d..390c2de5a73e 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -647,6 +647,19 @@ static void ar9003_hw_override_ini(struct ath_hw *ah)
ah->enabled_cals |= TX_CL_CAL;
else
ah->enabled_cals &= ~TX_CL_CAL;
+
+ if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+ if (ah->is_clk_25mhz) {
+ REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
+ REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
+ REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
+ } else {
+ REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
+ REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
+ REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
+ }
+ udelay(100);
+ }
}
static void ar9003_hw_prog_ini(struct ath_hw *ah,
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 9078a6c5a74e..dcc14940c9df 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -858,19 +858,6 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
udelay(RTC_PLL_SETTLE_DELAY);
REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
-
- if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) {
- if (ah->is_clk_25mhz) {
- REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
- REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
- REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
- } else {
- REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
- REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
- REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
- }
- udelay(100);
- }
}
static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 1f065cf4a4ba..d090ed79ada0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -514,6 +514,7 @@ enum iwl_trans_state {
* Set during transport allocation.
* @hw_id_str: a string with info about HW ID. Set during transport allocation.
* @pm_support: set to true in start_hw if link pm is supported
+ * @ltr_enabled: set to true if the LTR is enabled
* @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
* The user should use iwl_trans_{alloc,free}_tx_cmd.
* @dev_cmd_headroom: room needed for the transport's private use before the
@@ -539,6 +540,7 @@ struct iwl_trans {
u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size;
bool pm_support;
+ bool ltr_enabled;
/* The following fields are internal only */
struct kmem_cache *dev_cmd_pool;
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index 884c08725308..fa66471283d9 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -66,13 +66,46 @@
/* Power Management Commands, Responses, Notifications */
+/**
+ * enum iwl_ltr_config_flags - masks for LTR config command flags
+ * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status
+ * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow
+ * memory access
+ * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR
+ * reg change
+ * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from
+ * D0 to D3
+ * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register
+ * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register
+ * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD
+ */
+enum iwl_ltr_config_flags {
+ LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0),
+ LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1),
+ LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2),
+ LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3),
+ LTR_CFG_FLAG_SW_SET_SHORT = BIT(4),
+ LTR_CFG_FLAG_SW_SET_LONG = BIT(5),
+ LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6),
+};
+
+/**
+ * struct iwl_ltr_config_cmd - configures the LTR
+ * @flags: See %enum iwl_ltr_config_flags
+ */
+struct iwl_ltr_config_cmd {
+ __le32 flags;
+ __le32 static_long;
+ __le32 static_short;
+} __packed;
+
/* Radio LP RX Energy Threshold measured in dBm */
#define POWER_LPRX_RSSI_THRESHOLD 75
#define POWER_LPRX_RSSI_THRESHOLD_MAX 94
#define POWER_LPRX_RSSI_THRESHOLD_MIN 30
/**
- * enum iwl_scan_flags - masks for power table command flags
+ * enum iwl_power_flags - masks for power table command flags
* @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
* receiver and transmitter. '0' - does not allow.
* @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management,
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index d0a04779d734..d8948aa9c2d2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -142,6 +142,7 @@ enum {
/* Power - legacy power table command */
POWER_TABLE_CMD = 0x77,
PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78,
+ LTR_CONFIG = 0xee,
/* Thermal Throttling*/
REPLY_THERMAL_MNG_BACKOFF = 0x7e,
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index c03d39541f9e..2ef344fc0acb 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -439,6 +439,15 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
goto error;
}
+ if (mvm->trans->ltr_enabled) {
+ struct iwl_ltr_config_cmd cmd = {
+ .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
+ };
+
+ WARN_ON(iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
+ sizeof(cmd), &cmd));
+ }
+
ret = iwl_mvm_power_update_device_mode(mvm);
if (ret)
goto error;
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index a3d43de342d7..dbff7f0bc6a8 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -313,6 +313,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(REPLY_BEACON_FILTERING_CMD),
CMD(REPLY_THERMAL_MNG_BACKOFF),
CMD(MAC_PM_POWER_TABLE),
+ CMD(LTR_CONFIG),
CMD(BT_COEX_CI),
CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
};
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 16be0c07c64a..fb62927ca44d 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -94,6 +94,7 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u16 lctl;
+ u16 cap;
/*
* HW bug W/A for instability in PCIe bus L0S->L1 transition.
@@ -104,16 +105,17 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans)
* power savings, even without L1.
*/
pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl);
- if (lctl & PCI_EXP_LNKCTL_ASPM_L1) {
- /* L1-ASPM enabled; disable(!) L0S */
+ if (lctl & PCI_EXP_LNKCTL_ASPM_L1)
iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
- dev_info(trans->dev, "L1 Enabled; Disabling L0S\n");
- } else {
- /* L1-ASPM disabled; enable(!) L0S */
+ else
iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
- dev_info(trans->dev, "L1 Disabled; Enabling L0S\n");
- }
trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
+
+ pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap);
+ trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN;
+ dev_info(trans->dev, "L1 %sabled - LTR %sabled\n",
+ (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis",
+ trans->ltr_enabled ? "En" : "Dis");
}
/*
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 98718e4adb9f..5e836b10f0ce 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1976,7 +1976,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
if (err != 0) {
printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
err);
- goto failed_hw;
+ goto failed_bind;
}
skb_queue_head_init(&data->pending);
@@ -2159,6 +2159,8 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
return idx;
failed_hw:
+ device_release_driver(data->dev);
+failed_bind:
device_unregister(data->dev);
failed_drvdata:
ieee80211_free_hw(hw);
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 5642ccceca7c..22d49d575d3f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -158,55 +158,29 @@ void rt2x00queue_align_frame(struct sk_buff *skb)
skb_trim(skb, frame_length);
}
-void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length)
+/*
+ * H/W needs L2 padding between the header and the paylod if header size
+ * is not 4 bytes aligned.
+ */
+void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len)
{
- unsigned int payload_length = skb->len - header_length;
- unsigned int header_align = ALIGN_SIZE(skb, 0);
- unsigned int payload_align = ALIGN_SIZE(skb, header_length);
- unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0;
+ unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
- /*
- * Adjust the header alignment if the payload needs to be moved more
- * than the header.
- */
- if (payload_align > header_align)
- header_align += 4;
-
- /* There is nothing to do if no alignment is needed */
- if (!header_align)
+ if (!l2pad)
return;
- /* Reserve the amount of space needed in front of the frame */
- skb_push(skb, header_align);
-
- /*
- * Move the header.
- */
- memmove(skb->data, skb->data + header_align, header_length);
-
- /* Move the payload, if present and if required */
- if (payload_length && payload_align)
- memmove(skb->data + header_length + l2pad,
- skb->data + header_length + l2pad + payload_align,
- payload_length);
-
- /* Trim the skb to the correct size */
- skb_trim(skb, header_length + l2pad + payload_length);
+ skb_push(skb, l2pad);
+ memmove(skb->data, skb->data + l2pad, hdr_len);
}
-void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
+void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len)
{
- /*
- * L2 padding is only present if the skb contains more than just the
- * IEEE 802.11 header.
- */
- unsigned int l2pad = (skb->len > header_length) ?
- L2PAD_SIZE(header_length) : 0;
+ unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
if (!l2pad)
return;
- memmove(skb->data + l2pad, skb->data, header_length);
+ memmove(skb->data + l2pad, skb->data, hdr_len);
skb_pull(skb, l2pad);
}
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 1a54f1ffaadb..005c65715846 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -401,6 +401,21 @@ static struct of_bus *of_match_bus(struct device_node *np)
return NULL;
}
+static int of_empty_ranges_quirk(void)
+{
+ if (IS_ENABLED(CONFIG_PPC)) {
+ /* To save cycles, we cache the result */
+ static int quirk_state = -1;
+
+ if (quirk_state < 0)
+ quirk_state =
+ of_machine_is_compatible("Power Macintosh") ||
+ of_machine_is_compatible("MacRISC");
+ return quirk_state;
+ }
+ return false;
+}
+
static int of_translate_one(struct device_node *parent, struct of_bus *bus,
struct of_bus *pbus, __be32 *addr,
int na, int ns, int pna, const char *rprop)
@@ -426,12 +441,10 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
* This code is only enabled on powerpc. --gcl
*/
ranges = of_get_property(parent, rprop, &rlen);
-#if !defined(CONFIG_PPC)
- if (ranges == NULL) {
+ if (ranges == NULL && !of_empty_ranges_quirk()) {
pr_err("OF: no ranges; cannot translate\n");
return 1;
}
-#endif /* !defined(CONFIG_PPC) */
if (ranges == NULL || rlen == 0) {
offset = of_read_number(addr, na);
memset(addr, 0, pna * 4);
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index fb02fc2fb034..ced17f2ac782 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -599,6 +599,20 @@ error_attrs:
return ret;
}
+static int msi_verify_entries(struct pci_dev *dev)
+{
+ struct msi_desc *entry;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (!dev->no_64bit_msi || !entry->msg.address_hi)
+ continue;
+ dev_err(&dev->dev, "Device has broken 64-bit MSI but arch"
+ " tried to assign one above 4G\n");
+ return -EIO;
+ }
+ return 0;
+}
+
/**
* msi_capability_init - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
@@ -652,6 +666,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
return ret;
}
+ ret = msi_verify_entries(dev);
+ if (ret) {
+ msi_mask_irq(entry, mask, ~mask);
+ free_msi_irqs(dev);
+ return ret;
+ }
+
ret = populate_msi_sysfs(dev);
if (ret) {
msi_mask_irq(entry, mask, ~mask);
@@ -767,6 +788,11 @@ static int msix_capability_init(struct pci_dev *dev,
if (ret)
goto out_avail;
+ /* Check if all MSI entries honor device restrictions */
+ ret = msi_verify_entries(dev);
+ if (ret)
+ goto out_free;
+
/*
* Some devices require MSI-X to be enabled before we can touch the
* MSI-X registers. We need to mask all the vectors to prevent
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 6e34498ec9f0..34dff3a09b98 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -395,15 +395,16 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
{
struct pci_dev *dev = child->self;
u16 mem_base_lo, mem_limit_lo;
- unsigned long base, limit;
+ u64 base64, limit64;
+ dma_addr_t base, limit;
struct pci_bus_region region;
struct resource *res;
res = child->resource[2];
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
- base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
- limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
+ base64 = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
+ limit64 = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
u32 mem_base_hi, mem_limit_hi;
@@ -417,18 +418,20 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
* this, just assume they are not being used.
*/
if (mem_base_hi <= mem_limit_hi) {
-#if BITS_PER_LONG == 64
- base |= ((unsigned long) mem_base_hi) << 32;
- limit |= ((unsigned long) mem_limit_hi) << 32;
-#else
- if (mem_base_hi || mem_limit_hi) {
- dev_err(&dev->dev, "can't handle 64-bit "
- "address space for bridge\n");
- return;
- }
-#endif
+ base64 |= (u64) mem_base_hi << 32;
+ limit64 |= (u64) mem_limit_hi << 32;
}
}
+
+ base = (dma_addr_t) base64;
+ limit = (dma_addr_t) limit64;
+
+ if (base != base64) {
+ dev_err(&dev->dev, "can't handle bridge window above 4GB (bus address %#010llx)\n",
+ (unsigned long long) base64);
+ return;
+ }
+
if (base <= limit) {
res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
IORESOURCE_MEM | IORESOURCE_PREFETCH;
diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c
index 55acbb84072b..fc45cc952056 100644
--- a/drivers/phy/phy-omap-control.c
+++ b/drivers/phy/phy-omap-control.c
@@ -29,10 +29,9 @@
/**
* omap_control_pcie_pcs - set the PCS delay count
* @dev: the control module device
- * @id: index of the pcie PHY (should be 1 or 2)
* @delay: 8 bit delay value
*/
-void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
+void omap_control_pcie_pcs(struct device *dev, u8 delay)
{
u32 val;
struct omap_control_phy *control_phy;
@@ -55,8 +54,8 @@ void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
val = readl(control_phy->pcie_pcs);
val &= ~(OMAP_CTRL_PCIE_PCS_MASK <<
- (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT));
- val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
+ OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
+ val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
writel(val, control_phy->pcie_pcs);
}
EXPORT_SYMBOL_GPL(omap_control_pcie_pcs);
diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
index bd3bb1c02d5a..5786792b9c8c 100644
--- a/drivers/phy/phy-omap-usb2.c
+++ b/drivers/phy/phy-omap-usb2.c
@@ -414,6 +414,9 @@ static int omap_usb2_suspend(struct device *dev)
if (!pm_runtime_suspended(dev))
omap_usb2_disable_clocks(phy);
+ if (!device_may_wakeup(dev))
+ pinctrl_pm_select_sleep_state(dev);
+
return 0;
}
@@ -423,6 +426,9 @@ static int omap_usb2_resume(struct device *dev)
struct omap_usb *phy = platform_get_drvdata(pdev);
int ret;
+ if (!device_may_wakeup(dev))
+ pinctrl_pm_select_default_state(dev);
+
if (!pm_runtime_suspended(dev)) {
ret = omap_usb2_enable_clocks(phy);
if (ret)
diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c
index 93bcd67f1b22..84df7b690c1d 100644
--- a/drivers/phy/phy-ti-pipe3.c
+++ b/drivers/phy/phy-ti-pipe3.c
@@ -82,7 +82,6 @@ struct ti_pipe3 {
struct clk *refclk;
struct clk *div_clk;
struct pipe3_dpll_map *dpll_map;
- u8 id;
};
static struct pipe3_dpll_map dpll_map_usb[] = {
@@ -217,8 +216,13 @@ static int ti_pipe3_init(struct phy *x)
u32 val;
int ret = 0;
+ /*
+ * Set pcie_pcs register to 0x96 for proper functioning of phy
+ * as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table
+ * 18-1804.
+ */
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
- omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1);
+ omap_control_pcie_pcs(phy->control_dev, 0x96);
return 0;
}
@@ -348,8 +352,6 @@ static int ti_pipe3_probe(struct platform_device *pdev)
}
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
- if (of_property_read_u8(node, "id", &phy->id) < 0)
- phy->id = 1;
clk = devm_clk_get(phy->dev, "dpll_ref");
if (IS_ERR(clk)) {
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 390e8e33d5e3..25721bf20092 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -163,18 +163,24 @@ static void dell_wmi_notify(u32 value, void *context)
const struct key_entry *key;
int reported_key;
u16 *buffer_entry = (u16 *)obj->buffer.pointer;
+ int buffer_size = obj->buffer.length/2;
- if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
+ if (buffer_size >= 2 && dell_new_hk_type && buffer_entry[1] != 0x10) {
pr_info("Received unknown WMI event (0x%x)\n",
buffer_entry[1]);
kfree(obj);
return;
}
- if (dell_new_hk_type || buffer_entry[1] == 0x0)
+ if (buffer_size >= 3 && (dell_new_hk_type || buffer_entry[1] == 0x0))
reported_key = (int)buffer_entry[2];
- else
+ else if (buffer_size >= 2)
reported_key = (int)buffer_entry[1] & 0xffff;
+ else {
+ pr_info("Received unknown WMI event\n");
+ kfree(obj);
+ return;
+ }
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
reported_key);
diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c
index e384844a1ae1..1f49986fc605 100644
--- a/drivers/power/bq2415x_charger.c
+++ b/drivers/power/bq2415x_charger.c
@@ -1579,8 +1579,15 @@ static int bq2415x_probe(struct i2c_client *client,
if (np) {
bq->notify_psy = power_supply_get_by_phandle(np, "ti,usb-charger-detection");
- if (!bq->notify_psy)
- return -EPROBE_DEFER;
+ if (IS_ERR(bq->notify_psy)) {
+ dev_info(&client->dev,
+ "no 'ti,usb-charger-detection' property (err=%ld)\n",
+ PTR_ERR(bq->notify_psy));
+ bq->notify_psy = NULL;
+ } else if (!bq->notify_psy) {
+ ret = -EPROBE_DEFER;
+ goto error_2;
+ }
}
else if (pdata->notify_device)
bq->notify_psy = power_supply_get_by_name(pdata->notify_device);
@@ -1602,27 +1609,27 @@ static int bq2415x_probe(struct i2c_client *client,
ret = of_property_read_u32(np, "ti,current-limit",
&bq->init_data.current_limit);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,weak-battery-voltage",
&bq->init_data.weak_battery_voltage);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,battery-regulation-voltage",
&bq->init_data.battery_regulation_voltage);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,charge-current",
&bq->init_data.charge_current);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,termination-current",
&bq->init_data.termination_current);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,resistor-sense",
&bq->init_data.resistor_sense);
if (ret)
- return ret;
+ goto error_2;
} else {
memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
}
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
index ef1f4c928431..03bfac3655ef 100644
--- a/drivers/power/charger-manager.c
+++ b/drivers/power/charger-manager.c
@@ -97,6 +97,7 @@ static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
static bool is_batt_present(struct charger_manager *cm)
{
union power_supply_propval val;
+ struct power_supply *psy;
bool present = false;
int i, ret;
@@ -107,16 +108,27 @@ static bool is_batt_present(struct charger_manager *cm)
case CM_NO_BATTERY:
break;
case CM_FUEL_GAUGE:
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!psy)
+ break;
+
+ ret = psy->get_property(psy,
POWER_SUPPLY_PROP_PRESENT, &val);
if (ret == 0 && val.intval)
present = true;
break;
case CM_CHARGER_STAT:
- for (i = 0; cm->charger_stat[i]; i++) {
- ret = cm->charger_stat[i]->get_property(
- cm->charger_stat[i],
- POWER_SUPPLY_PROP_PRESENT, &val);
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+ psy = power_supply_get_by_name(
+ cm->desc->psy_charger_stat[i]);
+ if (!psy) {
+ dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+ cm->desc->psy_charger_stat[i]);
+ continue;
+ }
+
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_PRESENT,
+ &val);
if (ret == 0 && val.intval) {
present = true;
break;
@@ -139,14 +151,20 @@ static bool is_batt_present(struct charger_manager *cm)
static bool is_ext_pwr_online(struct charger_manager *cm)
{
union power_supply_propval val;
+ struct power_supply *psy;
bool online = false;
int i, ret;
/* If at least one of them has one, it's yes. */
- for (i = 0; cm->charger_stat[i]; i++) {
- ret = cm->charger_stat[i]->get_property(
- cm->charger_stat[i],
- POWER_SUPPLY_PROP_ONLINE, &val);
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+ psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
+ if (!psy) {
+ dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+ cm->desc->psy_charger_stat[i]);
+ continue;
+ }
+
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
if (ret == 0 && val.intval) {
online = true;
break;
@@ -167,12 +185,14 @@ static bool is_ext_pwr_online(struct charger_manager *cm)
static int get_batt_uV(struct charger_manager *cm, int *uV)
{
union power_supply_propval val;
+ struct power_supply *fuel_gauge;
int ret;
- if (!cm->fuel_gauge)
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge)
return -ENODEV;
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
if (ret)
return ret;
@@ -189,6 +209,7 @@ static bool is_charging(struct charger_manager *cm)
{
int i, ret;
bool charging = false;
+ struct power_supply *psy;
union power_supply_propval val;
/* If there is no battery, it cannot be charged */
@@ -196,17 +217,22 @@ static bool is_charging(struct charger_manager *cm)
return false;
/* If at least one of the charger is charging, return yes */
- for (i = 0; cm->charger_stat[i]; i++) {
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
/* 1. The charger sholuld not be DISABLED */
if (cm->emergency_stop)
continue;
if (!cm->charger_enabled)
continue;
+ psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
+ if (!psy) {
+ dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+ cm->desc->psy_charger_stat[i]);
+ continue;
+ }
+
/* 2. The charger should be online (ext-power) */
- ret = cm->charger_stat[i]->get_property(
- cm->charger_stat[i],
- POWER_SUPPLY_PROP_ONLINE, &val);
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
if (ret) {
dev_warn(cm->dev, "Cannot read ONLINE value from %s\n",
cm->desc->psy_charger_stat[i]);
@@ -219,9 +245,7 @@ static bool is_charging(struct charger_manager *cm)
* 3. The charger should not be FULL, DISCHARGING,
* or NOT_CHARGING.
*/
- ret = cm->charger_stat[i]->get_property(
- cm->charger_stat[i],
- POWER_SUPPLY_PROP_STATUS, &val);
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
if (ret) {
dev_warn(cm->dev, "Cannot read STATUS value from %s\n",
cm->desc->psy_charger_stat[i]);
@@ -248,6 +272,7 @@ static bool is_full_charged(struct charger_manager *cm)
{
struct charger_desc *desc = cm->desc;
union power_supply_propval val;
+ struct power_supply *fuel_gauge;
int ret = 0;
int uV;
@@ -255,11 +280,15 @@ static bool is_full_charged(struct charger_manager *cm)
if (!is_batt_present(cm))
return false;
- if (cm->fuel_gauge && desc->fullbatt_full_capacity > 0) {
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge)
+ return false;
+
+ if (desc->fullbatt_full_capacity > 0) {
val.intval = 0;
/* Not full if capacity of fuel gauge isn't full */
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_FULL, &val);
if (!ret && val.intval > desc->fullbatt_full_capacity)
return true;
@@ -273,10 +302,10 @@ static bool is_full_charged(struct charger_manager *cm)
}
/* Full, if the capacity is more than fullbatt_soc */
- if (cm->fuel_gauge && desc->fullbatt_soc > 0) {
+ if (desc->fullbatt_soc > 0) {
val.intval = 0;
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CAPACITY, &val);
if (!ret && val.intval >= desc->fullbatt_soc)
return true;
@@ -551,6 +580,20 @@ static int check_charging_duration(struct charger_manager *cm)
return ret;
}
+static int cm_get_battery_temperature_by_psy(struct charger_manager *cm,
+ int *temp)
+{
+ struct power_supply *fuel_gauge;
+
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge)
+ return -ENODEV;
+
+ return fuel_gauge->get_property(fuel_gauge,
+ POWER_SUPPLY_PROP_TEMP,
+ (union power_supply_propval *)temp);
+}
+
static int cm_get_battery_temperature(struct charger_manager *cm,
int *temp)
{
@@ -560,15 +603,18 @@ static int cm_get_battery_temperature(struct charger_manager *cm,
return -ENODEV;
#ifdef CONFIG_THERMAL
- ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
- if (!ret)
- /* Calibrate temperature unit */
- *temp /= 100;
-#else
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
- POWER_SUPPLY_PROP_TEMP,
- (union power_supply_propval *)temp);
+ if (cm->tzd_batt) {
+ ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
+ if (!ret)
+ /* Calibrate temperature unit */
+ *temp /= 100;
+ } else
#endif
+ {
+ /* if-else continued from CONFIG_THERMAL */
+ ret = cm_get_battery_temperature_by_psy(cm, temp);
+ }
+
return ret;
}
@@ -827,6 +873,7 @@ static int charger_get_property(struct power_supply *psy,
struct charger_manager *cm = container_of(psy,
struct charger_manager, charger_psy);
struct charger_desc *desc = cm->desc;
+ struct power_supply *fuel_gauge;
int ret = 0;
int uV;
@@ -857,14 +904,20 @@ static int charger_get_property(struct power_supply *psy,
ret = get_batt_uV(cm, &val->intval);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge) {
+ ret = -ENODEV;
+ break;
+ }
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CURRENT_NOW, val);
break;
case POWER_SUPPLY_PROP_TEMP:
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
return cm_get_battery_temperature(cm, &val->intval);
case POWER_SUPPLY_PROP_CAPACITY:
- if (!cm->fuel_gauge) {
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge) {
ret = -ENODEV;
break;
}
@@ -875,7 +928,7 @@ static int charger_get_property(struct power_supply *psy,
break;
}
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CAPACITY, val);
if (ret)
break;
@@ -924,7 +977,14 @@ static int charger_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
if (is_charging(cm)) {
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ fuel_gauge = power_supply_get_by_name(
+ cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge) {
+ ret = -ENODEV;
+ break;
+ }
+
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_NOW,
val);
if (ret) {
@@ -1485,14 +1545,15 @@ err:
return ret;
}
-static int cm_init_thermal_data(struct charger_manager *cm)
+static int cm_init_thermal_data(struct charger_manager *cm,
+ struct power_supply *fuel_gauge)
{
struct charger_desc *desc = cm->desc;
union power_supply_propval val;
int ret;
/* Verify whether fuel gauge provides battery temperature */
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_TEMP, &val);
if (!ret) {
@@ -1502,8 +1563,6 @@ static int cm_init_thermal_data(struct charger_manager *cm)
cm->desc->measure_battery_temp = true;
}
#ifdef CONFIG_THERMAL
- cm->tzd_batt = cm->fuel_gauge->tzd;
-
if (ret && desc->thermal_zone) {
cm->tzd_batt =
thermal_zone_get_zone_by_name(desc->thermal_zone);
@@ -1666,6 +1725,7 @@ static int charger_manager_probe(struct platform_device *pdev)
int ret = 0, i = 0;
int j = 0;
union power_supply_propval val;
+ struct power_supply *fuel_gauge;
if (g_desc && !rtc_dev && g_desc->rtc_name) {
rtc_dev = rtc_class_open(g_desc->rtc_name);
@@ -1729,23 +1789,20 @@ static int charger_manager_probe(struct platform_device *pdev)
while (desc->psy_charger_stat[i])
i++;
- cm->charger_stat = devm_kzalloc(&pdev->dev,
- sizeof(struct power_supply *) * i, GFP_KERNEL);
- if (!cm->charger_stat)
- return -ENOMEM;
-
+ /* Check if charger's supplies are present at probe */
for (i = 0; desc->psy_charger_stat[i]; i++) {
- cm->charger_stat[i] = power_supply_get_by_name(
- desc->psy_charger_stat[i]);
- if (!cm->charger_stat[i]) {
+ struct power_supply *psy;
+
+ psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
+ if (!psy) {
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
desc->psy_charger_stat[i]);
return -ENODEV;
}
}
- cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
- if (!cm->fuel_gauge) {
+ fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
+ if (!fuel_gauge) {
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
desc->psy_fuel_gauge);
return -ENODEV;
@@ -1788,13 +1845,13 @@ static int charger_manager_probe(struct platform_device *pdev)
cm->charger_psy.num_properties = psy_default.num_properties;
/* Find which optional psy-properties are available */
- if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
+ if (!fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_CHARGE_NOW;
cm->charger_psy.num_properties++;
}
- if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
+ if (!fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CURRENT_NOW,
&val)) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
@@ -1802,7 +1859,7 @@ static int charger_manager_probe(struct platform_device *pdev)
cm->charger_psy.num_properties++;
}
- ret = cm_init_thermal_data(cm);
+ ret = cm_init_thermal_data(cm, fuel_gauge);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize thermal data\n");
cm->desc->measure_battery_temp = false;
@@ -2059,8 +2116,8 @@ static bool find_power_supply(struct charger_manager *cm,
int i;
bool found = false;
- for (i = 0; cm->charger_stat[i]; i++) {
- if (psy == cm->charger_stat[i]) {
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+ if (!strcmp(psy->name, cm->desc->psy_charger_stat[i])) {
found = true;
break;
}
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index bed0bf1e8478..7151316a68ea 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -94,7 +94,7 @@ config DA8XX_REMOTEPROC
config PRUSS_REMOTEPROC
tristate "TI PRUSS remoteproc support"
- depends on HAS_DMA && (SOC_AM33XX || SOC_AM43XX)
+ depends on HAS_DMA && (SOC_AM33XX || SOC_AM43XX || SOC_DRA7XX)
select REMOTEPROC
select MAILBOX
select OMAP2PLUS_MBOX
diff --git a/drivers/remoteproc/pruss_remoteproc.c b/drivers/remoteproc/pruss_remoteproc.c
index 772cf10259cf..abfe217cc16a 100644
--- a/drivers/remoteproc/pruss_remoteproc.c
+++ b/drivers/remoteproc/pruss_remoteproc.c
@@ -140,11 +140,23 @@ struct pru_rproc_platform_data {
* @num_irqs: number of interrupts to MPU
* @host_events: bit mask of PRU host interrupts that are routed to MPU
* @aux_data: auxiliary data used for creating the child nodes
+ * @has_reset: flag to indicate the presence of global module reset
*/
struct pruss_private_data {
int num_irqs;
int host_events;
struct of_dev_auxdata *aux_data;
+ bool has_reset;
+};
+
+/**
+ * struct pruss_match_private_data - match private data to handle multiple instances
+ * @device_name: device name of the PRUSS instance
+ * @priv_data: PRUSS driver private data for this PRUSS instance
+ */
+struct pruss_match_private_data {
+ const char *device_name;
+ struct pruss_private_data *priv_data;
};
struct pru_rproc;
@@ -920,6 +932,30 @@ static irqreturn_t pruss_handler(int irq, void *data)
static struct of_dev_auxdata pru_rproc_auxdata_lookup[];
static const struct of_device_id pruss_of_match[];
+static const
+struct pruss_private_data *pruss_get_private_data(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct pruss_match_private_data *data;
+ const struct of_device_id *match;
+
+ match = of_match_device(pruss_of_match, &pdev->dev);
+ if (!match)
+ return ERR_PTR(-ENODEV);
+
+ if (of_device_is_compatible(np, "ti,am335x-pruss") ||
+ of_device_is_compatible(np, "ti,am4372-pruss"))
+ return match->data;
+
+ data = match->data;
+ for (; data && data->device_name; data++) {
+ if (!strcmp(dev_name(&pdev->dev), data->device_name))
+ return data->priv_data;
+ }
+
+ return NULL;
+}
+
static int pruss_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -939,18 +975,17 @@ static int pruss_probe(struct platform_device *pdev)
return -ENODEV;
}
- if (!pdata || !pdata->deassert_reset || !pdata->assert_reset ||
- !pdata->reset_name) {
- dev_err(dev, "platform data (reset configuration information) missing\n");
+ data = pruss_get_private_data(pdev);
+ if (IS_ERR_OR_NULL(data)) {
+ dev_err(dev, "missing private data\n");
return -ENODEV;
}
- data = of_match_device(pruss_of_match, dev)->data;
- if (!data) {
- dev_err(dev, "missing private data\n");
+ if (data->has_reset && (!pdata || !pdata->deassert_reset ||
+ !pdata->assert_reset || !pdata->reset_name)) {
+ dev_err(dev, "platform data (reset configuration information) missing\n");
return -ENODEV;
}
- num_irqs = data->num_irqs;
err = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
if (err) {
@@ -967,6 +1002,7 @@ static int pruss_probe(struct platform_device *pdev)
pruss->pdev = pdev;
pruss->data = data;
+ num_irqs = data->num_irqs;
pruss->irqs = devm_kzalloc(dev, sizeof(*pruss->irqs) * num_irqs,
GFP_KERNEL);
if (!pruss->irqs) {
@@ -1009,10 +1045,12 @@ static int pruss_probe(struct platform_device *pdev)
pruss->mem_size[i], pruss->mem_va[i]);
}
- err = pdata->deassert_reset(pdev, pdata->reset_name);
- if (err) {
- dev_err(dev, "deassert_reset failed: %d\n", err);
- goto err_fail;
+ if (data->has_reset) {
+ err = pdata->deassert_reset(pdev, pdata->reset_name);
+ if (err) {
+ dev_err(dev, "deassert_reset failed: %d\n", err);
+ goto err_fail;
+ }
}
pm_runtime_enable(dev);
@@ -1045,7 +1083,8 @@ err_irq_fail:
pm_runtime_put_sync(dev);
err_rpm_fail:
pm_runtime_disable(dev);
- pdata->assert_reset(pdev, pdata->reset_name);
+ if (data->has_reset)
+ pdata->assert_reset(pdev, pdata->reset_name);
if (rproc)
rproc_put(rproc);
@@ -1066,13 +1105,15 @@ static int pruss_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pruss_platform_data *pdata = dev_get_platdata(dev);
+ struct pruss *pruss = platform_get_drvdata(pdev);
dev_info(dev, "remove platform devices for PRU cores\n");
device_for_each_child(dev, NULL, pru_rproc_unregister);
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
- pdata->assert_reset(pdev, pdata->reset_name);
+ if (pruss->data->has_reset)
+ pdata->assert_reset(pdev, pdata->reset_name);
return 0;
}
@@ -1089,6 +1130,26 @@ static struct pru_rproc_platform_data pru1_rproc_pdata = {
.fw_name = "rproc-pru1-fw",
};
+static struct pru_rproc_platform_data pru1_0_rproc_pdata = {
+ .id = 0,
+ .fw_name = "am57xx-pru1_0-fw",
+};
+
+static struct pru_rproc_platform_data pru1_1_rproc_pdata = {
+ .id = 1,
+ .fw_name = "am57xx-pru1_1-fw",
+};
+
+static struct pru_rproc_platform_data pru2_0_rproc_pdata = {
+ .id = 0,
+ .fw_name = "am57xx-pru2_0-fw",
+};
+
+static struct pru_rproc_platform_data pru2_1_rproc_pdata = {
+ .id = 1,
+ .fw_name = "am57xx-pru2_1-fw",
+};
+
/* platform data to be added when creating the PRU platform devices */
static struct of_dev_auxdata am335x_pru_rproc_auxdata_lookup[] = {
OF_DEV_AUXDATA("ti,pru-rproc", 0x4a334000, "4a334000.pru0",
@@ -1106,6 +1167,22 @@ static struct of_dev_auxdata am4372_pru_rproc_auxdata_lookup[] = {
{ /* sentinel */ },
};
+static struct of_dev_auxdata am5728_pruss1_rproc_auxdata_lookup[] = {
+ OF_DEV_AUXDATA("ti,pru-rproc", 0x4b234000, "4b234000.pru0",
+ &pru1_0_rproc_pdata),
+ OF_DEV_AUXDATA("ti,pru-rproc", 0x4b238000, "4b238000.pru1",
+ &pru1_1_rproc_pdata),
+ { /* sentinel */ },
+};
+
+static struct of_dev_auxdata am5728_pruss2_rproc_auxdata_lookup[] = {
+ OF_DEV_AUXDATA("ti,pru-rproc", 0x4b2b4000, "4b2b4000.pru0",
+ &pru2_0_rproc_pdata),
+ OF_DEV_AUXDATA("ti,pru-rproc", 0x4b2b8000, "4b2b8000.pru1",
+ &pru2_1_rproc_pdata),
+ { /* sentinel */ },
+};
+
static const struct of_device_id pru_rproc_match[] = {
{ .compatible = "ti,pru-rproc", .data = NULL, },
{},
@@ -1132,6 +1209,7 @@ static struct pruss_private_data am335x_priv_data = {
.host_events = (BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(8) | BIT(9)),
.aux_data = am335x_pru_rproc_auxdata_lookup,
+ .has_reset = true,
};
static struct pruss_private_data am4372_priv_data = {
@@ -1139,11 +1217,41 @@ static struct pruss_private_data am4372_priv_data = {
.host_events = (BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(8) | BIT(9)),
.aux_data = am4372_pru_rproc_auxdata_lookup,
+ .has_reset = true,
+};
+
+static struct pruss_private_data am5728_pruss1_priv_data = {
+ .num_irqs = 8,
+ .host_events = (BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(8) | BIT(9)),
+ .aux_data = am5728_pruss1_rproc_auxdata_lookup,
+};
+
+static struct pruss_private_data am5728_pruss2_priv_data = {
+ .num_irqs = 8,
+ .host_events = (BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(8) | BIT(9)),
+ .aux_data = am5728_pruss2_rproc_auxdata_lookup,
+};
+
+static struct pruss_match_private_data am5728_match_data[] = {
+ {
+ .device_name = "4b200000.pruss",
+ .priv_data = &am5728_pruss1_priv_data,
+ },
+ {
+ .device_name = "4b280000.pruss",
+ .priv_data = &am5728_pruss2_priv_data,
+ },
+ {
+ /* sentinel */
+ },
};
static const struct of_device_id pruss_of_match[] = {
{ .compatible = "ti,am335x-pruss", .data = &am335x_priv_data, },
{ .compatible = "ti,am4372-pruss", .data = &am4372_priv_data, },
+ { .compatible = "ti,am5728-pruss", .data = &am5728_match_data, },
{},
};
MODULE_DEVICE_TABLE(of, pruss_of_match);
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 345905760280..a9e665fb56ab 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1792,6 +1792,8 @@ module_init(remoteproc_init);
static void __exit remoteproc_exit(void)
{
+ ida_destroy(&rproc_dev_index);
+
rproc_exit_debugfs();
}
module_exit(remoteproc_exit);
diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c
index ed39a798372b..211a8de7af62 100644
--- a/drivers/remoteproc/wkup_m3_rproc.c
+++ b/drivers/remoteproc/wkup_m3_rproc.c
@@ -150,11 +150,6 @@ static void wkup_m3_fw_version_clear(void)
wkup_m3_ctrl_ipc_write(m3_rproc_static, val, 2);
}
-static void wkup_m3_mbox_callback(struct mbox_client *client, void *data)
-{
- omap_mbox_disable_irq(m3_rproc_static->mbox, IRQ_RX);
-}
-
void wkup_m3_set_rtc_only_mode(void)
{
m3_rproc_static->is_rtc_only = true;
@@ -179,7 +174,7 @@ static int wkup_m3_rproc_start(struct rproc *rproc)
m3_rproc->mbox_client.dev = dev;
m3_rproc->mbox_client.tx_done = NULL;
- m3_rproc->mbox_client.rx_callback = wkup_m3_mbox_callback;
+ m3_rproc->mbox_client.rx_callback = NULL;
m3_rproc->mbox_client.tx_block = false;
m3_rproc->mbox_client.knows_txdone = false;
@@ -283,13 +278,13 @@ int wkup_m3_ping(void)
* the RX callback to avoid multiple interrupts being received
* by the CM3.
*/
- omap_mbox_enable_irq(m3_rproc_static->mbox, IRQ_RX);
ret = mbox_send_message(m3_rproc_static->mbox, (void *)dummy_msg);
if (ret < 0) {
pr_err("%s: mbox_send_message() failed: %d\n", __func__, ret);
return ret;
}
+ mbox_client_txdone(m3_rproc_static->mbox, 0);
return 0;
}
@@ -389,6 +384,9 @@ static int wkup_m3_rproc_probe(struct platform_device *pdev)
struct resource *res;
struct task_struct *task;
+ if (!wkup_m3_pm_ops)
+ return -EPROBE_DEFER;
+
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index 9b948505d118..cc6b13b81c53 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -411,6 +411,7 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
struct fc_frame_header *fh;
struct fcoe_rcv_info *fr;
struct fcoe_percpu_s *bg;
+ struct sk_buff *tmp_skb;
unsigned short oxid;
interface = container_of(ptype, struct bnx2fc_interface,
@@ -423,6 +424,12 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
goto err;
}
+ tmp_skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!tmp_skb)
+ goto err;
+
+ skb = tmp_skb;
+
if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
printk(KERN_ERR PFX "bnx2fc_rcv: Wrong FC type frame\n");
goto err;
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 49014a143c6a..c1d04d4d3c6c 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -202,6 +202,7 @@ static struct {
{"IOMEGA", "Io20S *F", NULL, BLIST_KEY},
{"INSITE", "Floptical F*8I", NULL, BLIST_KEY},
{"INSITE", "I325VM", NULL, BLIST_KEY},
+ {"Intel", "Multi-Flex", NULL, BLIST_NO_RSOC},
{"iRiver", "iFP Mass Driver", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36},
{"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN},
{"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index edb4d46fa874..96b6664bb1cf 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -1984,8 +1984,10 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
* is no point trying to lock the door of an off-line device.
*/
shost_for_each_device(sdev, shost) {
- if (scsi_device_online(sdev) && sdev->locked)
+ if (scsi_device_online(sdev) && sdev->was_reset && sdev->locked) {
scsi_eh_lock_door(sdev);
+ sdev->was_reset = 0;
+ }
}
/*
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index e63d27013142..e543b80d610e 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -394,9 +394,6 @@ static void pump_transfers(unsigned long data)
chip = dws->cur_chip;
spi = message->spi;
- if (unlikely(!chip->clk_div))
- chip->clk_div = dws->max_freq / chip->speed_hz;
-
if (message->state == ERROR_STATE) {
message->status = -EIO;
goto early_exit;
@@ -437,7 +434,7 @@ static void pump_transfers(unsigned long data)
if (transfer->speed_hz) {
speed = chip->speed_hz;
- if (transfer->speed_hz != speed) {
+ if ((transfer->speed_hz != speed) || (!chip->clk_div)) {
speed = transfer->speed_hz;
if (speed > dws->max_freq) {
printk(KERN_ERR "MRST SPI0: unsupported"
@@ -659,7 +656,6 @@ static int dw_spi_setup(struct spi_device *spi)
dev_err(&spi->dev, "No max speed HZ parameter\n");
return -EINVAL;
}
- chip->speed_hz = spi->max_speed_hz;
chip->tmode = 0; /* Tx & Rx */
/* Default SPI mode is SCPOL = 0, SCPH = 0 */
diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
index fed699fc5918..2185a71055f2 100644
--- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c
+++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
@@ -57,6 +57,7 @@ static struct usb_device_id rtw_usb_id_tbl[] = {
{USB_DEVICE(0x07b8, 0x8179)}, /* Abocom - Abocom */
{USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */
{USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */
+ {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */
{USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */
{} /* Terminating entry */
};
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 9232c7738ed1..e6463ef33cd2 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -2230,7 +2230,7 @@ transport_generic_new_cmd(struct se_cmd *cmd)
* and let it call back once the write buffers are ready.
*/
target_add_to_state_list(cmd);
- if (cmd->data_direction != DMA_TO_DEVICE) {
+ if (cmd->data_direction != DMA_TO_DEVICE || cmd->data_length == 0) {
target_execute_cmd(cmd);
return 0;
}
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 60c8db053fe8..04c94974936d 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -1631,6 +1631,9 @@ static int serial_omap_probe(struct platform_device *pdev)
int uartirq = 0;
int wakeirq = 0;
int ret;
+ enum of_gpio_flags flags;
+ int gpio_sel;
+ unsigned long gpio_flags;
/* The optional wakeirq may be specified in the board dts file */
if (pdev->dev.of_node) {
@@ -1646,6 +1649,24 @@ static int serial_omap_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
+ /* Check if the UART needs to be selected */
+ gpio_sel = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
+ if (gpio_is_valid(gpio_sel)) {
+ dev_dbg(&pdev->dev, "using gpio %d for uart%d_sel\n",
+ gpio_sel, pdev->id);
+ gpio_flags = (flags & OF_GPIO_ACTIVE_LOW) ?
+ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
+ ret = devm_gpio_request_one(&pdev->dev, gpio_sel,
+ gpio_flags, "uart_sel");
+ if (ret) {
+ dev_err(&pdev->dev, "gpio%d request failed, ret %d\n",
+ gpio_sel, ret);
+ return ret;
+ }
+ } else if (gpio_sel == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ }
+
up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
if (!up)
return -ENOMEM;
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 2e6b832e004b..4d80716c600d 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -44,6 +44,21 @@ config USB_COMMON
default y
depends on USB || USB_GADGET
+config DRD_LIB
+ tristate "DRD Library support"
+ default y
+ depends on USB && USB_GADGET
+ ---help---
+ This option adds DRD Library support for Universal Serial Bus (USB).
+ DRD Library faciliatets the Role switching by HOST and DEVICE roles,
+ If your hardware has a Dual Role Device.
+
+ The DRD Library uses USB core API's to start/stop HOST controllers,
+ UDC API's to start/stop DEVICE controllers, ther by enabling to
+ switch roles between HOST and Device modes.
+
+ Say N if unsure.
+
config USB_ARCH_HAS_HCD
def_bool y
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 1ae2bf39d84b..932a61e30a6d 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
obj-$(CONFIG_USB_GADGET) += gadget/
obj-$(CONFIG_USB_COMMON) += usb-common.o
+obj-$(CONFIG_DRD_LIB) += drd-lib.o
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index d2bd9d7c8f4b..c7a2245a84b5 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -141,19 +141,27 @@ static int usb_device_supports_lpm(struct usb_device *udev)
return 0;
}
- /* All USB 3.0 must support LPM, but we need their max exit latency
- * information from the SuperSpeed Extended Capabilities BOS descriptor.
+ /*
+ * According to the USB 3.0 spec, all USB 3.0 devices must support LPM.
+ * However, there are some that don't, and they set the U1/U2 exit
+ * latencies to zero.
*/
if (!udev->bos->ss_cap) {
- dev_warn(&udev->dev, "No LPM exit latency info found. "
- "Power management will be impacted.\n");
+ dev_info(&udev->dev, "No LPM exit latency info found, disabling LPM.\n");
return 0;
}
- if (udev->parent->lpm_capable)
- return 1;
- dev_warn(&udev->dev, "Parent hub missing LPM exit latency info. "
- "Power management will be impacted.\n");
+ if (udev->bos->ss_cap->bU1devExitLat == 0 &&
+ udev->bos->ss_cap->bU2DevExitLat == 0) {
+ if (udev->parent)
+ dev_info(&udev->dev, "LPM exit latency is zeroed, disabling LPM.\n");
+ else
+ dev_info(&udev->dev, "We don't know the algorithms for LPM for this host, disabling LPM.\n");
+ return 0;
+ }
+
+ if (!udev->parent || udev->parent->lpm_capable)
+ return 1;
return 0;
}
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index c85459338991..b195fdb1effc 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -44,6 +44,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Microsoft Wireless Laser Mouse 6000 Receiver */
+ { USB_DEVICE(0x045e, 0x00e1), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Microsoft LifeCam-VX700 v2.0 */
{ USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/drd-lib.c b/drivers/usb/drd-lib.c
new file mode 100644
index 000000000000..f6104f24d281
--- /dev/null
+++ b/drivers/usb/drd-lib.c
@@ -0,0 +1,346 @@
+/**
+ * drd-lib.c - USB DRD library functions
+ *
+ * Copyright (C) 2014 Texas Instruments
+ * Author: George Cherian <george.cherian@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 of
+ * the License 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/>.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/drd.h>
+
+/**
+ * struct usb_drd - describes one dual role device
+ * @host - the HOST controller device of this drd
+ * @gadget - the gadget of drd
+ * @parent - the device to the actual controller
+ * @list - for use by the drd lib
+ * @state - specifies the current state
+ *
+ * This represents the internal data structure which is used by the UDC-class
+ * to hold information about udc driver and gadget together.
+*/
+struct usb_drd {
+ struct usb_drd_host *host;
+ struct usb_drd_gadget *gadget;
+ struct device *parent;
+ struct list_head list;
+ unsigned int state;
+};
+
+static LIST_HEAD(drd_list);
+static DEFINE_SPINLOCK(drd_lock);
+
+static struct usb_drd *usb_drd_get_dev(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ spin_lock(&drd_lock);
+ list_for_each_entry(drd, &drd_list, list)
+ if (drd->parent == parent)
+ goto out;
+ drd = NULL;
+out:
+ spin_unlock(&drd_lock);
+
+ return drd;
+}
+
+int usb_drd_get_state(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ return drd->state;
+}
+EXPORT_SYMBOL_GPL(usb_drd_get_state);
+
+int usb_drd_release(struct device *parent)
+{
+ struct usb_drd *drd;
+ int ret;
+
+ spin_lock(&drd_lock);
+ list_for_each_entry(drd, &drd_list, list) {
+ if (drd->parent == parent) {
+ kfree(drd);
+ ret = 0;
+ goto out;
+ }
+ }
+ ret = -ENODEV;
+out:
+ spin_unlock(&drd_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_drd_release);
+
+int usb_drd_add(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ drd = kzalloc(sizeof(*drd), GFP_KERNEL);
+ if (!drd)
+ return -ENOMEM;
+
+ spin_lock(&drd_lock);
+ drd->parent = parent;
+ list_add_tail(&drd->list, &drd_list);
+ drd->state = DRD_UNREGISTERED;
+
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_add);
+
+int usb_drd_register_hcd(struct device *parent, struct usb_drd_host *host)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->host = host;
+ drd->state |= DRD_HOST_REGISTERED | DRD_HOST_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_register_hcd);
+
+int usb_drd_unregister_hcd(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->state &= ~(DRD_HOST_REGISTERED | DRD_HOST_ACTIVE);
+ spin_unlock(&drd_lock);
+ kfree(drd->host);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_unregister_hcd);
+
+int usb_drd_start_hcd(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_setup *setup;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ if (WARN_ON(!(drd->state & DRD_HOST_REGISTERED)))
+ return -EINVAL;
+
+ setup = drd->host->host_setup;
+ if (setup && setup->ll_start)
+ setup->ll_start(setup->data);
+
+ usb_add_hcd(drd->host->main_hcd,
+ drd->host->hcd_irq, IRQF_SHARED);
+ if (drd->host->shared_hcd)
+ usb_add_hcd(drd->host->shared_hcd,
+ drd->host->hcd_irq, IRQF_SHARED);
+
+ spin_lock(&drd_lock);
+ drd->state |= DRD_HOST_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_start_hcd);
+
+int usb_drd_stop_hcd(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_setup *setup;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ if (WARN_ON(!(drd->state & DRD_HOST_ACTIVE)))
+ return -EINVAL;
+
+ setup = drd->host->host_setup;
+ if (setup && setup->ll_stop)
+ setup->ll_stop(setup->data);
+ if (drd->host->shared_hcd)
+ usb_remove_hcd(drd->host->shared_hcd);
+
+ usb_remove_hcd(drd->host->main_hcd);
+
+ spin_lock(&drd_lock);
+ drd->state = drd->state & ~DRD_HOST_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_stop_hcd);
+
+int usb_drd_register_udc(struct device *parent, struct usb_drd_gadget *gadget)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->gadget = gadget;
+ drd->state |= DRD_DEVICE_REGISTERED;
+ if (drd->gadget->g_driver)
+ drd->state |= DRD_DEVICE_ACTIVE;
+
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_register_udc);
+
+int usb_drd_register_udc_driver(struct device *parent,
+ struct usb_gadget_driver *driver)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->gadget->g_driver = driver;
+ drd->state |= DRD_DEVICE_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_register_udc_driver);
+
+int usb_drd_unregister_udc(struct device *parent)
+{
+ struct usb_drd *drd;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ spin_lock(&drd_lock);
+ drd->state &= ~(DRD_DEVICE_REGISTERED | DRD_DEVICE_ACTIVE);
+ spin_unlock(&drd_lock);
+ kfree(drd->gadget->gadget_setup);
+ kfree(drd->gadget);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_unregister_udc);
+
+int usb_drd_unregister_udc_driver(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_gadget *drd_gadget;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+ drd_gadget = drd->gadget;
+
+ spin_lock(&drd_lock);
+ drd->state &= ~DRD_DEVICE_ACTIVE;
+ drd_gadget->g_driver = NULL;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_unregister_udc_driver);
+
+int usb_drd_start_udc(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_gadget *drd_gadget;
+ struct usb_drd_setup *setup;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ if (WARN_ON(!(drd->state & DRD_DEVICE_REGISTERED)))
+ return -EINVAL;
+
+ drd_gadget = drd->gadget;
+ setup = drd_gadget->gadget_setup;
+
+ if (setup && setup->ll_start)
+ setup->ll_start(setup->data);
+
+ usb_add_gadget_udc_release(parent, drd_gadget->gadget,
+ setup->ll_release);
+ spin_lock(&drd_lock);
+ drd->state |= DRD_DEVICE_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_start_udc);
+
+int usb_drd_stop_udc(struct device *parent)
+{
+ struct usb_drd *drd;
+ struct usb_drd_gadget *drd_gadget;
+ struct usb_drd_setup *setup;
+
+ drd = usb_drd_get_dev(parent);
+ if (!drd)
+ return -ENODEV;
+
+ if (WARN_ON(!(drd->state & DRD_DEVICE_REGISTERED)))
+ return -EINVAL;
+
+ drd_gadget = drd->gadget;
+ setup = drd_gadget->gadget_setup;
+ if (setup && setup->ll_stop)
+ setup->ll_stop(setup->data);
+
+ usb_del_gadget_udc(drd_gadget->gadget);
+
+ spin_lock(&drd_lock);
+ drd->state = drd->state & ~DRD_DEVICE_ACTIVE;
+ spin_unlock(&drd_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_drd_stop_udc);
+
+MODULE_DESCRIPTION("USB-DRD Library");
+MODULE_AUTHOR("George Cherian <george.cherian@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 8eb996e4f058..75a8af4e49e8 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -92,4 +92,11 @@ config USB_DWC3_VERBOSE
help
Say Y here to enable verbose debugging messages on DWC3 Driver.
+config DWC3_HOST_USB3_LPM_ENABLE
+ bool "Enable USB3 LPM Capability"
+ depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y
+ default n
+ help
+ Select this when you want to enable USB3 LPM with dwc3 xhci host.
+
endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 10ac3e72482e..bc1bce1e1144 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -9,6 +9,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
dwc3-y += host.o
endif
+ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
+ dwc3-y += otg.o
+endif
+
ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
dwc3-y += gadget.o ep0.o
endif
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fc79e27406dd..717ff4e47f0e 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -254,6 +254,11 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
}
}
+int dwc3_core_gadget_helper(struct dwc3 *dwc)
+{
+ return dwc3_event_buffers_setup(dwc);
+}
+
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
{
if (!dwc->has_hibernation)
@@ -577,15 +582,9 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
break;
case USB_DR_MODE_OTG:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
- ret = dwc3_host_init(dwc);
- if (ret) {
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
-
- ret = dwc3_gadget_init(dwc);
+ ret = dwc3_otg_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize gadget\n");
+ dev_err(dev, "failed to initialize otg\n");
return ret;
}
break;
@@ -650,6 +649,18 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->xhci_resources[1].flags = res->flags;
dwc->xhci_resources[1].name = res->name;
+ dwc->otg_irq = platform_get_irq_byname(pdev, "otg");
+ if (!dwc->otg_irq)
+ dev_err(dev, "missing OTG IRQ\n");
+
+ dwc->gadget_irq = platform_get_irq_byname(pdev, "peripheral");
+ if (!dwc->gadget_irq)
+ dev_err(dev, "missing peripheral IRQ\n");
+
+ dwc->xhci_irq = platform_get_irq_byname(pdev, "host");
+ if (!dwc->xhci_irq)
+ dev_err(dev, "missing HOST IRQ\n");
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory resource\n");
@@ -834,8 +845,9 @@ static int dwc3_suspend(struct device *dev)
spin_lock_irqsave(&dwc->lock, flags);
switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
case USB_DR_MODE_OTG:
+ dwc3_otg_suspend(dwc);
+ case USB_DR_MODE_PERIPHERAL:
dwc3_gadget_suspend(dwc);
/* FALLTHROUGH */
case USB_DR_MODE_HOST:
@@ -877,8 +889,9 @@ static int dwc3_resume(struct device *dev)
dwc3_event_buffers_setup(dwc);
switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
case USB_DR_MODE_OTG:
+ dwc3_otg_resume(dwc);
+ case USB_DR_MODE_PERIPHERAL:
dwc3_gadget_resume(dwc);
/* FALLTHROUGH */
case USB_DR_MODE_HOST:
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e5699b3529de..bb73aba17463 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -357,6 +357,74 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
+/* OTG Configuration Register */
+#define DWC3_OCFG_DISPWRCUTTOFF (1 << 5)
+#define DWC3_OCFG_HIBDISMASK (1 << 4)
+#define DWC3_OCFG_SFTRSTMASK (1 << 3)
+#define DWC3_OCFG_OTGVERSION (1 << 2)
+#define DWC3_OCFG_HNPCAP (1 << 1)
+#define DWC3_OCFG_SRPCAP (1 << 0)
+
+/* OTG CTL Register */
+#define DWC3_OCTL_OTG3GOERR (1 << 7)
+#define DWC3_OCTL_PERIMODE (1 << 6)
+#define DWC3_OCTL_PRTPWRCTL (1 << 5)
+#define DWC3_OCTL_HNPREQ (1 << 4)
+#define DWC3_OCTL_SESREQ (1 << 3)
+#define DWC3_OCTL_TERMSELIDPULSE (1 << 2)
+#define DWC3_OCTL_DEVSETHNPEN (1 << 1)
+#define DWC3_OCTL_HOSTSETHNPEN (1 << 0)
+
+/* OTG Event Register */
+#define DWC3_OEVT_DEVICEMODE (1 << 31)
+#define DWC3_OEVT_XHCIRUNSTPSET (1 << 27)
+#define DWC3_OEVT_DEVRUNSTPSET (1 << 26)
+#define DWC3_OEVT_HIBENTRY (1 << 25)
+#define DWC3_OEVT_IDSTSCHNG (1 << 24)
+#define DWC3_OEVT_HRRCONFNOTIF (1 << 23)
+#define DWC3_OEVT_HRRINITNOTIF (1 << 22)
+#define DWC3_OEVT_ADEVIDLE (1 << 21)
+#define DWC3_OEVT_ADEVBHOSTEND (1 << 20)
+#define DWC3_OEVT_ADEVHOST (1 << 19)
+#define DWC3_OEVT_ADEVHNPCHNG (1 << 18)
+#define DWC3_OEVT_ADEVSRPDET (1 << 17)
+#define DWC3_OEVT_ADEVSESSENDDET (1 << 16)
+#define DWC3_OEVT_BDEVBHOSTEND (1 << 11)
+#define DWC3_OEVT_BDEVHNPCHNG (1 << 10)
+#define DWC3_OEVT_BDEVSESSVLDDET (1 << 9)
+#define DWC3_OEVT_BDEVVBUSCHNG (1 << 8)
+#define DWC3_OEVT_BSESSVLD (1 << 3)
+#define DWC3_OEVT_HOSTNEGSTS (1 << 2)
+#define DWC3_OEVT_SESSREQSTS (1 << 1)
+#define DWC3_OEVT_ERR (1 << 0)
+
+/* OTG Event Enable Register */
+#define DWC3_OEVTEN_XHCIRUNSTPSETEN (1 << 27)
+#define DWC3_OEVTEN_DEVRUNSTPSETEN (1 << 26)
+#define DWC3_OEVTEN_HIBENTRYEN (1 << 25)
+#define DWC3_OEVTEN_CONIDSTSCHNGEN (1 << 24)
+#define DWC3_OEVTEN_HRRCONFNOTIFEN (1 << 23)
+#define DWC3_OEVTEN_HRRINITNOTIFEN (1 << 22)
+#define DWC3_OEVTEN_ADEVIDLEEN (1 << 21)
+#define DWC3_OEVTEN_ADEVBHOSTENDEN (1 << 20)
+#define DWC3_OEVTEN_ADEVHOSTEN (1 << 19)
+#define DWC3_OEVTEN_ADEVHNPCHNGEN (1 << 18)
+#define DWC3_OEVTEN_ADEVSRPDETEN (1 << 17)
+#define DWC3_OEVTEN_ADEVSESSENDDETEN (1 << 16)
+#define DWC3_OEVTEN_BDEVHOSTENDEN (1 << 11)
+#define DWC3_OEVTEN_BDEVHNPCHNGEN (1 << 10)
+#define DWC3_OEVTEN_BDEVSESSVLDDETEN (1 << 9)
+#define DWC3_OEVTEN_BDEVVBUSCHNGEVNTEN (1 << 8)
+
+/* OTG Status Register */
+#define DWC3_OSTS_DEVRUNSTP (1 << 13)
+#define DWC3_OSTS_XHCIRUNSTP (1 << 12)
+#define DWC3_OSTS_PERIPHERALSTATE (1 << 4)
+#define DWC3_OSTS_XHCIPORTPOWER (1 << 3)
+#define DWC3_OSTS_BSESVLD (1 << 2)
+#define DWC3_OSTS_VBUSVLD (1 << 1)
+#define DWC3_OSTS_CONIDSTS (1 << 0)
+
/* Structures */
struct dwc3_trb;
@@ -600,6 +668,11 @@ struct dwc3_scratchpad_array {
__le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
};
+struct dwc3_gadget {
+ struct usb_gadget gadget;
+ struct dwc3 *dwc;
+};
+
/**
* struct dwc3 - representation of our controller
* @ctrl_req: usb control request which is used for ep0
@@ -625,6 +698,9 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @dr_mode: requested mode of operation
+ * @xhci_irq: IRQ number for XHCI IRQs
+ * @gadget_irq: IRQ number for Peripheral IRQs
+ * @otg_irq: IRQ number for OTG IRQs
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
@@ -683,7 +759,7 @@ struct dwc3 {
struct dwc3_event_buffer **ev_buffs;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
- struct usb_gadget gadget;
+ struct dwc3_gadget *dwc_gadget;
struct usb_gadget_driver *gadget_driver;
struct usb_phy *usb2_phy;
@@ -697,9 +773,18 @@ struct dwc3 {
enum usb_dr_mode dr_mode;
+ int gadget_irq;
+ int xhci_irq;
+ int otg_irq;
+
/* used for suspend/resume */
u32 dcfg;
u32 gctl;
+ u32 ocfg;
+ u32 octl;
+ u32 oevt;
+ u32 oevten;
+ u32 osts;
u32 nr_scratch;
u32 num_event_buffers;
@@ -912,6 +997,7 @@ struct dwc3_gadget_ep_cmd_params {
/* prototypes */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+int dwc3_core_gadget_helper(struct dwc3 *dwc);
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
@@ -953,6 +1039,22 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
{ return 0; }
#endif
+#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+int dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+void dwc3_otg_suspend(struct dwc3 *dwc);
+void dwc3_otg_resume(struct dwc3 *dwc);
+#else
+static inline int dwc3_otg_init(struct dwc3 *dwc)
+{ return 0; }
+static inline void dwc3_otg_exit(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_suspend(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_resume(struct dwc3 *dwc)
+{ }
+#endif
+
/* power management interface */
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
int dwc3_gadget_suspend(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index ddcf31d3d9a4..4ccdef7333c0 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -206,6 +206,18 @@ static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value)
omap->irq0_offset, value);
}
+static void dwc3_omap_write_irqmisc_clr(struct dwc3_omap *omap, u32 value)
+{
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_MISC +
+ omap->irqmisc_offset, value);
+}
+
+static void dwc3_omap_write_irq0_clr(struct dwc3_omap *omap, u32 value)
+{
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_0 -
+ omap->irq0_offset, value);
+}
+
static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
enum omap_dwc3_vbus_id_status status)
{
@@ -346,9 +358,23 @@ static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
{
+ u32 reg;
+
/* disable all IRQs */
- dwc3_omap_write_irqmisc_set(omap, 0x00);
- dwc3_omap_write_irq0_set(omap, 0x00);
+ reg = USBOTGSS_IRQO_COREIRQ_ST;
+ dwc3_omap_write_irq0_clr(omap, reg);
+
+ reg = (USBOTGSS_IRQMISC_OEVT |
+ USBOTGSS_IRQMISC_DRVVBUS_RISE |
+ USBOTGSS_IRQMISC_CHRGVBUS_RISE |
+ USBOTGSS_IRQMISC_DISCHRGVBUS_RISE |
+ USBOTGSS_IRQMISC_IDPULLUP_RISE |
+ USBOTGSS_IRQMISC_DRVVBUS_FALL |
+ USBOTGSS_IRQMISC_CHRGVBUS_FALL |
+ USBOTGSS_IRQMISC_DISCHRGVBUS_FALL |
+ USBOTGSS_IRQMISC_IDPULLUP_FALL);
+
+ dwc3_omap_write_irqmisc_clr(omap, reg);
}
static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);
@@ -536,8 +562,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
- ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
- "dwc3-omap", omap);
+ ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, IRQF_SHARED,
+ "dwc3-omap", omap);
if (ret) {
dev_err(dev, "failed to request IRQ #%d --> %d\n",
omap->irq, ret);
@@ -611,6 +637,7 @@ static int dwc3_omap_suspend(struct device *dev)
omap->utmi_otg_status = dwc3_omap_read_utmi_status(omap);
dwc3_omap_disable_irqs(omap);
+ pinctrl_pm_select_sleep_state(dev);
return 0;
}
@@ -618,13 +645,14 @@ static int dwc3_omap_resume(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
- dwc3_omap_write_utmi_status(omap, omap->utmi_otg_status);
- dwc3_omap_enable_irqs(omap);
+ pinctrl_pm_select_default_state(dev);
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
+ dwc3_omap_write_utmi_status(omap, omap->utmi_otg_status);
+ dwc3_omap_enable_irqs(omap);
return 0;
}
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index dbf3d332f2ad..7b349ad045e5 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -105,6 +105,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
req->request.actual = 0;
req->request.status = -EINPROGRESS;
@@ -148,7 +149,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
- usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
+ usb_gadget_set_state(&dwc_gadget->gadget, USB_STATE_CONFIGURED);
if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
@@ -384,6 +385,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl, int set)
{
struct dwc3_ep *dep;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
u32 recip;
u32 wValue;
u32 wIndex;
@@ -394,7 +396,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
recip = ctrl->bRequestType & USB_RECIP_MASK;
- state = dwc->gadget.state;
+ state = dwc_gadget->gadget.state;
switch (recip) {
case USB_RECIP_DEVICE:
@@ -493,7 +495,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+ enum usb_device_state state = dwc_gadget->gadget.state;
u32 addr;
u32 reg;
@@ -514,26 +517,28 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
if (addr)
- usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
+ usb_gadget_set_state(&dwc_gadget->gadget, USB_STATE_ADDRESS);
else
- usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
+ usb_gadget_set_state(&dwc_gadget->gadget, USB_STATE_DEFAULT);
return 0;
}
static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int ret;
spin_unlock(&dwc->lock);
- ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
+ ret = dwc->gadget_driver->setup(&dwc_gadget->gadget, ctrl);
spin_lock(&dwc->lock);
return ret;
}
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+ enum usb_device_state state = dwc_gadget->gadget.state;
u32 cfg;
int ret;
u32 reg;
@@ -558,7 +563,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
* to change the state on the next usb_ep_queue()
*/
if (ret == 0)
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(&dwc_gadget->gadget,
USB_STATE_CONFIGURED);
/*
@@ -577,7 +582,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
case USB_STATE_CONFIGURED:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (!cfg && !ret)
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(&dwc_gadget->gadget,
USB_STATE_ADDRESS);
break;
default:
@@ -633,7 +638,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
struct dwc3_ep *dep;
- enum usb_device_state state = dwc->gadget.state;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+ enum usb_device_state state = dwc_gadget->gadget.state;
u16 wLength;
u16 wValue;
@@ -905,6 +911,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req)
{
int ret;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
req->direction = !!dep->number;
@@ -917,7 +924,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
u32 transfer_size;
u32 maxpacket;
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
+ ret = usb_gadget_map_request(&dwc_gadget->gadget, &req->request,
dep->number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
@@ -940,7 +947,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA);
} else {
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
+ ret = usb_gadget_map_request(&dwc_gadget->gadget, &req->request,
dep->number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 73e0ac4233e1..40a513791f8c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -29,6 +29,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/drd.h>
#include "core.h"
#include "gadget.h"
@@ -233,6 +234,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int i;
if (req->queued) {
@@ -260,7 +262,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (dwc->ep0_bounced && dep->number == 0)
dwc->ep0_bounced = false;
else
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
+ usb_gadget_unmap_request(&dwc_gadget->gadget, &req->request,
req->direction);
dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
@@ -497,6 +499,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
bool ignore)
{
struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
memset(&params, 0x00, sizeof(params));
@@ -504,7 +507,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
/* Burst size is only needed in SuperSpeed mode */
- if (dwc->gadget.speed == USB_SPEED_SUPER) {
+ if (dwc_gadget->gadget.speed == USB_SPEED_SUPER) {
u32 burst = dep->endpoint.maxburst - 1;
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
@@ -1010,6 +1013,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_request *req;
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int ret;
u32 cmd;
@@ -1062,7 +1066,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
* here and stop, unmap, free and del each of the linked
* requests instead of what we do now.
*/
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
+ usb_gadget_unmap_request(&dwc_gadget->gadget, &req->request,
req->direction);
list_del(&req->list);
return ret;
@@ -1111,6 +1115,7 @@ static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int ret;
req->request.actual = 0;
@@ -1130,7 +1135,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* This will also avoid Host cancelling URBs due to too
* many NAKs.
*/
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
+ ret = usb_gadget_map_request(&dwc_gadget->gadget, &req->request,
dep->direction);
if (ret)
return ret;
@@ -1332,7 +1337,15 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
ret = __dwc3_gadget_ep_set_halt(dep, value, false);
+out:
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1391,7 +1404,8 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = {
static int dwc3_gadget_get_frame(struct usb_gadget *g)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -1400,7 +1414,8 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
static int dwc3_gadget_wakeup(struct usb_gadget *g)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
unsigned long timeout;
unsigned long flags;
@@ -1481,7 +1496,8 @@ out:
static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
int is_selfpowered)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
@@ -1547,7 +1563,8 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
unsigned long flags;
int ret;
@@ -1590,7 +1607,8 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
static int dwc3_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
struct dwc3_ep *dep;
unsigned long flags;
int ret = 0;
@@ -1610,7 +1628,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
if (dwc->gadget_driver) {
dev_err(dwc->dev, "%s is already bound to %s\n",
- dwc->gadget.name,
+ dwc_gadget->gadget.name,
dwc->gadget_driver->driver.name);
ret = -EBUSY;
goto err1;
@@ -1702,7 +1720,8 @@ err0:
static int dwc3_gadget_stop(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
- struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(g);
+ struct dwc3 *dwc = dwc_gadget->dwc;
unsigned long flags;
int irq;
@@ -1737,6 +1756,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
u8 num, u32 direction)
{
struct dwc3_ep *dep;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
u8 i;
for (i = 0; i < num; i++) {
@@ -1766,7 +1786,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.maxburst = 1;
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!epnum)
- dwc->gadget.ep0 = &dep->endpoint;
+ dwc_gadget->gadget.ep0 = &dep->endpoint;
} else {
int ret;
@@ -1774,7 +1794,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.max_streams = 15;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ &dwc_gadget->gadget.ep_list);
ret = dwc3_alloc_trb_pool(dep);
if (ret)
@@ -1791,8 +1811,9 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
{
int ret;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
- INIT_LIST_HEAD(&dwc->gadget.ep_list);
+ INIT_LIST_HEAD(&dwc_gadget->gadget.ep_list);
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
if (ret < 0) {
@@ -1833,6 +1854,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
}
kfree(dep);
+ dwc->eps[epnum] = NULL;
}
}
@@ -2094,27 +2116,33 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
static void dwc3_disconnect_gadget(struct dwc3 *dwc)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+
if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->disconnect(&dwc->gadget);
+ dwc->gadget_driver->disconnect(&dwc_gadget->gadget);
spin_lock(&dwc->lock);
}
}
static void dwc3_suspend_gadget(struct dwc3 *dwc)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+
if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->suspend(&dwc->gadget);
+ dwc->gadget_driver->suspend(&dwc_gadget->gadget);
spin_lock(&dwc->lock);
}
}
static void dwc3_resume_gadget(struct dwc3 *dwc)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->resume(&dwc->gadget);
+ dwc->gadget_driver->resume(&dwc_gadget->gadget);
spin_lock(&dwc->lock);
}
}
@@ -2208,6 +2236,7 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
{
int reg;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
dev_vdbg(dwc->dev, "%s\n", __func__);
@@ -2221,13 +2250,14 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc3_disconnect_gadget(dwc);
dwc->start_config_issued = false;
- dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc_gadget->gadget.speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
{
u32 reg;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
dev_vdbg(dwc->dev, "%s\n", __func__);
@@ -2263,9 +2293,9 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
}
/* after reset -> Default State */
- usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
+ usb_gadget_set_state(&dwc_gadget->gadget, USB_STATE_DEFAULT);
- if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
+ if (dwc_gadget->gadget.speed != USB_SPEED_UNKNOWN)
dwc3_disconnect_gadget(dwc);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -2311,6 +2341,7 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
int ret;
u32 reg;
u8 speed;
@@ -2342,24 +2373,24 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_reset_interrupt(dwc);
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget.ep0->maxpacket = 512;
- dwc->gadget.speed = USB_SPEED_SUPER;
+ dwc_gadget->gadget.ep0->maxpacket = 512;
+ dwc_gadget->gadget.speed = USB_SPEED_SUPER;
break;
case DWC3_DCFG_HIGHSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_HIGH;
+ dwc_gadget->gadget.ep0->maxpacket = 64;
+ dwc_gadget->gadget.speed = USB_SPEED_HIGH;
break;
case DWC3_DCFG_FULLSPEED2:
case DWC3_DCFG_FULLSPEED1:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_FULL;
+ dwc_gadget->gadget.ep0->maxpacket = 64;
+ dwc_gadget->gadget.speed = USB_SPEED_FULL;
break;
case DWC3_DCFG_LOWSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
- dwc->gadget.ep0->maxpacket = 8;
- dwc->gadget.speed = USB_SPEED_LOW;
+ dwc_gadget->gadget.ep0->maxpacket = 8;
+ dwc_gadget->gadget.speed = USB_SPEED_LOW;
break;
}
@@ -2412,6 +2443,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
{
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
+
dev_vdbg(dwc->dev, "%s\n", __func__);
/*
@@ -2419,7 +2452,7 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
* implemented.
*/
- dwc->gadget_driver->resume(&dwc->gadget);
+ dwc->gadget_driver->resume(&dwc_gadget->gadget);
}
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
@@ -2723,6 +2756,89 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
return ret;
}
+static void dwc3_gadget_release(struct device *dev)
+{
+ struct usb_gadget *gadget = container_of(dev, struct usb_gadget, dev);
+ struct dwc3_gadget *dwc_gadget = gadget_to_dwc_gadget(gadget);
+ struct dwc3 *dwc = dwc_gadget->dwc;
+
+ dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
+ dwc3_gadget_free_endpoints(dwc);
+ dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dwc->ep0_bounce, dwc->ep0_bounce_addr);
+ kfree(dwc->setup_buf);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dwc->ep0_trb, dwc->ep0_trb_addr);
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req, dwc->ctrl_req_addr);
+ usb_drd_unregister_udc(dwc->dev);
+ kfree(dwc_gadget);
+}
+
+static int dwc3_gadget_setup(void *data)
+{
+ struct dwc3 *dwc = data;
+ struct dwc3_gadget *dwc_gadget;
+ struct usb_drd_gadget *drd_gadget;
+ struct usb_drd_setup *gadget_setup;
+ int ret;
+
+ drd_gadget = kzalloc(sizeof(*drd_gadget), GFP_KERNEL);
+ if (!drd_gadget) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ gadget_setup = kzalloc(sizeof(*gadget_setup), GFP_KERNEL);
+ if (!gadget_setup) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ dwc_gadget = kzalloc(sizeof(*dwc_gadget), GFP_KERNEL);
+ if (!dwc_gadget) {
+ ret = -ENOMEM;
+ goto err3;
+ }
+
+ drd_gadget->g_driver = dwc->gadget_driver;
+
+ /*
+ * Pass the DWC3 specific routines for
+ * switching roles to the drd library
+ */
+ gadget_setup->ll_start = NULL;
+ gadget_setup->ll_stop = NULL;
+ gadget_setup->ll_release = dwc3_gadget_release;
+ gadget_setup->data = (void *)dwc;
+ drd_gadget->gadget_setup = gadget_setup;
+
+ dwc_gadget->gadget.ops = &dwc3_gadget_ops;
+ dwc_gadget->gadget.max_speed = USB_SPEED_SUPER;
+ dwc_gadget->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc_gadget->gadget.sg_supported = true;
+ dwc_gadget->gadget.name = "dwc3-gadget";
+ dwc_gadget->dwc = dwc;
+ drd_gadget->gadget = &dwc_gadget->gadget;
+
+ /*
+ * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
+ * on ep out.
+ */
+ dwc_gadget->gadget.quirk_ep_out_aligned_size = true;
+ dwc->dwc_gadget = dwc_gadget;
+ usb_drd_register_udc(dwc->dev, drd_gadget);
+
+ return 0;
+
+err3:
+ kfree(gadget_setup);
+err2:
+ kfree(drd_gadget);
+err1:
+ return ret;
+}
+
/**
* dwc3_gadget_init - Initializes gadget related registers
* @dwc: pointer to our controller context structure
@@ -2765,17 +2881,9 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err3;
}
- dwc->gadget.ops = &dwc3_gadget_ops;
- dwc->gadget.max_speed = USB_SPEED_SUPER;
- dwc->gadget.speed = USB_SPEED_UNKNOWN;
- dwc->gadget.sg_supported = true;
- dwc->gadget.name = "dwc3-gadget";
-
- /*
- * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
- * on ep out.
- */
- dwc->gadget.quirk_ep_out_aligned_size = true;
+ ret = dwc3_gadget_setup(dwc);
+ if (ret)
+ goto err3;
/*
* REVISIT: Here we should clear all pending IRQs to be
@@ -2786,7 +2894,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
if (ret)
goto err4;
- ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
+ ret = usb_add_gadget_udc_release(dwc->dev, &dwc->dwc_gadget->gadget,
+ dwc3_gadget_release);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
goto err4;
@@ -2795,6 +2904,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
return 0;
err4:
+ usb_drd_unregister_udc(dwc->dev);
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
@@ -2818,20 +2928,25 @@ err0:
void dwc3_gadget_exit(struct dwc3 *dwc)
{
- usb_del_gadget_udc(&dwc->gadget);
+ struct dwc3_gadget *dwc_gadget = dwc->dwc_gadget;
- dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
- dwc->ep0_bounce, dwc->ep0_bounce_addr);
+ if (usb_drd_get_state(dwc->dev) & DRD_DEVICE_REGISTERED) {
+ usb_del_gadget_udc(&dwc_gadget->gadget);
- kfree(dwc->setup_buf);
+ dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
- dwc->ep0_trb, dwc->ep0_trb_addr);
+ dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dwc->ep0_bounce, dwc->ep0_bounce_addr);
- dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
- dwc->ctrl_req, dwc->ctrl_req_addr);
+ kfree(dwc->setup_buf);
+
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dwc->ep0_trb, dwc->ep0_trb_addr);
+
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req, dwc->ctrl_req_addr);
+ }
}
int dwc3_gadget_suspend(struct dwc3 *dwc)
@@ -2843,8 +2958,10 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
dwc->pullups_connected = true;
}
- __dwc3_gadget_ep_disable(dwc->eps[0]);
- __dwc3_gadget_ep_disable(dwc->eps[1]);
+ if (dwc->eps[0] && dwc->eps[1]) {
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+ }
dwc->dcfg = dwc3_readl(dwc->regs, DWC3_DCFG);
@@ -2856,30 +2973,33 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
struct dwc3_ep *dep;
int ret;
- /* Start with SuperSpeed Default */
- dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ if (dwc->eps[0] && dwc->eps[1]) {
+ /* Start with SuperSpeed Default */
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
- if (ret)
- goto err0;
+ dep = dwc->eps[0];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc,
+ NULL, false);
+ if (ret)
+ goto err0;
- dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
- if (ret)
- goto err1;
+ dep = dwc->eps[1];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc,
+ NULL, false);
+ if (ret)
+ goto err1;
- /* begin to receive SETUP packets */
- dwc->ep0state = EP0_SETUP_PHASE;
- dwc3_ep0_out_start(dwc);
+ /* begin to receive SETUP packets */
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
- dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
+ dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
- if (dwc->pullups_connected) {
- dwc3_gadget_enable_irq(dwc);
- dwc3_gadget_run_stop(dwc, true, false);
+ if (dwc->pullups_connected) {
+ dwc3_gadget_enable_irq(dwc);
+ dwc3_gadget_run_stop(dwc, true, false);
+ }
}
-
return 0;
err1:
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 9b68577105ee..1dfd9381aeb3 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -26,6 +26,7 @@
struct dwc3;
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
+#define gadget_to_dwc_gadget(g) (container_of(g, struct dwc3_gadget, gadget))
/* DEPCFG parameter 1 */
#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 32db328cc769..257b5b56e6d5 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -16,12 +16,14 @@
*/
#include <linux/platform_device.h>
+#include <linux/usb/xhci_pdriver.h>
#include "core.h"
int dwc3_host_init(struct dwc3 *dwc)
{
struct platform_device *xhci;
+ struct usb_xhci_pdata pdata;
int ret;
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
@@ -46,6 +48,20 @@ int dwc3_host_init(struct dwc3 *dwc)
goto err1;
}
+ memset(&pdata, 0, sizeof(pdata));
+
+#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE
+ pdata.usb3_lpm_capable = 1;
+#endif
+ pdata.usb_drd_support = 1;
+ pdata.usb_needs_lhc_reset = 1;
+
+ ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
+ if (ret) {
+ dev_err(dwc->dev, "couldn't add platform data to xHCI device\n");
+ goto err1;
+ }
+
ret = platform_device_add(xhci);
if (ret) {
dev_err(dwc->dev, "failed to register xHCI device\n");
diff --git a/drivers/usb/dwc3/otg.c b/drivers/usb/dwc3/otg.c
new file mode 100644
index 000000000000..b0dc7b228d93
--- /dev/null
+++ b/drivers/usb/dwc3/otg.c
@@ -0,0 +1,144 @@
+/**
+ * otg.c - DesignWare USB3 DRD Controller OTG
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: George Cherian <george.cherian@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 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include <linux/usb/drd.h>
+#include "core.h"
+#include "io.h"
+
+#define DWC3_GSTS_OTG_IP (1 << 10)
+
+static irqreturn_t dwc3_otg_interrupt(int irq , void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ u32 reg;
+
+ spin_lock(&dwc->lock);
+ reg = dwc3_readl(dwc->regs, DWC3_GSTS);
+ if (reg & DWC3_GSTS_OTG_IP) {
+ reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ dev_vdbg(dwc->dev, "OTG Interrupt %x\n", reg);
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ spin_unlock(&dwc->lock);
+ return IRQ_WAKE_THREAD;
+ }
+
+ spin_unlock(&dwc->lock);
+ return IRQ_NONE;
+}
+
+static irqreturn_t dwc3_otg_thread_interrupt(int irq, void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+
+ dev_vdbg(dwc->dev, "OTG thread interrupt\n");
+ if ((reg & DWC3_OSTS_CONIDSTS)) {
+ usb_drd_stop_hcd(dwc->dev);
+ dwc3_writel(dwc->regs, DWC3_OCFG, DWC3_OCFG_SFTRSTMASK);
+ dwc3_writel(dwc->regs, DWC3_OCTL,
+ DWC3_OCTL_SESREQ | DWC3_OCTL_PERIMODE);
+ if (usb_drd_get_state(dwc->dev) & DRD_DEVICE_REGISTERED) {
+ usb_drd_start_udc(dwc->dev);
+ } else {
+ dwc3_core_gadget_helper(dwc);
+ dwc3_gadget_init(dwc);
+ }
+ dwc3_writel(dwc->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_CONIDSTSCHNGEN);
+ } else if (!(reg & DWC3_OSTS_CONIDSTS)) {
+ usb_drd_stop_udc(dwc->dev);
+ dwc3_writel(dwc->regs, DWC3_OCFG,
+ DWC3_OCFG_DISPWRCUTTOFF | DWC3_OCFG_SFTRSTMASK);
+ dwc3_writel(dwc->regs, DWC3_OCTL, DWC3_OCTL_PRTPWRCTL);
+ if (usb_drd_get_state(dwc->dev) & DRD_HOST_REGISTERED)
+ usb_drd_start_hcd(dwc->dev);
+ else
+ dwc3_host_init(dwc);
+
+ dwc3_writel(dwc->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_CONIDSTSCHNGEN);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int dwc3_otg_init(struct dwc3 *dwc)
+{
+ u32 reg, ret;
+
+ usb_drd_add(dwc->dev);
+ dwc3_writel(dwc->regs, DWC3_OEVT, 0xFFFF);
+ if (dwc->otg_irq > 0) {
+ ret = devm_request_threaded_irq(dwc->dev, dwc->otg_irq,
+ dwc3_otg_interrupt,
+ dwc3_otg_thread_interrupt,
+ IRQF_SHARED, "dwc3-otg", dwc);
+ } else {
+ WARN(1, "Trying to request invalid otg_irq");
+ return -ENODEV;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, DWC3_OEVTEN_CONIDSTSCHNGEN);
+ dwc3_writel(dwc->regs, DWC3_OCTL, DWC3_OCTL_PERIMODE);
+
+ reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+ if ((reg & DWC3_OSTS_CONIDSTS)) {
+ dev_vdbg(dwc->dev, "Gadget init\n");
+ dwc3_writel(dwc->regs, DWC3_OCFG, DWC3_OCFG_SFTRSTMASK);
+ dwc3_writel(dwc->regs, DWC3_OCTL,
+ DWC3_OCTL_SESREQ | DWC3_OCTL_PERIMODE);
+ dwc3_gadget_init(dwc);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_CONIDSTSCHNGEN);
+
+ } else if (!(reg & DWC3_OSTS_CONIDSTS)) {
+ dev_vdbg(dwc->dev, "Host init\n");
+ dwc3_writel(dwc->regs, DWC3_OCFG,
+ DWC3_OCFG_DISPWRCUTTOFF | DWC3_OCFG_SFTRSTMASK);
+ dwc3_writel(dwc->regs, DWC3_OCTL, DWC3_OCTL_PRTPWRCTL);
+ dwc3_host_init(dwc);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_CONIDSTSCHNGEN);
+ }
+
+ return 0;
+}
+
+void dwc3_otg_suspend(struct dwc3 *dwc)
+{
+ dwc->ocfg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ dwc->octl = dwc3_readl(dwc->regs, DWC3_OCTL);
+ dwc->oevt = dwc3_readl(dwc->regs, DWC3_OEVT);
+ dwc->oevten = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+ dwc->osts = dwc3_readl(dwc->regs, DWC3_OSTS);
+}
+
+void dwc3_otg_resume(struct dwc3 *dwc)
+{
+ dwc3_writel(dwc->regs, DWC3_OCFG, dwc->ocfg);
+ dwc3_writel(dwc->regs, DWC3_OCTL, dwc->octl);
+ dwc3_writel(dwc->regs, DWC3_OEVT, dwc->oevt);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, dwc->oevten);
+ dwc3_writel(dwc->regs, DWC3_OSTS, dwc->osts);
+}
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 6f5c11c9edf5..7f5ee260187b 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -29,6 +29,22 @@ if USB_XHCI_HCD
config USB_XHCI_PLATFORM
tristate
+config USB_XHCI_MVEBU
+ tristate "xHCI support for Marvell Armada 375/38x"
+ select USB_XHCI_PLATFORM
+ depends on ARCH_MVEBU || COMPILE_TEST
+ ---help---
+ Say 'Y' to enable the support for the xHCI host controller
+ found in Marvell Armada 375/38x ARM SOCs.
+
+config USB_XHCI_RCAR
+ tristate "xHCI support for Renesas R-Car SoCs"
+ select USB_XHCI_PLATFORM
+ depends on ARCH_SHMOBILE || COMPILE_TEST
+ ---help---
+ Say 'Y' to enable the support for the xHCI host controller
+ found in Renesas R-Car ARM SoCs.
+
endif # USB_XHCI_HCD
config USB_EHCI_HCD
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 968d9214fe49..d02853242532 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -19,6 +19,12 @@ xhci-hcd-$(CONFIG_PCI) += xhci-pci.o
ifneq ($(CONFIG_USB_XHCI_PLATFORM), )
xhci-hcd-y += xhci-plat.o
+ifneq ($(CONFIG_USB_XHCI_MVEBU), )
+ xhci-hcd-y += xhci-mvebu.o
+endif
+ifneq ($(CONFIG_USB_XHCI_RCAR), )
+ xhci-hcd-y += xhci-rcar.o
+endif
endif
obj-$(CONFIG_USB_WHCI_HCD) += whci/
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 93fe089cd51a..69aece31143a 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -20,7 +20,9 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/gfp.h>
+
+#include <linux/slab.h>
+#include <linux/device.h>
#include <asm/unaligned.h>
#include "xhci.h"
@@ -270,7 +272,6 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
struct xhci_virt_device *virt_dev;
struct xhci_command *cmd;
unsigned long flags;
- int timeleft;
int ret;
int i;
@@ -284,34 +285,31 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
spin_lock_irqsave(&xhci->lock, flags);
for (i = LAST_EP_INDEX; i > 0; i--) {
- if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue)
- xhci_queue_stop_endpoint(xhci, slot_id, i, suspend);
+ if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) {
+ struct xhci_command *command;
+ command = xhci_alloc_command(xhci, false, false,
+ GFP_NOWAIT);
+ if (!command) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_free_command(xhci, cmd);
+ return -ENOMEM;
+
+ }
+ xhci_queue_stop_endpoint(xhci, command, slot_id, i,
+ suspend);
+ }
}
- cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
- list_add_tail(&cmd->cmd_list, &virt_dev->cmd_list);
- xhci_queue_stop_endpoint(xhci, slot_id, 0, suspend);
+ xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend);
xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
/* Wait for last stop endpoint command to finish */
- timeleft = wait_for_completion_interruptible_timeout(
- cmd->completion,
- XHCI_CMD_DEFAULT_TIMEOUT);
- if (timeleft <= 0) {
- xhci_warn(xhci, "%s while waiting for stop endpoint command\n",
- timeleft == 0 ? "Timeout" : "Signal");
- spin_lock_irqsave(&xhci->lock, flags);
- /* The timeout might have raced with the event ring handler, so
- * only delete from the list if the item isn't poisoned.
- */
- if (cmd->cmd_list.next != LIST_POISON1)
- list_del(&cmd->cmd_list);
- spin_unlock_irqrestore(&xhci->lock, flags);
+ wait_for_completion(cmd->completion);
+
+ if (cmd->status == COMP_CMD_ABORT || cmd->status == COMP_CMD_STOP) {
+ xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n");
ret = -ETIME;
- goto command_cleanup;
}
-
-command_cleanup:
xhci_free_command(xhci, cmd);
return ret;
}
@@ -734,9 +732,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* Set the U1 and U2 exit latencies. */
memcpy(buf, &usb_bos_descriptor,
USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
- temp = readl(&xhci->cap_regs->hcs_params3);
- buf[12] = HCS_U1_LATENCY(temp);
- put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
+ if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
+ temp = readl(&xhci->cap_regs->hcs_params3);
+ buf[12] = HCS_U1_LATENCY(temp);
+ put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
+ }
/* Indicate whether the host has LTM support. */
temp = readl(&xhci->cap_regs->hcc_params);
@@ -1142,7 +1142,9 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
* including the USB 3.0 roothub, but only if CONFIG_PM_RUNTIME
* is enabled, so also enable remote wake here.
*/
- if (hcd->self.root_hub->do_remote_wakeup) {
+ if (hcd->self.root_hub->do_remote_wakeup
+ && device_may_wakeup(hcd->self.controller)) {
+
if (t1 & PORT_CONNECT) {
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
t2 &= ~PORT_WKCONN_E;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 9bce4f0e99be..8936211b161d 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -149,14 +149,140 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
}
}
+/*
+ * We need a radix tree for mapping physical addresses of TRBs to which stream
+ * ID they belong to. We need to do this because the host controller won't tell
+ * us which stream ring the TRB came from. We could store the stream ID in an
+ * event data TRB, but that doesn't help us for the cancellation case, since the
+ * endpoint may stop before it reaches that event data TRB.
+ *
+ * The radix tree maps the upper portion of the TRB DMA address to a ring
+ * segment that has the same upper portion of DMA addresses. For example, say I
+ * have segments of size 1KB, that are always 1KB aligned. A segment may
+ * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the
+ * key to the stream ID is 0x43244. I can use the DMA address of the TRB to
+ * pass the radix tree a key to get the right stream ID:
+ *
+ * 0x10c90fff >> 10 = 0x43243
+ * 0x10c912c0 >> 10 = 0x43244
+ * 0x10c91400 >> 10 = 0x43245
+ *
+ * Obviously, only those TRBs with DMA addresses that are within the segment
+ * will make the radix tree return the stream ID for that ring.
+ *
+ * Caveats for the radix tree:
+ *
+ * The radix tree uses an unsigned long as a key pair. On 32-bit systems, an
+ * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be
+ * 64-bits. Since we only request 32-bit DMA addresses, we can use that as the
+ * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit
+ * PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit
+ * extended systems (where the DMA address can be bigger than 32-bits),
+ * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that.
+ */
+static int xhci_insert_segment_mapping(struct radix_tree_root *trb_address_map,
+ struct xhci_ring *ring,
+ struct xhci_segment *seg,
+ gfp_t mem_flags)
+{
+ unsigned long key;
+ int ret;
+
+ key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
+ /* Skip any segments that were already added. */
+ if (radix_tree_lookup(trb_address_map, key))
+ return 0;
+
+ ret = radix_tree_maybe_preload(mem_flags);
+ if (ret)
+ return ret;
+ ret = radix_tree_insert(trb_address_map,
+ key, ring);
+ radix_tree_preload_end();
+ return ret;
+}
+
+static void xhci_remove_segment_mapping(struct radix_tree_root *trb_address_map,
+ struct xhci_segment *seg)
+{
+ unsigned long key;
+
+ key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
+ if (radix_tree_lookup(trb_address_map, key))
+ radix_tree_delete(trb_address_map, key);
+}
+
+static int xhci_update_stream_segment_mapping(
+ struct radix_tree_root *trb_address_map,
+ struct xhci_ring *ring,
+ struct xhci_segment *first_seg,
+ struct xhci_segment *last_seg,
+ gfp_t mem_flags)
+{
+ struct xhci_segment *seg;
+ struct xhci_segment *failed_seg;
+ int ret;
+
+ if (WARN_ON_ONCE(trb_address_map == NULL))
+ return 0;
+
+ seg = first_seg;
+ do {
+ ret = xhci_insert_segment_mapping(trb_address_map,
+ ring, seg, mem_flags);
+ if (ret)
+ goto remove_streams;
+ if (seg == last_seg)
+ return 0;
+ seg = seg->next;
+ } while (seg != first_seg);
+
+ return 0;
+
+remove_streams:
+ failed_seg = seg;
+ seg = first_seg;
+ do {
+ xhci_remove_segment_mapping(trb_address_map, seg);
+ if (seg == failed_seg)
+ return ret;
+ seg = seg->next;
+ } while (seg != first_seg);
+
+ return ret;
+}
+
+static void xhci_remove_stream_mapping(struct xhci_ring *ring)
+{
+ struct xhci_segment *seg;
+
+ if (WARN_ON_ONCE(ring->trb_address_map == NULL))
+ return;
+
+ seg = ring->first_seg;
+ do {
+ xhci_remove_segment_mapping(ring->trb_address_map, seg);
+ seg = seg->next;
+ } while (seg != ring->first_seg);
+}
+
+static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags)
+{
+ return xhci_update_stream_segment_mapping(ring->trb_address_map, ring,
+ ring->first_seg, ring->last_seg, mem_flags);
+}
+
/* XXX: Do we need the hcd structure in all these functions? */
void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
if (!ring)
return;
- if (ring->first_seg)
+ if (ring->first_seg) {
+ if (ring->type == TYPE_STREAM)
+ xhci_remove_stream_mapping(ring);
xhci_free_segments_for_ring(xhci, ring->first_seg);
+ }
kfree(ring);
}
@@ -349,6 +475,21 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
if (ret)
return -ENOMEM;
+ if (ring->type == TYPE_STREAM)
+ ret = xhci_update_stream_segment_mapping(ring->trb_address_map,
+ ring, first, last, flags);
+ if (ret) {
+ struct xhci_segment *next;
+ do {
+ next = first->next;
+ xhci_segment_free(xhci, first);
+ if (first == last)
+ break;
+ first = next;
+ } while (true);
+ return ret;
+ }
+
xhci_link_rings(xhci, ring, first, last, num_segs);
xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion,
"ring expansion succeed, now has %d segments",
@@ -434,12 +575,12 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci,
struct xhci_stream_ctx *stream_ctx, dma_addr_t dma)
{
struct device *dev = xhci_to_hcd(xhci)->self.controller;
+ size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
- if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
- dma_free_coherent(dev,
- sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
+ if (size > MEDIUM_STREAM_ARRAY_SIZE)
+ dma_free_coherent(dev, size,
stream_ctx, dma);
- else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
+ else if (size <= SMALL_STREAM_ARRAY_SIZE)
return dma_pool_free(xhci->small_streams_pool,
stream_ctx, dma);
else
@@ -462,12 +603,12 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci,
gfp_t mem_flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.controller;
+ size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
- if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
- return dma_alloc_coherent(dev,
- sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
+ if (size > MEDIUM_STREAM_ARRAY_SIZE)
+ return dma_alloc_coherent(dev, size,
dma, mem_flags);
- else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
+ else if (size <= SMALL_STREAM_ARRAY_SIZE)
return dma_pool_alloc(xhci->small_streams_pool,
mem_flags, dma);
else
@@ -510,36 +651,6 @@ struct xhci_ring *xhci_stream_id_to_ring(
* The number of stream contexts in the stream context array may be bigger than
* the number of streams the driver wants to use. This is because the number of
* stream context array entries must be a power of two.
- *
- * We need a radix tree for mapping physical addresses of TRBs to which stream
- * ID they belong to. We need to do this because the host controller won't tell
- * us which stream ring the TRB came from. We could store the stream ID in an
- * event data TRB, but that doesn't help us for the cancellation case, since the
- * endpoint may stop before it reaches that event data TRB.
- *
- * The radix tree maps the upper portion of the TRB DMA address to a ring
- * segment that has the same upper portion of DMA addresses. For example, say I
- * have segments of size 1KB, that are always 64-byte aligned. A segment may
- * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the
- * key to the stream ID is 0x43244. I can use the DMA address of the TRB to
- * pass the radix tree a key to get the right stream ID:
- *
- * 0x10c90fff >> 10 = 0x43243
- * 0x10c912c0 >> 10 = 0x43244
- * 0x10c91400 >> 10 = 0x43245
- *
- * Obviously, only those TRBs with DMA addresses that are within the segment
- * will make the radix tree return the stream ID for that ring.
- *
- * Caveats for the radix tree:
- *
- * The radix tree uses an unsigned long as a key pair. On 32-bit systems, an
- * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be
- * 64-bits. Since we only request 32-bit DMA addresses, we can use that as the
- * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit
- * PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit
- * extended systems (where the DMA address can be bigger than 32-bits),
- * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that.
*/
struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
unsigned int num_stream_ctxs,
@@ -548,7 +659,6 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
struct xhci_stream_info *stream_info;
u32 cur_stream;
struct xhci_ring *cur_ring;
- unsigned long key;
u64 addr;
int ret;
@@ -603,6 +713,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
if (!cur_ring)
goto cleanup_rings;
cur_ring->stream_id = cur_stream;
+ cur_ring->trb_address_map = &stream_info->trb_address_map;
/* Set deq ptr, cycle bit, and stream context type */
addr = cur_ring->first_seg->dma |
SCT_FOR_CTX(SCT_PRI_TR) |
@@ -612,10 +723,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n",
cur_stream, (unsigned long long) addr);
- key = (unsigned long)
- (cur_ring->first_seg->dma >> TRB_SEGMENT_SHIFT);
- ret = radix_tree_insert(&stream_info->trb_address_map,
- key, cur_ring);
+ ret = xhci_update_stream_mapping(cur_ring, mem_flags);
if (ret) {
xhci_ring_free(xhci, cur_ring);
stream_info->stream_rings[cur_stream] = NULL;
@@ -635,9 +743,6 @@ cleanup_rings:
for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
cur_ring = stream_info->stream_rings[cur_stream];
if (cur_ring) {
- addr = cur_ring->first_seg->dma;
- radix_tree_delete(&stream_info->trb_address_map,
- addr >> TRB_SEGMENT_SHIFT);
xhci_ring_free(xhci, cur_ring);
stream_info->stream_rings[cur_stream] = NULL;
}
@@ -698,7 +803,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
{
int cur_stream;
struct xhci_ring *cur_ring;
- dma_addr_t addr;
if (!stream_info)
return;
@@ -707,9 +811,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
cur_stream++) {
cur_ring = stream_info->stream_rings[cur_stream];
if (cur_ring) {
- addr = cur_ring->first_seg->dma;
- radix_tree_delete(&stream_info->trb_address_map,
- addr >> TRB_SEGMENT_SHIFT);
xhci_ring_free(xhci, cur_ring);
stream_info->stream_rings[cur_stream] = NULL;
}
@@ -919,7 +1020,6 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
dev->num_rings_cached = 0;
init_completion(&dev->cmd_completion);
- INIT_LIST_HEAD(&dev->cmd_list);
dev->udev = udev;
/* Point to output device context in dcbaa. */
@@ -1693,10 +1793,11 @@ void xhci_free_command(struct xhci_hcd *xhci,
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.controller;
- struct xhci_cd *cur_cd, *next_cd;
int size;
int i, j, num_ports;
+ del_timer_sync(&xhci->cmd_timer);
+
/* Free the Event Ring Segment Table and the actual Event Ring */
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
if (xhci->erst.entries)
@@ -1711,16 +1812,12 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
if (xhci->lpm_command)
xhci_free_command(xhci, xhci->lpm_command);
- xhci->cmd_ring_reserved_trbs = 0;
+ xhci->lpm_command = NULL;
if (xhci->cmd_ring)
xhci_ring_free(xhci, xhci->cmd_ring);
xhci->cmd_ring = NULL;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
- list_for_each_entry_safe(cur_cd, next_cd,
- &xhci->cancel_cmd_list, cancel_cmd_list) {
- list_del(&cur_cd->cancel_cmd_list);
- kfree(cur_cd);
- }
+ xhci_cleanup_command_queue(xhci);
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
for (i = 0; i < num_ports && xhci->rh_bw; i++) {
@@ -1776,6 +1873,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
}
no_bw:
+ xhci->cmd_ring_reserved_trbs = 0;
xhci->num_usb2_ports = 0;
xhci->num_usb3_ports = 0;
xhci->num_active_eps = 0;
@@ -2222,7 +2320,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
u32 page_size, temp;
int i;
- INIT_LIST_HEAD(&xhci->cancel_cmd_list);
+ INIT_LIST_HEAD(&xhci->cmd_list);
page_size = readl(&xhci->op_regs->page_size);
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
@@ -2274,11 +2372,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
/*
* Initialize the ring segment pool. The ring must be a contiguous
* structure comprised of TRBs. The TRBs must be 16 byte aligned,
- * however, the command ring segment needs 64-byte aligned segments,
- * so we pick the greater alignment need.
+ * however, the command ring segment needs 64-byte aligned segments
+ * and our use of dma addresses in the trb_address_map radix tree needs
+ * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need.
*/
xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
- TRB_SEGMENT_SIZE, 64, xhci->page_size);
+ TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
/* See Table 46 and Note on Figure 55 */
xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
@@ -2407,6 +2506,11 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
"Wrote ERST address to ir_set 0.");
xhci_print_ir_set(xhci, 0);
+ /* init command timeout timer */
+ init_timer(&xhci->cmd_timer);
+ xhci->cmd_timer.data = (unsigned long) xhci;
+ xhci->cmd_timer.function = xhci_handle_command_timeout;
+
/*
* XXX: Might need to set the Interrupter Moderation Register to
* something other than the default (~1ms minimum between interrupts).
diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c
new file mode 100644
index 000000000000..1eefc988192d
--- /dev/null
+++ b/drivers/usb/host/xhci-mvebu.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 Marvell
+ * Author: Gregory CLEMENT <gregory.clement@free-electrons.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/io.h>
+#include <linux/mbus.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "xhci-mvebu.h"
+
+#define USB3_MAX_WINDOWS 4
+#define USB3_WIN_CTRL(w) (0x0 + ((w) * 8))
+#define USB3_WIN_BASE(w) (0x4 + ((w) * 8))
+
+static void xhci_mvebu_mbus_config(void __iomem *base,
+ const struct mbus_dram_target_info *dram)
+{
+ int win;
+
+ /* Clear all existing windows */
+ for (win = 0; win < USB3_MAX_WINDOWS; win++) {
+ writel(0, base + USB3_WIN_CTRL(win));
+ writel(0, base + USB3_WIN_BASE(win));
+ }
+
+ /* Program each DRAM CS in a seperate window */
+ for (win = 0; win < dram->num_cs; win++) {
+ const struct mbus_dram_window *cs = dram->cs + win;
+
+ writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
+ (dram->mbus_dram_target_id << 4) | 1,
+ base + USB3_WIN_CTRL(win));
+
+ writel((cs->base & 0xffff0000), base + USB3_WIN_BASE(win));
+ }
+}
+
+int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *base;
+ const struct mbus_dram_target_info *dram;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return -ENODEV;
+
+ /*
+ * We don't use devm_ioremap() because this mapping should
+ * only exists for the duration of this probe function.
+ */
+ base = ioremap(res->start, resource_size(res));
+ if (!base)
+ return -ENODEV;
+
+ dram = mv_mbus_dram_info();
+ xhci_mvebu_mbus_config(base, dram);
+
+ /*
+ * This memory area was only needed to configure the MBus
+ * windows, and is therefore no longer useful.
+ */
+ iounmap(base);
+
+ return 0;
+}
diff --git a/drivers/usb/host/xhci-mvebu.h b/drivers/usb/host/xhci-mvebu.h
new file mode 100644
index 000000000000..7ede92aa41f6
--- /dev/null
+++ b/drivers/usb/host/xhci-mvebu.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory Clement <gregory.clement@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.
+ */
+
+#ifndef __LINUX_XHCI_MVEBU_H
+#define __LINUX_XHCI_MVEBU_H
+#if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
+int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev);
+#else
+static inline int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+#endif /* __LINUX_XHCI_MVEBU_H */
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 75cb1ff9d26b..319d3755e6b3 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -33,7 +33,7 @@
#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400
#define PCI_VENDOR_ID_ETRON 0x1b6f
-#define PCI_DEVICE_ID_ASROCK_P67 0x7023
+#define PCI_DEVICE_ID_EJ168 0x7023
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
@@ -138,21 +138,31 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
*/
if (pdev->subsystem_vendor == PCI_VENDOR_ID_HP)
xhci->quirks |= XHCI_SPURIOUS_WAKEUP;
-
+ }
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
- pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
+ pdev->device == PCI_DEVICE_ID_EJ168) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
- xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
- "QUIRK: Resetting on resume");
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
}
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
pdev->device == 0x0015)
xhci->quirks |= XHCI_RESET_ON_RESUME;
if (pdev->vendor == PCI_VENDOR_ID_VIA)
xhci->quirks |= XHCI_RESET_ON_RESUME;
+
+ /* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */
+ if (pdev->vendor == PCI_VENDOR_ID_VIA &&
+ pdev->device == 0x3432)
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+
+ if (xhci->quirks & XHCI_RESET_ON_RESUME)
+ xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+ "QUIRK: Resetting on resume");
}
/* called during probe() after chip reset completes */
@@ -230,11 +240,9 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto put_usb3_hcd;
/* Roothub already marked as USB 3.0 speed */
- /* We know the LPM timeout algorithms for this host, let the USB core
- * enable and disable LPM for devices under the USB 3.0 roothub.
- */
- if (xhci->quirks & XHCI_LPM_SUPPORT)
- hcd_to_bus(xhci->shared_hcd)->root_hub->lpm_capable = 1;
+ if (!(xhci->quirks & XHCI_BROKEN_STREAMS) &&
+ HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ xhci->shared_hcd->can_do_streams = 1;
/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
pm_runtime_put_noidle(&dev->dev);
@@ -281,7 +289,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
if (xhci_compliance_mode_recovery_timer_quirk_check())
pdev->no_d3cold = true;
- return xhci_suspend(xhci);
+ return xhci_suspend(xhci, do_wakeup);
}
static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 8abda5c73ca1..8b084554542e 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -11,16 +11,29 @@
* version 2 as published by the Free Software Foundation.
*/
-#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
#include <linux/module.h>
-#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/drd.h>
+#include <linux/usb/xhci_pdriver.h>
#include "xhci.h"
+#include "xhci-mvebu.h"
+#include "xhci-rcar.h"
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
+ struct usb_xhci_pdata *pdata = dev_get_platdata(dev);
+
+ if (pdata->usb_drd_support)
+ xhci->quirks |= XHCI_DRD_SUPPORT;
+
+ if (pdata->usb_needs_lhc_reset)
+ xhci->quirks |= XHCI_NEEDS_LHC_RESET;
/*
* As of now platform drivers don't provide MSI support so we ensure
* here that the generic code does not try to make a pci_dev from our
@@ -32,9 +45,30 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
/* called during probe() after chip reset completes */
static int xhci_plat_setup(struct usb_hcd *hcd)
{
+ struct device_node *of_node = hcd->self.controller->of_node;
+ int ret;
+
+ if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") ||
+ of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) {
+ ret = xhci_rcar_init_quirk(hcd);
+ if (ret)
+ return ret;
+ }
+
return xhci_gen_setup(hcd, xhci_plat_quirks);
}
+static int xhci_plat_start(struct usb_hcd *hcd)
+{
+ struct device_node *of_node = hcd->self.controller->of_node;
+
+ if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") ||
+ of_device_is_compatible(of_node, "renesas,xhci-r8a7791"))
+ xhci_rcar_start(hcd);
+
+ return xhci_run(hcd);
+}
+
static const struct hc_driver xhci_plat_xhci_driver = {
.description = "xhci-hcd",
.product_desc = "xHCI Host Controller",
@@ -50,7 +84,7 @@ static const struct hc_driver xhci_plat_xhci_driver = {
* basic lifecycle operations
*/
.reset = xhci_plat_setup,
- .start = xhci_run,
+ .start = xhci_plat_start,
.stop = xhci_stop,
.shutdown = xhci_shutdown,
@@ -83,14 +117,21 @@ static const struct hc_driver xhci_plat_xhci_driver = {
.hub_status_data = xhci_hub_status_data,
.bus_suspend = xhci_bus_suspend,
.bus_resume = xhci_bus_resume,
+
+ .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout,
+ .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
};
static int xhci_plat_probe(struct platform_device *pdev)
{
+ struct device_node *node = pdev->dev.of_node;
+ struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
const struct hc_driver *driver;
struct xhci_hcd *xhci;
struct resource *res;
struct usb_hcd *hcd;
+ struct clk *clk;
+ struct usb_drd_host *drd_host;
int ret;
int irq;
@@ -107,6 +148,15 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (!res)
return -ENODEV;
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "marvell,armada-375-xhci") ||
+ of_device_is_compatible(pdev->dev.of_node,
+ "marvell,armada-380-xhci")) {
+ ret = xhci_mvebu_mbus_init_quirk(pdev);
+ if (ret)
+ return ret;
+ }
+
/* Initialize dma_mask and coherent_dma_mask to 32-bits */
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
@@ -123,28 +173,33 @@ static int xhci_plat_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- ret = -EBUSY;
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
goto put_hcd;
}
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs) {
- dev_dbg(&pdev->dev, "error mapping memory\n");
- ret = -EFAULT;
- goto release_mem_region;
+ /*
+ * Not all platforms have a clk so it is not an error if the
+ * clock does not exists.
+ */
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(clk)) {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto put_hcd;
}
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
- goto unmap_registers;
+ goto disable_clk;
+
device_wakeup_enable(hcd->self.controller);
/* USB 2.0 roothub is stored in the platform_device now. */
hcd = platform_get_drvdata(pdev);
xhci = hcd_to_xhci(hcd);
+ xhci->clk = clk;
xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
dev_name(&pdev->dev), hcd);
if (!xhci->shared_hcd) {
@@ -152,16 +207,33 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto dealloc_usb2_hcd;
}
+ if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
+ (pdata && pdata->usb3_lpm_capable))
+ xhci->quirks |= XHCI_LPM_SUPPORT;
/*
* Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
* is called by usb_add_hcd().
*/
*((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
+ if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ xhci->shared_hcd->can_do_streams = 1;
+
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
if (ret)
goto put_usb3_hcd;
+ drd_host = kzalloc(sizeof(*drd_host), GFP_KERNEL);
+ if (!drd_host)
+ return -ENOMEM;
+
+ drd_host->main_hcd = xhci->main_hcd;
+ drd_host->shared_hcd = xhci->shared_hcd;
+ drd_host->hcd_irq = irq;
+ drd_host->host_setup = NULL;
+
+ usb_drd_register_hcd(pdev->dev.parent, drd_host);
+
return 0;
put_usb3_hcd:
@@ -170,11 +242,9 @@ put_usb3_hcd:
dealloc_usb2_hcd:
usb_remove_hcd(hcd);
-unmap_registers:
- iounmap(hcd->regs);
-
-release_mem_region:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+disable_clk:
+ if (!IS_ERR(clk))
+ clk_disable_unprepare(clk);
put_hcd:
usb_put_hcd(hcd);
@@ -186,26 +256,40 @@ static int xhci_plat_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct clk *clk = xhci->clk;
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ if (!IS_ERR(clk))
+ clk_disable_unprepare(clk);
usb_put_hcd(hcd);
+ usb_drd_unregister_hcd(dev->dev.parent);
kfree(xhci);
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int xhci_plat_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- return xhci_suspend(xhci);
+ /*
+ * xhci_suspend() needs `do_wakeup` to know whether host is allowed
+ * to do wakeup during suspend. Since xhci_plat_suspend is currently
+ * only designed for system suspend, device_may_wakeup() is enough
+ * to dertermine whether host is allowed to do wakeup. Need to
+ * reconsider this when xhci_plat_suspend enlarges its scope, e.g.,
+ * also applies to runtime suspend.
+ */
+
+ if (usb_drd_get_state(dev->parent) & DRD_HOST_ACTIVE)
+ return xhci_suspend(xhci, device_may_wakeup(dev));
+
+ return 0;
}
static int xhci_plat_resume(struct device *dev)
@@ -213,7 +297,10 @@ static int xhci_plat_resume(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- return xhci_resume(xhci, 0);
+ if (usb_drd_get_state(dev->parent) & DRD_HOST_ACTIVE)
+ return xhci_resume(xhci, 0);
+
+ return 0;
}
static const struct dev_pm_ops xhci_plat_pm_ops = {
@@ -226,7 +313,12 @@ static const struct dev_pm_ops xhci_plat_pm_ops = {
#ifdef CONFIG_OF
static const struct of_device_id usb_xhci_of_match[] = {
+ { .compatible = "generic-xhci" },
{ .compatible = "xhci-platform" },
+ { .compatible = "marvell,armada-375-xhci"},
+ { .compatible = "marvell,armada-380-xhci"},
+ { .compatible = "renesas,xhci-r8a7790"},
+ { .compatible = "renesas,xhci-r8a7791"},
{ },
};
MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
new file mode 100644
index 000000000000..ff0d1b44ea58
--- /dev/null
+++ b/drivers/usb/host/xhci-rcar.c
@@ -0,0 +1,148 @@
+/*
+ * xHCI host controller driver for R-Car SoCs
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * 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/firmware.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/phy.h>
+
+#include "xhci.h"
+#include "xhci-rcar.h"
+
+#define FIRMWARE_NAME "r8a779x_usb3_v1.dlmem"
+MODULE_FIRMWARE(FIRMWARE_NAME);
+
+/*** Register Offset ***/
+#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */
+#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */
+#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */
+
+#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */
+#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */
+#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */
+#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */
+#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */
+#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */
+
+/*** Register Settings ***/
+/* Interrupt Enable */
+#define RCAR_USB3_INT_XHC_ENA 0x00000001
+#define RCAR_USB3_INT_PME_ENA 0x00000002
+#define RCAR_USB3_INT_HSE_ENA 0x00000004
+#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \
+ RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA)
+
+/* FW Download Control & Status */
+#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001
+#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010
+#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100
+
+/* LCLK Select */
+#define RCAR_USB3_LCLK_ENA_VAL 0x01030001
+
+/* USB3.0 Configuration */
+#define RCAR_USB3_CONF1_VAL 0x00030204
+#define RCAR_USB3_CONF2_VAL 0x00030300
+#define RCAR_USB3_CONF3_VAL 0x13802007
+
+/* USB3.0 Polarity */
+#define RCAR_USB3_RX_POL_VAL BIT(21)
+#define RCAR_USB3_TX_POL_VAL BIT(4)
+
+void xhci_rcar_start(struct usb_hcd *hcd)
+{
+ u32 temp;
+
+ if (hcd->regs != NULL) {
+ /* Interrupt Enable */
+ temp = readl(hcd->regs + RCAR_USB3_INT_ENA);
+ temp |= RCAR_USB3_INT_ENA_VAL;
+ writel(temp, hcd->regs + RCAR_USB3_INT_ENA);
+ /* LCLK Select */
+ writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK);
+ /* USB3.0 Configuration */
+ writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1);
+ writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2);
+ writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3);
+ /* USB3.0 Polarity */
+ writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL);
+ writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL);
+ }
+}
+
+static int xhci_rcar_download_firmware(struct device *dev, void __iomem *regs)
+{
+ const struct firmware *fw;
+ int retval, index, j, time;
+ int timeout = 10000;
+ u32 data, val, temp;
+
+ /* request R-Car USB3.0 firmware */
+ retval = request_firmware(&fw, FIRMWARE_NAME, dev);
+ if (retval)
+ return retval;
+
+ /* download R-Car USB3.0 firmware */
+ temp = readl(regs + RCAR_USB3_DL_CTRL);
+ temp |= RCAR_USB3_DL_CTRL_ENABLE;
+ writel(temp, regs + RCAR_USB3_DL_CTRL);
+
+ for (index = 0; index < fw->size; index += 4) {
+ /* to avoid reading beyond the end of the buffer */
+ for (data = 0, j = 3; j >= 0; j--) {
+ if ((j + index) < fw->size)
+ data |= fw->data[index + j] << (8 * j);
+ }
+ writel(data, regs + RCAR_USB3_FW_DATA0);
+ temp = readl(regs + RCAR_USB3_DL_CTRL);
+ temp |= RCAR_USB3_DL_CTRL_FW_SET_DATA0;
+ writel(temp, regs + RCAR_USB3_DL_CTRL);
+
+ for (time = 0; time < timeout; time++) {
+ val = readl(regs + RCAR_USB3_DL_CTRL);
+ if ((val & RCAR_USB3_DL_CTRL_FW_SET_DATA0) == 0)
+ break;
+ udelay(1);
+ }
+ if (time == timeout) {
+ retval = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ temp = readl(regs + RCAR_USB3_DL_CTRL);
+ temp &= ~RCAR_USB3_DL_CTRL_ENABLE;
+ writel(temp, regs + RCAR_USB3_DL_CTRL);
+
+ for (time = 0; time < timeout; time++) {
+ val = readl(regs + RCAR_USB3_DL_CTRL);
+ if (val & RCAR_USB3_DL_CTRL_FW_SUCCESS) {
+ retval = 0;
+ break;
+ }
+ udelay(1);
+ }
+ if (time == timeout)
+ retval = -ETIMEDOUT;
+
+ release_firmware(fw);
+
+ return retval;
+}
+
+/* This function needs to initialize a "phy" of usb before */
+int xhci_rcar_init_quirk(struct usb_hcd *hcd)
+{
+ /* If hcd->regs is NULL, we don't just call the following function */
+ if (!hcd->regs)
+ return 0;
+
+ return xhci_rcar_download_firmware(hcd->self.controller, hcd->regs);
+}
diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h
new file mode 100644
index 000000000000..58501256715d
--- /dev/null
+++ b/drivers/usb/host/xhci-rcar.h
@@ -0,0 +1,27 @@
+/*
+ * drivers/usb/host/xhci-rcar.h
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef _XHCI_RCAR_H
+#define _XHCI_RCAR_H
+
+#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
+void xhci_rcar_start(struct usb_hcd *hcd);
+int xhci_rcar_init_quirk(struct usb_hcd *hcd);
+#else
+static inline void xhci_rcar_start(struct usb_hcd *hcd)
+{
+}
+
+static inline int xhci_rcar_init_quirk(struct usb_hcd *hcd)
+{
+ return 0;
+}
+#endif
+#endif /* _XHCI_RCAR_H */
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 0e6665a82e88..c2534ada5a03 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -69,10 +69,6 @@
#include "xhci.h"
#include "xhci-trace.h"
-static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
- struct xhci_virt_device *virt_dev,
- struct xhci_event_cmd *event);
-
/*
* Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
* address of the TRB.
@@ -123,16 +119,6 @@ static int enqueue_is_link_trb(struct xhci_ring *ring)
return TRB_TYPE_LINK_LE32(link->control);
}
-union xhci_trb *xhci_find_next_enqueue(struct xhci_ring *ring)
-{
- /* Enqueue pointer can be left pointing to the link TRB,
- * we must handle that
- */
- if (TRB_TYPE_LINK_LE32(ring->enqueue->link.control))
- return ring->enq_seg->next->trbs;
- return ring->enqueue;
-}
-
/* Updates trb to point to the next TRB in the ring, and updates seg if the next
* TRB is in a new segment. This does not skip over link TRBs, and it does not
* effect the ring dequeue or enqueue pointers.
@@ -301,17 +287,7 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
xhci_dbg(xhci, "Abort command ring\n");
- if (!(xhci->cmd_ring_state & CMD_RING_STATE_RUNNING)) {
- xhci_dbg(xhci, "The command ring isn't running, "
- "Have the command ring been stopped?\n");
- return 0;
- }
-
temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
- if (!(temp_64 & CMD_RING_RUNNING)) {
- xhci_dbg(xhci, "Command ring had been stopped\n");
- return 0;
- }
xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
&xhci->op_regs->cmd_ring);
@@ -337,71 +313,6 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
return 0;
}
-static int xhci_queue_cd(struct xhci_hcd *xhci,
- struct xhci_command *command,
- union xhci_trb *cmd_trb)
-{
- struct xhci_cd *cd;
- cd = kzalloc(sizeof(struct xhci_cd), GFP_ATOMIC);
- if (!cd)
- return -ENOMEM;
- INIT_LIST_HEAD(&cd->cancel_cmd_list);
-
- cd->command = command;
- cd->cmd_trb = cmd_trb;
- list_add_tail(&cd->cancel_cmd_list, &xhci->cancel_cmd_list);
-
- return 0;
-}
-
-/*
- * Cancel the command which has issue.
- *
- * Some commands may hang due to waiting for acknowledgement from
- * usb device. It is outside of the xHC's ability to control and
- * will cause the command ring is blocked. When it occurs software
- * should intervene to recover the command ring.
- * See Section 4.6.1.1 and 4.6.1.2
- */
-int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
- union xhci_trb *cmd_trb)
-{
- int retval = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&xhci->lock, flags);
-
- if (xhci->xhc_state & XHCI_STATE_DYING) {
- xhci_warn(xhci, "Abort the command ring,"
- " but the xHCI is dead.\n");
- retval = -ESHUTDOWN;
- goto fail;
- }
-
- /* queue the cmd desriptor to cancel_cmd_list */
- retval = xhci_queue_cd(xhci, command, cmd_trb);
- if (retval) {
- xhci_warn(xhci, "Queuing command descriptor failed.\n");
- goto fail;
- }
-
- /* abort command ring */
- retval = xhci_abort_cmd_ring(xhci);
- if (retval) {
- xhci_err(xhci, "Abort command ring failed\n");
- if (unlikely(retval == -ESHUTDOWN)) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
- xhci_dbg(xhci, "xHCI host controller is dead.\n");
- return retval;
- }
- }
-
-fail:
- spin_unlock_irqrestore(&xhci->lock, flags);
- return retval;
-}
-
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
unsigned int slot_id,
unsigned int ep_index,
@@ -453,32 +364,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
}
}
-/*
- * Find the segment that trb is in. Start searching in start_seg.
- * If we must move past a segment that has a link TRB with a toggle cycle state
- * bit set, then we will toggle the value pointed at by cycle_state.
- */
-static struct xhci_segment *find_trb_seg(
- struct xhci_segment *start_seg,
- union xhci_trb *trb, int *cycle_state)
-{
- struct xhci_segment *cur_seg = start_seg;
- struct xhci_generic_trb *generic_trb;
-
- while (cur_seg->trbs > trb ||
- &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) {
- generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic;
- if (generic_trb->field[3] & cpu_to_le32(LINK_TOGGLE))
- *cycle_state ^= 0x1;
- cur_seg = cur_seg->next;
- if (cur_seg == start_seg)
- /* Looped over the entire list. Oops! */
- return NULL;
- }
- return cur_seg;
-}
-
-
static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id)
@@ -546,11 +431,14 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
struct xhci_dequeue_state *state)
{
struct xhci_virt_device *dev = xhci->devs[slot_id];
+ struct xhci_virt_ep *ep = &dev->eps[ep_index];
struct xhci_ring *ep_ring;
- struct xhci_generic_trb *trb;
- struct xhci_ep_ctx *ep_ctx;
+ struct xhci_segment *new_seg;
+ union xhci_trb *new_deq;
dma_addr_t addr;
u64 hw_dequeue;
+ bool cycle_found = false;
+ bool td_last_trb_found = false;
ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
ep_index, stream_id);
@@ -564,48 +452,56 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
/* Dig out the cycle state saved by the xHC during the stop ep cmd */
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Finding endpoint context");
- ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
- hw_dequeue = le64_to_cpu(ep_ctx->deq);
-
- /* Find virtual address and segment of hardware dequeue pointer */
- state->new_deq_seg = ep_ring->deq_seg;
- state->new_deq_ptr = ep_ring->dequeue;
- while (xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr)
- != (dma_addr_t)(hw_dequeue & ~0xf)) {
- next_trb(xhci, ep_ring, &state->new_deq_seg,
- &state->new_deq_ptr);
- if (state->new_deq_ptr == ep_ring->dequeue) {
- WARN_ON(1);
- return;
- }
+ /* 4.6.9 the css flag is written to the stream context for streams */
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ struct xhci_stream_ctx *ctx =
+ &ep->stream_info->stream_ctx_array[stream_id];
+ hw_dequeue = le64_to_cpu(ctx->stream_ring);
+ } else {
+ struct xhci_ep_ctx *ep_ctx
+ = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
+ hw_dequeue = le64_to_cpu(ep_ctx->deq);
}
+
+ new_seg = ep_ring->deq_seg;
+ new_deq = ep_ring->dequeue;
+ state->new_cycle_state = hw_dequeue & 0x1;
+
/*
- * Find cycle state for last_trb, starting at old cycle state of
- * hw_dequeue. If there is only one segment ring, find_trb_seg() will
- * return immediately and cannot toggle the cycle state if this search
- * wraps around, so add one more toggle manually in that case.
+ * We want to find the pointer, segment and cycle state of the new trb
+ * (the one after current TD's last_trb). We know the cycle state at
+ * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are
+ * found.
*/
- state->new_cycle_state = hw_dequeue & 0x1;
- if (ep_ring->first_seg == ep_ring->first_seg->next &&
- cur_td->last_trb < state->new_deq_ptr)
- state->new_cycle_state ^= 0x1;
+ do {
+ if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq)
+ == (dma_addr_t)(hw_dequeue & ~0xf)) {
+ cycle_found = true;
+ if (td_last_trb_found)
+ break;
+ }
+ if (new_deq == cur_td->last_trb)
+ td_last_trb_found = true;
- state->new_deq_ptr = cur_td->last_trb;
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Finding segment containing last TRB in TD.");
- state->new_deq_seg = find_trb_seg(state->new_deq_seg,
- state->new_deq_ptr, &state->new_cycle_state);
- if (!state->new_deq_seg) {
- WARN_ON(1);
- return;
- }
+ if (cycle_found &&
+ TRB_TYPE_LINK_LE32(new_deq->generic.field[3]) &&
+ new_deq->generic.field[3] & cpu_to_le32(LINK_TOGGLE))
+ state->new_cycle_state ^= 0x1;
+
+ next_trb(xhci, ep_ring, &new_seg, &new_deq);
- /* Increment to find next TRB after last_trb. Cycle if appropriate. */
- trb = &state->new_deq_ptr->generic;
- if (TRB_TYPE_LINK_LE32(trb->field[3]) &&
- (trb->field[3] & cpu_to_le32(LINK_TOGGLE)))
- state->new_cycle_state ^= 0x1;
- next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr);
+ /* Search wrapped around, bail out */
+ if (new_deq == ep->ring->dequeue) {
+ xhci_err(xhci, "Error: Failed finding new dequeue state\n");
+ state->new_deq_seg = NULL;
+ state->new_deq_ptr = NULL;
+ return;
+ }
+
+ } while (!cycle_found || !td_last_trb_found);
+
+ state->new_deq_seg = new_seg;
+ state->new_deq_ptr = new_deq;
/* Don't update the ring cycle state for the producer (us). */
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
@@ -676,12 +572,14 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
}
}
-static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
+static int queue_set_tr_deq(struct xhci_hcd *xhci,
+ struct xhci_command *cmd, int slot_id,
unsigned int ep_index, unsigned int stream_id,
struct xhci_segment *deq_seg,
union xhci_trb *deq_ptr, u32 cycle_state);
void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
+ struct xhci_command *cmd,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id,
struct xhci_dequeue_state *deq_state)
@@ -696,7 +594,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
deq_state->new_deq_ptr,
(unsigned long long)xhci_trb_virt_to_dma(deq_state->new_deq_seg, deq_state->new_deq_ptr),
deq_state->new_cycle_state);
- queue_set_tr_deq(xhci, slot_id, ep_index, stream_id,
+ queue_set_tr_deq(xhci, cmd, slot_id, ep_index, stream_id,
deq_state->new_deq_seg,
deq_state->new_deq_ptr,
(u32) deq_state->new_cycle_state);
@@ -765,7 +663,6 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
union xhci_trb *trb, struct xhci_event_cmd *event)
{
unsigned int ep_index;
- struct xhci_virt_device *virt_dev;
struct xhci_ring *ep_ring;
struct xhci_virt_ep *ep;
struct list_head *entry;
@@ -775,11 +672,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
struct xhci_dequeue_state deq_state;
if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) {
- virt_dev = xhci->devs[slot_id];
- if (virt_dev)
- handle_cmd_in_cmd_wait_list(xhci, virt_dev,
- event);
- else
+ if (!xhci->devs[slot_id])
xhci_warn(xhci, "Stop endpoint command "
"completion for disabled slot %u\n",
slot_id);
@@ -850,7 +743,9 @@ remove_finished_td:
/* If necessary, queue a Set Transfer Ring Dequeue Pointer command */
if (deq_state.new_deq_ptr && deq_state.new_deq_seg) {
- xhci_queue_new_dequeue_state(xhci,
+ struct xhci_command *command;
+ command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ xhci_queue_new_dequeue_state(xhci, command,
slot_id, ep_index,
ep->stopped_td->urb->stream_id,
&deq_state);
@@ -891,6 +786,57 @@ remove_finished_td:
/* Return to the event handler with xhci->lock re-acquired */
}
+static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring)
+{
+ struct xhci_td *cur_td;
+
+ while (!list_empty(&ring->td_list)) {
+ cur_td = list_first_entry(&ring->td_list,
+ struct xhci_td, td_list);
+ list_del_init(&cur_td->td_list);
+ if (!list_empty(&cur_td->cancelled_td_list))
+ list_del_init(&cur_td->cancelled_td_list);
+ xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
+ }
+}
+
+static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
+ int slot_id, int ep_index)
+{
+ struct xhci_td *cur_td;
+ struct xhci_virt_ep *ep;
+ struct xhci_ring *ring;
+
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ if ((ep->ep_state & EP_HAS_STREAMS) ||
+ (ep->ep_state & EP_GETTING_NO_STREAMS)) {
+ int stream_id;
+
+ for (stream_id = 0; stream_id < ep->stream_info->num_streams;
+ stream_id++) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Killing URBs for slot ID %u, ep index %u, stream %u",
+ slot_id, ep_index, stream_id + 1);
+ xhci_kill_ring_urbs(xhci,
+ ep->stream_info->stream_rings[stream_id]);
+ }
+ } else {
+ ring = ep->ring;
+ if (!ring)
+ return;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+ "Killing URBs for slot ID %u, ep index %u",
+ slot_id, ep_index);
+ xhci_kill_ring_urbs(xhci, ring);
+ }
+ while (!list_empty(&ep->cancelled_td_list)) {
+ cur_td = list_first_entry(&ep->cancelled_td_list,
+ struct xhci_td, cancelled_td_list);
+ list_del_init(&cur_td->cancelled_td_list);
+ xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
+ }
+}
+
/* Watchdog timer function for when a stop endpoint command fails to complete.
* In this case, we assume the host controller is broken or dying or dead. The
* host may still be completing some other events, so we have to be careful to
@@ -914,9 +860,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
{
struct xhci_hcd *xhci;
struct xhci_virt_ep *ep;
- struct xhci_virt_ep *temp_ep;
- struct xhci_ring *ring;
- struct xhci_td *cur_td;
int ret, i, j;
unsigned long flags;
@@ -973,34 +916,8 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
for (i = 0; i < MAX_HC_SLOTS; i++) {
if (!xhci->devs[i])
continue;
- for (j = 0; j < 31; j++) {
- temp_ep = &xhci->devs[i]->eps[j];
- ring = temp_ep->ring;
- if (!ring)
- continue;
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Killing URBs for slot ID %u, "
- "ep index %u", i, j);
- while (!list_empty(&ring->td_list)) {
- cur_td = list_first_entry(&ring->td_list,
- struct xhci_td,
- td_list);
- list_del_init(&cur_td->td_list);
- if (!list_empty(&cur_td->cancelled_td_list))
- list_del_init(&cur_td->cancelled_td_list);
- xhci_giveback_urb_in_irq(xhci, cur_td,
- -ESHUTDOWN);
- }
- while (!list_empty(&temp_ep->cancelled_td_list)) {
- cur_td = list_first_entry(
- &temp_ep->cancelled_td_list,
- struct xhci_td,
- cancelled_td_list);
- list_del_init(&cur_td->cancelled_td_list);
- xhci_giveback_urb_in_irq(xhci, cur_td,
- -ESHUTDOWN);
- }
- }
+ for (j = 0; j < 31; j++)
+ xhci_kill_endpoint_urbs(xhci, i, j);
}
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
@@ -1072,17 +989,18 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
unsigned int stream_id;
struct xhci_ring *ep_ring;
struct xhci_virt_device *dev;
+ struct xhci_virt_ep *ep;
struct xhci_ep_ctx *ep_ctx;
struct xhci_slot_ctx *slot_ctx;
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
dev = xhci->devs[slot_id];
+ ep = &dev->eps[ep_index];
ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id);
if (!ep_ring) {
- xhci_warn(xhci, "WARN Set TR deq ptr command for "
- "freed stream ID %u\n",
+ xhci_warn(xhci, "WARN Set TR deq ptr command for freed stream ID %u\n",
stream_id);
/* XXX: Harmless??? */
dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
@@ -1098,12 +1016,10 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
switch (cmd_comp_code) {
case COMP_TRB_ERR:
- xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because "
- "of stream ID configuration\n");
+ xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because of stream ID configuration\n");
break;
case COMP_CTX_STATE:
- xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due "
- "to incorrect slot or ep state.\n");
+ xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state.\n");
ep_state = le32_to_cpu(ep_ctx->ep_info);
ep_state &= EP_STATE_MASK;
slot_state = le32_to_cpu(slot_ctx->dev_state);
@@ -1113,13 +1029,12 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
slot_state, ep_state);
break;
case COMP_EBADSLT:
- xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because "
- "slot %u was not enabled.\n", slot_id);
+ xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because slot %u was not enabled.\n",
+ slot_id);
break;
default:
- xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown "
- "completion code of %u.\n",
- cmd_comp_code);
+ xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown completion code of %u.\n",
+ cmd_comp_code);
break;
}
/* OK what do we do now? The endpoint state is hosed, and we
@@ -1129,23 +1044,28 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
* cancelling URBs, which might not be an error...
*/
} else {
+ u64 deq;
+ /* 4.6.10 deq ptr is written to the stream ctx for streams */
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ struct xhci_stream_ctx *ctx =
+ &ep->stream_info->stream_ctx_array[stream_id];
+ deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
+ } else {
+ deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
+ }
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Successful Set TR Deq Ptr cmd, deq = @%08llx",
- le64_to_cpu(ep_ctx->deq));
- if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg,
- dev->eps[ep_index].queued_deq_ptr) ==
- (le64_to_cpu(ep_ctx->deq) & ~(EP_CTX_CYCLE_MASK))) {
+ "Successful Set TR Deq Ptr cmd, deq = @%08llx", deq);
+ if (xhci_trb_virt_to_dma(ep->queued_deq_seg,
+ ep->queued_deq_ptr) == deq) {
/* Update the ring's dequeue segment and dequeue pointer
* to reflect the new position.
*/
update_ring_for_set_deq_completion(xhci, dev,
ep_ring, ep_index);
} else {
- xhci_warn(xhci, "Mismatch between completed Set TR Deq "
- "Ptr command & xHCI internal state.\n");
+ xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n");
xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n",
- dev->eps[ep_index].queued_deq_seg,
- dev->eps[ep_index].queued_deq_ptr);
+ ep->queued_deq_seg, ep->queued_deq_ptr);
}
}
@@ -1173,200 +1093,24 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
* because the HW can't handle two commands being queued in a row.
*/
if (xhci->quirks & XHCI_RESET_EP_QUIRK) {
+ struct xhci_command *command;
+ command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command) {
+ xhci_warn(xhci, "WARN Cannot submit cfg ep: ENOMEM\n");
+ return;
+ }
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"Queueing configure endpoint command");
- xhci_queue_configure_endpoint(xhci,
+ xhci_queue_configure_endpoint(xhci, command,
xhci->devs[slot_id]->in_ctx->dma, slot_id,
false);
xhci_ring_cmd_db(xhci);
} else {
- /* Clear our internal halted state and restart the ring(s) */
+ /* Clear our internal halted state */
xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
- ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}
}
-/* Complete the command and detele it from the devcie's command queue.
- */
-static void xhci_complete_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
- struct xhci_command *command, u32 status)
-{
- command->status = status;
- list_del(&command->cmd_list);
- if (command->completion)
- complete(command->completion);
- else
- xhci_free_command(xhci, command);
-}
-
-
-/* Check to see if a command in the device's command queue matches this one.
- * Signal the completion or free the command, and return 1. Return 0 if the
- * completed command isn't at the head of the command list.
- */
-static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
- struct xhci_virt_device *virt_dev,
- struct xhci_event_cmd *event)
-{
- struct xhci_command *command;
-
- if (list_empty(&virt_dev->cmd_list))
- return 0;
-
- command = list_entry(virt_dev->cmd_list.next,
- struct xhci_command, cmd_list);
- if (xhci->cmd_ring->dequeue != command->command_trb)
- return 0;
-
- xhci_complete_cmd_in_cmd_wait_list(xhci, command,
- GET_COMP_CODE(le32_to_cpu(event->status)));
- return 1;
-}
-
-/*
- * Finding the command trb need to be cancelled and modifying it to
- * NO OP command. And if the command is in device's command wait
- * list, finishing and freeing it.
- *
- * If we can't find the command trb, we think it had already been
- * executed.
- */
-static void xhci_cmd_to_noop(struct xhci_hcd *xhci, struct xhci_cd *cur_cd)
-{
- struct xhci_segment *cur_seg;
- union xhci_trb *cmd_trb;
- u32 cycle_state;
-
- if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue)
- return;
-
- /* find the current segment of command ring */
- cur_seg = find_trb_seg(xhci->cmd_ring->first_seg,
- xhci->cmd_ring->dequeue, &cycle_state);
-
- if (!cur_seg) {
- xhci_warn(xhci, "Command ring mismatch, dequeue = %p %llx (dma)\n",
- xhci->cmd_ring->dequeue,
- (unsigned long long)
- xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
- xhci->cmd_ring->dequeue));
- xhci_debug_ring(xhci, xhci->cmd_ring);
- xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring);
- return;
- }
-
- /* find the command trb matched by cd from command ring */
- for (cmd_trb = xhci->cmd_ring->dequeue;
- cmd_trb != xhci->cmd_ring->enqueue;
- next_trb(xhci, xhci->cmd_ring, &cur_seg, &cmd_trb)) {
- /* If the trb is link trb, continue */
- if (TRB_TYPE_LINK_LE32(cmd_trb->generic.field[3]))
- continue;
-
- if (cur_cd->cmd_trb == cmd_trb) {
-
- /* If the command in device's command list, we should
- * finish it and free the command structure.
- */
- if (cur_cd->command)
- xhci_complete_cmd_in_cmd_wait_list(xhci,
- cur_cd->command, COMP_CMD_STOP);
-
- /* get cycle state from the origin command trb */
- cycle_state = le32_to_cpu(cmd_trb->generic.field[3])
- & TRB_CYCLE;
-
- /* modify the command trb to NO OP command */
- cmd_trb->generic.field[0] = 0;
- cmd_trb->generic.field[1] = 0;
- cmd_trb->generic.field[2] = 0;
- cmd_trb->generic.field[3] = cpu_to_le32(
- TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
- break;
- }
- }
-}
-
-static void xhci_cancel_cmd_in_cd_list(struct xhci_hcd *xhci)
-{
- struct xhci_cd *cur_cd, *next_cd;
-
- if (list_empty(&xhci->cancel_cmd_list))
- return;
-
- list_for_each_entry_safe(cur_cd, next_cd,
- &xhci->cancel_cmd_list, cancel_cmd_list) {
- xhci_cmd_to_noop(xhci, cur_cd);
- list_del(&cur_cd->cancel_cmd_list);
- kfree(cur_cd);
- }
-}
-
-/*
- * traversing the cancel_cmd_list. If the command descriptor according
- * to cmd_trb is found, the function free it and return 1, otherwise
- * return 0.
- */
-static int xhci_search_cmd_trb_in_cd_list(struct xhci_hcd *xhci,
- union xhci_trb *cmd_trb)
-{
- struct xhci_cd *cur_cd, *next_cd;
-
- if (list_empty(&xhci->cancel_cmd_list))
- return 0;
-
- list_for_each_entry_safe(cur_cd, next_cd,
- &xhci->cancel_cmd_list, cancel_cmd_list) {
- if (cur_cd->cmd_trb == cmd_trb) {
- if (cur_cd->command)
- xhci_complete_cmd_in_cmd_wait_list(xhci,
- cur_cd->command, COMP_CMD_STOP);
- list_del(&cur_cd->cancel_cmd_list);
- kfree(cur_cd);
- return 1;
- }
- }
-
- return 0;
-}
-
-/*
- * If the cmd_trb_comp_code is COMP_CMD_ABORT, we just check whether the
- * trb pointed by the command ring dequeue pointer is the trb we want to
- * cancel or not. And if the cmd_trb_comp_code is COMP_CMD_STOP, we will
- * traverse the cancel_cmd_list to trun the all of the commands according
- * to command descriptor to NO-OP trb.
- */
-static int handle_stopped_cmd_ring(struct xhci_hcd *xhci,
- int cmd_trb_comp_code)
-{
- int cur_trb_is_good = 0;
-
- /* Searching the cmd trb pointed by the command ring dequeue
- * pointer in command descriptor list. If it is found, free it.
- */
- cur_trb_is_good = xhci_search_cmd_trb_in_cd_list(xhci,
- xhci->cmd_ring->dequeue);
-
- if (cmd_trb_comp_code == COMP_CMD_ABORT)
- xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- else if (cmd_trb_comp_code == COMP_CMD_STOP) {
- /* traversing the cancel_cmd_list and canceling
- * the command according to command descriptor
- */
- xhci_cancel_cmd_in_cd_list(xhci);
-
- xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
- /*
- * ring command ring doorbell again to restart the
- * command ring
- */
- if (xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue)
- xhci_ring_cmd_db(xhci);
- }
- return cur_trb_is_good;
-}
-
static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
u32 cmd_comp_code)
{
@@ -1374,7 +1118,6 @@ static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
xhci->slot_id = slot_id;
else
xhci->slot_id = 0;
- complete(&xhci->addr_dev);
}
static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
@@ -1399,9 +1142,6 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
unsigned int ep_state;
u32 add_flags, drop_flags;
- virt_dev = xhci->devs[slot_id];
- if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
- return;
/*
* Configure endpoint commands can come from the USB core
* configuration or alt setting changes, or because the HW
@@ -1410,6 +1150,7 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
* If the command was for a halted endpoint, the xHCI driver
* is not waiting on the configure endpoint command.
*/
+ virt_dev = xhci->devs[slot_id];
ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "Could not get input context, bad type.\n");
@@ -1432,7 +1173,7 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
add_flags - SLOT_FLAG == drop_flags) {
ep_state = virt_dev->eps[ep_index].ep_state;
if (!(ep_state & EP_HALTED))
- goto bandwidth_change;
+ return;
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"Completed config ep cmd - "
"last ep index = %d, state = %d",
@@ -1442,43 +1183,14 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
return;
}
-bandwidth_change:
- xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
- "Completed config ep cmd");
- virt_dev->cmd_status = cmd_comp_code;
- complete(&virt_dev->cmd_completion);
return;
}
-static void xhci_handle_cmd_eval_ctx(struct xhci_hcd *xhci, int slot_id,
- struct xhci_event_cmd *event, u32 cmd_comp_code)
-{
- struct xhci_virt_device *virt_dev;
-
- virt_dev = xhci->devs[slot_id];
- if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
- return;
- virt_dev->cmd_status = cmd_comp_code;
- complete(&virt_dev->cmd_completion);
-}
-
-static void xhci_handle_cmd_addr_dev(struct xhci_hcd *xhci, int slot_id,
- u32 cmd_comp_code)
-{
- xhci->devs[slot_id]->cmd_status = cmd_comp_code;
- complete(&xhci->addr_dev);
-}
-
static void xhci_handle_cmd_reset_dev(struct xhci_hcd *xhci, int slot_id,
struct xhci_event_cmd *event)
{
- struct xhci_virt_device *virt_dev;
-
xhci_dbg(xhci, "Completed reset device command.\n");
- virt_dev = xhci->devs[slot_id];
- if (virt_dev)
- handle_cmd_in_cmd_wait_list(xhci, virt_dev, event);
- else
+ if (!xhci->devs[slot_id])
xhci_warn(xhci, "Reset device command completion "
"for disabled slot %u\n", slot_id);
}
@@ -1496,6 +1208,116 @@ static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
NEC_FW_MINOR(le32_to_cpu(event->status)));
}
+static void xhci_complete_del_and_free_cmd(struct xhci_command *cmd, u32 status)
+{
+ list_del(&cmd->cmd_list);
+
+ if (cmd->completion) {
+ cmd->status = status;
+ complete(cmd->completion);
+ } else {
+ kfree(cmd);
+ }
+}
+
+void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
+{
+ struct xhci_command *cur_cmd, *tmp_cmd;
+ list_for_each_entry_safe(cur_cmd, tmp_cmd, &xhci->cmd_list, cmd_list)
+ xhci_complete_del_and_free_cmd(cur_cmd, COMP_CMD_ABORT);
+}
+
+/*
+ * Turn all commands on command ring with status set to "aborted" to no-op trbs.
+ * If there are other commands waiting then restart the ring and kick the timer.
+ * This must be called with command ring stopped and xhci->lock held.
+ */
+static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
+ struct xhci_command *cur_cmd)
+{
+ struct xhci_command *i_cmd, *tmp_cmd;
+ u32 cycle_state;
+
+ /* Turn all aborted commands in list to no-ops, then restart */
+ list_for_each_entry_safe(i_cmd, tmp_cmd, &xhci->cmd_list,
+ cmd_list) {
+
+ if (i_cmd->status != COMP_CMD_ABORT)
+ continue;
+
+ i_cmd->status = COMP_CMD_STOP;
+
+ xhci_dbg(xhci, "Turn aborted command %p to no-op\n",
+ i_cmd->command_trb);
+ /* get cycle state from the original cmd trb */
+ cycle_state = le32_to_cpu(
+ i_cmd->command_trb->generic.field[3]) & TRB_CYCLE;
+ /* modify the command trb to no-op command */
+ i_cmd->command_trb->generic.field[0] = 0;
+ i_cmd->command_trb->generic.field[1] = 0;
+ i_cmd->command_trb->generic.field[2] = 0;
+ i_cmd->command_trb->generic.field[3] = cpu_to_le32(
+ TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
+
+ /*
+ * caller waiting for completion is called when command
+ * completion event is received for these no-op commands
+ */
+ }
+
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
+
+ /* ring command ring doorbell to restart the command ring */
+ if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
+ !(xhci->xhc_state & XHCI_STATE_DYING)) {
+ xhci->current_cmd = cur_cmd;
+ mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_ring_cmd_db(xhci);
+ }
+ return;
+}
+
+
+void xhci_handle_command_timeout(unsigned long data)
+{
+ struct xhci_hcd *xhci;
+ int ret;
+ unsigned long flags;
+ u64 hw_ring_state;
+ struct xhci_command *cur_cmd = NULL;
+ xhci = (struct xhci_hcd *) data;
+
+ /* mark this command to be cancelled */
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->current_cmd) {
+ cur_cmd = xhci->current_cmd;
+ cur_cmd->status = COMP_CMD_ABORT;
+ }
+
+
+ /* Make sure command ring is running before aborting it */
+ hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) &&
+ (hw_ring_state & CMD_RING_RUNNING)) {
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg(xhci, "Command timeout\n");
+ ret = xhci_abort_cmd_ring(xhci);
+ if (unlikely(ret == -ESHUTDOWN)) {
+ xhci_err(xhci, "Abort command ring failed\n");
+ xhci_cleanup_command_queue(xhci);
+ usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+ xhci_dbg(xhci, "xHCI host controller is dead.\n");
+ }
+ return;
+ }
+ /* command timeout on stopped ring, ring can't be aborted */
+ xhci_dbg(xhci, "Command timeout on stopped ring\n");
+ xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return;
+}
+
static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_event_cmd *event)
{
@@ -1504,6 +1326,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
dma_addr_t cmd_dequeue_dma;
u32 cmd_comp_code;
union xhci_trb *cmd_trb;
+ struct xhci_command *cmd;
u32 cmd_type;
cmd_dma = le64_to_cpu(event->cmd_trb);
@@ -1521,26 +1344,35 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
return;
}
+ cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
+
+ if (cmd->command_trb != xhci->cmd_ring->dequeue) {
+ xhci_err(xhci,
+ "Command completion event does not match command\n");
+ return;
+ }
+
+ del_timer(&xhci->cmd_timer);
+
trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status));
- if (cmd_comp_code == COMP_CMD_ABORT || cmd_comp_code == COMP_CMD_STOP) {
- /* If the return value is 0, we think the trb pointed by
- * command ring dequeue pointer is a good trb. The good
- * trb means we don't want to cancel the trb, but it have
- * been stopped by host. So we should handle it normally.
- * Otherwise, driver should invoke inc_deq() and return.
- */
- if (handle_stopped_cmd_ring(xhci, cmd_comp_code)) {
- inc_deq(xhci, xhci->cmd_ring);
- return;
- }
- /* There is no command to handle if we get a stop event when the
- * command ring is empty, event->cmd_trb points to the next
- * unset command
- */
- if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue)
- return;
+
+ /* If CMD ring stopped we own the trbs between enqueue and dequeue */
+ if (cmd_comp_code == COMP_CMD_STOP) {
+ xhci_handle_stopped_cmd_ring(xhci, cmd);
+ return;
+ }
+ /*
+ * Host aborted the command ring, check if the current command was
+ * supposed to be aborted, otherwise continue normally.
+ * The command ring is stopped now, but the xHC will issue a Command
+ * Ring Stopped event which will cause us to restart it.
+ */
+ if (cmd_comp_code == COMP_CMD_ABORT) {
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ if (cmd->status == COMP_CMD_ABORT)
+ goto event_handled;
}
cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
@@ -1552,13 +1384,13 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
xhci_handle_cmd_disable_slot(xhci, slot_id);
break;
case TRB_CONFIG_EP:
- xhci_handle_cmd_config_ep(xhci, slot_id, event, cmd_comp_code);
+ if (!cmd->completion)
+ xhci_handle_cmd_config_ep(xhci, slot_id, event,
+ cmd_comp_code);
break;
case TRB_EVAL_CONTEXT:
- xhci_handle_cmd_eval_ctx(xhci, slot_id, event, cmd_comp_code);
break;
case TRB_ADDR_DEV:
- xhci_handle_cmd_addr_dev(xhci, slot_id, cmd_comp_code);
break;
case TRB_STOP_RING:
WARN_ON(slot_id != TRB_TO_SLOT_ID(
@@ -1571,6 +1403,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
xhci_handle_cmd_set_deq(xhci, slot_id, cmd_trb, cmd_comp_code);
break;
case TRB_CMD_NOOP:
+ /* Is this an aborted command turned to NO-OP? */
+ if (cmd->status == COMP_CMD_STOP)
+ cmd_comp_code = COMP_CMD_STOP;
break;
case TRB_RESET_EP:
WARN_ON(slot_id != TRB_TO_SLOT_ID(
@@ -1593,6 +1428,17 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
xhci->error_bitmask |= 1 << 6;
break;
}
+
+ /* restart timer if this wasn't the last command */
+ if (cmd->cmd_list.next != &xhci->cmd_list) {
+ xhci->current_cmd = list_entry(cmd->cmd_list.next,
+ struct xhci_command, cmd_list);
+ mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ }
+
+event_handled:
+ xhci_complete_del_and_free_cmd(cmd, cmd_comp_code);
+
inc_deq(xhci, xhci->cmd_ring);
}
@@ -1908,11 +1754,16 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
struct xhci_td *td, union xhci_trb *event_trb)
{
struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
+ struct xhci_command *command;
+ command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command)
+ return;
+
ep->ep_state |= EP_HALTED;
ep->stopped_td = td;
ep->stopped_stream = stream_id;
- xhci_queue_reset_ep(xhci, slot_id, ep_index);
+ xhci_queue_reset_ep(xhci, command, slot_id, ep_index);
xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index);
ep->stopped_td = NULL;
@@ -3967,11 +3818,14 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
* Don't decrement xhci->cmd_ring_reserved_trbs after we've queued the TRB
* because the command event handler may want to resubmit a failed command.
*/
-static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2,
- u32 field3, u32 field4, bool command_must_succeed)
+static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 field1, u32 field2,
+ u32 field3, u32 field4, bool command_must_succeed)
{
int reserved_trbs = xhci->cmd_ring_reserved_trbs;
int ret;
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ return -ESHUTDOWN;
if (!command_must_succeed)
reserved_trbs++;
@@ -3985,57 +3839,71 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2,
"unfailable commands failed.\n");
return ret;
}
+
+ cmd->command_trb = xhci->cmd_ring->enqueue;
+ list_add_tail(&cmd->cmd_list, &xhci->cmd_list);
+
+ /* if there are no other commands queued we start the timeout timer */
+ if (xhci->cmd_list.next == &cmd->cmd_list &&
+ !timer_pending(&xhci->cmd_timer)) {
+ xhci->current_cmd = cmd;
+ mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ }
+
queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
field4 | xhci->cmd_ring->cycle_state);
return 0;
}
/* Queue a slot enable or disable request on the command ring */
-int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id)
+int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 trb_type, u32 slot_id)
{
- return queue_command(xhci, 0, 0, 0,
+ return queue_command(xhci, cmd, 0, 0, 0,
TRB_TYPE(trb_type) | SLOT_ID_FOR_TRB(slot_id), false);
}
/* Queue an address device command TRB */
-int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
- u32 slot_id, enum xhci_setup_dev setup)
+int xhci_queue_address_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ dma_addr_t in_ctx_ptr, u32 slot_id, enum xhci_setup_dev setup)
{
- return queue_command(xhci, lower_32_bits(in_ctx_ptr),
+ return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
upper_32_bits(in_ctx_ptr), 0,
TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(slot_id)
| (setup == SETUP_CONTEXT_ONLY ? TRB_BSR : 0), false);
}
-int xhci_queue_vendor_command(struct xhci_hcd *xhci,
+int xhci_queue_vendor_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
u32 field1, u32 field2, u32 field3, u32 field4)
{
- return queue_command(xhci, field1, field2, field3, field4, false);
+ return queue_command(xhci, cmd, field1, field2, field3, field4, false);
}
/* Queue a reset device command TRB */
-int xhci_queue_reset_device(struct xhci_hcd *xhci, u32 slot_id)
+int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 slot_id)
{
- return queue_command(xhci, 0, 0, 0,
+ return queue_command(xhci, cmd, 0, 0, 0,
TRB_TYPE(TRB_RESET_DEV) | SLOT_ID_FOR_TRB(slot_id),
false);
}
/* Queue a configure endpoint command TRB */
-int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
+int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
+ struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
u32 slot_id, bool command_must_succeed)
{
- return queue_command(xhci, lower_32_bits(in_ctx_ptr),
+ return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
upper_32_bits(in_ctx_ptr), 0,
TRB_TYPE(TRB_CONFIG_EP) | SLOT_ID_FOR_TRB(slot_id),
command_must_succeed);
}
/* Queue an evaluate context command TRB */
-int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
- u32 slot_id, bool command_must_succeed)
+int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed)
{
- return queue_command(xhci, lower_32_bits(in_ctx_ptr),
+ return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
upper_32_bits(in_ctx_ptr), 0,
TRB_TYPE(TRB_EVAL_CONTEXT) | SLOT_ID_FOR_TRB(slot_id),
command_must_succeed);
@@ -4045,30 +3913,32 @@ int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
* Suspend is set to indicate "Stop Endpoint Command" is being issued to stop
* activity on an endpoint that is about to be suspended.
*/
-int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
- unsigned int ep_index, int suspend)
+int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ int slot_id, unsigned int ep_index, int suspend)
{
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
u32 type = TRB_TYPE(TRB_STOP_RING);
u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend);
- return queue_command(xhci, 0, 0, 0,
+ return queue_command(xhci, cmd, 0, 0, 0,
trb_slot_id | trb_ep_index | type | trb_suspend, false);
}
/* Set Transfer Ring Dequeue Pointer command.
* This should not be used for endpoints that have streams enabled.
*/
-static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
- unsigned int ep_index, unsigned int stream_id,
- struct xhci_segment *deq_seg,
- union xhci_trb *deq_ptr, u32 cycle_state)
+static int queue_set_tr_deq(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ int slot_id,
+ unsigned int ep_index, unsigned int stream_id,
+ struct xhci_segment *deq_seg,
+ union xhci_trb *deq_ptr, u32 cycle_state)
{
dma_addr_t addr;
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id);
+ u32 trb_sct = 0;
u32 type = TRB_TYPE(TRB_SET_DEQ);
struct xhci_virt_ep *ep;
@@ -4087,18 +3957,21 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
}
ep->queued_deq_seg = deq_seg;
ep->queued_deq_ptr = deq_ptr;
- return queue_command(xhci, lower_32_bits(addr) | cycle_state,
+ if (stream_id)
+ trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
+ return queue_command(xhci, cmd,
+ lower_32_bits(addr) | trb_sct | cycle_state,
upper_32_bits(addr), trb_stream_id,
trb_slot_id | trb_ep_index | type, false);
}
-int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id,
- unsigned int ep_index)
+int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ int slot_id, unsigned int ep_index)
{
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
u32 type = TRB_TYPE(TRB_RESET_EP);
- return queue_command(xhci, 0, 0, 0, trb_slot_id | trb_ep_index | type,
- false);
+ return queue_command(xhci, cmd, 0, 0, 0,
+ trb_slot_id | trb_ep_index | type, false);
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 82b563fc4fd6..065230578147 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -35,6 +35,8 @@
#define DRIVER_AUTHOR "Sarah Sharp"
#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
+#define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E)
+
/* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */
static int link_quirk;
module_param(link_quirk, int, S_IRUGO | S_IWUSR);
@@ -170,11 +172,12 @@ int xhci_reset(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Reset the HC");
command = readl(&xhci->op_regs->command);
- command |= CMD_RESET;
+ command |= (xhci->quirks & XHCI_NEEDS_LHC_RESET) ? CMD_LRESET : CMD_RESET;
writel(command, &xhci->op_regs->command);
ret = xhci_handshake(xhci, &xhci->op_regs->command,
- CMD_RESET, 0, 10 * 1000 * 1000);
+ (xhci->quirks & XHCI_NEEDS_LHC_RESET) ? CMD_LRESET : CMD_RESET,
+ 0, 10 * 1000 * 1000);
if (ret)
return ret;
@@ -291,7 +294,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
xhci->msix_entries[i].vector = 0;
}
- ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count);
+ ret = pci_enable_msix_exact(pdev, xhci->msix_entries, xhci->msix_count);
if (ret) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Failed to enable MSI-X");
@@ -390,6 +393,10 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
}
legacy_irq:
+ if (!strlen(hcd->irq_descr))
+ snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
+ hcd->driver->description, hcd->self.busnum);
+
/* fall back to legacy interrupt*/
ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
hcd->irq_descr, hcd);
@@ -637,10 +644,14 @@ int xhci_run(struct usb_hcd *hcd)
writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending);
xhci_print_ir_set(xhci, 0);
- if (xhci->quirks & XHCI_NEC_HOST)
- xhci_queue_vendor_command(xhci, 0, 0, 0,
+ if (xhci->quirks & XHCI_NEC_HOST) {
+ struct xhci_command *command;
+ command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ if (!command)
+ return -ENOMEM;
+ xhci_queue_vendor_command(xhci, command, 0, 0, 0,
TRB_TYPE(TRB_NEC_GET_FW));
-
+ }
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Finished xhci_run for USB2 roothub");
return 0;
@@ -657,7 +668,8 @@ static void xhci_only_stop_hcd(struct usb_hcd *hcd)
* calls this function when allocation fails in usb_add_hcd(), or
* usb_remove_hcd() is called). So we need to unset xHCI's pointer.
*/
- xhci->shared_hcd = NULL;
+ if (!(xhci->quirks & XHCI_DRD_SUPPORT))
+ xhci->shared_hcd = NULL;
spin_unlock_irq(&xhci->lock);
}
@@ -842,13 +854,47 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci)
xhci_set_cmd_ring_deq(xhci);
}
+static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
+{
+ int port_index;
+ __le32 __iomem **port_array;
+ unsigned long flags;
+ u32 t1, t2;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* disble usb3 ports Wake bits*/
+ port_index = xhci->num_usb3_ports;
+ port_array = xhci->usb3_ports;
+ while (port_index--) {
+ t1 = readl(port_array[port_index]);
+ t1 = xhci_port_state_to_neutral(t1);
+ t2 = t1 & ~PORT_WAKE_BITS;
+ if (t1 != t2)
+ writel(t2, port_array[port_index]);
+ }
+
+ /* disble usb2 ports Wake bits*/
+ port_index = xhci->num_usb2_ports;
+ port_array = xhci->usb2_ports;
+ while (port_index--) {
+ t1 = readl(port_array[port_index]);
+ t1 = xhci_port_state_to_neutral(t1);
+ t2 = t1 & ~PORT_WAKE_BITS;
+ if (t1 != t2)
+ writel(t2, port_array[port_index]);
+ }
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+}
+
/*
* Stop HC (not bus-specific)
*
* This is called when the machine transition into S3/S4 mode.
*
*/
-int xhci_suspend(struct xhci_hcd *xhci)
+int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
{
int rc = 0;
unsigned int delay = XHCI_MAX_HALT_USEC;
@@ -859,6 +905,10 @@ int xhci_suspend(struct xhci_hcd *xhci)
xhci->shared_hcd->state != HC_STATE_SUSPENDED)
return -EINVAL;
+ /* Clear root port wake on bits if wakeup not allowed. */
+ if (!do_wakeup)
+ xhci_disable_port_wake_on_bits(xhci);
+
/* Don't poll the roothubs on bus suspend. */
xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
@@ -1187,10 +1237,10 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, struct urb *urb)
{
- struct xhci_container_ctx *in_ctx;
struct xhci_container_ctx *out_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
struct xhci_ep_ctx *ep_ctx;
+ struct xhci_command *command;
int max_packet_size;
int hw_max_packet_size;
int ret = 0;
@@ -1215,18 +1265,24 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
/* FIXME: This won't work if a non-default control endpoint
* changes max packet sizes.
*/
- in_ctx = xhci->devs[slot_id]->in_ctx;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
+
+ command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
+ if (!command)
+ return -ENOMEM;
+
+ command->in_ctx = xhci->devs[slot_id]->in_ctx;
+ ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto command_cleanup;
}
/* Set up the modified control endpoint 0 */
xhci_endpoint_copy(xhci, xhci->devs[slot_id]->in_ctx,
xhci->devs[slot_id]->out_ctx, ep_index);
- ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index);
+ ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, ep_index);
ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK);
ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size));
@@ -1234,17 +1290,20 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
ctrl_ctx->drop_flags = 0;
xhci_dbg(xhci, "Slot %d input context\n", slot_id);
- xhci_dbg_ctx(xhci, in_ctx, ep_index);
+ xhci_dbg_ctx(xhci, command->in_ctx, ep_index);
xhci_dbg(xhci, "Slot %d output context\n", slot_id);
xhci_dbg_ctx(xhci, out_ctx, ep_index);
- ret = xhci_configure_endpoint(xhci, urb->dev, NULL,
+ ret = xhci_configure_endpoint(xhci, urb->dev, command,
true, false);
/* Clean up the input context for later use by bandwidth
* functions.
*/
ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG);
+command_cleanup:
+ kfree(command->completion);
+ kfree(command);
}
return ret;
}
@@ -1465,6 +1524,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
unsigned int ep_index;
struct xhci_ring *ep_ring;
struct xhci_virt_ep *ep;
+ struct xhci_command *command;
xhci = hcd_to_xhci(hcd);
spin_lock_irqsave(&xhci->lock, flags);
@@ -1534,12 +1594,18 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
* the first cancellation to be handled.
*/
if (!(ep->ep_state & EP_HALT_PENDING)) {
+ command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command) {
+ ret = -ENOMEM;
+ goto done;
+ }
ep->ep_state |= EP_HALT_PENDING;
ep->stop_cmds_pending++;
ep->stop_cmd_timer.expires = jiffies +
XHCI_STOP_EP_CMD_TIMEOUT * HZ;
add_timer(&ep->stop_cmd_timer);
- xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index, 0);
+ xhci_queue_stop_endpoint(xhci, command, urb->dev->slot_id,
+ ep_index, 0);
xhci_ring_cmd_db(xhci);
}
done:
@@ -1566,12 +1632,10 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct xhci_hcd *xhci;
struct xhci_container_ctx *in_ctx, *out_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
- struct xhci_slot_ctx *slot_ctx;
- unsigned int last_ctx;
unsigned int ep_index;
struct xhci_ep_ctx *ep_ctx;
u32 drop_flag;
- u32 new_add_flags, new_drop_flags, new_slot_info;
+ u32 new_add_flags, new_drop_flags;
int ret;
ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
@@ -1618,24 +1682,13 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag);
new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
- last_ctx = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags));
- slot_ctx = xhci_get_slot_ctx(xhci, in_ctx);
- /* Update the last valid endpoint context, if we deleted the last one */
- if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) >
- LAST_CTX(last_ctx)) {
- slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
- slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx));
- }
- new_slot_info = le32_to_cpu(slot_ctx->dev_info);
-
xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
- xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n",
+ xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
(unsigned int) ep->desc.bEndpointAddress,
udev->slot_id,
(unsigned int) new_drop_flags,
- (unsigned int) new_add_flags,
- (unsigned int) new_slot_info);
+ (unsigned int) new_add_flags);
return 0;
}
@@ -1658,11 +1711,9 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct xhci_hcd *xhci;
struct xhci_container_ctx *in_ctx, *out_ctx;
unsigned int ep_index;
- struct xhci_slot_ctx *slot_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
u32 added_ctxs;
- unsigned int last_ctx;
- u32 new_add_flags, new_drop_flags, new_slot_info;
+ u32 new_add_flags, new_drop_flags;
struct xhci_virt_device *virt_dev;
int ret = 0;
@@ -1677,7 +1728,6 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
return -ENODEV;
added_ctxs = xhci_get_endpoint_flag(&ep->desc);
- last_ctx = xhci_last_valid_endpoint(added_ctxs);
if (added_ctxs == SLOT_FLAG || added_ctxs == EP0_FLAG) {
/* FIXME when we have to issue an evaluate endpoint command to
* deal with ep0 max packet size changing once we get the
@@ -1743,24 +1793,14 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
*/
new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
- slot_ctx = xhci_get_slot_ctx(xhci, in_ctx);
- /* Update the last valid endpoint context, if we just added one past */
- if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) <
- LAST_CTX(last_ctx)) {
- slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
- slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx));
- }
- new_slot_info = le32_to_cpu(slot_ctx->dev_info);
-
/* Store the usb_device pointer for later use */
ep->hcpriv = udev;
- xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n",
+ xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
(unsigned int) ep->desc.bEndpointAddress,
udev->slot_id,
(unsigned int) new_drop_flags,
- (unsigned int) new_add_flags,
- (unsigned int) new_slot_info);
+ (unsigned int) new_add_flags);
return 0;
}
@@ -1804,16 +1844,21 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
int ret;
switch (*cmd_status) {
+ case COMP_CMD_ABORT:
+ case COMP_CMD_STOP:
+ xhci_warn(xhci, "Timeout while waiting for configure endpoint command\n");
+ ret = -ETIME;
+ break;
case COMP_ENOMEM:
- dev_warn(&udev->dev, "Not enough host controller resources "
- "for new device state.\n");
+ dev_warn(&udev->dev,
+ "Not enough host controller resources for new device state.\n");
ret = -ENOMEM;
/* FIXME: can we allocate more resources for the HC? */
break;
case COMP_BW_ERR:
case COMP_2ND_BW_ERR:
- dev_warn(&udev->dev, "Not enough bandwidth "
- "for new device state.\n");
+ dev_warn(&udev->dev,
+ "Not enough bandwidth for new device state.\n");
ret = -ENOSPC;
/* FIXME: can we go back to the old state? */
break;
@@ -1825,8 +1870,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
ret = -EINVAL;
break;
case COMP_DEV_ERR:
- dev_warn(&udev->dev, "ERROR: Incompatible device for endpoint "
- "configure command.\n");
+ dev_warn(&udev->dev,
+ "ERROR: Incompatible device for endpoint configure command.\n");
ret = -ENODEV;
break;
case COMP_SUCCESS:
@@ -1835,8 +1880,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
ret = 0;
break;
default:
- xhci_err(xhci, "ERROR: unexpected command completion "
- "code 0x%x.\n", *cmd_status);
+ xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n",
+ *cmd_status);
ret = -EINVAL;
break;
}
@@ -1850,25 +1895,30 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];
switch (*cmd_status) {
+ case COMP_CMD_ABORT:
+ case COMP_CMD_STOP:
+ xhci_warn(xhci, "Timeout while waiting for evaluate context command\n");
+ ret = -ETIME;
+ break;
case COMP_EINVAL:
- dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate "
- "context command.\n");
+ dev_warn(&udev->dev,
+ "WARN: xHCI driver setup invalid evaluate context command.\n");
ret = -EINVAL;
break;
case COMP_EBADSLT:
- dev_warn(&udev->dev, "WARN: slot not enabled for"
- "evaluate context command.\n");
+ dev_warn(&udev->dev,
+ "WARN: slot not enabled for evaluate context command.\n");
ret = -EINVAL;
break;
case COMP_CTX_STATE:
- dev_warn(&udev->dev, "WARN: invalid context state for "
- "evaluate context command.\n");
+ dev_warn(&udev->dev,
+ "WARN: invalid context state for evaluate context command.\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1);
ret = -EINVAL;
break;
case COMP_DEV_ERR:
- dev_warn(&udev->dev, "ERROR: Incompatible device for evaluate "
- "context command.\n");
+ dev_warn(&udev->dev,
+ "ERROR: Incompatible device for evaluate context command.\n");
ret = -ENODEV;
break;
case COMP_MEL_ERR:
@@ -1882,8 +1932,8 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci,
ret = 0;
break;
default:
- xhci_err(xhci, "ERROR: unexpected command completion "
- "code 0x%x.\n", *cmd_status);
+ xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n",
+ *cmd_status);
ret = -EINVAL;
break;
}
@@ -2574,23 +2624,17 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
bool ctx_change, bool must_succeed)
{
int ret;
- int timeleft;
unsigned long flags;
- struct xhci_container_ctx *in_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
- struct completion *cmd_completion;
- u32 *cmd_status;
struct xhci_virt_device *virt_dev;
- union xhci_trb *cmd_trb;
+
+ if (!command)
+ return -EINVAL;
spin_lock_irqsave(&xhci->lock, flags);
virt_dev = xhci->devs[udev->slot_id];
- if (command)
- in_ctx = command->in_ctx;
- else
- in_ctx = virt_dev->in_ctx;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
if (!ctrl_ctx) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
@@ -2607,7 +2651,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
return -ENOMEM;
}
if ((xhci->quirks & XHCI_SW_BW_CHECKING) &&
- xhci_reserve_bandwidth(xhci, virt_dev, in_ctx)) {
+ xhci_reserve_bandwidth(xhci, virt_dev, command->in_ctx)) {
if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK))
xhci_free_host_resources(xhci, ctrl_ctx);
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -2615,27 +2659,15 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
return -ENOMEM;
}
- if (command) {
- cmd_completion = command->completion;
- cmd_status = &command->status;
- command->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
- list_add_tail(&command->cmd_list, &virt_dev->cmd_list);
- } else {
- cmd_completion = &virt_dev->cmd_completion;
- cmd_status = &virt_dev->cmd_status;
- }
- init_completion(cmd_completion);
-
- cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
if (!ctx_change)
- ret = xhci_queue_configure_endpoint(xhci, in_ctx->dma,
+ ret = xhci_queue_configure_endpoint(xhci, command,
+ command->in_ctx->dma,
udev->slot_id, must_succeed);
else
- ret = xhci_queue_evaluate_context(xhci, in_ctx->dma,
+ ret = xhci_queue_evaluate_context(xhci, command,
+ command->in_ctx->dma,
udev->slot_id, must_succeed);
if (ret < 0) {
- if (command)
- list_del(&command->cmd_list);
if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK))
xhci_free_host_resources(xhci, ctrl_ctx);
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -2647,26 +2679,14 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
spin_unlock_irqrestore(&xhci->lock, flags);
/* Wait for the configure endpoint command to complete */
- timeleft = wait_for_completion_interruptible_timeout(
- cmd_completion,
- XHCI_CMD_DEFAULT_TIMEOUT);
- if (timeleft <= 0) {
- xhci_warn(xhci, "%s while waiting for %s command\n",
- timeleft == 0 ? "Timeout" : "Signal",
- ctx_change == 0 ?
- "configure endpoint" :
- "evaluate context");
- /* cancel the configure endpoint command */
- ret = xhci_cancel_cmd(xhci, command, cmd_trb);
- if (ret < 0)
- return ret;
- return -ETIME;
- }
+ wait_for_completion(command->completion);
if (!ctx_change)
- ret = xhci_configure_endpoint_result(xhci, udev, cmd_status);
+ ret = xhci_configure_endpoint_result(xhci, udev,
+ &command->status);
else
- ret = xhci_evaluate_context_result(xhci, udev, cmd_status);
+ ret = xhci_evaluate_context_result(xhci, udev,
+ &command->status);
if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) {
spin_lock_irqsave(&xhci->lock, flags);
@@ -2682,6 +2702,20 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
return ret;
}
+static void xhci_check_bw_drop_ep_streams(struct xhci_hcd *xhci,
+ struct xhci_virt_device *vdev, int i)
+{
+ struct xhci_virt_ep *ep = &vdev->eps[i];
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on set_interface, freeing streams.\n",
+ xhci_get_endpoint_address(i));
+ xhci_free_stream_info(xhci, ep->stream_info);
+ ep->stream_info = NULL;
+ ep->ep_state &= ~EP_HAS_STREAMS;
+ }
+}
+
/* Called after one or more calls to xhci_add_endpoint() or
* xhci_drop_endpoint(). If this call fails, the USB core is expected
* to call xhci_reset_bandwidth().
@@ -2700,6 +2734,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
struct xhci_virt_device *virt_dev;
struct xhci_input_control_ctx *ctrl_ctx;
struct xhci_slot_ctx *slot_ctx;
+ struct xhci_command *command;
ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
if (ret <= 0)
@@ -2711,12 +2746,19 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
virt_dev = xhci->devs[udev->slot_id];
+ command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
+ if (!command)
+ return -ENOMEM;
+
+ command->in_ctx = virt_dev->in_ctx;
+
/* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */
- ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto command_cleanup;
}
ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG);
@@ -2724,20 +2766,31 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
/* Don't issue the command if there's no endpoints to update. */
if (ctrl_ctx->add_flags == cpu_to_le32(SLOT_FLAG) &&
- ctrl_ctx->drop_flags == 0)
- return 0;
+ ctrl_ctx->drop_flags == 0) {
+ ret = 0;
+ goto command_cleanup;
+ }
+ /* Fix up Context Entries field. Minimum value is EP0 == BIT(1). */
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
+ for (i = 31; i >= 1; i--) {
+ __le32 le32 = cpu_to_le32(BIT(i));
+ if ((virt_dev->eps[i-1].ring && !(ctrl_ctx->drop_flags & le32))
+ || (ctrl_ctx->add_flags & le32) || i == 1) {
+ slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(i));
+ break;
+ }
+ }
xhci_dbg(xhci, "New Input Control Context:\n");
- slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
xhci_dbg_ctx(xhci, virt_dev->in_ctx,
LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info)));
- ret = xhci_configure_endpoint(xhci, udev, NULL,
+ ret = xhci_configure_endpoint(xhci, udev, command,
false, false);
- if (ret) {
+ if (ret)
/* Callee should call reset_bandwidth() */
- return ret;
- }
+ goto command_cleanup;
xhci_dbg(xhci, "Output context after successful config ep cmd:\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx,
@@ -2746,8 +2799,10 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
/* Free any rings that were dropped, but not changed. */
for (i = 1; i < 31; ++i) {
if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) &&
- !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1))))
+ !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) {
xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
+ xhci_check_bw_drop_ep_streams(xhci, virt_dev, i);
+ }
}
xhci_zero_in_ctx(xhci, virt_dev);
/*
@@ -2763,9 +2818,13 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
if (virt_dev->eps[i].ring) {
xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
}
+ xhci_check_bw_drop_ep_streams(xhci, virt_dev, i);
virt_dev->eps[i].ring = virt_dev->eps[i].new_ring;
virt_dev->eps[i].new_ring = NULL;
}
+command_cleanup:
+ kfree(command->completion);
+ kfree(command);
return ret;
}
@@ -2863,13 +2922,21 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci,
ep_index, ep->stopped_stream, ep->stopped_td,
&deq_state);
+ if (!deq_state.new_deq_ptr || !deq_state.new_deq_seg)
+ return;
+
/* HW with the reset endpoint quirk will use the saved dequeue state to
* issue a configure endpoint command later.
*/
if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) {
+ struct xhci_command *command;
+ /* Can't sleep if we're called from cleanup_halted_endpoint() */
+ command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command)
+ return;
xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
"Queueing new dequeue state");
- xhci_queue_new_dequeue_state(xhci, udev->slot_id,
+ xhci_queue_new_dequeue_state(xhci, command, udev->slot_id,
ep_index, ep->stopped_stream, &deq_state);
} else {
/* Better hope no one uses the input context between now and the
@@ -2900,6 +2967,7 @@ void xhci_endpoint_reset(struct usb_hcd *hcd,
unsigned long flags;
int ret;
struct xhci_virt_ep *virt_ep;
+ struct xhci_command *command;
xhci = hcd_to_xhci(hcd);
udev = (struct usb_device *) ep->hcpriv;
@@ -2922,10 +2990,14 @@ void xhci_endpoint_reset(struct usb_hcd *hcd,
return;
}
+ command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command)
+ return;
+
xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
"Queueing reset endpoint command");
spin_lock_irqsave(&xhci->lock, flags);
- ret = xhci_queue_reset_ep(xhci, udev->slot_id, ep_index);
+ ret = xhci_queue_reset_ep(xhci, command, udev->slot_id, ep_index);
/*
* Can't change the ring dequeue pointer until it's transitioned to the
* stopped state, which is only upon a successful reset endpoint
@@ -2957,7 +3029,7 @@ static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__);
if (ret <= 0)
return -EINVAL;
- if (ep->ss_ep_comp.bmAttributes == 0) {
+ if (usb_ss_max_streams(&ep->ss_ep_comp) == 0) {
xhci_warn(xhci, "WARN: SuperSpeed Endpoint Companion"
" descriptor for ep 0x%x does not support streams\n",
ep->desc.bEndpointAddress);
@@ -3124,6 +3196,13 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg(xhci, "Driver wants %u stream IDs (including stream 0).\n",
num_streams);
+ /* MaxPSASize value 0 (2 streams) means streams are not supported */
+ if ((xhci->quirks & XHCI_BROKEN_STREAMS) ||
+ HCC_MAX_PSA(xhci->hcc_params) < 4) {
+ xhci_dbg(xhci, "xHCI controller does not support streams.\n");
+ return -ENOSYS;
+ }
+
config_cmd = xhci_alloc_command(xhci, true, true, mem_flags);
if (!config_cmd) {
xhci_dbg(xhci, "Could not allocate xHCI command structure.\n");
@@ -3393,7 +3472,6 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
unsigned int slot_id;
struct xhci_virt_device *virt_dev;
struct xhci_command *reset_device_cmd;
- int timeleft;
int last_freed_endpoint;
struct xhci_slot_ctx *slot_ctx;
int old_active_eps = 0;
@@ -3450,13 +3528,10 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Attempt to submit the Reset Device command to the command ring */
spin_lock_irqsave(&xhci->lock, flags);
- reset_device_cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
- list_add_tail(&reset_device_cmd->cmd_list, &virt_dev->cmd_list);
- ret = xhci_queue_reset_device(xhci, slot_id);
+ ret = xhci_queue_reset_device(xhci, reset_device_cmd, slot_id);
if (ret) {
xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
- list_del(&reset_device_cmd->cmd_list);
spin_unlock_irqrestore(&xhci->lock, flags);
goto command_cleanup;
}
@@ -3464,22 +3539,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
spin_unlock_irqrestore(&xhci->lock, flags);
/* Wait for the Reset Device command to finish */
- timeleft = wait_for_completion_interruptible_timeout(
- reset_device_cmd->completion,
- XHCI_CMD_DEFAULT_TIMEOUT);
- if (timeleft <= 0) {
- xhci_warn(xhci, "%s while waiting for reset device command\n",
- timeleft == 0 ? "Timeout" : "Signal");
- spin_lock_irqsave(&xhci->lock, flags);
- /* The timeout might have raced with the event ring handler, so
- * only delete from the list if the item isn't poisoned.
- */
- if (reset_device_cmd->cmd_list.next != LIST_POISON1)
- list_del(&reset_device_cmd->cmd_list);
- spin_unlock_irqrestore(&xhci->lock, flags);
- ret = -ETIME;
- goto command_cleanup;
- }
+ wait_for_completion(reset_device_cmd->completion);
/* The Reset Device command can't fail, according to the 0.95/0.96 spec,
* unless we tried to reset a slot ID that wasn't enabled,
@@ -3487,6 +3547,11 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
*/
ret = reset_device_cmd->status;
switch (ret) {
+ case COMP_CMD_ABORT:
+ case COMP_CMD_STOP:
+ xhci_warn(xhci, "Timeout waiting for reset device command\n");
+ ret = -ETIME;
+ goto command_cleanup;
case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */
case COMP_CTX_STATE: /* 0.96 completion code for same thing */
xhci_dbg(xhci, "Can't reset device (slot ID %u) in %s state\n",
@@ -3522,6 +3587,8 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
struct xhci_virt_ep *ep = &virt_dev->eps[i];
if (ep->ep_state & EP_HAS_STREAMS) {
+ xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on device reset, freeing streams.\n",
+ xhci_get_endpoint_address(i));
xhci_free_stream_info(xhci, ep->stream_info);
ep->stream_info = NULL;
ep->ep_state &= ~EP_HAS_STREAMS;
@@ -3564,6 +3631,11 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
unsigned long flags;
u32 state;
int i, ret;
+ struct xhci_command *command;
+
+ command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ if (!command)
+ return;
#ifndef CONFIG_USB_DEFAULT_PERSIST
/*
@@ -3579,8 +3651,10 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
/* If the host is halted due to driver unload, we still need to free the
* device.
*/
- if (ret <= 0 && ret != -ENODEV)
+ if (ret <= 0 && ret != -ENODEV) {
+ kfree(command);
return;
+ }
virt_dev = xhci->devs[udev->slot_id];
@@ -3597,16 +3671,19 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
(xhci->xhc_state & XHCI_STATE_HALTED)) {
xhci_free_virt_device(xhci, udev->slot_id);
spin_unlock_irqrestore(&xhci->lock, flags);
+ kfree(command);
return;
}
- if (xhci_queue_slot_control(xhci, TRB_DISABLE_SLOT, udev->slot_id)) {
+ if (xhci_queue_slot_control(xhci, command, TRB_DISABLE_SLOT,
+ udev->slot_id)) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
return;
}
xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
+
/*
* Event command completion handler will free any data structures
* associated with the slot. XXX Can free sleep?
@@ -3644,33 +3721,33 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
unsigned long flags;
- int timeleft;
int ret;
- union xhci_trb *cmd_trb;
+ struct xhci_command *command;
+
+ command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ if (!command)
+ return 0;
spin_lock_irqsave(&xhci->lock, flags);
- cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
- ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0);
+ command->completion = &xhci->addr_dev;
+ ret = xhci_queue_slot_control(xhci, command, TRB_ENABLE_SLOT, 0);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
+ kfree(command);
return 0;
}
xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
- /* XXX: how much time for xHC slot assignment? */
- timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
- XHCI_CMD_DEFAULT_TIMEOUT);
- if (timeleft <= 0) {
- xhci_warn(xhci, "%s while waiting for a slot\n",
- timeleft == 0 ? "Timeout" : "Signal");
- /* cancel the enable slot request */
- return xhci_cancel_cmd(xhci, NULL, cmd_trb);
- }
+ wait_for_completion(command->completion);
- if (!xhci->slot_id) {
+ if (!xhci->slot_id || command->status != COMP_SUCCESS) {
xhci_err(xhci, "Error while assigning device slot ID\n");
+ xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
+ HCS_MAX_SLOTS(
+ readl(&xhci->cap_regs->hcs_params1)));
+ kfree(command);
return 0;
}
@@ -3705,6 +3782,8 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
pm_runtime_get_noresume(hcd->self.controller);
#endif
+
+ kfree(command);
/* Is this a LS or FS device under a HS hub? */
/* Hub or peripherial? */
return 1;
@@ -3712,7 +3791,10 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
disable_slot:
/* Disable slot, if we can do it without mem alloc */
spin_lock_irqsave(&xhci->lock, flags);
- if (!xhci_queue_slot_control(xhci, TRB_DISABLE_SLOT, udev->slot_id))
+ command->completion = NULL;
+ command->status = 0;
+ if (!xhci_queue_slot_control(xhci, command, TRB_DISABLE_SLOT,
+ udev->slot_id))
xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
return 0;
@@ -3729,14 +3811,13 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
{
const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address";
unsigned long flags;
- int timeleft;
struct xhci_virt_device *virt_dev;
int ret = 0;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_slot_ctx *slot_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
u64 temp_64;
- union xhci_trb *cmd_trb;
+ struct xhci_command *command;
if (!udev->slot_id) {
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
@@ -3757,11 +3838,19 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
return -EINVAL;
}
+ command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ if (!command)
+ return -ENOMEM;
+
+ command->in_ctx = virt_dev->in_ctx;
+ command->completion = &xhci->addr_dev;
+
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
+ kfree(command);
return -EINVAL;
}
/*
@@ -3783,36 +3872,31 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
le32_to_cpu(slot_ctx->dev_info) >> 27);
spin_lock_irqsave(&xhci->lock, flags);
- cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
- ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma,
+ ret = xhci_queue_address_device(xhci, command, virt_dev->in_ctx->dma,
udev->slot_id, setup);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"FIXME: allocate a command ring segment");
+ kfree(command);
return ret;
}
xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
/* ctrl tx can take up to 5 sec; XXX: need more time for xHC? */
- timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
- XHCI_CMD_DEFAULT_TIMEOUT);
+ wait_for_completion(command->completion);
+
/* FIXME: From section 4.3.4: "Software shall be responsible for timing
* the SetAddress() "recovery interval" required by USB and aborting the
* command on a timeout.
*/
- if (timeleft <= 0) {
- xhci_warn(xhci, "%s while waiting for setup %s command\n",
- timeleft == 0 ? "Timeout" : "Signal", act);
- /* cancel the address device command */
- ret = xhci_cancel_cmd(xhci, NULL, cmd_trb);
- if (ret < 0)
- return ret;
- return -ETIME;
- }
-
- switch (virt_dev->cmd_status) {
+ switch (command->status) {
+ case COMP_CMD_ABORT:
+ case COMP_CMD_STOP:
+ xhci_warn(xhci, "Timeout while waiting for setup device command\n");
+ ret = -ETIME;
+ break;
case COMP_CTX_STATE:
case COMP_EBADSLT:
xhci_err(xhci, "Setup ERROR: setup %s command for slot %d.\n",
@@ -3835,7 +3919,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
default:
xhci_err(xhci,
"ERROR: unexpected setup %s command completion code 0x%x.\n",
- act, virt_dev->cmd_status);
+ act, command->status);
xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id);
xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2);
trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1);
@@ -3843,6 +3927,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
break;
}
if (ret) {
+ kfree(command);
return ret;
}
temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
@@ -3877,7 +3962,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Internal device address = %d",
le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
-
+ kfree(command);
return 0;
}
@@ -4075,7 +4160,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
- enable ? "enable" : "disable", port_num);
+ enable ? "enable" : "disable", port_num + 1);
if (enable) {
/* Host supports BESL timeout instead of HIRD */
@@ -4261,8 +4346,7 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev,
return USB3_LPM_DISABLED;
}
-/* Returns the hub-encoded U1 timeout value.
- * The U1 timeout should be the maximum of the following values:
+/* The U1 timeout should be the maximum of the following values:
* - For control endpoints, U1 system exit latency (SEL) * 3
* - For bulk endpoints, U1 SEL * 5
* - For interrupt endpoints:
@@ -4270,7 +4354,8 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev,
* - Periodic EPs, max(105% of bInterval, U1 SEL * 2)
* - For isochronous endpoints, max(105% of bInterval, U1 SEL * 2)
*/
-static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev,
+static unsigned long long xhci_calculate_intel_u1_timeout(
+ struct usb_device *udev,
struct usb_endpoint_descriptor *desc)
{
unsigned long long timeout_ns;
@@ -4302,11 +4387,28 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev,
return 0;
}
- /* The U1 timeout is encoded in 1us intervals. */
- timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000);
- /* Don't return a timeout of zero, because that's USB3_LPM_DISABLED. */
+ return timeout_ns;
+}
+
+/* Returns the hub-encoded U1 timeout value. */
+static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc)
+{
+ unsigned long long timeout_ns;
+
+ if (xhci->quirks & XHCI_INTEL_HOST)
+ timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
+ else
+ timeout_ns = udev->u1_params.sel;
+
+ /* The U1 timeout is encoded in 1us intervals.
+ * Don't return a timeout of zero, because that's USB3_LPM_DISABLED.
+ */
if (timeout_ns == USB3_LPM_DISABLED)
- timeout_ns++;
+ timeout_ns = 1;
+ else
+ timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000);
/* If the necessary timeout value is bigger than what we can set in the
* USB 3.0 hub, we have to disable hub-initiated U1.
@@ -4318,14 +4420,14 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev,
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1);
}
-/* Returns the hub-encoded U2 timeout value.
- * The U2 timeout should be the maximum of:
+/* The U2 timeout should be the maximum of:
* - 10 ms (to avoid the bandwidth impact on the scheduler)
* - largest bInterval of any active periodic endpoint (to avoid going
* into lower power link states between intervals).
* - the U2 Exit Latency of the device
*/
-static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev,
+static unsigned long long xhci_calculate_intel_u2_timeout(
+ struct usb_device *udev,
struct usb_endpoint_descriptor *desc)
{
unsigned long long timeout_ns;
@@ -4341,6 +4443,21 @@ static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev,
if (u2_del_ns > timeout_ns)
timeout_ns = u2_del_ns;
+ return timeout_ns;
+}
+
+/* Returns the hub-encoded U2 timeout value. */
+static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc)
+{
+ unsigned long long timeout_ns;
+
+ if (xhci->quirks & XHCI_INTEL_HOST)
+ timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
+ else
+ timeout_ns = udev->u2_params.sel;
+
/* The U2 timeout is encoded in 256us intervals */
timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000);
/* If the necessary timeout value is bigger than what we can set in the
@@ -4359,13 +4476,10 @@ static u16 xhci_call_host_update_timeout_for_endpoint(struct xhci_hcd *xhci,
enum usb3_link_state state,
u16 *timeout)
{
- if (state == USB3_LPM_U1) {
- if (xhci->quirks & XHCI_INTEL_HOST)
- return xhci_calculate_intel_u1_timeout(udev, desc);
- } else {
- if (xhci->quirks & XHCI_INTEL_HOST)
- return xhci_calculate_intel_u2_timeout(udev, desc);
- }
+ if (state == USB3_LPM_U1)
+ return xhci_calculate_u1_timeout(xhci, udev, desc);
+ else if (state == USB3_LPM_U2)
+ return xhci_calculate_u2_timeout(xhci, udev, desc);
return USB3_LPM_DISABLED;
}
@@ -4442,7 +4556,8 @@ static int xhci_check_tier_policy(struct xhci_hcd *xhci,
{
if (xhci->quirks & XHCI_INTEL_HOST)
return xhci_check_intel_tier_policy(udev, state);
- return -EINVAL;
+ else
+ return 0;
}
/* Returns the U1 or U2 timeout that should be enabled.
@@ -4740,6 +4855,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
struct xhci_hcd *xhci;
struct device *dev = hcd->self.controller;
int retval;
+ bool allocated = false;
/* Accept arbitrarily long scatter-gather lists */
hcd->self.sg_tablesize = ~0;
@@ -4751,10 +4867,15 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
hcd->self.no_stop_on_short = 1;
if (usb_hcd_is_primary_hcd(hcd)) {
- xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
- if (!xhci)
- return -ENOMEM;
- *((struct xhci_hcd **) hcd->hcd_priv) = xhci;
+ if (*((struct xhci_hcd **)hcd->hcd_priv) == NULL) {
+ xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
+ if (!xhci)
+ return -ENOMEM;
+ *((struct xhci_hcd **)hcd->hcd_priv) = xhci;
+ allocated = true;
+ } else {
+ xhci = *((struct xhci_hcd **)hcd->hcd_priv);
+ }
xhci->main_hcd = hcd;
/* Mark the first roothub as being USB 2.0.
* The xHCI driver will register the USB 3.0 roothub.
@@ -4827,7 +4948,10 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci_dbg(xhci, "Called HCD init\n");
return 0;
error:
- kfree(xhci);
+ if (allocated) {
+ *((struct xhci_hcd **)hcd->hcd_priv) = NULL;
+ kfree(xhci);
+ }
return retval;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 8faef64371c6..23bba281aa1f 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -703,6 +703,7 @@ struct xhci_ep_ctx {
/* deq bitmasks */
#define EP_CTX_CYCLE_MASK (1 << 0)
+#define SCTX_DEQ_MASK (~0xfL)
/**
@@ -936,9 +937,6 @@ struct xhci_virt_device {
#define XHCI_MAX_RINGS_CACHED 31
struct xhci_virt_ep eps[31];
struct completion cmd_completion;
- /* Status of the last command issued for this device */
- u32 cmd_status;
- struct list_head cmd_list;
u8 fake_port;
u8 real_port;
struct xhci_interval_bw_table *bw_table;
@@ -1116,9 +1114,10 @@ enum xhci_setup_dev {
#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
#define LAST_EP_INDEX 30
-/* Set TR Dequeue Pointer command TRB fields */
+/* Set TR Dequeue Pointer command TRB fields, 6.4.3.9 */
#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16))
#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16)
+#define SCT_FOR_TRB(p) (((p) << 1) & 0x7)
/* Port Status Change Event TRB fields */
@@ -1296,7 +1295,6 @@ struct xhci_td {
/* command descriptor */
struct xhci_cd {
- struct list_head cancel_cmd_list;
struct xhci_command *command;
union xhci_trb *cmd_trb;
};
@@ -1339,6 +1337,7 @@ struct xhci_ring {
unsigned int num_trbs_free_temp;
enum xhci_ring_type type;
bool last_td_was_short;
+ struct radix_tree_root *trb_address_map;
};
struct xhci_erst_entry {
@@ -1473,6 +1472,8 @@ struct xhci_hcd {
/* msi-x vectors */
int msix_count;
struct msix_entry *msix_entries;
+ /* optional clock */
+ struct clk *clk;
/* data structures */
struct xhci_device_context_array *dcbaa;
struct xhci_ring *cmd_ring;
@@ -1480,8 +1481,10 @@ struct xhci_hcd {
#define CMD_RING_STATE_RUNNING (1 << 0)
#define CMD_RING_STATE_ABORTED (1 << 1)
#define CMD_RING_STATE_STOPPED (1 << 2)
- struct list_head cancel_cmd_list;
+ struct list_head cmd_list;
unsigned int cmd_ring_reserved_trbs;
+ struct timer_list cmd_timer;
+ struct xhci_command *current_cmd;
struct xhci_ring *event_ring;
struct xhci_erst erst;
/* Scratchpad */
@@ -1555,6 +1558,10 @@ struct xhci_hcd {
#define XHCI_PLAT (1 << 16)
#define XHCI_SLOW_SUSPEND (1 << 17)
#define XHCI_SPURIOUS_WAKEUP (1 << 18)
+/* For controllers with a broken beyond repair streams implementation */
+#define XHCI_BROKEN_STREAMS (1 << 19)
+#define XHCI_DRD_SUPPORT (1 << 20)
+#define XHCI_NEEDS_LHC_RESET (1 << 21)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1735,8 +1742,7 @@ static inline int xhci_register_pci(void) { return 0; }
static inline void xhci_unregister_pci(void) {}
#endif
-#if defined(CONFIG_USB_XHCI_PLATFORM) \
- || defined(CONFIG_USB_XHCI_PLATFORM_MODULE)
+#if IS_ENABLED(CONFIG_USB_XHCI_PLATFORM)
int xhci_register_plat(void);
void xhci_unregister_plat(void);
#else
@@ -1760,7 +1766,7 @@ void xhci_shutdown(struct usb_hcd *hcd);
int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks);
#ifdef CONFIG_PM
-int xhci_suspend(struct xhci_hcd *xhci);
+int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
#else
#define xhci_suspend NULL
@@ -1805,13 +1811,14 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
dma_addr_t suspect_dma);
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
-int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id);
-int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
- u32 slot_id, enum xhci_setup_dev);
-int xhci_queue_vendor_command(struct xhci_hcd *xhci,
+int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 trb_type, u32 slot_id);
+int xhci_queue_address_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ dma_addr_t in_ctx_ptr, u32 slot_id, enum xhci_setup_dev);
+int xhci_queue_vendor_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
u32 field1, u32 field2, u32 field3, u32 field4);
-int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
- unsigned int ep_index, int suspend);
+int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ int slot_id, unsigned int ep_index, int suspend);
int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
int slot_id, unsigned int ep_index);
int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
@@ -1820,18 +1827,21 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
int slot_id, unsigned int ep_index);
int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
struct urb *urb, int slot_id, unsigned int ep_index);
-int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
- u32 slot_id, bool command_must_succeed);
-int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
- u32 slot_id, bool command_must_succeed);
-int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id,
- unsigned int ep_index);
-int xhci_queue_reset_device(struct xhci_hcd *xhci, u32 slot_id);
+int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
+ struct xhci_command *cmd, dma_addr_t in_ctx_ptr, u32 slot_id,
+ bool command_must_succeed);
+int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed);
+int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ int slot_id, unsigned int ep_index);
+int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
+ u32 slot_id);
void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id, struct xhci_td *cur_td,
struct xhci_dequeue_state *state);
void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
+ struct xhci_command *cmd,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id,
struct xhci_dequeue_state *deq_state);
@@ -1841,11 +1851,11 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
struct xhci_dequeue_state *deq_state);
void xhci_stop_endpoint_command_watchdog(unsigned long arg);
-int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
- union xhci_trb *cmd_trb);
+void xhci_handle_command_timeout(unsigned long data);
+
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id);
-union xhci_trb *xhci_find_next_enqueue(struct xhci_ring *ring);
+void xhci_cleanup_command_queue(struct xhci_hcd *xhci);
/* xHCI roothub code */
void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 2ff1b37a46a6..5e0f28439738 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -423,6 +423,7 @@ void musb_hnp_stop(struct musb *musb)
musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16);
}
+static void musb_generic_disable(struct musb *musb);
/*
* Interrupt Service Routine to record USB "global" interrupts.
* Since these do not happen often and signify things of
@@ -846,9 +847,11 @@ b_host:
}
/* handle babble condition */
- if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb))
+ if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) {
+ musb_generic_disable(musb);
schedule_delayed_work(&musb->recover_work,
msecs_to_jiffies(100));
+ }
#if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or
@@ -2321,6 +2324,14 @@ static int musb_resume(struct device *dev)
schedule_delayed_work(&musb->finish_resume_work,
msecs_to_jiffies(20));
}
+
+ /*
+ * The USB HUB code expects the device to be in RPM_ACTIVE once it came
+ * out of suspend
+ */
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
return 0;
}
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index debfcc0107b8..eb9ede3630c5 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -240,6 +240,7 @@ static void cppi41_dma_callback(void *private_data)
cppi41_trans_done(cppi41_channel);
} else {
struct cppi41_dma_controller *controller;
+ int is_hs = 0;
/*
* On AM335x it has been observed that the TX interrupt fires
* too early that means the TXFIFO is not yet empty but the DMA
@@ -252,7 +253,14 @@ static void cppi41_dma_callback(void *private_data)
*/
controller = cppi41_channel->controller;
- if (musb->g.speed == USB_SPEED_HIGH) {
+ if (is_host_active(musb)) {
+ if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED)
+ is_hs = 1;
+ } else {
+ if (musb->g.speed == USB_SPEED_HIGH)
+ is_hs = 1;
+ }
+ if (is_hs) {
unsigned wait = 25;
do {
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 20b524c635bc..85a6c1dd14d4 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2663,7 +2663,6 @@ void musb_host_cleanup(struct musb *musb)
if (musb->port_mode == MUSB_PORT_MODE_GADGET)
return;
usb_remove_hcd(musb->hcd);
- musb->hcd = NULL;
}
void musb_host_free(struct musb *musb)
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 3beae723ad3a..5741e9405069 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -120,6 +120,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
{ USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
{ USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
+ { USB_DEVICE(0x10C4, 0x8875) }, /* CEL MeshConnect USB Stick */
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index a523adad6380..debcdef4cbf0 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -483,6 +483,39 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_4701_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9300_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9301_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9302_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9303_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9304_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9305_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9306_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9307_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9308_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9309_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930A_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930B_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930C_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930D_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930E_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930F_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9310_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9311_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9312_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9313_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9314_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9315_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9316_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9317_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9318_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9319_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931A_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931B_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931C_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931D_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931E_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931F_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 6786b705ccf6..e52409c9be99 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -926,8 +926,8 @@
#define BAYER_CONTOUR_CABLE_PID 0x6001
/*
- * The following are the values for the Matrix Orbital FTDI Range
- * Anything in this range will use an FT232RL.
+ * Matrix Orbital Intelligent USB displays.
+ * http://www.matrixorbital.com
*/
#define MTXORB_VID 0x1B3D
#define MTXORB_FTDI_RANGE_0100_PID 0x0100
@@ -1186,8 +1186,39 @@
#define MTXORB_FTDI_RANGE_01FD_PID 0x01FD
#define MTXORB_FTDI_RANGE_01FE_PID 0x01FE
#define MTXORB_FTDI_RANGE_01FF_PID 0x01FF
-
-
+#define MTXORB_FTDI_RANGE_4701_PID 0x4701
+#define MTXORB_FTDI_RANGE_9300_PID 0x9300
+#define MTXORB_FTDI_RANGE_9301_PID 0x9301
+#define MTXORB_FTDI_RANGE_9302_PID 0x9302
+#define MTXORB_FTDI_RANGE_9303_PID 0x9303
+#define MTXORB_FTDI_RANGE_9304_PID 0x9304
+#define MTXORB_FTDI_RANGE_9305_PID 0x9305
+#define MTXORB_FTDI_RANGE_9306_PID 0x9306
+#define MTXORB_FTDI_RANGE_9307_PID 0x9307
+#define MTXORB_FTDI_RANGE_9308_PID 0x9308
+#define MTXORB_FTDI_RANGE_9309_PID 0x9309
+#define MTXORB_FTDI_RANGE_930A_PID 0x930A
+#define MTXORB_FTDI_RANGE_930B_PID 0x930B
+#define MTXORB_FTDI_RANGE_930C_PID 0x930C
+#define MTXORB_FTDI_RANGE_930D_PID 0x930D
+#define MTXORB_FTDI_RANGE_930E_PID 0x930E
+#define MTXORB_FTDI_RANGE_930F_PID 0x930F
+#define MTXORB_FTDI_RANGE_9310_PID 0x9310
+#define MTXORB_FTDI_RANGE_9311_PID 0x9311
+#define MTXORB_FTDI_RANGE_9312_PID 0x9312
+#define MTXORB_FTDI_RANGE_9313_PID 0x9313
+#define MTXORB_FTDI_RANGE_9314_PID 0x9314
+#define MTXORB_FTDI_RANGE_9315_PID 0x9315
+#define MTXORB_FTDI_RANGE_9316_PID 0x9316
+#define MTXORB_FTDI_RANGE_9317_PID 0x9317
+#define MTXORB_FTDI_RANGE_9318_PID 0x9318
+#define MTXORB_FTDI_RANGE_9319_PID 0x9319
+#define MTXORB_FTDI_RANGE_931A_PID 0x931A
+#define MTXORB_FTDI_RANGE_931B_PID 0x931B
+#define MTXORB_FTDI_RANGE_931C_PID 0x931C
+#define MTXORB_FTDI_RANGE_931D_PID 0x931D
+#define MTXORB_FTDI_RANGE_931E_PID 0x931E
+#define MTXORB_FTDI_RANGE_931F_PID 0x931F
/*
* The Mobility Lab (TML)
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 265c6776b081..49101fe45d38 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -311,24 +311,30 @@ static void usa26_indat_callback(struct urb *urb)
if ((data[0] & 0x80) == 0) {
/* no errors on individual bytes, only
possible overrun err */
- if (data[0] & RXERROR_OVERRUN)
- err = TTY_OVERRUN;
- else
- err = 0;
+ if (data[0] & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
for (i = 1; i < urb->actual_length ; ++i)
- tty_insert_flip_char(&port->port, data[i], err);
+ tty_insert_flip_char(&port->port, data[i],
+ TTY_NORMAL);
} else {
/* some bytes had errors, every byte has status */
dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
for (i = 0; i + 1 < urb->actual_length; i += 2) {
- int stat = data[i], flag = 0;
- if (stat & RXERROR_OVERRUN)
- flag |= TTY_OVERRUN;
- if (stat & RXERROR_FRAMING)
- flag |= TTY_FRAME;
- if (stat & RXERROR_PARITY)
- flag |= TTY_PARITY;
+ int stat = data[i];
+ int flag = TTY_NORMAL;
+
+ if (stat & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
/* XXX should handle break (0x10) */
+ if (stat & RXERROR_PARITY)
+ flag = TTY_PARITY;
+ else if (stat & RXERROR_FRAMING)
+ flag = TTY_FRAME;
+
tty_insert_flip_char(&port->port, data[i+1],
flag);
}
@@ -666,14 +672,19 @@ static void usa49_indat_callback(struct urb *urb)
} else {
/* some bytes had errors, every byte has status */
for (i = 0; i + 1 < urb->actual_length; i += 2) {
- int stat = data[i], flag = 0;
- if (stat & RXERROR_OVERRUN)
- flag |= TTY_OVERRUN;
- if (stat & RXERROR_FRAMING)
- flag |= TTY_FRAME;
- if (stat & RXERROR_PARITY)
- flag |= TTY_PARITY;
+ int stat = data[i];
+ int flag = TTY_NORMAL;
+
+ if (stat & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
/* XXX should handle break (0x10) */
+ if (stat & RXERROR_PARITY)
+ flag = TTY_PARITY;
+ else if (stat & RXERROR_FRAMING)
+ flag = TTY_FRAME;
+
tty_insert_flip_char(&port->port, data[i+1],
flag);
}
@@ -730,15 +741,19 @@ static void usa49wg_indat_callback(struct urb *urb)
*/
for (x = 0; x + 1 < len &&
i + 1 < urb->actual_length; x += 2) {
- int stat = data[i], flag = 0;
+ int stat = data[i];
+ int flag = TTY_NORMAL;
- if (stat & RXERROR_OVERRUN)
- flag |= TTY_OVERRUN;
- if (stat & RXERROR_FRAMING)
- flag |= TTY_FRAME;
- if (stat & RXERROR_PARITY)
- flag |= TTY_PARITY;
+ if (stat & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
/* XXX should handle break (0x10) */
+ if (stat & RXERROR_PARITY)
+ flag = TTY_PARITY;
+ else if (stat & RXERROR_FRAMING)
+ flag = TTY_FRAME;
+
tty_insert_flip_char(&port->port, data[i+1],
flag);
i += 2;
@@ -790,25 +805,31 @@ static void usa90_indat_callback(struct urb *urb)
if ((data[0] & 0x80) == 0) {
/* no errors on individual bytes, only
possible overrun err*/
- if (data[0] & RXERROR_OVERRUN)
- err = TTY_OVERRUN;
- else
- err = 0;
+ if (data[0] & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
for (i = 1; i < urb->actual_length ; ++i)
tty_insert_flip_char(&port->port,
- data[i], err);
+ data[i], TTY_NORMAL);
} else {
/* some bytes had errors, every byte has status */
dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
for (i = 0; i + 1 < urb->actual_length; i += 2) {
- int stat = data[i], flag = 0;
- if (stat & RXERROR_OVERRUN)
- flag |= TTY_OVERRUN;
- if (stat & RXERROR_FRAMING)
- flag |= TTY_FRAME;
- if (stat & RXERROR_PARITY)
- flag |= TTY_PARITY;
+ int stat = data[i];
+ int flag = TTY_NORMAL;
+
+ if (stat & RXERROR_OVERRUN) {
+ tty_insert_flip_char(
+ &port->port, 0,
+ TTY_OVERRUN);
+ }
/* XXX should handle break (0x10) */
+ if (stat & RXERROR_PARITY)
+ flag = TTY_PARITY;
+ else if (stat & RXERROR_FRAMING)
+ flag = TTY_FRAME;
+
tty_insert_flip_char(&port->port,
data[i+1], flag);
}
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index a7fe664b6b7d..70a098de429f 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -490,10 +490,9 @@ static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr,
if (*tty_flag == TTY_NORMAL)
*tty_flag = TTY_FRAME;
}
- if (lsr & UART_LSR_OE){
+ if (lsr & UART_LSR_OE) {
port->icount.overrun++;
- if (*tty_flag == TTY_NORMAL)
- *tty_flag = TTY_OVERRUN;
+ tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
}
}
@@ -511,12 +510,8 @@ static void ssu100_process_read_urb(struct urb *urb)
if ((len >= 4) &&
(packet[0] == 0x1b) && (packet[1] == 0x1b) &&
((packet[2] == 0x00) || (packet[2] == 0x01))) {
- if (packet[2] == 0x00) {
+ if (packet[2] == 0x00)
ssu100_update_lsr(port, packet[3], &flag);
- if (flag == TTY_OVERRUN)
- tty_insert_flip_char(&port->port, 0,
- TTY_OVERRUN);
- }
if (packet[2] == 0x01)
ssu100_update_msr(port, packet[3]);
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index e48d4a672580..5d0b7b846440 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -1200,6 +1200,7 @@ static int
vhost_scsi_set_endpoint(struct vhost_scsi *vs,
struct vhost_scsi_target *t)
{
+ struct se_portal_group *se_tpg;
struct tcm_vhost_tport *tv_tport;
struct tcm_vhost_tpg *tpg;
struct tcm_vhost_tpg **vs_tpg;
@@ -1247,6 +1248,21 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs,
ret = -EEXIST;
goto out;
}
+ /*
+ * In order to ensure individual vhost-scsi configfs
+ * groups cannot be removed while in use by vhost ioctl,
+ * go ahead and take an explicit se_tpg->tpg_group.cg_item
+ * dependency now.
+ */
+ se_tpg = &tpg->se_tpg;
+ ret = configfs_depend_item(se_tpg->se_tpg_tfo->tf_subsys,
+ &se_tpg->tpg_group.cg_item);
+ if (ret) {
+ pr_warn("configfs_depend_item() failed: %d\n", ret);
+ kfree(vs_tpg);
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ goto out;
+ }
tpg->tv_tpg_vhost_count++;
tpg->vhost_scsi = vs;
vs_tpg[tpg->tport_tpgt] = tpg;
@@ -1289,6 +1305,7 @@ static int
vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
struct vhost_scsi_target *t)
{
+ struct se_portal_group *se_tpg;
struct tcm_vhost_tport *tv_tport;
struct tcm_vhost_tpg *tpg;
struct vhost_virtqueue *vq;
@@ -1337,6 +1354,13 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
vs->vs_tpg[target] = NULL;
match = true;
mutex_unlock(&tpg->tv_tpg_mutex);
+ /*
+ * Release se_tpg->tpg_group.cg_item configfs dependency now
+ * to allow vhost-scsi WWPN se_tpg->tpg_group shutdown to occur.
+ */
+ se_tpg = &tpg->se_tpg;
+ configfs_undepend_item(se_tpg->se_tpg_tfo->tf_subsys,
+ &se_tpg->tpg_group.cg_item);
}
if (match) {
for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) {
diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c
index 88e5eaae3512..83bb978455aa 100644
--- a/drivers/video/fbdev/omap2/dss/dispc.c
+++ b/drivers/video/fbdev/omap2/dss/dispc.c
@@ -1334,8 +1334,18 @@ static void dispc_init_mflag(void)
{
int i;
+ /*
+ * HACK: NV12 color format and MFLAG seem to have problems working
+ * together: using two displays, and having an NV12 overlay on one of
+ * the displays will cause underflows/synclosts when MFLAG_CTRL=2.
+ * Changing MFLAG thresholds and PRELOAD to certain values seem to
+ * remove the errors, but there doesn't seem to be a clear logic on
+ * which values work and which not.
+ *
+ * As a work-around, set force MFLAG to always on.
+ */
dispc_write_reg(DISPC_GLOBAL_MFLAG_ATTRIBUTE,
- (2 << 0) | /* MFLAG_CTRL = enable */
+ (1 << 0) | /* MFLAG_CTRL = force always on */
(0 << 2)); /* MFLAG_START = disable */
for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
@@ -2526,7 +2536,7 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
unsigned long pclk = dispc_plane_pclk_rate(plane);
unsigned long lclk = dispc_plane_lclk_rate(plane);
- if (paddr == 0)
+ if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER)
return -EINVAL;
out_width = out_width == 0 ? width : out_width;
@@ -3961,6 +3971,7 @@ static int __init omap_dispchw_probe(struct platform_device *pdev)
}
pm_runtime_enable(&pdev->dev);
+ pm_runtime_irq_safe(&pdev->dev);
r = dispc_runtime_get();
if (r)
diff --git a/drivers/video/fbdev/omap2/dss/dss.c b/drivers/video/fbdev/omap2/dss/dss.c
index f21bc0e1cef5..abe17f01fa63 100644
--- a/drivers/video/fbdev/omap2/dss/dss.c
+++ b/drivers/video/fbdev/omap2/dss/dss.c
@@ -1075,6 +1075,7 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
goto err_setup_clocks;
pm_runtime_enable(&pdev->dev);
+ pm_runtime_irq_safe(&pdev->dev);
r = dss_runtime_get();
if (r)
@@ -1152,12 +1153,18 @@ static int dss_runtime_suspend(struct device *dev)
{
dss_save_context();
dss_set_min_bus_tput(dev, 0);
+
+ pinctrl_pm_select_sleep_state(dev);
+
return 0;
}
static int dss_runtime_resume(struct device *dev)
{
int r;
+
+ pinctrl_pm_select_default_state(dev);
+
/*
* Set an arbitrarily high tput request to ensure OPP100.
* What we should really do is to make a request to stay in OPP100,
diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c
index b670cbda38e3..ffe024b830fc 100644
--- a/drivers/video/logo/logo.c
+++ b/drivers/video/logo/logo.c
@@ -21,6 +21,21 @@ static bool nologo;
module_param(nologo, bool, 0);
MODULE_PARM_DESC(nologo, "Disables startup logo");
+/*
+ * Logos are located in the initdata, and will be freed in kernel_init.
+ * Use late_init to mark the logos as freed to prevent any further use.
+ */
+
+static bool logos_freed;
+
+static int __init fb_logo_late_init(void)
+{
+ logos_freed = true;
+ return 0;
+}
+
+late_initcall(fb_logo_late_init);
+
/* logo's are marked __initdata. Use __init_refok to tell
* modpost that it is intended that this function uses data
* marked __initdata.
@@ -29,7 +44,7 @@ const struct linux_logo * __init_refok fb_find_logo(int depth)
{
const struct linux_logo *logo = NULL;
- if (nologo)
+ if (nologo || logos_freed)
return NULL;
if (depth >= 1) {
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index fed0ce198ae3..64eba4f51f71 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -249,6 +249,7 @@ static int virtio_init(void)
static void __exit virtio_exit(void)
{
bus_unregister(&virtio_bus);
+ ida_destroy(&virtio_index_ida);
}
core_initcall(virtio_init);
module_exit(virtio_exit);
diff --git a/fs/aio.c b/fs/aio.c
index f45ddaa4fffa..2f7e8c2e3e76 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -165,6 +165,15 @@ static struct vfsmount *aio_mnt;
static const struct file_operations aio_ring_fops;
static const struct address_space_operations aio_ctx_aops;
+/* Backing dev info for aio fs.
+ * -no dirty page accounting or writeback happens
+ */
+static struct backing_dev_info aio_fs_backing_dev_info = {
+ .name = "aiofs",
+ .state = 0,
+ .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_MAP_COPY,
+};
+
static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages)
{
struct qstr this = QSTR_INIT("[aio]", 5);
@@ -176,6 +185,7 @@ static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages)
inode->i_mapping->a_ops = &aio_ctx_aops;
inode->i_mapping->private_data = ctx;
+ inode->i_mapping->backing_dev_info = &aio_fs_backing_dev_info;
inode->i_size = PAGE_SIZE * nr_pages;
path.dentry = d_alloc_pseudo(aio_mnt->mnt_sb, &this);
@@ -221,6 +231,9 @@ static int __init aio_setup(void)
if (IS_ERR(aio_mnt))
panic("Failed to create aio fs mount.");
+ if (bdi_init(&aio_fs_backing_dev_info))
+ panic("Failed to init aio fs backing dev info.");
+
kiocb_cachep = KMEM_CACHE(kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC);
kioctx_cachep = KMEM_CACHE(kioctx,SLAB_HWCACHE_ALIGN|SLAB_PANIC);
@@ -282,11 +295,6 @@ static const struct file_operations aio_ring_fops = {
.mmap = aio_ring_mmap,
};
-static int aio_set_page_dirty(struct page *page)
-{
- return 0;
-}
-
#if IS_ENABLED(CONFIG_MIGRATION)
static int aio_migratepage(struct address_space *mapping, struct page *new,
struct page *old, enum migrate_mode mode)
@@ -358,7 +366,7 @@ out:
#endif
static const struct address_space_operations aio_ctx_aops = {
- .set_page_dirty = aio_set_page_dirty,
+ .set_page_dirty = __set_page_dirty_no_writeback,
#if IS_ENABLED(CONFIG_MIGRATION)
.migratepage = aio_migratepage,
#endif
@@ -413,7 +421,6 @@ static int aio_setup_ring(struct kioctx *ctx)
pr_debug("pid(%d) page[%d]->count=%d\n",
current->pid, i, page_count(page));
SetPageUptodate(page);
- SetPageDirty(page);
unlock_page(page);
ctx->ring_pages[i] = page;
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index b01fb6c527e3..d43c544d3b68 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -472,7 +472,7 @@ static noinline int add_ra_bio_pages(struct inode *inode,
rcu_read_lock();
page = radix_tree_lookup(&mapping->page_tree, pg_index);
rcu_read_unlock();
- if (page) {
+ if (page && !radix_tree_exceptional_entry(page)) {
misses++;
if (misses > 4)
break;
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index a9a881ed8cbe..f6d00df99a8c 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -425,13 +425,8 @@ static noinline int btrfs_copy_from_user(loff_t pos, int num_pages,
struct page *page = prepared_pages[pg];
/*
* Copy data from userspace to the current page
- *
- * Disable pagefault to avoid recursive lock since
- * the pages are already locked
*/
- pagefault_disable();
copied = iov_iter_copy_from_user_atomic(page, i, offset, count);
- pagefault_enable();
/* Flush processor's dcache for this page */
flush_dcache_page(page);
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 06610cf94d57..a1f801c14fbc 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -195,8 +195,7 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i
struct page *page = NULL;
if (blocknr + i < devsize) {
- page = read_mapping_page_async(mapping, blocknr + i,
- NULL);
+ page = read_mapping_page(mapping, blocknr + i, NULL);
/* synchronous error? */
if (IS_ERR(page))
page = NULL;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 77bcc303c3ae..a91d3b4d32f3 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1003,9 +1003,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
if (mapping_writably_mapped(mapping))
flush_dcache_page(page);
- pagefault_disable();
tmp = iov_iter_copy_from_user_atomic(page, ii, offset, bytes);
- pagefault_enable();
flush_dcache_page(page);
mark_page_accessed(page);
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index c7f24690ed05..b82a9c99e18b 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -97,6 +97,11 @@ const struct address_space_operations gfs2_meta_aops = {
.releasepage = gfs2_releasepage,
};
+const struct address_space_operations gfs2_rgrp_aops = {
+ .writepage = gfs2_aspace_writepage,
+ .releasepage = gfs2_releasepage,
+};
+
/**
* gfs2_getbuf - Get a buffer with a given address space
* @gl: the glock
diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h
index 4823b934208a..ac5d8027d335 100644
--- a/fs/gfs2/meta_io.h
+++ b/fs/gfs2/meta_io.h
@@ -38,12 +38,15 @@ static inline void gfs2_buffer_copy_tail(struct buffer_head *to_bh,
}
extern const struct address_space_operations gfs2_meta_aops;
+extern const struct address_space_operations gfs2_rgrp_aops;
static inline struct gfs2_sbd *gfs2_mapping2sbd(struct address_space *mapping)
{
struct inode *inode = mapping->host;
if (mapping->a_ops == &gfs2_meta_aops)
return (((struct gfs2_glock *)mapping) - 1)->gl_sbd;
+ else if (mapping->a_ops == &gfs2_rgrp_aops)
+ return container_of(mapping, struct gfs2_sbd, sd_aspace);
else
return inode->i_sb->s_fs_info;
}
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index c6872d09561a..f6c9d83aa39b 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -104,7 +104,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
mapping = &sdp->sd_aspace;
address_space_init_once(mapping);
- mapping->a_ops = &gfs2_meta_aops;
+ mapping->a_ops = &gfs2_rgrp_aops;
mapping->host = sb->s_bdev->bd_inode;
mapping->flags = 0;
mapping_set_gfp_mask(mapping, GFP_NOFS);
diff --git a/fs/ioprio.c b/fs/ioprio.c
index e50170ca7c33..31666c92b46a 100644
--- a/fs/ioprio.c
+++ b/fs/ioprio.c
@@ -157,14 +157,16 @@ out:
int ioprio_best(unsigned short aprio, unsigned short bprio)
{
- unsigned short aclass = IOPRIO_PRIO_CLASS(aprio);
- unsigned short bclass = IOPRIO_PRIO_CLASS(bprio);
+ unsigned short aclass;
+ unsigned short bclass;
- if (aclass == IOPRIO_CLASS_NONE)
- aclass = IOPRIO_CLASS_BE;
- if (bclass == IOPRIO_CLASS_NONE)
- bclass = IOPRIO_CLASS_BE;
+ if (!ioprio_valid(aprio))
+ aprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
+ if (!ioprio_valid(bprio))
+ bprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
+ aclass = IOPRIO_PRIO_CLASS(aprio);
+ bclass = IOPRIO_PRIO_CLASS(bprio);
if (aclass == bclass)
return min(aprio, bprio);
if (aclass > bclass)
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index a69e426435dd..5b234db85854 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -687,7 +687,7 @@ unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
struct inode *inode = OFNI_EDONI_2SFFJ(f);
struct page *pg;
- pg = read_cache_page_async(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
+ pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
(void *)jffs2_do_readpage_unlock, inode);
if (IS_ERR(pg))
return (void *)pg;
diff --git a/fs/locks.c b/fs/locks.c
index 4dd39b98a6a3..2c61c4e9368c 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -2235,16 +2235,28 @@ void locks_remove_flock(struct file *filp)
while ((fl = *before) != NULL) {
if (fl->fl_file == filp) {
- if (IS_FLOCK(fl)) {
- locks_delete_lock(before);
- continue;
- }
if (IS_LEASE(fl)) {
lease_modify(before, F_UNLCK);
continue;
}
- /* What? */
- BUG();
+
+ /*
+ * There's a leftover lock on the list of a type that
+ * we didn't expect to see. Most likely a classic
+ * POSIX lock that ended up not getting released
+ * properly, or that raced onto the list somehow. Log
+ * some info about it and then just remove it from
+ * the list.
+ */
+ WARN(!IS_FLOCK(fl),
+ "leftover lock: dev=%u:%u ino=%lu type=%hhd flags=0x%x start=%lld end=%lld\n",
+ MAJOR(inode->i_sb->s_dev),
+ MINOR(inode->i_sb->s_dev), inode->i_ino,
+ fl->fl_type, fl->fl_flags,
+ fl->fl_start, fl->fl_end);
+
+ locks_delete_lock(before);
+ continue;
}
before = &fl->fl_next;
}
diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c
index 56ff823ca82e..65d849bdf77a 100644
--- a/fs/nfs/blocklayout/blocklayout.c
+++ b/fs/nfs/blocklayout/blocklayout.c
@@ -1213,7 +1213,7 @@ static u64 pnfs_num_cont_bytes(struct inode *inode, pgoff_t idx)
end = DIV_ROUND_UP(i_size_read(inode), PAGE_CACHE_SIZE);
if (end != NFS_I(inode)->npages) {
rcu_read_lock();
- end = radix_tree_next_hole(&mapping->page_tree, idx + 1, ULONG_MAX);
+ end = page_cache_next_hole(mapping, idx + 1, ULONG_MAX);
rcu_read_unlock();
}
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 5d8ccecf5f5c..3ed1be9aade3 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -109,6 +109,8 @@ again:
continue;
if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
continue;
+ if (!nfs4_valid_open_stateid(state))
+ continue;
if (!nfs4_stateid_match(&state->stateid, stateid))
continue;
get_nfs_open_context(ctx);
@@ -177,7 +179,11 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
{
int res = 0;
- res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
+ if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+ res = nfs4_proc_delegreturn(inode,
+ delegation->cred,
+ &delegation->stateid,
+ issync);
nfs_free_delegation(delegation);
return res;
}
@@ -364,11 +370,13 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode);
- int err;
+ int err = 0;
if (delegation == NULL)
return 0;
do {
+ if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+ break;
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
if (!issync || err != -EAGAIN)
break;
@@ -589,10 +597,23 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
rcu_read_unlock();
}
+static void nfs_revoke_delegation(struct inode *inode)
+{
+ struct nfs_delegation *delegation;
+ rcu_read_lock();
+ delegation = rcu_dereference(NFS_I(inode)->delegation);
+ if (delegation != NULL) {
+ set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
+ nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
+ }
+ rcu_read_unlock();
+}
+
void nfs_remove_bad_delegation(struct inode *inode)
{
struct nfs_delegation *delegation;
+ nfs_revoke_delegation(inode);
delegation = nfs_inode_detach_delegation(inode);
if (delegation) {
nfs_inode_find_state_and_recover(inode, &delegation->stateid);
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 9a79c7a99d6d..e02b090ab9da 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -31,6 +31,7 @@ enum {
NFS_DELEGATION_RETURN_IF_CLOSED,
NFS_DELEGATION_REFERENCED,
NFS_DELEGATION_RETURNING,
+ NFS_DELEGATION_REVOKED,
};
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index b8797ae6831f..de2543d3c283 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -178,6 +178,7 @@ static void nfs_direct_req_free(struct kref *kref)
{
struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref);
+ nfs_free_pnfs_ds_cinfo(&dreq->ds_cinfo);
if (dreq->l_ctx != NULL)
nfs_put_lock_context(dreq->l_ctx);
if (dreq->ctx != NULL)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 15f9d98627a4..6659ce545f15 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -592,7 +592,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
- int err;
+ int err = 0;
trace_nfs_getattr_enter(inode);
/* Flush out writes to the server in order to update c/mtime. */
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index da657b7804a5..bd01803d0656 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1587,7 +1587,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
nfs_inode_find_state_and_recover(state->inode,
stateid);
nfs4_schedule_stateid_recovery(server, state);
- return 0;
+ return -EAGAIN;
case -NFS4ERR_DELAY:
case -NFS4ERR_GRACE:
set_bit(NFS_DELEGATED_STATE, &state->flags);
@@ -2034,46 +2034,60 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
return ret;
}
+static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state)
+{
+ nfs_remove_bad_delegation(state->inode);
+ write_seqlock(&state->seqlock);
+ nfs4_stateid_copy(&state->stateid, &state->open_stateid);
+ write_sequnlock(&state->seqlock);
+ clear_bit(NFS_DELEGATED_STATE, &state->flags);
+}
+
+static void nfs40_clear_delegation_stateid(struct nfs4_state *state)
+{
+ if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL)
+ nfs_finish_clear_delegation_stateid(state);
+}
+
+static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
+{
+ /* NFSv4.0 doesn't allow for delegation recovery on open expire */
+ nfs40_clear_delegation_stateid(state);
+ return nfs4_open_expired(sp, state);
+}
+
#if defined(CONFIG_NFS_V4_1)
-static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
+static void nfs41_check_delegation_stateid(struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(state->inode);
- nfs4_stateid *stateid = &state->stateid;
+ nfs4_stateid stateid;
struct nfs_delegation *delegation;
- struct rpc_cred *cred = NULL;
- int status = -NFS4ERR_BAD_STATEID;
-
- /* If a state reset has been done, test_stateid is unneeded */
- if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
- return;
+ struct rpc_cred *cred;
+ int status;
/* Get the delegation credential for use by test/free_stateid */
rcu_read_lock();
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
- if (delegation != NULL &&
- nfs4_stateid_match(&delegation->stateid, stateid)) {
- cred = get_rpccred(delegation->cred);
- rcu_read_unlock();
- status = nfs41_test_stateid(server, stateid, cred);
- trace_nfs4_test_delegation_stateid(state, NULL, status);
- } else
+ if (delegation == NULL) {
rcu_read_unlock();
+ return;
+ }
+
+ nfs4_stateid_copy(&stateid, &delegation->stateid);
+ cred = get_rpccred(delegation->cred);
+ rcu_read_unlock();
+ status = nfs41_test_stateid(server, &stateid, cred);
+ trace_nfs4_test_delegation_stateid(state, NULL, status);
if (status != NFS_OK) {
/* Free the stateid unless the server explicitly
* informs us the stateid is unrecognized. */
if (status != -NFS4ERR_BAD_STATEID)
- nfs41_free_stateid(server, stateid, cred);
- nfs_remove_bad_delegation(state->inode);
-
- write_seqlock(&state->seqlock);
- nfs4_stateid_copy(&state->stateid, &state->open_stateid);
- write_sequnlock(&state->seqlock);
- clear_bit(NFS_DELEGATED_STATE, &state->flags);
+ nfs41_free_stateid(server, &stateid, cred);
+ nfs_finish_clear_delegation_stateid(state);
}
- if (cred != NULL)
- put_rpccred(cred);
+ put_rpccred(cred);
}
/**
@@ -2117,7 +2131,7 @@ static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
{
int status;
- nfs41_clear_delegation_stateid(state);
+ nfs41_check_delegation_stateid(state);
status = nfs41_check_open_stateid(state);
if (status != NFS_OK)
status = nfs4_open_expired(sp, state);
@@ -8255,7 +8269,7 @@ static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
.state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
- .recover_open = nfs4_open_expired,
+ .recover_open = nfs40_open_expired,
.recover_lock = nfs4_lock_expired,
.establish_clid = nfs4_init_clientid,
};
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 2ffebf2081ce..27d7f2742592 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -113,7 +113,7 @@ __nfs_iocounter_wait(struct nfs_io_counter *c)
if (atomic_read(&c->io_count) == 0)
break;
ret = nfs_wait_bit_killable(&c->flags);
- } while (atomic_read(&c->io_count) != 0);
+ } while (atomic_read(&c->io_count) != 0 && !ret);
finish_wait(wq, &q.wait);
return ret;
}
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index cc8c5b32043c..f42bbe5fbc0a 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -784,8 +784,12 @@ static bool nfsd41_cb_get_slot(struct nfs4_client *clp, struct rpc_task *task)
{
if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
- dprintk("%s slot is busy\n", __func__);
- return false;
+ /* Race breaker */
+ if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
+ dprintk("%s slot is busy\n", __func__);
+ return false;
+ }
+ rpc_wake_up_queued_task(&clp->cl_cb_waitq, task);
}
return true;
}
diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c
index f8f060ffbf4f..6040da8830ff 100644
--- a/fs/nfsd/nfscache.c
+++ b/fs/nfsd/nfscache.c
@@ -224,13 +224,6 @@ hash_refile(struct svc_cacherep *rp)
hlist_add_head(&rp->c_hash, cache_hash + hash_32(rp->c_xid, maskbits));
}
-static inline bool
-nfsd_cache_entry_expired(struct svc_cacherep *rp)
-{
- return rp->c_state != RC_INPROG &&
- time_after(jiffies, rp->c_timestamp + RC_EXPIRE);
-}
-
/*
* Walk the LRU list and prune off entries that are older than RC_EXPIRE.
* Also prune the oldest ones when the total exceeds the max number of entries.
@@ -242,8 +235,14 @@ prune_cache_entries(void)
long freed = 0;
list_for_each_entry_safe(rp, tmp, &lru_head, c_lru) {
- if (!nfsd_cache_entry_expired(rp) &&
- num_drc_entries <= max_drc_entries)
+ /*
+ * Don't free entries attached to calls that are still
+ * in-progress, but do keep scanning the list.
+ */
+ if (rp->c_state == RC_INPROG)
+ continue;
+ if (num_drc_entries <= max_drc_entries &&
+ time_before(jiffies, rp->c_timestamp + RC_EXPIRE))
break;
nfsd_reply_cache_free_locked(rp);
freed++;
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 479eb681c27c..f417fef17118 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -328,12 +328,15 @@ void nfsd_lockd_shutdown(void);
(NFSD4_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SUPPATTR_EXCLCREAT)
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
-#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
- (NFSD4_1_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SECURITY_LABEL)
+#define NFSD4_2_SECURITY_ATTRS FATTR4_WORD2_SECURITY_LABEL
#else
-#define NFSD4_2_SUPPORTED_ATTRS_WORD2 0
+#define NFSD4_2_SECURITY_ATTRS 0
#endif
+#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
+ (NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
+ NFSD4_2_SECURITY_ATTRS)
+
static inline u32 nfsd_suppattrs0(u32 minorversion)
{
return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD0
diff --git a/fs/super.c b/fs/super.c
index 88a6bc6e3cc9..440ef51cd696 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -114,9 +114,14 @@ static unsigned long super_cache_count(struct shrinker *shrink,
sb = container_of(shrink, struct super_block, s_shrink);
- if (!grab_super_passive(sb))
- return 0;
-
+ /*
+ * Don't call grab_super_passive as it is a potential
+ * scalability bottleneck. The counts could get updated
+ * between super_cache_count and super_cache_scan anyway.
+ * Call to super_cache_count with shrinker_rwsem held
+ * ensures the safety of call to list_lru_count_node() and
+ * s_op->nr_cached_objects().
+ */
if (sb->s_op && sb->s_op->nr_cached_objects)
total_objects = sb->s_op->nr_cached_objects(sb,
sc->nid);
@@ -127,7 +132,6 @@ static unsigned long super_cache_count(struct shrinker *shrink,
sc->nid);
total_objects = vfs_pressure_ratio(total_objects);
- drop_super(sb);
return total_objects;
}
@@ -278,10 +282,8 @@ void deactivate_locked_super(struct super_block *s)
struct file_system_type *fs = s->s_type;
if (atomic_dec_and_test(&s->s_active)) {
cleancache_invalidate_fs(s);
- fs->kill_sb(s);
-
- /* caches are now gone, we can safely kill the shrinker now */
unregister_shrinker(&s->s_shrink);
+ fs->kill_sb(s);
put_filesystem(fs);
put_super(s);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 3227b716ffdf..1e49058741cd 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1398,6 +1398,8 @@ extern void drm_send_vblank_event(struct drm_device *dev, int crtc,
extern bool drm_handle_vblank(struct drm_device *dev, int crtc);
extern int drm_vblank_get(struct drm_device *dev, int crtc);
extern void drm_vblank_put(struct drm_device *dev, int crtc);
+extern void drm_wait_one_vblank(struct drm_device *dev, int crtc);
+extern void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
extern void drm_vblank_off(struct drm_device *dev, int crtc);
extern void drm_vblank_cleanup(struct drm_device *dev);
extern u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
diff --git a/include/dt-bindings/pinctrl/dra.h b/include/dt-bindings/pinctrl/dra.h
index e665150d5899..3d1aa2575684 100644
--- a/include/dt-bindings/pinctrl/dra.h
+++ b/include/dt-bindings/pinctrl/dra.h
@@ -40,8 +40,8 @@
/* Active pin states */
#define PIN_OUTPUT (0 | PULL_DIS)
-#define PIN_OUTPUT_PULLUP (PIN_OUTPUT | PULL_ENA | PULL_UP)
-#define PIN_OUTPUT_PULLDOWN (PIN_OUTPUT | PULL_ENA)
+#define PIN_OUTPUT_PULLUP (PULL_UP)
+#define PIN_OUTPUT_PULLDOWN (0)
#define PIN_INPUT (INPUT_EN | PULL_DIS)
#define PIN_INPUT_SLEW (INPUT_EN | SLEWCONTROL)
#define PIN_INPUT_PULLUP (PULL_ENA | INPUT_EN | PULL_UP)
diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index be5fd38bd5a0..5d858e02997f 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -18,8 +18,11 @@
* position @h. For example
* GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
*/
-#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l))
-#define GENMASK_ULL(h, l) (((U64_C(1) << ((h) - (l) + 1)) - 1) << (l))
+#define GENMASK(h, l) \
+ (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
+
+#define GENMASK_ULL(h, l) \
+ (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
extern unsigned int __sw_hweight8(unsigned int w);
extern unsigned int __sw_hweight16(unsigned int w);
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 67301a405712..879065d8d208 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -289,7 +289,7 @@ extern struct clocksource* clocksource_get_next(void);
extern void clocksource_change_rating(struct clocksource *cs, int rating);
extern void clocksource_suspend(void);
extern void clocksource_resume(void);
-extern struct clocksource * __init __weak clocksource_default_clock(void);
+extern struct clocksource * __init clocksource_default_clock(void);
extern void clocksource_mark_unstable(struct clocksource *cs);
extern u64
diff --git a/include/linux/compaction.h b/include/linux/compaction.h
index 7e1c76e3cd68..01e3132820da 100644
--- a/include/linux/compaction.h
+++ b/include/linux/compaction.h
@@ -22,7 +22,7 @@ extern int sysctl_extfrag_handler(struct ctl_table *table, int write,
extern int fragmentation_index(struct zone *zone, unsigned int order);
extern unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *mask,
- bool sync, bool *contended);
+ enum migrate_mode mode, bool *contended);
extern void compact_pgdat(pg_data_t *pgdat, int order);
extern void reset_isolation_suitable(pg_data_t *pgdat);
extern unsigned long compaction_suitable(struct zone *zone, int order);
@@ -91,7 +91,7 @@ static inline bool compaction_restarting(struct zone *zone, int order)
#else
static inline unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync, bool *contended)
+ enum migrate_mode mode, bool *contended)
{
return COMPACT_CONTINUE;
}
diff --git a/include/linux/crash_dump.h b/include/linux/crash_dump.h
index 7032518f8542..60023e5d3169 100644
--- a/include/linux/crash_dump.h
+++ b/include/linux/crash_dump.h
@@ -14,14 +14,13 @@
extern unsigned long long elfcorehdr_addr;
extern unsigned long long elfcorehdr_size;
-extern int __weak elfcorehdr_alloc(unsigned long long *addr,
- unsigned long long *size);
-extern void __weak elfcorehdr_free(unsigned long long addr);
-extern ssize_t __weak elfcorehdr_read(char *buf, size_t count, u64 *ppos);
-extern ssize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos);
-extern int __weak remap_oldmem_pfn_range(struct vm_area_struct *vma,
- unsigned long from, unsigned long pfn,
- unsigned long size, pgprot_t prot);
+extern int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size);
+extern void elfcorehdr_free(unsigned long long addr);
+extern ssize_t elfcorehdr_read(char *buf, size_t count, u64 *ppos);
+extern ssize_t elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos);
+extern int remap_oldmem_pfn_range(struct vm_area_struct *vma,
+ unsigned long from, unsigned long pfn,
+ unsigned long size, pgprot_t prot);
extern ssize_t copy_oldmem_page(unsigned long, char *, size_t,
unsigned long, int);
diff --git a/include/linux/iio/events.h b/include/linux/iio/events.h
index 8bbd7bc1043d..03fa332ad2a8 100644
--- a/include/linux/iio/events.h
+++ b/include/linux/iio/events.h
@@ -72,7 +72,7 @@ struct iio_event_data {
#define IIO_EVENT_CODE_EXTRACT_TYPE(mask) ((mask >> 56) & 0xFF)
-#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 48) & 0xCF)
+#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 48) & 0x7F)
#define IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(mask) ((mask >> 32) & 0xFF)
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h
index 0068708161ff..0a21fbefdfbe 100644
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -242,7 +242,7 @@ static inline void in_dev_put(struct in_device *idev)
static __inline__ __be32 inet_make_mask(int logmask)
{
if (logmask)
- return htonl(~((1<<(32-logmask))-1));
+ return htonl(~((1U<<(32-logmask))-1));
return 0;
}
diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h
index 6b06d378f3df..e465bb15912d 100644
--- a/include/linux/kgdb.h
+++ b/include/linux/kgdb.h
@@ -283,7 +283,7 @@ struct kgdb_io {
extern struct kgdb_arch arch_kgdb_ops;
-extern unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs);
+extern unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs);
#ifdef CONFIG_SERIAL_KGDB_NMI
extern int kgdb_register_nmi_console(void);
diff --git a/include/linux/memory.h b/include/linux/memory.h
index bb7384e3c3d8..8b8d8d12348e 100644
--- a/include/linux/memory.h
+++ b/include/linux/memory.h
@@ -35,7 +35,7 @@ struct memory_block {
};
int arch_get_memory_phys_device(unsigned long start_pfn);
-unsigned long __weak memory_block_size_bytes(void);
+unsigned long memory_block_size_bytes(void);
/* These states are exposed to userspace as text strings in sysfs */
#define MEM_ONLINE (1<<0) /* exposed to userspace */
diff --git a/include/linux/migrate.h b/include/linux/migrate.h
index 84a31ad0b791..a2901c414664 100644
--- a/include/linux/migrate.h
+++ b/include/linux/migrate.h
@@ -5,7 +5,9 @@
#include <linux/mempolicy.h>
#include <linux/migrate_mode.h>
-typedef struct page *new_page_t(struct page *, unsigned long private, int **);
+typedef struct page *new_page_t(struct page *page, unsigned long private,
+ int **reason);
+typedef void free_page_t(struct page *page, unsigned long private);
/*
* Return values from addresss_space_operations.migratepage():
@@ -38,7 +40,7 @@ enum migrate_reason {
extern void putback_movable_pages(struct list_head *l);
extern int migrate_page(struct address_space *,
struct page *, struct page *, enum migrate_mode);
-extern int migrate_pages(struct list_head *l, new_page_t x,
+extern int migrate_pages(struct list_head *l, new_page_t new, free_page_t free,
unsigned long private, enum migrate_mode mode, int reason);
extern int migrate_prep(void);
@@ -56,8 +58,9 @@ extern int migrate_page_move_mapping(struct address_space *mapping,
#else
static inline void putback_movable_pages(struct list_head *l) {}
-static inline int migrate_pages(struct list_head *l, new_page_t x,
- unsigned long private, enum migrate_mode mode, int reason)
+static inline int migrate_pages(struct list_head *l, new_page_t new,
+ free_page_t free, unsigned long private, enum migrate_mode mode,
+ int reason)
{ return -ENOSYS; }
static inline int migrate_prep(void) { return -ENOSYS; }
diff --git a/include/linux/mm.h b/include/linux/mm.h
index e360d533adc5..548a520f226e 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1044,6 +1044,14 @@ extern bool skip_free_areas_node(unsigned int flags, int nid);
void shmem_set_file(struct vm_area_struct *vma, struct file *file);
int shmem_zero_setup(struct vm_area_struct *);
+#ifdef CONFIG_SHMEM
+bool shmem_mapping(struct address_space *mapping);
+#else
+static inline bool shmem_mapping(struct address_space *mapping)
+{
+ return false;
+}
+#endif
extern int can_do_mlock(void);
extern int user_shm_lock(size_t, struct user_struct *);
@@ -1851,9 +1859,6 @@ void page_cache_async_readahead(struct address_space *mapping,
unsigned long size);
unsigned long max_sane_readahead(unsigned long nr);
-unsigned long ra_submit(struct file_ra_state *ra,
- struct address_space *mapping,
- struct file *filp);
/* Generic expand stack which grows the stack according to GROWS{UP,DOWN} */
extern int expand_stack(struct vm_area_struct *vma, unsigned long address);
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index e6800f0c0d7b..18843532a0c9 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -361,9 +361,10 @@ struct zone {
/* Set to true when the PG_migrate_skip bits should be cleared */
bool compact_blockskip_flush;
- /* pfns where compaction scanners should start */
+ /* pfn where compaction free scanner should start */
unsigned long compact_cached_free_pfn;
- unsigned long compact_cached_migrate_pfn;
+ /* pfn where async and sync compaction migration scanner should start */
+ unsigned long compact_cached_migrate_pfn[2];
#endif
#ifdef CONFIG_MEMORY_HOTPLUG
/* see spanned/present_pages for more description */
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 5624e4e2763c..53988cb3c05a 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1247,11 +1247,22 @@ struct nfs41_free_stateid_res {
unsigned int status;
};
+static inline void
+nfs_free_pnfs_ds_cinfo(struct pnfs_ds_commit_info *cinfo)
+{
+ kfree(cinfo->buckets);
+}
+
#else
struct pnfs_ds_commit_info {
};
+static inline void
+nfs_free_pnfs_ds_cinfo(struct pnfs_ds_commit_info *cinfo)
+{
+}
+
#endif /* CONFIG_NFS_V4_1 */
struct nfs_page;
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 1710d1b060ba..09c1b03867d9 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -243,12 +243,20 @@ static inline struct page *page_cache_alloc_readahead(struct address_space *x)
typedef int filler_t(void *, struct page *);
-extern struct page * find_get_page(struct address_space *mapping,
- pgoff_t index);
-extern struct page * find_lock_page(struct address_space *mapping,
- pgoff_t index);
-extern struct page * find_or_create_page(struct address_space *mapping,
- pgoff_t index, gfp_t gfp_mask);
+pgoff_t page_cache_next_hole(struct address_space *mapping,
+ pgoff_t index, unsigned long max_scan);
+pgoff_t page_cache_prev_hole(struct address_space *mapping,
+ pgoff_t index, unsigned long max_scan);
+
+struct page *find_get_entry(struct address_space *mapping, pgoff_t offset);
+struct page *find_get_page(struct address_space *mapping, pgoff_t offset);
+struct page *find_lock_entry(struct address_space *mapping, pgoff_t offset);
+struct page *find_lock_page(struct address_space *mapping, pgoff_t offset);
+struct page *find_or_create_page(struct address_space *mapping, pgoff_t index,
+ gfp_t gfp_mask);
+unsigned find_get_entries(struct address_space *mapping, pgoff_t start,
+ unsigned int nr_entries, struct page **entries,
+ pgoff_t *indices);
unsigned find_get_pages(struct address_space *mapping, pgoff_t start,
unsigned int nr_pages, struct page **pages);
unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t start,
@@ -270,8 +278,6 @@ static inline struct page *grab_cache_page(struct address_space *mapping,
extern struct page * grab_cache_page_nowait(struct address_space *mapping,
pgoff_t index);
-extern struct page * read_cache_page_async(struct address_space *mapping,
- pgoff_t index, filler_t *filler, void *data);
extern struct page * read_cache_page(struct address_space *mapping,
pgoff_t index, filler_t *filler, void *data);
extern struct page * read_cache_page_gfp(struct address_space *mapping,
@@ -279,14 +285,6 @@ extern struct page * read_cache_page_gfp(struct address_space *mapping,
extern int read_cache_pages(struct address_space *mapping,
struct list_head *pages, filler_t *filler, void *data);
-static inline struct page *read_mapping_page_async(
- struct address_space *mapping,
- pgoff_t index, void *data)
-{
- filler_t *filler = (filler_t *)mapping->a_ops->readpage;
- return read_cache_page_async(mapping, index, filler, data);
-}
-
static inline struct page *read_mapping_page(struct address_space *mapping,
pgoff_t index, void *data)
{
diff --git a/include/linux/pagevec.h b/include/linux/pagevec.h
index e4dbfab37729..b45d391b4540 100644
--- a/include/linux/pagevec.h
+++ b/include/linux/pagevec.h
@@ -22,6 +22,11 @@ struct pagevec {
void __pagevec_release(struct pagevec *pvec);
void __pagevec_lru_add(struct pagevec *pvec);
+unsigned pagevec_lookup_entries(struct pagevec *pvec,
+ struct address_space *mapping,
+ pgoff_t start, unsigned nr_entries,
+ pgoff_t *indices);
+void pagevec_remove_exceptionals(struct pagevec *pvec);
unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
pgoff_t start, unsigned nr_pages);
unsigned pagevec_lookup_tag(struct pagevec *pvec,
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 33aa2caf0f0c..0e5e16c6f7f1 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -324,6 +324,7 @@ struct pci_dev {
unsigned int is_added:1;
unsigned int is_busmaster:1; /* device is busmaster */
unsigned int no_msi:1; /* device may not use msi */
+ unsigned int no_64bit_msi:1; /* device may only use 32-bit MSIs */
unsigned int block_cfg_access:1; /* config space access is blocked */
unsigned int broken_parity_status:1; /* Device generates false positive parity */
unsigned int irq_reroute_variant:2; /* device needs IRQ rerouting variant */
diff --git a/include/linux/phy/omap_control_phy.h b/include/linux/phy/omap_control_phy.h
index 32b0fb0ed1d7..e5cb6c616d4c 100644
--- a/include/linux/phy/omap_control_phy.h
+++ b/include/linux/phy/omap_control_phy.h
@@ -66,7 +66,7 @@ enum omap_control_usb_mode {
#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0
#define OMAP_CTRL_PCIE_PCS_MASK 0xff
-#define OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT 0x8
+#define OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT 16
#define OMAP_CTRL_USB2_PHY_PD BIT(28)
@@ -81,7 +81,7 @@ void omap_control_phy_power(struct device *dev, int on);
int omap_control_phy_wkup(struct device *dev, int on);
void omap_control_usb_set_mode(struct device *dev,
enum omap_control_usb_mode mode);
-void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay);
+void omap_control_pcie_pcs(struct device *dev, u8 delay);
#else
static inline void omap_control_phy_power(struct device *dev, int on)
@@ -98,7 +98,7 @@ static inline void omap_control_usb_set_mode(struct device *dev,
{
}
-static inline void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
+static inline void omap_control_pcie_pcs(struct device *dev, u8 delay)
{
}
#endif
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
index 07e7945a1ff2..e97fc656a058 100644
--- a/include/linux/power/charger-manager.h
+++ b/include/linux/power/charger-manager.h
@@ -253,9 +253,6 @@ struct charger_manager {
struct device *dev;
struct charger_desc *desc;
- struct power_supply *fuel_gauge;
- struct power_supply **charger_stat;
-
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd_batt;
#endif
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h
index 403940787be1..e8be53ecfc45 100644
--- a/include/linux/radix-tree.h
+++ b/include/linux/radix-tree.h
@@ -219,6 +219,7 @@ static inline void radix_tree_replace_slot(void **pslot, void *item)
int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
void *radix_tree_lookup(struct radix_tree_root *, unsigned long);
void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long);
+void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *);
void *radix_tree_delete(struct radix_tree_root *, unsigned long);
unsigned int
radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
@@ -226,10 +227,6 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root,
void ***results, unsigned long *indices,
unsigned long first_index, unsigned int max_items);
-unsigned long radix_tree_next_hole(struct radix_tree_root *root,
- unsigned long index, unsigned long max_scan);
-unsigned long radix_tree_prev_hole(struct radix_tree_root *root,
- unsigned long index, unsigned long max_scan);
int radix_tree_preload(gfp_t gfp_mask);
int radix_tree_maybe_preload(gfp_t gfp_mask);
void radix_tree_init(void);
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index 9d55438bc4ad..4d1771c2d29f 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -51,6 +51,7 @@ extern struct file *shmem_kernel_file_setup(const char *name, loff_t size,
unsigned long flags);
extern int shmem_zero_setup(struct vm_area_struct *);
extern int shmem_lock(struct file *file, int lock, struct user_struct *user);
+extern bool shmem_mapping(struct address_space *mapping);
extern void shmem_unlock_mapping(struct address_space *mapping);
extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
pgoff_t index, gfp_t gfp_mask);
diff --git a/include/linux/usb/drd.h b/include/linux/usb/drd.h
new file mode 100644
index 000000000000..71c64dcd93e5
--- /dev/null
+++ b/include/linux/usb/drd.h
@@ -0,0 +1,77 @@
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/hcd.h>
+
+struct usb_drd_setup {
+ int (*ll_start)(void *);
+ int (*ll_stop)(void *);
+ void (*ll_release)(struct device *);
+ void *data;
+};
+
+struct usb_drd_host {
+ struct usb_hcd *main_hcd;
+ struct usb_hcd *shared_hcd;
+ int hcd_irq;
+ struct usb_drd_setup *host_setup;
+};
+
+struct usb_drd_gadget {
+ struct usb_gadget_driver *g_driver;
+ struct usb_gadget *gadget;
+ struct usb_drd_setup *gadget_setup;
+};
+
+#define DRD_UNREGISTERED 0x0
+#define DRD_DEVICE_REGISTERED 0x1
+#define DRD_HOST_REGISTERED 0x2
+#define DRD_HOST_ACTIVE 0x4
+#define DRD_DEVICE_ACTIVE 0x8
+
+#if IS_ENABLED(CONFIG_DRD_LIB)
+int usb_drd_release(struct device *parent);
+int usb_drd_add(struct device *parent);
+int usb_drd_register_udc(struct device *parent,
+ struct usb_drd_gadget *gadget);
+int usb_drd_register_udc_driver(struct device *parent,
+ struct usb_gadget_driver *driver);
+int usb_drd_unregister_udc(struct device *parent);
+int usb_drd_unregister_udc_driver(struct device *parent);
+int usb_drd_register_hcd(struct device *parent,
+ struct usb_drd_host *host);
+int usb_drd_unregister_hcd(struct device *parent);
+int usb_drd_start_hcd(struct device *parent);
+int usb_drd_stop_hcd(struct device *parent);
+int usb_drd_start_udc(struct device *parent);
+int usb_drd_stop_udc(struct device *parent);
+int usb_drd_get_state(struct device *parent);
+#else
+static inline int usb_drd_release(struct device *parent)
+{ return 0; }
+static inline int usb_drd_add(struct device *parent)
+{ return 0; }
+static inline int usb_drd_register_udc(struct device *parent,
+ struct usb_drd_gadget *gadget)
+{ return 0; }
+static inline int usb_drd_register_udc_driver(struct device *parent,
+ struct usb_gadget_driver *driver)
+{ return 0; }
+static inline int usb_drd_unregister_udc(struct device *parent,
+ struct usb_drd_gadget *gadget)
+{ return 0; }
+static inline int usb_drd_unregister_udc_driver(struct device *parent)
+{ return 0; }
+static inline int usb_drd_register_hcd(struct device *parent,
+ struct usb_drd_host *host)
+{ return 0; }
+static inline int usb_drd_unregister_hcd(struct device *parent)
+{ return 0; }
+static inline int usb_drd_stop_hcd(struct device *parent)
+{ return 0; }
+static inline int usb_drd_start_udc(struct device *parent)
+{ return 0; }
+static inline int usb_drd_stop_udc(struct device *parent)
+{ return 0; }
+static inline int usb_drd_get_state(struct device *parent)
+{ return 0; }
+#endif
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index efe8d8a7c7ad..485cd5e2100c 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -143,6 +143,7 @@ struct usb_hcd {
unsigned authorized_default:1;
unsigned has_tt:1; /* Integrated TT in root hub */
unsigned amd_resume_bug:1; /* AMD remote wakeup quirk */
+ unsigned can_do_streams:1; /* HC supports streams */
unsigned int irq; /* irq allocated */
void __iomem *regs; /* device memory/io */
diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h
new file mode 100644
index 000000000000..8ef732155623
--- /dev/null
+++ b/include/linux/usb/xhci_pdriver.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.
+ *
+ */
+
+#ifndef __USB_CORE_XHCI_PDRIVER_H
+#define __USB_CORE_XHCI_PDRIVER_H
+
+/**
+ * struct usb_xhci_pdata - platform_data for generic xhci platform driver
+ *
+ * @usb3_lpm_capable: determines if this xhci platform supports USB3
+ * LPM capability
+ *
+ */
+struct usb_xhci_pdata {
+ unsigned usb3_lpm_capable:1;
+ unsigned usb_drd_support:1;
+ unsigned usb_needs_lhc_reset:1;
+};
+
+#endif /* __USB_CORE_XHCI_PDRIVER_H */
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index a3353f45ef94..ba41e01ebc54 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -433,6 +433,11 @@ static inline void sctp_assoc_pending_pmtu(struct sock *sk, struct sctp_associat
asoc->pmtu_pending = 0;
}
+static inline bool sctp_chunk_pending(const struct sctp_chunk *chunk)
+{
+ return !list_empty(&chunk->list);
+}
+
/* Walk through a list of TLV parameters. Don't trust the
* individual parameter lengths and instead depend on
* the chunk length to indicate when to stop. Make sure
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
index 7f4eeb340a54..72a31db47ded 100644
--- a/include/net/sctp/sm.h
+++ b/include/net/sctp/sm.h
@@ -248,9 +248,9 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *,
int, __be16);
struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
union sctp_addr *addr);
-int sctp_verify_asconf(const struct sctp_association *asoc,
- struct sctp_paramhdr *param_hdr, void *chunk_end,
- struct sctp_paramhdr **errp);
+bool sctp_verify_asconf(const struct sctp_association *asoc,
+ struct sctp_chunk *chunk, bool addr_param_needed,
+ struct sctp_paramhdr **errp);
struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
struct sctp_chunk *asconf);
int sctp_process_asconf_ack(struct sctp_association *asoc,
diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h
index 2883a7a6f9f3..98f2ade0266e 100644
--- a/include/sound/soc-dpcm.h
+++ b/include/sound/soc-dpcm.h
@@ -102,6 +102,8 @@ struct snd_soc_dpcm_runtime {
/* state and update */
enum snd_soc_dpcm_update runtime_update;
enum snd_soc_dpcm_state state;
+
+ int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
};
/* can this BE stop and free */
diff --git a/include/trace/events/compaction.h b/include/trace/events/compaction.h
index 06f544ef2f6f..c6814b917bdf 100644
--- a/include/trace/events/compaction.h
+++ b/include/trace/events/compaction.h
@@ -5,6 +5,7 @@
#define _TRACE_COMPACTION_H
#include <linux/types.h>
+#include <linux/list.h>
#include <linux/tracepoint.h>
#include <trace/events/gfpflags.h>
@@ -47,10 +48,11 @@ DEFINE_EVENT(mm_compaction_isolate_template, mm_compaction_isolate_freepages,
TRACE_EVENT(mm_compaction_migratepages,
- TP_PROTO(unsigned long nr_migrated,
- unsigned long nr_failed),
+ TP_PROTO(unsigned long nr_all,
+ int migrate_rc,
+ struct list_head *migratepages),
- TP_ARGS(nr_migrated, nr_failed),
+ TP_ARGS(nr_all, migrate_rc, migratepages),
TP_STRUCT__entry(
__field(unsigned long, nr_migrated)
@@ -58,7 +60,22 @@ TRACE_EVENT(mm_compaction_migratepages,
),
TP_fast_assign(
- __entry->nr_migrated = nr_migrated;
+ unsigned long nr_failed = 0;
+ struct list_head *page_lru;
+
+ /*
+ * migrate_pages() returns either a non-negative number
+ * with the number of pages that failed migration, or an
+ * error code, in which case we need to count the remaining
+ * pages manually
+ */
+ if (migrate_rc >= 0)
+ nr_failed = migrate_rc;
+ else
+ list_for_each(page_lru, migratepages)
+ nr_failed++;
+
+ __entry->nr_migrated = nr_all - nr_failed;
__entry->nr_failed = nr_failed;
),
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 4c42e9e3805c..6db71bb78c54 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -335,6 +335,8 @@ header-y += rfkill.h
header-y += romfs_fs.h
header-y += rose.h
header-y += route.h
+header-y += rpmsg_rpc.h
+header-y += rpmsg_socket.h
header-y += rtc.h
header-y += rtnetlink.h
header-y += scc.h
diff --git a/include/uapi/linux/net_switch_config.h b/include/uapi/linux/net_switch_config.h
index 149fcdc0680e..df7601567216 100644
--- a/include/uapi/linux/net_switch_config.h
+++ b/include/uapi/linux/net_switch_config.h
@@ -31,6 +31,9 @@ enum {
CONFIG_SWITCH_ADD_UNKNOWN_VLAN_INFO,
CONFIG_SWITCH_GET_PORT_STATE,
CONFIG_SWITCH_SET_PORT_STATE,
+ CONFIG_SWITCH_GET_PORT_VLAN_CONFIG,
+ CONFIG_SWITCH_SET_PORT_VLAN_CONFIG,
+ CONFIG_SWITCH_RATELIMIT,
};
enum {
@@ -55,6 +58,11 @@ struct net_switch_config {
unsigned int unknown_vlan_unreg_multi;
unsigned int unknown_vlan_reg_multi;
unsigned int port_state;
+ unsigned int prio;
+ bool vlan_cfi;
+ unsigned int bcast_rate_limit;
+ unsigned int mcast_rate_limit;
+ bool direction;
struct ethtool_cmd ecmd;
unsigned int ret_type; /* Return Success/Failure */
diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h
index 5dda450eb55b..2ec9fbcd06f9 100644
--- a/include/uapi/linux/netfilter/xt_bpf.h
+++ b/include/uapi/linux/netfilter/xt_bpf.h
@@ -6,6 +6,8 @@
#define XT_BPF_MAX_NUM_INSTR 64
+struct sk_filter;
+
struct xt_bpf_info {
__u16 bpf_program_num_elem;
struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
diff --git a/include/uapi/linux/rpmsg_socket.h b/include/uapi/linux/rpmsg_socket.h
index 3b26bffacf3d..34867ada2c22 100644
--- a/include/uapi/linux/rpmsg_socket.h
+++ b/include/uapi/linux/rpmsg_socket.h
@@ -28,14 +28,6 @@
#define PF_RPMSG AF_RPMSG
#endif
-/* Connection and socket states */
-enum {
- RPMSG_CONNECTED = 1, /* wait_for_packet() wants this... */
- RPMSG_OPEN,
- RPMSG_LISTENING,
- RPMSG_CLOSED,
-};
-
struct sockaddr_rpmsg {
__kernel_sa_family_t family;
__u32 vproc_id;
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
index 17028648cfeb..cadddc8388f3 100644
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -123,7 +123,6 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
struct ctl_table ipc_table;
- size_t lenp_bef = *lenp;
int oldval;
int rc;
@@ -133,7 +132,7 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
- if (write && !rc && lenp_bef == *lenp) {
+ if (write && !rc) {
int newval = *((int *)(ipc_table.data));
/*
* The file "auto_msgmni" has correctly been set.
diff --git a/kernel/audit.c b/kernel/audit.c
index 2c0ecd1753de..b45b2daa9f92 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -687,7 +687,7 @@ static int audit_get_feature(struct sk_buff *skb)
seq = nlmsg_hdr(skb)->nlmsg_seq;
- audit_send_reply(skb, seq, AUDIT_GET, 0, 0, &af, sizeof(af));
+ audit_send_reply(skb, seq, AUDIT_GET_FEATURE, 0, 0, &af, sizeof(af));
return 0;
}
@@ -702,7 +702,7 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
audit_log_task_info(ab, current);
- audit_log_format(ab, "feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
+ audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
audit_feature_names[which], !!old_feature, !!new_feature,
!!old_lock, !!new_lock, res);
audit_log_end(ab);
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index 135944a7b28a..a79db03990db 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -154,6 +154,7 @@ static struct audit_chunk *alloc_chunk(int count)
chunk->owners[i].index = i;
}
fsnotify_init_mark(&chunk->mark, audit_tree_destroy_watch);
+ chunk->mark.mask = FS_IN_IGNORED;
return chunk;
}
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 4ced342f1ba9..4bbb27adf23d 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -39,6 +39,7 @@
#include <linux/hw_breakpoint.h>
#include <linux/mm_types.h>
#include <linux/cgroup.h>
+#include <linux/compat.h>
#include "internal.h"
@@ -3693,6 +3694,26 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return 0;
}
+#ifdef CONFIG_COMPAT
+static long perf_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(PERF_EVENT_IOC_SET_FILTER):
+ case _IOC_NR(PERF_EVENT_IOC_ID):
+ /* Fix up pointer size (usually 4 -> 8 in 32-on-64-bit case */
+ if (_IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
+ cmd &= ~IOCSIZE_MASK;
+ cmd |= sizeof(void *) << IOCSIZE_SHIFT;
+ }
+ break;
+ }
+ return perf_ioctl(file, cmd, arg);
+}
+#else
+# define perf_compat_ioctl NULL
+#endif
+
int perf_event_task_enable(void)
{
struct perf_event *event;
@@ -4185,7 +4206,7 @@ static const struct file_operations perf_fops = {
.read = perf_read,
.poll = perf_poll,
.unlocked_ioctl = perf_ioctl,
- .compat_ioctl = perf_ioctl,
+ .compat_ioctl = perf_compat_ioctl,
.mmap = perf_mmap,
.fasync = perf_fasync,
};
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 307d87c0991a..1139b228befc 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1621,7 +1621,6 @@ bool uprobe_deny_signal(void)
if (__fatal_signal_pending(t) || arch_uprobe_xol_was_trapped(t)) {
utask->state = UTASK_SSTEP_TRAPPED;
set_tsk_thread_flag(t, TIF_UPROBE);
- set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
}
}
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index b3d116cd072d..6705d947ef14 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -1228,6 +1228,22 @@ static int rcu_future_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp)
}
/*
+ * Awaken the grace-period kthread for the specified flavor of RCU.
+ * Don't do a self-awaken, and don't bother awakening when there is
+ * nothing for the grace-period kthread to do (as in several CPUs
+ * raced to awaken, and we lost), and finally don't try to awaken
+ * a kthread that has not yet been created.
+ */
+static void rcu_gp_kthread_wake(struct rcu_state *rsp)
+{
+ if (current == rsp->gp_kthread ||
+ !ACCESS_ONCE(rsp->gp_flags) ||
+ !rsp->gp_kthread)
+ return;
+ wake_up(&rsp->gp_wq);
+}
+
+/*
* If there is room, assign a ->completed number to any callbacks on
* this CPU that have not already been assigned. Also accelerate any
* callbacks that were previously assigned a ->completed number that has
@@ -1670,7 +1686,7 @@ static void rsp_wakeup(struct irq_work *work)
struct rcu_state *rsp = container_of(work, struct rcu_state, wakeup_work);
/* Wake up rcu_gp_kthread() to start the grace period. */
- wake_up(&rsp->gp_wq);
+ rcu_gp_kthread_wake(rsp);
}
/*
@@ -1746,7 +1762,7 @@ static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags)
{
WARN_ON_ONCE(!rcu_gp_in_progress(rsp));
raw_spin_unlock_irqrestore(&rcu_get_root(rsp)->lock, flags);
- wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */
+ rcu_gp_kthread_wake(rsp);
}
/*
@@ -2322,7 +2338,7 @@ static void force_quiescent_state(struct rcu_state *rsp)
}
rsp->gp_flags |= RCU_GP_FLAG_FQS;
raw_spin_unlock_irqrestore(&rnp_old->lock, flags);
- wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */
+ rcu_gp_kthread_wake(rsp);
}
/*
diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index bd4a8dfdf0b8..7e30d2a7f346 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -946,81 +946,6 @@ next:
}
EXPORT_SYMBOL(radix_tree_range_tag_if_tagged);
-
-/**
- * radix_tree_next_hole - find the next hole (not-present entry)
- * @root: tree root
- * @index: index key
- * @max_scan: maximum range to search
- *
- * Search the set [index, min(index+max_scan-1, MAX_INDEX)] for the lowest
- * indexed hole.
- *
- * Returns: the index of the hole if found, otherwise returns an index
- * outside of the set specified (in which case 'return - index >= max_scan'
- * will be true). In rare cases of index wrap-around, 0 will be returned.
- *
- * radix_tree_next_hole may be called under rcu_read_lock. However, like
- * radix_tree_gang_lookup, this will not atomically search a snapshot of
- * the tree at a single point in time. For example, if a hole is created
- * at index 5, then subsequently a hole is created at index 10,
- * radix_tree_next_hole covering both indexes may return 10 if called
- * under rcu_read_lock.
- */
-unsigned long radix_tree_next_hole(struct radix_tree_root *root,
- unsigned long index, unsigned long max_scan)
-{
- unsigned long i;
-
- for (i = 0; i < max_scan; i++) {
- if (!radix_tree_lookup(root, index))
- break;
- index++;
- if (index == 0)
- break;
- }
-
- return index;
-}
-EXPORT_SYMBOL(radix_tree_next_hole);
-
-/**
- * radix_tree_prev_hole - find the prev hole (not-present entry)
- * @root: tree root
- * @index: index key
- * @max_scan: maximum range to search
- *
- * Search backwards in the range [max(index-max_scan+1, 0), index]
- * for the first hole.
- *
- * Returns: the index of the hole if found, otherwise returns an index
- * outside of the set specified (in which case 'index - return >= max_scan'
- * will be true). In rare cases of wrap-around, ULONG_MAX will be returned.
- *
- * radix_tree_next_hole may be called under rcu_read_lock. However, like
- * radix_tree_gang_lookup, this will not atomically search a snapshot of
- * the tree at a single point in time. For example, if a hole is created
- * at index 10, then subsequently a hole is created at index 5,
- * radix_tree_prev_hole covering both indexes may return 5 if called under
- * rcu_read_lock.
- */
-unsigned long radix_tree_prev_hole(struct radix_tree_root *root,
- unsigned long index, unsigned long max_scan)
-{
- unsigned long i;
-
- for (i = 0; i < max_scan; i++) {
- if (!radix_tree_lookup(root, index))
- break;
- index--;
- if (index == ULONG_MAX)
- break;
- }
-
- return index;
-}
-EXPORT_SYMBOL(radix_tree_prev_hole);
-
/**
* radix_tree_gang_lookup - perform multiple lookup on a radix tree
* @root: radix tree root
@@ -1337,15 +1262,18 @@ static inline void radix_tree_shrink(struct radix_tree_root *root)
}
/**
- * radix_tree_delete - delete an item from a radix tree
+ * radix_tree_delete_item - delete an item from a radix tree
* @root: radix tree root
* @index: index key
+ * @item: expected item
*
- * Remove the item at @index from the radix tree rooted at @root.
+ * Remove @item at @index from the radix tree rooted at @root.
*
- * Returns the address of the deleted item, or NULL if it was not present.
+ * Returns the address of the deleted item, or NULL if it was not present
+ * or the entry at the given @index was not @item.
*/
-void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
+void *radix_tree_delete_item(struct radix_tree_root *root,
+ unsigned long index, void *item)
{
struct radix_tree_node *node = NULL;
struct radix_tree_node *slot = NULL;
@@ -1380,6 +1308,11 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
if (slot == NULL)
goto out;
+ if (item && slot != item) {
+ slot = NULL;
+ goto out;
+ }
+
/*
* Clear all tags associated with the item to be deleted.
* This way of doing it would be inefficient, but seldom is any set.
@@ -1424,6 +1357,21 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
out:
return slot;
}
+EXPORT_SYMBOL(radix_tree_delete_item);
+
+/**
+ * radix_tree_delete - delete an item from a radix tree
+ * @root: radix tree root
+ * @index: index key
+ *
+ * Remove the item at @index from the radix tree rooted at @root.
+ *
+ * Returns the address of the deleted item, or NULL if it was not present.
+ */
+void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
+{
+ return radix_tree_delete_item(root, index, NULL);
+}
EXPORT_SYMBOL(radix_tree_delete);
/**
diff --git a/mm/compaction.c b/mm/compaction.c
index 5e38e5706f62..4229fc22a477 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -89,7 +89,8 @@ static void __reset_isolation_suitable(struct zone *zone)
unsigned long end_pfn = zone_end_pfn(zone);
unsigned long pfn;
- zone->compact_cached_migrate_pfn = start_pfn;
+ zone->compact_cached_migrate_pfn[0] = start_pfn;
+ zone->compact_cached_migrate_pfn[1] = start_pfn;
zone->compact_cached_free_pfn = end_pfn;
zone->compact_blockskip_flush = false;
@@ -131,9 +132,10 @@ void reset_isolation_suitable(pg_data_t *pgdat)
*/
static void update_pageblock_skip(struct compact_control *cc,
struct page *page, unsigned long nr_isolated,
- bool migrate_scanner)
+ bool set_unsuitable, bool migrate_scanner)
{
struct zone *zone = cc->zone;
+ unsigned long pfn;
if (cc->ignore_skip_hint)
return;
@@ -141,20 +143,32 @@ static void update_pageblock_skip(struct compact_control *cc,
if (!page)
return;
- if (!nr_isolated) {
- unsigned long pfn = page_to_pfn(page);
+ if (nr_isolated)
+ return;
+
+ /*
+ * Only skip pageblocks when all forms of compaction will be known to
+ * fail in the near future.
+ */
+ if (set_unsuitable)
set_pageblock_skip(page);
- /* Update where compaction should restart */
- if (migrate_scanner) {
- if (!cc->finished_update_migrate &&
- pfn > zone->compact_cached_migrate_pfn)
- zone->compact_cached_migrate_pfn = pfn;
- } else {
- if (!cc->finished_update_free &&
- pfn < zone->compact_cached_free_pfn)
- zone->compact_cached_free_pfn = pfn;
- }
+ pfn = page_to_pfn(page);
+
+ /* Update where async and sync compaction should restart */
+ if (migrate_scanner) {
+ if (cc->finished_update_migrate)
+ return;
+ if (pfn > zone->compact_cached_migrate_pfn[0])
+ zone->compact_cached_migrate_pfn[0] = pfn;
+ if (cc->mode != MIGRATE_ASYNC &&
+ pfn > zone->compact_cached_migrate_pfn[1])
+ zone->compact_cached_migrate_pfn[1] = pfn;
+ } else {
+ if (cc->finished_update_free)
+ return;
+ if (pfn < zone->compact_cached_free_pfn)
+ zone->compact_cached_free_pfn = pfn;
}
}
#else
@@ -166,7 +180,7 @@ static inline bool isolation_suitable(struct compact_control *cc,
static void update_pageblock_skip(struct compact_control *cc,
struct page *page, unsigned long nr_isolated,
- bool migrate_scanner)
+ bool set_unsuitable, bool migrate_scanner)
{
}
#endif /* CONFIG_COMPACTION */
@@ -195,7 +209,7 @@ static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
}
/* async aborts if taking too long or contended */
- if (!cc->sync) {
+ if (cc->mode == MIGRATE_ASYNC) {
cc->contended = true;
return false;
}
@@ -208,10 +222,28 @@ static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
return true;
}
-static inline bool compact_trylock_irqsave(spinlock_t *lock,
- unsigned long *flags, struct compact_control *cc)
+/*
+ * Aside from avoiding lock contention, compaction also periodically checks
+ * need_resched() and either schedules in sync compaction or aborts async
+ * compaction. This is similar to what compact_checklock_irqsave() does, but
+ * is used where no lock is concerned.
+ *
+ * Returns false when no scheduling was needed, or sync compaction scheduled.
+ * Returns true when async compaction should abort.
+ */
+static inline bool compact_should_abort(struct compact_control *cc)
{
- return compact_checklock_irqsave(lock, flags, false, cc);
+ /* async compaction aborts if contended */
+ if (need_resched()) {
+ if (cc->mode == MIGRATE_ASYNC) {
+ cc->contended = true;
+ return true;
+ }
+
+ cond_resched();
+ }
+
+ return false;
}
/* Returns true if the page is within a block suitable for migration to */
@@ -329,7 +361,8 @@ isolate_fail:
/* Update the pageblock-skip if the whole pageblock was scanned */
if (blockpfn == end_pfn)
- update_pageblock_skip(cc, valid_page, total_isolated, false);
+ update_pageblock_skip(cc, valid_page, total_isolated, true,
+ false);
count_compact_events(COMPACTFREE_SCANNED, nr_scanned);
if (total_isolated)
@@ -464,8 +497,9 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
unsigned long flags;
bool locked = false;
struct page *page = NULL, *valid_page = NULL;
- bool skipped_async_unsuitable = false;
- const isolate_mode_t mode = (!cc->sync ? ISOLATE_ASYNC_MIGRATE : 0) |
+ bool set_unsuitable = true;
+ const isolate_mode_t mode = (cc->mode == MIGRATE_ASYNC ?
+ ISOLATE_ASYNC_MIGRATE : 0) |
(unevictable ? ISOLATE_UNEVICTABLE : 0);
/*
@@ -475,7 +509,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
*/
while (unlikely(too_many_isolated(zone))) {
/* async migration should just abort */
- if (!cc->sync)
+ if (cc->mode == MIGRATE_ASYNC)
return 0;
congestion_wait(BLK_RW_ASYNC, HZ/10);
@@ -484,8 +518,10 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
return 0;
}
+ if (compact_should_abort(cc))
+ return 0;
+
/* Time to isolate some pages for migration */
- cond_resched();
for (; low_pfn < end_pfn; low_pfn++) {
/* give a chance to irqs before checking need_resched() */
if (locked && !(low_pfn % SWAP_CLUSTER_MAX)) {
@@ -540,9 +576,9 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
* the minimum amount of work satisfies the allocation
*/
mt = get_pageblock_migratetype(page);
- if (!cc->sync && !migrate_async_suitable(mt)) {
- cc->finished_update_migrate = true;
- skipped_async_unsuitable = true;
+ if (cc->mode == MIGRATE_ASYNC &&
+ !migrate_async_suitable(mt)) {
+ set_unsuitable = false;
goto next_pageblock;
}
}
@@ -646,11 +682,10 @@ next_pageblock:
/*
* Update the pageblock-skip information and cached scanner pfn,
* if the whole pageblock was scanned without isolating any page.
- * This is not done when pageblock was skipped due to being unsuitable
- * for async compaction, so that eventual sync compaction can try.
*/
- if (low_pfn == end_pfn && !skipped_async_unsuitable)
- update_pageblock_skip(cc, valid_page, nr_isolated, true);
+ if (low_pfn == end_pfn)
+ update_pageblock_skip(cc, valid_page, nr_isolated,
+ set_unsuitable, true);
trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
@@ -671,7 +706,9 @@ static void isolate_freepages(struct zone *zone,
struct compact_control *cc)
{
struct page *page;
- unsigned long high_pfn, low_pfn, pfn, z_end_pfn;
+ unsigned long block_start_pfn; /* start of current pageblock */
+ unsigned long block_end_pfn; /* end of current pageblock */
+ unsigned long low_pfn; /* lowest pfn scanner is able to scan */
int nr_freepages = cc->nr_freepages;
struct list_head *freelist = &cc->freepages;
@@ -679,41 +716,38 @@ static void isolate_freepages(struct zone *zone,
* Initialise the free scanner. The starting point is where we last
* successfully isolated from, zone-cached value, or the end of the
* zone when isolating for the first time. We need this aligned to
- * the pageblock boundary, because we do pfn -= pageblock_nr_pages
- * in the for loop.
+ * the pageblock boundary, because we do
+ * block_start_pfn -= pageblock_nr_pages in the for loop.
+ * For ending point, take care when isolating in last pageblock of a
+ * a zone which ends in the middle of a pageblock.
* The low boundary is the end of the pageblock the migration scanner
* is using.
*/
- pfn = cc->free_pfn & ~(pageblock_nr_pages-1);
+ block_start_pfn = cc->free_pfn & ~(pageblock_nr_pages-1);
+ block_end_pfn = min(block_start_pfn + pageblock_nr_pages,
+ zone_end_pfn(zone));
low_pfn = ALIGN(cc->migrate_pfn + 1, pageblock_nr_pages);
/*
- * Take care that if the migration scanner is at the end of the zone
- * that the free scanner does not accidentally move to the next zone
- * in the next isolation cycle.
- */
- high_pfn = min(low_pfn, pfn);
-
- z_end_pfn = zone_end_pfn(zone);
-
- /*
* Isolate free pages until enough are available to migrate the
* pages on cc->migratepages. We stop searching if the migrate
* and free page scanners meet or enough free pages are isolated.
*/
- for (; pfn >= low_pfn && cc->nr_migratepages > nr_freepages;
- pfn -= pageblock_nr_pages) {
+ for (; block_start_pfn >= low_pfn && cc->nr_migratepages > nr_freepages;
+ block_end_pfn = block_start_pfn,
+ block_start_pfn -= pageblock_nr_pages) {
unsigned long isolated;
- unsigned long end_pfn;
/*
* This can iterate a massively long zone without finding any
* suitable migration targets, so periodically check if we need
- * to schedule.
+ * to schedule, or even abort async compaction.
*/
- cond_resched();
+ if (!(block_start_pfn % (SWAP_CLUSTER_MAX * pageblock_nr_pages))
+ && compact_should_abort(cc))
+ break;
- if (!pfn_valid(pfn))
+ if (!pfn_valid(block_start_pfn))
continue;
/*
@@ -723,7 +757,7 @@ static void isolate_freepages(struct zone *zone,
* i.e. it's possible that all pages within a zones range of
* pages do not belong to a single zone.
*/
- page = pfn_to_page(pfn);
+ page = pfn_to_page(block_start_pfn);
if (page_zone(page) != zone)
continue;
@@ -736,26 +770,26 @@ static void isolate_freepages(struct zone *zone,
continue;
/* Found a block suitable for isolating free pages from */
- isolated = 0;
+ cc->free_pfn = block_start_pfn;
+ isolated = isolate_freepages_block(cc, block_start_pfn,
+ block_end_pfn, freelist, false);
+ nr_freepages += isolated;
/*
- * Take care when isolating in last pageblock of a zone which
- * ends in the middle of a pageblock.
+ * Set a flag that we successfully isolated in this pageblock.
+ * In the next loop iteration, zone->compact_cached_free_pfn
+ * will not be updated and thus it will effectively contain the
+ * highest pageblock we isolated pages from.
*/
- end_pfn = min(pfn + pageblock_nr_pages, z_end_pfn);
- isolated = isolate_freepages_block(cc, pfn, end_pfn,
- freelist, false);
- nr_freepages += isolated;
+ if (isolated)
+ cc->finished_update_free = true;
/*
- * Record the highest PFN we isolated pages from. When next
- * looking for free pages, the search will restart here as
- * page migration may have returned some pages to the allocator
+ * isolate_freepages_block() might have aborted due to async
+ * compaction being contended
*/
- if (isolated) {
- cc->finished_update_free = true;
- high_pfn = max(high_pfn, pfn);
- }
+ if (cc->contended)
+ break;
}
/* split_free_page does not map the pages */
@@ -765,10 +799,9 @@ static void isolate_freepages(struct zone *zone,
* If we crossed the migrate scanner, we want to keep it that way
* so that compact_finished() may detect this
*/
- if (pfn < low_pfn)
- cc->free_pfn = max(pfn, zone->zone_start_pfn);
- else
- cc->free_pfn = high_pfn;
+ if (block_start_pfn < low_pfn)
+ cc->free_pfn = cc->migrate_pfn;
+
cc->nr_freepages = nr_freepages;
}
@@ -783,9 +816,13 @@ static struct page *compaction_alloc(struct page *migratepage,
struct compact_control *cc = (struct compact_control *)data;
struct page *freepage;
- /* Isolate free pages if necessary */
+ /*
+ * Isolate free pages if necessary, and if we are not aborting due to
+ * contention.
+ */
if (list_empty(&cc->freepages)) {
- isolate_freepages(cc->zone, cc);
+ if (!cc->contended)
+ isolate_freepages(cc->zone, cc);
if (list_empty(&cc->freepages))
return NULL;
@@ -799,23 +836,16 @@ static struct page *compaction_alloc(struct page *migratepage,
}
/*
- * We cannot control nr_migratepages and nr_freepages fully when migration is
- * running as migrate_pages() has no knowledge of compact_control. When
- * migration is complete, we count the number of pages on the lists by hand.
+ * This is a migrate-callback that "frees" freepages back to the isolated
+ * freelist. All pages on the freelist are from the same zone, so there is no
+ * special handling needed for NUMA.
*/
-static void update_nr_listpages(struct compact_control *cc)
+static void compaction_free(struct page *page, unsigned long data)
{
- int nr_migratepages = 0;
- int nr_freepages = 0;
- struct page *page;
-
- list_for_each_entry(page, &cc->migratepages, lru)
- nr_migratepages++;
- list_for_each_entry(page, &cc->freepages, lru)
- nr_freepages++;
+ struct compact_control *cc = (struct compact_control *)data;
- cc->nr_migratepages = nr_migratepages;
- cc->nr_freepages = nr_freepages;
+ list_add(&page->lru, &cc->freepages);
+ cc->nr_freepages++;
}
/* possible outcome of isolate_migratepages */
@@ -862,13 +892,14 @@ static int compact_finished(struct zone *zone,
unsigned int order;
unsigned long watermark;
- if (fatal_signal_pending(current))
+ if (cc->contended || fatal_signal_pending(current))
return COMPACT_PARTIAL;
/* Compaction run completes if the migrate and free scanner meet */
if (cc->free_pfn <= cc->migrate_pfn) {
/* Let the next compaction start anew. */
- zone->compact_cached_migrate_pfn = zone->zone_start_pfn;
+ zone->compact_cached_migrate_pfn[0] = zone->zone_start_pfn;
+ zone->compact_cached_migrate_pfn[1] = zone->zone_start_pfn;
zone->compact_cached_free_pfn = zone_end_pfn(zone);
/*
@@ -968,6 +999,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
int ret;
unsigned long start_pfn = zone->zone_start_pfn;
unsigned long end_pfn = zone_end_pfn(zone);
+ const bool sync = cc->mode != MIGRATE_ASYNC;
ret = compaction_suitable(zone, cc->order);
switch (ret) {
@@ -993,7 +1025,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
* information on where the scanners should start but check that it
* is initialised by ensuring the values are within zone boundaries.
*/
- cc->migrate_pfn = zone->compact_cached_migrate_pfn;
+ cc->migrate_pfn = zone->compact_cached_migrate_pfn[sync];
cc->free_pfn = zone->compact_cached_free_pfn;
if (cc->free_pfn < start_pfn || cc->free_pfn > end_pfn) {
cc->free_pfn = end_pfn & ~(pageblock_nr_pages-1);
@@ -1001,7 +1033,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
}
if (cc->migrate_pfn < start_pfn || cc->migrate_pfn > end_pfn) {
cc->migrate_pfn = start_pfn;
- zone->compact_cached_migrate_pfn = cc->migrate_pfn;
+ zone->compact_cached_migrate_pfn[0] = cc->migrate_pfn;
+ zone->compact_cached_migrate_pfn[1] = cc->migrate_pfn;
}
trace_mm_compaction_begin(start_pfn, cc->migrate_pfn, cc->free_pfn, end_pfn);
@@ -1009,7 +1042,6 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
migrate_prep_local();
while ((ret = compact_finished(zone, cc)) == COMPACT_CONTINUE) {
- unsigned long nr_migrate, nr_remaining;
int err;
switch (isolate_migratepages(zone, cc)) {
@@ -1024,21 +1056,20 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
;
}
- nr_migrate = cc->nr_migratepages;
+ if (!cc->nr_migratepages)
+ continue;
+
err = migrate_pages(&cc->migratepages, compaction_alloc,
- (unsigned long)cc,
- cc->sync ? MIGRATE_SYNC_LIGHT : MIGRATE_ASYNC,
+ compaction_free, (unsigned long)cc, cc->mode,
MR_COMPACTION);
- update_nr_listpages(cc);
- nr_remaining = cc->nr_migratepages;
- trace_mm_compaction_migratepages(nr_migrate - nr_remaining,
- nr_remaining);
+ trace_mm_compaction_migratepages(cc->nr_migratepages, err,
+ &cc->migratepages);
- /* Release isolated pages not migrated */
+ /* All pages were either migrated or will be released */
+ cc->nr_migratepages = 0;
if (err) {
putback_movable_pages(&cc->migratepages);
- cc->nr_migratepages = 0;
/*
* migrate_pages() may return -ENOMEM when scanners meet
* and we want compact_finished() to detect it
@@ -1060,9 +1091,8 @@ out:
return ret;
}
-static unsigned long compact_zone_order(struct zone *zone,
- int order, gfp_t gfp_mask,
- bool sync, bool *contended)
+static unsigned long compact_zone_order(struct zone *zone, int order,
+ gfp_t gfp_mask, enum migrate_mode mode, bool *contended)
{
unsigned long ret;
struct compact_control cc = {
@@ -1071,7 +1101,7 @@ static unsigned long compact_zone_order(struct zone *zone,
.order = order,
.migratetype = allocflags_to_migratetype(gfp_mask),
.zone = zone,
- .sync = sync,
+ .mode = mode,
};
INIT_LIST_HEAD(&cc.freepages);
INIT_LIST_HEAD(&cc.migratepages);
@@ -1093,7 +1123,7 @@ int sysctl_extfrag_threshold = 500;
* @order: The order of the current allocation
* @gfp_mask: The GFP mask of the current allocation
* @nodemask: The allowed nodes to allocate from
- * @sync: Whether migration is synchronous or not
+ * @mode: The migration mode for async, sync light, or sync migration
* @contended: Return value that is true if compaction was aborted due to lock contention
* @page: Optionally capture a free page of the requested order during compaction
*
@@ -1101,7 +1131,7 @@ int sysctl_extfrag_threshold = 500;
*/
unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync, bool *contended)
+ enum migrate_mode mode, bool *contended)
{
enum zone_type high_zoneidx = gfp_zone(gfp_mask);
int may_enter_fs = gfp_mask & __GFP_FS;
@@ -1126,7 +1156,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
nodemask) {
int status;
- status = compact_zone_order(zone, order, gfp_mask, sync,
+ status = compact_zone_order(zone, order, gfp_mask, mode,
contended);
rc = max(status, rc);
@@ -1165,9 +1195,6 @@ static void __compact_pgdat(pg_data_t *pgdat, struct compact_control *cc)
if (zone_watermark_ok(zone, cc->order,
low_wmark_pages(zone), 0, 0))
compaction_defer_reset(zone, cc->order, false);
- /* Currently async compaction is never deferred. */
- else if (cc->sync)
- defer_compaction(zone, cc->order);
}
VM_BUG_ON(!list_empty(&cc->freepages));
@@ -1179,7 +1206,7 @@ void compact_pgdat(pg_data_t *pgdat, int order)
{
struct compact_control cc = {
.order = order,
- .sync = false,
+ .mode = MIGRATE_ASYNC,
};
if (!order)
@@ -1192,7 +1219,7 @@ static void compact_node(int nid)
{
struct compact_control cc = {
.order = -1,
- .sync = true,
+ .mode = MIGRATE_SYNC,
.ignore_skip_hint = true,
};
diff --git a/mm/filemap.c b/mm/filemap.c
index c2cc7c95eff1..bdaa21555abe 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -448,6 +448,29 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
}
EXPORT_SYMBOL_GPL(replace_page_cache_page);
+static int page_cache_tree_insert(struct address_space *mapping,
+ struct page *page)
+{
+ void **slot;
+ int error;
+
+ slot = radix_tree_lookup_slot(&mapping->page_tree, page->index);
+ if (slot) {
+ void *p;
+
+ p = radix_tree_deref_slot_protected(slot, &mapping->tree_lock);
+ if (!radix_tree_exceptional_entry(p))
+ return -EEXIST;
+ radix_tree_replace_slot(slot, page);
+ mapping->nrpages++;
+ return 0;
+ }
+ error = radix_tree_insert(&mapping->page_tree, page->index, page);
+ if (!error)
+ mapping->nrpages++;
+ return error;
+}
+
/**
* add_to_page_cache_locked - add a locked page to the pagecache
* @page: page to add
@@ -482,11 +505,10 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
page->index = offset;
spin_lock_irq(&mapping->tree_lock);
- error = radix_tree_insert(&mapping->page_tree, offset, page);
+ error = page_cache_tree_insert(mapping, page);
radix_tree_preload_end();
if (unlikely(error))
goto err_insert;
- mapping->nrpages++;
__inc_zone_page_state(page, NR_FILE_PAGES);
spin_unlock_irq(&mapping->tree_lock);
trace_mm_filemap_add_to_page_cache(page);
@@ -688,14 +710,101 @@ int __lock_page_or_retry(struct page *page, struct mm_struct *mm,
}
/**
- * find_get_page - find and get a page reference
+ * page_cache_next_hole - find the next hole (not-present entry)
+ * @mapping: mapping
+ * @index: index
+ * @max_scan: maximum range to search
+ *
+ * Search the set [index, min(index+max_scan-1, MAX_INDEX)] for the
+ * lowest indexed hole.
+ *
+ * Returns: the index of the hole if found, otherwise returns an index
+ * outside of the set specified (in which case 'return - index >=
+ * max_scan' will be true). In rare cases of index wrap-around, 0 will
+ * be returned.
+ *
+ * page_cache_next_hole may be called under rcu_read_lock. However,
+ * like radix_tree_gang_lookup, this will not atomically search a
+ * snapshot of the tree at a single point in time. For example, if a
+ * hole is created at index 5, then subsequently a hole is created at
+ * index 10, page_cache_next_hole covering both indexes may return 10
+ * if called under rcu_read_lock.
+ */
+pgoff_t page_cache_next_hole(struct address_space *mapping,
+ pgoff_t index, unsigned long max_scan)
+{
+ unsigned long i;
+
+ for (i = 0; i < max_scan; i++) {
+ struct page *page;
+
+ page = radix_tree_lookup(&mapping->page_tree, index);
+ if (!page || radix_tree_exceptional_entry(page))
+ break;
+ index++;
+ if (index == 0)
+ break;
+ }
+
+ return index;
+}
+EXPORT_SYMBOL(page_cache_next_hole);
+
+/**
+ * page_cache_prev_hole - find the prev hole (not-present entry)
+ * @mapping: mapping
+ * @index: index
+ * @max_scan: maximum range to search
+ *
+ * Search backwards in the range [max(index-max_scan+1, 0), index] for
+ * the first hole.
+ *
+ * Returns: the index of the hole if found, otherwise returns an index
+ * outside of the set specified (in which case 'index - return >=
+ * max_scan' will be true). In rare cases of wrap-around, ULONG_MAX
+ * will be returned.
+ *
+ * page_cache_prev_hole may be called under rcu_read_lock. However,
+ * like radix_tree_gang_lookup, this will not atomically search a
+ * snapshot of the tree at a single point in time. For example, if a
+ * hole is created at index 10, then subsequently a hole is created at
+ * index 5, page_cache_prev_hole covering both indexes may return 5 if
+ * called under rcu_read_lock.
+ */
+pgoff_t page_cache_prev_hole(struct address_space *mapping,
+ pgoff_t index, unsigned long max_scan)
+{
+ unsigned long i;
+
+ for (i = 0; i < max_scan; i++) {
+ struct page *page;
+
+ page = radix_tree_lookup(&mapping->page_tree, index);
+ if (!page || radix_tree_exceptional_entry(page))
+ break;
+ index--;
+ if (index == ULONG_MAX)
+ break;
+ }
+
+ return index;
+}
+EXPORT_SYMBOL(page_cache_prev_hole);
+
+/**
+ * find_get_entry - find and get a page cache entry
* @mapping: the address_space to search
- * @offset: the page index
+ * @offset: the page cache index
*
- * Is there a pagecache struct page at the given (mapping, offset) tuple?
- * If yes, increment its refcount and return it; if no, return NULL.
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned with an increased refcount.
+ *
+ * If the slot holds a shadow entry of a previously evicted page, it
+ * is returned.
+ *
+ * Otherwise, %NULL is returned.
*/
-struct page *find_get_page(struct address_space *mapping, pgoff_t offset)
+struct page *find_get_entry(struct address_space *mapping, pgoff_t offset)
{
void **pagep;
struct page *page;
@@ -736,24 +845,50 @@ out:
return page;
}
-EXPORT_SYMBOL(find_get_page);
+EXPORT_SYMBOL(find_get_entry);
/**
- * find_lock_page - locate, pin and lock a pagecache page
+ * find_get_page - find and get a page reference
* @mapping: the address_space to search
* @offset: the page index
*
- * Locates the desired pagecache page, locks it, increments its reference
- * count and returns its address.
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned with an increased refcount.
*
- * Returns zero if the page was not present. find_lock_page() may sleep.
+ * Otherwise, %NULL is returned.
*/
-struct page *find_lock_page(struct address_space *mapping, pgoff_t offset)
+struct page *find_get_page(struct address_space *mapping, pgoff_t offset)
+{
+ struct page *page = find_get_entry(mapping, offset);
+
+ if (radix_tree_exceptional_entry(page))
+ page = NULL;
+ return page;
+}
+EXPORT_SYMBOL(find_get_page);
+
+/**
+ * find_lock_entry - locate, pin and lock a page cache entry
+ * @mapping: the address_space to search
+ * @offset: the page cache index
+ *
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned locked and with an increased
+ * refcount.
+ *
+ * If the slot holds a shadow entry of a previously evicted page, it
+ * is returned.
+ *
+ * Otherwise, %NULL is returned.
+ *
+ * find_lock_entry() may sleep.
+ */
+struct page *find_lock_entry(struct address_space *mapping, pgoff_t offset)
{
struct page *page;
repeat:
- page = find_get_page(mapping, offset);
+ page = find_get_entry(mapping, offset);
if (page && !radix_tree_exception(page)) {
lock_page(page);
/* Has the page been truncated? */
@@ -766,6 +901,29 @@ repeat:
}
return page;
}
+EXPORT_SYMBOL(find_lock_entry);
+
+/**
+ * find_lock_page - locate, pin and lock a pagecache page
+ * @mapping: the address_space to search
+ * @offset: the page index
+ *
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned locked and with an increased
+ * refcount.
+ *
+ * Otherwise, %NULL is returned.
+ *
+ * find_lock_page() may sleep.
+ */
+struct page *find_lock_page(struct address_space *mapping, pgoff_t offset)
+{
+ struct page *page = find_lock_entry(mapping, offset);
+
+ if (radix_tree_exceptional_entry(page))
+ page = NULL;
+ return page;
+}
EXPORT_SYMBOL(find_lock_page);
/**
@@ -774,16 +932,18 @@ EXPORT_SYMBOL(find_lock_page);
* @index: the page's index into the mapping
* @gfp_mask: page allocation mode
*
- * Locates a page in the pagecache. If the page is not present, a new page
- * is allocated using @gfp_mask and is added to the pagecache and to the VM's
- * LRU list. The returned page is locked and has its reference count
- * incremented.
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned locked and with an increased
+ * refcount.
+ *
+ * If the page is not present, a new page is allocated using @gfp_mask
+ * and added to the page cache and the VM's LRU list. The page is
+ * returned locked and with an increased refcount.
*
- * find_or_create_page() may sleep, even if @gfp_flags specifies an atomic
- * allocation!
+ * On memory exhaustion, %NULL is returned.
*
- * find_or_create_page() returns the desired page's address, or zero on
- * memory exhaustion.
+ * find_or_create_page() may sleep, even if @gfp_flags specifies an
+ * atomic allocation!
*/
struct page *find_or_create_page(struct address_space *mapping,
pgoff_t index, gfp_t gfp_mask)
@@ -816,6 +976,76 @@ repeat:
EXPORT_SYMBOL(find_or_create_page);
/**
+ * find_get_entries - gang pagecache lookup
+ * @mapping: The address_space to search
+ * @start: The starting page cache index
+ * @nr_entries: The maximum number of entries
+ * @entries: Where the resulting entries are placed
+ * @indices: The cache indices corresponding to the entries in @entries
+ *
+ * find_get_entries() will search for and return a group of up to
+ * @nr_entries entries in the mapping. The entries are placed at
+ * @entries. find_get_entries() takes a reference against any actual
+ * pages it returns.
+ *
+ * The search returns a group of mapping-contiguous page cache entries
+ * with ascending indexes. There may be holes in the indices due to
+ * not-present pages.
+ *
+ * Any shadow entries of evicted pages are included in the returned
+ * array.
+ *
+ * find_get_entries() returns the number of pages and shadow entries
+ * which were found.
+ */
+unsigned find_get_entries(struct address_space *mapping,
+ pgoff_t start, unsigned int nr_entries,
+ struct page **entries, pgoff_t *indices)
+{
+ void **slot;
+ unsigned int ret = 0;
+ struct radix_tree_iter iter;
+
+ if (!nr_entries)
+ return 0;
+
+ rcu_read_lock();
+restart:
+ radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) {
+ struct page *page;
+repeat:
+ page = radix_tree_deref_slot(slot);
+ if (unlikely(!page))
+ continue;
+ if (radix_tree_exception(page)) {
+ if (radix_tree_deref_retry(page))
+ goto restart;
+ /*
+ * Otherwise, we must be storing a swap entry
+ * here as an exceptional entry: so return it
+ * without attempting to raise page count.
+ */
+ goto export;
+ }
+ if (!page_cache_get_speculative(page))
+ goto repeat;
+
+ /* Has the page moved? */
+ if (unlikely(page != *slot)) {
+ page_cache_release(page);
+ goto repeat;
+ }
+export:
+ indices[ret] = iter.index;
+ entries[ret] = page;
+ if (++ret == nr_entries)
+ break;
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+/**
* find_get_pages - gang pagecache lookup
* @mapping: The address_space to search
* @start: The starting page index
@@ -1797,6 +2027,18 @@ int generic_file_readonly_mmap(struct file * file, struct vm_area_struct * vma)
EXPORT_SYMBOL(generic_file_mmap);
EXPORT_SYMBOL(generic_file_readonly_mmap);
+static struct page *wait_on_page_read(struct page *page)
+{
+ if (!IS_ERR(page)) {
+ wait_on_page_locked(page);
+ if (!PageUptodate(page)) {
+ page_cache_release(page);
+ page = ERR_PTR(-EIO);
+ }
+ }
+ return page;
+}
+
static struct page *__read_cache_page(struct address_space *mapping,
pgoff_t index,
int (*filler)(void *, struct page *),
@@ -1823,6 +2065,8 @@ repeat:
if (err < 0) {
page_cache_release(page);
page = ERR_PTR(err);
+ } else {
+ page = wait_on_page_read(page);
}
}
return page;
@@ -1859,6 +2103,10 @@ retry:
if (err < 0) {
page_cache_release(page);
return ERR_PTR(err);
+ } else {
+ page = wait_on_page_read(page);
+ if (IS_ERR(page))
+ return page;
}
out:
mark_page_accessed(page);
@@ -1866,40 +2114,25 @@ out:
}
/**
- * read_cache_page_async - read into page cache, fill it if needed
+ * read_cache_page - read into page cache, fill it if needed
* @mapping: the page's address_space
* @index: the page index
* @filler: function to perform the read
* @data: first arg to filler(data, page) function, often left as NULL
*
- * Same as read_cache_page, but don't wait for page to become unlocked
- * after submitting it to the filler.
- *
* Read into the page cache. If a page already exists, and PageUptodate() is
- * not set, try to fill the page but don't wait for it to become unlocked.
+ * not set, try to fill the page and wait for it to become unlocked.
*
* If the page does not get brought uptodate, return -EIO.
*/
-struct page *read_cache_page_async(struct address_space *mapping,
+struct page *read_cache_page(struct address_space *mapping,
pgoff_t index,
int (*filler)(void *, struct page *),
void *data)
{
return do_read_cache_page(mapping, index, filler, data, mapping_gfp_mask(mapping));
}
-EXPORT_SYMBOL(read_cache_page_async);
-
-static struct page *wait_on_page_read(struct page *page)
-{
- if (!IS_ERR(page)) {
- wait_on_page_locked(page);
- if (!PageUptodate(page)) {
- page_cache_release(page);
- page = ERR_PTR(-EIO);
- }
- }
- return page;
-}
+EXPORT_SYMBOL(read_cache_page);
/**
* read_cache_page_gfp - read into page cache, using specified page allocation flags.
@@ -1918,31 +2151,10 @@ struct page *read_cache_page_gfp(struct address_space *mapping,
{
filler_t *filler = (filler_t *)mapping->a_ops->readpage;
- return wait_on_page_read(do_read_cache_page(mapping, index, filler, NULL, gfp));
+ return do_read_cache_page(mapping, index, filler, NULL, gfp);
}
EXPORT_SYMBOL(read_cache_page_gfp);
-/**
- * read_cache_page - read into page cache, fill it if needed
- * @mapping: the page's address_space
- * @index: the page index
- * @filler: function to perform the read
- * @data: first arg to filler(data, page) function, often left as NULL
- *
- * Read into the page cache. If a page already exists, and PageUptodate() is
- * not set, try to fill the page then wait for it to become unlocked.
- *
- * If the page does not get brought uptodate, return -EIO.
- */
-struct page *read_cache_page(struct address_space *mapping,
- pgoff_t index,
- int (*filler)(void *, struct page *),
- void *data)
-{
- return wait_on_page_read(read_cache_page_async(mapping, index, filler, data));
-}
-EXPORT_SYMBOL(read_cache_page);
-
static size_t __iovec_copy_from_user_inatomic(char *vaddr,
const struct iovec *iov, size_t base, size_t bytes)
{
@@ -1976,7 +2188,6 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
char *kaddr;
size_t copied;
- BUG_ON(!in_atomic());
kaddr = kmap_atomic(page);
if (likely(i->nr_segs == 1)) {
int left;
@@ -2350,9 +2561,7 @@ again:
if (mapping_writably_mapped(mapping))
flush_dcache_page(page);
- pagefault_disable();
copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
- pagefault_enable();
flush_dcache_page(page);
mark_page_accessed(page);
diff --git a/mm/internal.h b/mm/internal.h
index 3e910000fda4..1a8a0d4b687a 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -11,6 +11,7 @@
#ifndef __MM_INTERNAL_H
#define __MM_INTERNAL_H
+#include <linux/fs.h>
#include <linux/mm.h>
void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *start_vma,
@@ -21,6 +22,20 @@ static inline void set_page_count(struct page *page, int v)
atomic_set(&page->_count, v);
}
+extern int __do_page_cache_readahead(struct address_space *mapping,
+ struct file *filp, pgoff_t offset, unsigned long nr_to_read,
+ unsigned long lookahead_size);
+
+/*
+ * Submit IO for the read-ahead request in file_ra_state.
+ */
+static inline unsigned long ra_submit(struct file_ra_state *ra,
+ struct address_space *mapping, struct file *filp)
+{
+ return __do_page_cache_readahead(mapping, filp,
+ ra->start, ra->size, ra->async_size);
+}
+
/*
* Turn a non-refcounted page (->_count == 0) into refcounted with
* a count of one.
@@ -119,7 +134,7 @@ struct compact_control {
unsigned long nr_migratepages; /* Number of pages to migrate */
unsigned long free_pfn; /* isolate_freepages search base */
unsigned long migrate_pfn; /* isolate_migratepages search base */
- bool sync; /* Synchronous migration */
+ enum migrate_mode mode; /* Async or sync migration mode */
bool ignore_skip_hint; /* Scan blocks even if marked skip */
bool finished_update_free; /* True when the zone cached pfns are
* no longer being updated
@@ -129,7 +144,10 @@ struct compact_control {
int order; /* order a direct compactor needs */
int migratetype; /* MOVABLE, RECLAIMABLE etc */
struct zone *zone;
- bool contended; /* True if a lock was contended */
+ bool contended; /* True if a lock was contended, or
+ * need_resched() true during async
+ * compaction
+ */
};
unsigned long
diff --git a/mm/madvise.c b/mm/madvise.c
index 2590a5250660..b4200533d54e 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -196,7 +196,7 @@ static void force_shm_swapin_readahead(struct vm_area_struct *vma,
for (; start < end; start += PAGE_SIZE) {
index = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
- page = find_get_page(mapping, index);
+ page = find_get_entry(mapping, index);
if (!radix_tree_exceptional_entry(page)) {
if (page)
page_cache_release(page);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 33365e9ce6a7..a98c7fce470a 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1540,7 +1540,7 @@ static int soft_offline_huge_page(struct page *page, int flags)
/* Keep page count to indicate a given hugepage is isolated. */
list_move(&hpage->lru, &pagelist);
- ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
+ ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
MIGRATE_SYNC, MR_MEMORY_FAILURE);
if (ret) {
pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
@@ -1621,7 +1621,7 @@ static int __soft_offline_page(struct page *page, int flags)
inc_zone_page_state(page, NR_ISOLATED_ANON +
page_is_file_cache(page));
list_add(&page->lru, &pagelist);
- ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
+ ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
MIGRATE_SYNC, MR_MEMORY_FAILURE);
if (ret) {
if (!list_empty(&pagelist)) {
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index a650db29606f..f6f23833de44 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1332,7 +1332,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
* alloc_migrate_target should be improooooved!!
* migrate_pages returns # of failed pages.
*/
- ret = migrate_pages(&source, alloc_migrate_target, 0,
+ ret = migrate_pages(&source, alloc_migrate_target, NULL, 0,
MIGRATE_SYNC, MR_MEMORY_HOTPLUG);
if (ret)
putback_movable_pages(&source);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 0409d22b5af0..9df45ffa9d63 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1060,7 +1060,7 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest,
flags | MPOL_MF_DISCONTIG_OK, &pagelist);
if (!list_empty(&pagelist)) {
- err = migrate_pages(&pagelist, new_node_page, dest,
+ err = migrate_pages(&pagelist, new_node_page, NULL, dest,
MIGRATE_SYNC, MR_SYSCALL);
if (err)
putback_movable_pages(&pagelist);
@@ -1306,7 +1306,7 @@ static long do_mbind(unsigned long start, unsigned long len,
if (!list_empty(&pagelist)) {
WARN_ON_ONCE(flags & MPOL_MF_LAZY);
- nr_failed = migrate_pages(&pagelist, new_page,
+ nr_failed = migrate_pages(&pagelist, new_page, NULL,
start, MIGRATE_SYNC, MR_MEMPOLICY_MBIND);
if (nr_failed)
putback_movable_pages(&pagelist);
diff --git a/mm/migrate.c b/mm/migrate.c
index 13f47fbe3550..3acac4a62c4b 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -941,8 +941,9 @@ out:
* Obtain the lock on page, remove all ptes and migrate the page
* to the newly allocated page in newpage.
*/
-static int unmap_and_move(new_page_t get_new_page, unsigned long private,
- struct page *page, int force, enum migrate_mode mode)
+static int unmap_and_move(new_page_t get_new_page, free_page_t put_new_page,
+ unsigned long private, struct page *page, int force,
+ enum migrate_mode mode)
{
int rc = 0;
int *result = NULL;
@@ -986,11 +987,18 @@ out:
page_is_file_cache(page));
putback_lru_page(page);
}
+
/*
- * Move the new page to the LRU. If migration was not successful
- * then this will free the page.
+ * If migration was not successful and there's a freeing callback, use
+ * it. Otherwise, putback_lru_page() will drop the reference grabbed
+ * during isolation.
*/
- putback_lru_page(newpage);
+ if (rc != MIGRATEPAGE_SUCCESS && put_new_page) {
+ ClearPageSwapBacked(newpage);
+ put_new_page(newpage, private);
+ } else
+ putback_lru_page(newpage);
+
if (result) {
if (rc)
*result = rc;
@@ -1019,8 +1027,9 @@ out:
* will wait in the page fault for migration to complete.
*/
static int unmap_and_move_huge_page(new_page_t get_new_page,
- unsigned long private, struct page *hpage,
- int force, enum migrate_mode mode)
+ free_page_t put_new_page, unsigned long private,
+ struct page *hpage, int force,
+ enum migrate_mode mode)
{
int rc = 0;
int *result = NULL;
@@ -1059,20 +1068,30 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
if (!page_mapped(hpage))
rc = move_to_new_page(new_hpage, hpage, 1, mode);
- if (rc)
+ if (rc != MIGRATEPAGE_SUCCESS)
remove_migration_ptes(hpage, hpage);
if (anon_vma)
put_anon_vma(anon_vma);
- if (!rc)
+ if (rc == MIGRATEPAGE_SUCCESS)
hugetlb_cgroup_migrate(hpage, new_hpage);
unlock_page(hpage);
out:
if (rc != -EAGAIN)
putback_active_hugepage(hpage);
- put_page(new_hpage);
+
+ /*
+ * If migration was not successful and there's a freeing callback, use
+ * it. Otherwise, put_page() will drop the reference grabbed during
+ * isolation.
+ */
+ if (rc != MIGRATEPAGE_SUCCESS && put_new_page)
+ put_new_page(new_hpage, private);
+ else
+ put_page(new_hpage);
+
if (result) {
if (rc)
*result = rc;
@@ -1089,6 +1108,8 @@ out:
* @from: The list of pages to be migrated.
* @get_new_page: The function used to allocate free pages to be used
* as the target of the page migration.
+ * @put_new_page: The function used to free target pages if migration
+ * fails, or NULL if no special handling is necessary.
* @private: Private data to be passed on to get_new_page()
* @mode: The migration mode that specifies the constraints for
* page migration, if any.
@@ -1102,7 +1123,8 @@ out:
* Returns the number of pages that were not migrated, or an error code.
*/
int migrate_pages(struct list_head *from, new_page_t get_new_page,
- unsigned long private, enum migrate_mode mode, int reason)
+ free_page_t put_new_page, unsigned long private,
+ enum migrate_mode mode, int reason)
{
int retry = 1;
int nr_failed = 0;
@@ -1124,10 +1146,11 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
if (PageHuge(page))
rc = unmap_and_move_huge_page(get_new_page,
- private, page, pass > 2, mode);
+ put_new_page, private, page,
+ pass > 2, mode);
else
- rc = unmap_and_move(get_new_page, private,
- page, pass > 2, mode);
+ rc = unmap_and_move(get_new_page, put_new_page,
+ private, page, pass > 2, mode);
switch(rc) {
case -ENOMEM:
@@ -1276,7 +1299,7 @@ set_status:
err = 0;
if (!list_empty(&pagelist)) {
- err = migrate_pages(&pagelist, new_page_node,
+ err = migrate_pages(&pagelist, new_page_node, NULL,
(unsigned long)pm, MIGRATE_SYNC, MR_SYSCALL);
if (err)
putback_movable_pages(&pagelist);
@@ -1732,7 +1755,8 @@ int migrate_misplaced_page(struct page *page, struct vm_area_struct *vma,
list_add(&page->lru, &migratepages);
nr_remaining = migrate_pages(&migratepages, alloc_misplaced_dst_page,
- node, MIGRATE_ASYNC, MR_NUMA_MISPLACED);
+ NULL, node, MIGRATE_ASYNC,
+ MR_NUMA_MISPLACED);
if (nr_remaining) {
if (!list_empty(&migratepages)) {
list_del(&page->lru);
diff --git a/mm/mincore.c b/mm/mincore.c
index 101623378fbf..725c80961048 100644
--- a/mm/mincore.c
+++ b/mm/mincore.c
@@ -70,13 +70,21 @@ static unsigned char mincore_page(struct address_space *mapping, pgoff_t pgoff)
* any other file mapping (ie. marked !present and faulted in with
* tmpfs's .fault). So swapped out tmpfs mappings are tested here.
*/
- page = find_get_page(mapping, pgoff);
#ifdef CONFIG_SWAP
- /* shmem/tmpfs may return swap: account for swapcache page too. */
- if (radix_tree_exceptional_entry(page)) {
- swp_entry_t swap = radix_to_swp_entry(page);
- page = find_get_page(swap_address_space(swap), swap.val);
- }
+ if (shmem_mapping(mapping)) {
+ page = find_get_entry(mapping, pgoff);
+ /*
+ * shmem/tmpfs may return swap: account for swapcache
+ * page too.
+ */
+ if (radix_tree_exceptional_entry(page)) {
+ swp_entry_t swp = radix_to_swp_entry(page);
+ page = find_get_page(swap_address_space(swp), swp.val);
+ }
+ } else
+ page = find_get_page(mapping, pgoff);
+#else
+ page = find_get_page(mapping, pgoff);
#endif
if (page) {
present = PageUptodate(page);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index c826f5fb3175..8c5ba3fe9431 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -956,6 +956,7 @@ struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
rmv_page_order(page);
area->nr_free--;
expand(zone, page, order, current_order, area, migratetype);
+ set_freepage_migratetype(page, migratetype);
return page;
}
@@ -1082,7 +1083,9 @@ static int try_to_steal_freepages(struct zone *zone, struct page *page,
/*
* When borrowing from MIGRATE_CMA, we need to release the excess
- * buddy pages to CMA itself.
+ * buddy pages to CMA itself. We also ensure the freepage_migratetype
+ * is set to CMA so it is returned to the correct freelist in case
+ * the page ends up being not actually allocated from the pcp lists.
*/
if (is_migrate_cma(fallback_type))
return fallback_type;
@@ -1150,6 +1153,12 @@ __rmqueue_fallback(struct zone *zone, int order, int start_migratetype)
expand(zone, page, order, current_order, area,
new_type);
+ /* The freepage_migratetype may differ from pageblock's
+ * migratetype depending on the decisions in
+ * try_to_steal_freepages. This is OK as long as it does
+ * not differ for MIGRATE_CMA type.
+ */
+ set_freepage_migratetype(page, new_type);
trace_mm_page_alloc_extfrag(page, order, current_order,
start_migratetype, migratetype, new_type);
@@ -1200,7 +1209,7 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
unsigned long count, struct list_head *list,
int migratetype, int cold)
{
- int mt = migratetype, i;
+ int i;
spin_lock(&zone->lock);
for (i = 0; i < count; ++i) {
@@ -1221,14 +1230,8 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
list_add(&page->lru, list);
else
list_add_tail(&page->lru, list);
- if (IS_ENABLED(CONFIG_CMA)) {
- mt = get_pageblock_migratetype(page);
- if (!is_migrate_cma(mt) && !is_migrate_isolate(mt))
- mt = migratetype;
- }
- set_freepage_migratetype(page, mt);
list = &page->lru;
- if (is_migrate_cma(mt))
+ if (is_migrate_cma(get_freepage_migratetype(page)))
__mod_zone_page_state(zone, NR_FREE_CMA_PAGES,
-(1 << order));
}
@@ -1597,7 +1600,7 @@ again:
if (!page)
goto failed;
__mod_zone_freepage_state(zone, -(1 << order),
- get_pageblock_migratetype(page));
+ get_freepage_migratetype(page));
}
__mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order));
@@ -2259,7 +2262,7 @@ static struct page *
__alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
- int migratetype, bool sync_migration,
+ int migratetype, enum migrate_mode mode,
bool *contended_compaction, bool *deferred_compaction,
unsigned long *did_some_progress)
{
@@ -2273,7 +2276,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
current->flags |= PF_MEMALLOC;
*did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
- nodemask, sync_migration,
+ nodemask, mode,
contended_compaction);
current->flags &= ~PF_MEMALLOC;
@@ -2306,7 +2309,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
* As async compaction considers a subset of pageblocks, only
* defer if the failure was a sync compaction failure.
*/
- if (sync_migration)
+ if (mode != MIGRATE_ASYNC)
defer_compaction(preferred_zone, order);
cond_resched();
@@ -2319,9 +2322,8 @@ static inline struct page *
__alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
- int migratetype, bool sync_migration,
- bool *contended_compaction, bool *deferred_compaction,
- unsigned long *did_some_progress)
+ int migratetype, enum migrate_mode mode, bool *contended_compaction,
+ bool *deferred_compaction, unsigned long *did_some_progress)
{
return NULL;
}
@@ -2516,7 +2518,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
int alloc_flags;
unsigned long pages_reclaimed = 0;
unsigned long did_some_progress;
- bool sync_migration = false;
+ enum migrate_mode migration_mode = MIGRATE_ASYNC;
bool deferred_compaction = false;
bool contended_compaction = false;
@@ -2610,17 +2612,15 @@ rebalance:
* Try direct compaction. The first pass is asynchronous. Subsequent
* attempts after direct reclaim are synchronous
*/
- page = __alloc_pages_direct_compact(gfp_mask, order,
- zonelist, high_zoneidx,
- nodemask,
- alloc_flags, preferred_zone,
- migratetype, sync_migration,
- &contended_compaction,
+ page = __alloc_pages_direct_compact(gfp_mask, order, zonelist,
+ high_zoneidx, nodemask, alloc_flags,
+ preferred_zone, migratetype,
+ migration_mode, &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
goto got_pg;
- sync_migration = true;
+ migration_mode = MIGRATE_SYNC_LIGHT;
/*
* If compaction is deferred for high-order allocations, it is because
@@ -2695,12 +2695,10 @@ rebalance:
* direct reclaim and reclaim/compaction depends on compaction
* being called after reclaim so call directly if necessary
*/
- page = __alloc_pages_direct_compact(gfp_mask, order,
- zonelist, high_zoneidx,
- nodemask,
- alloc_flags, preferred_zone,
- migratetype, sync_migration,
- &contended_compaction,
+ page = __alloc_pages_direct_compact(gfp_mask, order, zonelist,
+ high_zoneidx, nodemask, alloc_flags,
+ preferred_zone, migratetype,
+ migration_mode, &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
@@ -6280,7 +6278,7 @@ static int __alloc_contig_migrate_range(struct compact_control *cc,
cc->nr_migratepages -= nr_reclaimed;
ret = migrate_pages(&cc->migratepages, alloc_migrate_target,
- 0, MIGRATE_SYNC, MR_CMA);
+ NULL, 0, cc->mode, MR_CMA);
}
if (ret < 0) {
putback_movable_pages(&cc->migratepages);
@@ -6319,7 +6317,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
.nr_migratepages = 0,
.order = -1,
.zone = page_zone(pfn_to_page(start)),
- .sync = true,
+ .mode = MIGRATE_SYNC,
.ignore_skip_hint = true,
};
INIT_LIST_HEAD(&cc.migratepages);
diff --git a/mm/readahead.c b/mm/readahead.c
index 1fa0d6fca556..0ca36a7770b1 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -8,9 +8,7 @@
*/
#include <linux/kernel.h>
-#include <linux/fs.h>
#include <linux/gfp.h>
-#include <linux/mm.h>
#include <linux/export.h>
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
@@ -20,6 +18,8 @@
#include <linux/syscalls.h>
#include <linux/file.h>
+#include "internal.h"
+
/*
* Initialise a struct file's readahead state. Assumes that the caller has
* memset *ra to zero.
@@ -149,8 +149,7 @@ out:
*
* Returns the number of pages requested, or the maximum amount of I/O allowed.
*/
-static int
-__do_page_cache_readahead(struct address_space *mapping, struct file *filp,
+int __do_page_cache_readahead(struct address_space *mapping, struct file *filp,
pgoff_t offset, unsigned long nr_to_read,
unsigned long lookahead_size)
{
@@ -179,7 +178,7 @@ __do_page_cache_readahead(struct address_space *mapping, struct file *filp,
rcu_read_lock();
page = radix_tree_lookup(&mapping->page_tree, page_offset);
rcu_read_unlock();
- if (page)
+ if (page && !radix_tree_exceptional_entry(page))
continue;
page = page_cache_alloc_readahead(mapping);
@@ -244,20 +243,6 @@ unsigned long max_sane_readahead(unsigned long nr)
}
/*
- * Submit IO for the read-ahead request in file_ra_state.
- */
-unsigned long ra_submit(struct file_ra_state *ra,
- struct address_space *mapping, struct file *filp)
-{
- int actual;
-
- actual = __do_page_cache_readahead(mapping, filp,
- ra->start, ra->size, ra->async_size);
-
- return actual;
-}
-
-/*
* Set the initial window size, round to next power of 2 and square
* for small size, x 4 for medium, and x 2 for large
* for 128k (32 page) max ra
@@ -347,7 +332,7 @@ static pgoff_t count_history_pages(struct address_space *mapping,
pgoff_t head;
rcu_read_lock();
- head = radix_tree_prev_hole(&mapping->page_tree, offset - 1, max);
+ head = page_cache_prev_hole(mapping, offset - 1, max);
rcu_read_unlock();
return offset - 1 - head;
@@ -427,7 +412,7 @@ ondemand_readahead(struct address_space *mapping,
pgoff_t start;
rcu_read_lock();
- start = radix_tree_next_hole(&mapping->page_tree, offset+1,max);
+ start = page_cache_next_hole(mapping, offset + 1, max);
rcu_read_unlock();
if (!start || start - offset > max)
diff --git a/mm/shmem.c b/mm/shmem.c
index f96693a86515..6eed8ebba3a1 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -243,19 +243,17 @@ static int shmem_radix_tree_replace(struct address_space *mapping,
pgoff_t index, void *expected, void *replacement)
{
void **pslot;
- void *item = NULL;
+ void *item;
VM_BUG_ON(!expected);
+ VM_BUG_ON(!replacement);
pslot = radix_tree_lookup_slot(&mapping->page_tree, index);
- if (pslot)
- item = radix_tree_deref_slot_protected(pslot,
- &mapping->tree_lock);
+ if (!pslot)
+ return -ENOENT;
+ item = radix_tree_deref_slot_protected(pslot, &mapping->tree_lock);
if (item != expected)
return -ENOENT;
- if (replacement)
- radix_tree_replace_slot(pslot, replacement);
- else
- radix_tree_delete(&mapping->page_tree, index);
+ radix_tree_replace_slot(pslot, replacement);
return 0;
}
@@ -332,84 +330,20 @@ static void shmem_delete_from_page_cache(struct page *page, void *radswap)
}
/*
- * Like find_get_pages, but collecting swap entries as well as pages.
- */
-static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping,
- pgoff_t start, unsigned int nr_pages,
- struct page **pages, pgoff_t *indices)
-{
- void **slot;
- unsigned int ret = 0;
- struct radix_tree_iter iter;
-
- if (!nr_pages)
- return 0;
-
- rcu_read_lock();
-restart:
- radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) {
- struct page *page;
-repeat:
- page = radix_tree_deref_slot(slot);
- if (unlikely(!page))
- continue;
- if (radix_tree_exception(page)) {
- if (radix_tree_deref_retry(page))
- goto restart;
- /*
- * Otherwise, we must be storing a swap entry
- * here as an exceptional entry: so return it
- * without attempting to raise page count.
- */
- goto export;
- }
- if (!page_cache_get_speculative(page))
- goto repeat;
-
- /* Has the page moved? */
- if (unlikely(page != *slot)) {
- page_cache_release(page);
- goto repeat;
- }
-export:
- indices[ret] = iter.index;
- pages[ret] = page;
- if (++ret == nr_pages)
- break;
- }
- rcu_read_unlock();
- return ret;
-}
-
-/*
* Remove swap entry from radix tree, free the swap and its page cache.
*/
static int shmem_free_swap(struct address_space *mapping,
pgoff_t index, void *radswap)
{
- int error;
+ void *old;
spin_lock_irq(&mapping->tree_lock);
- error = shmem_radix_tree_replace(mapping, index, radswap, NULL);
+ old = radix_tree_delete_item(&mapping->page_tree, index, radswap);
spin_unlock_irq(&mapping->tree_lock);
- if (!error)
- free_swap_and_cache(radix_to_swp_entry(radswap));
- return error;
-}
-
-/*
- * Pagevec may contain swap entries, so shuffle up pages before releasing.
- */
-static void shmem_deswap_pagevec(struct pagevec *pvec)
-{
- int i, j;
-
- for (i = 0, j = 0; i < pagevec_count(pvec); i++) {
- struct page *page = pvec->pages[i];
- if (!radix_tree_exceptional_entry(page))
- pvec->pages[j++] = page;
- }
- pvec->nr = j;
+ if (old != radswap)
+ return -ENOENT;
+ free_swap_and_cache(radix_to_swp_entry(radswap));
+ return 0;
}
/*
@@ -430,12 +364,12 @@ void shmem_unlock_mapping(struct address_space *mapping)
* Avoid pagevec_lookup(): find_get_pages() returns 0 as if it
* has finished, if it hits a row of PAGEVEC_SIZE swap entries.
*/
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
- PAGEVEC_SIZE, pvec.pages, indices);
+ pvec.nr = find_get_entries(mapping, index,
+ PAGEVEC_SIZE, pvec.pages, indices);
if (!pvec.nr)
break;
index = indices[pvec.nr - 1] + 1;
- shmem_deswap_pagevec(&pvec);
+ pagevec_remove_exceptionals(&pvec);
check_move_unevictable_pages(pvec.pages, pvec.nr);
pagevec_release(&pvec);
cond_resched();
@@ -467,9 +401,9 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
pagevec_init(&pvec, 0);
index = start;
while (index < end) {
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE),
- pvec.pages, indices);
+ pvec.nr = find_get_entries(mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE),
+ pvec.pages, indices);
if (!pvec.nr)
break;
mem_cgroup_uncharge_start();
@@ -498,7 +432,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
}
unlock_page(page);
}
- shmem_deswap_pagevec(&pvec);
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
cond_resched();
@@ -536,9 +470,10 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
index = start;
while (index < end) {
cond_resched();
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
+
+ pvec.nr = find_get_entries(mapping, index,
min(end - index, (pgoff_t)PAGEVEC_SIZE),
- pvec.pages, indices);
+ pvec.pages, indices);
if (!pvec.nr) {
/* If all gone or hole-punch or unfalloc, we're done */
if (index == start || end != -1)
@@ -581,7 +516,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
}
unlock_page(page);
}
- shmem_deswap_pagevec(&pvec);
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
index++;
@@ -1088,7 +1023,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
return -EFBIG;
repeat:
swap.val = 0;
- page = find_lock_page(mapping, index);
+ page = find_lock_entry(mapping, index);
if (radix_tree_exceptional_entry(page)) {
swap = radix_to_swp_entry(page);
page = NULL;
@@ -1483,6 +1418,11 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
return inode;
}
+bool shmem_mapping(struct address_space *mapping)
+{
+ return mapping->backing_dev_info == &shmem_backing_dev_info;
+}
+
#ifdef CONFIG_TMPFS
static const struct inode_operations shmem_symlink_inode_operations;
static const struct inode_operations shmem_short_symlink_operations;
@@ -1795,7 +1735,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
pagevec_init(&pvec, 0);
pvec.nr = 1; /* start small: we may be there already */
while (!done) {
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
+ pvec.nr = find_get_entries(mapping, index,
pvec.nr, pvec.pages, indices);
if (!pvec.nr) {
if (whence == SEEK_DATA)
@@ -1822,7 +1762,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
break;
}
}
- shmem_deswap_pagevec(&pvec);
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
pvec.nr = PAGEVEC_SIZE;
cond_resched();
diff --git a/mm/swap.c b/mm/swap.c
index 0092097b3f4c..c8048d71c642 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -948,6 +948,57 @@ void __pagevec_lru_add(struct pagevec *pvec)
EXPORT_SYMBOL(__pagevec_lru_add);
/**
+ * pagevec_lookup_entries - gang pagecache lookup
+ * @pvec: Where the resulting entries are placed
+ * @mapping: The address_space to search
+ * @start: The starting entry index
+ * @nr_entries: The maximum number of entries
+ * @indices: The cache indices corresponding to the entries in @pvec
+ *
+ * pagevec_lookup_entries() will search for and return a group of up
+ * to @nr_entries pages and shadow entries in the mapping. All
+ * entries are placed in @pvec. pagevec_lookup_entries() takes a
+ * reference against actual pages in @pvec.
+ *
+ * The search returns a group of mapping-contiguous entries with
+ * ascending indexes. There may be holes in the indices due to
+ * not-present entries.
+ *
+ * pagevec_lookup_entries() returns the number of entries which were
+ * found.
+ */
+unsigned pagevec_lookup_entries(struct pagevec *pvec,
+ struct address_space *mapping,
+ pgoff_t start, unsigned nr_pages,
+ pgoff_t *indices)
+{
+ pvec->nr = find_get_entries(mapping, start, nr_pages,
+ pvec->pages, indices);
+ return pagevec_count(pvec);
+}
+
+/**
+ * pagevec_remove_exceptionals - pagevec exceptionals pruning
+ * @pvec: The pagevec to prune
+ *
+ * pagevec_lookup_entries() fills both pages and exceptional radix
+ * tree entries into the pagevec. This function prunes all
+ * exceptionals from @pvec without leaving holes, so that it can be
+ * passed on to page-only pagevec operations.
+ */
+void pagevec_remove_exceptionals(struct pagevec *pvec)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < pagevec_count(pvec); i++) {
+ struct page *page = pvec->pages[i];
+ if (!radix_tree_exceptional_entry(page))
+ pvec->pages[j++] = page;
+ }
+ pvec->nr = j;
+}
+
+/**
* pagevec_lookup - gang pagecache lookup
* @pvec: Where the resulting pages are placed
* @mapping: The address_space to search
diff --git a/mm/truncate.c b/mm/truncate.c
index ac18edc30649..827ad8d2b5cd 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -23,6 +23,22 @@
#include <linux/rmap.h>
#include "internal.h"
+static void clear_exceptional_entry(struct address_space *mapping,
+ pgoff_t index, void *entry)
+{
+ /* Handled by shmem itself */
+ if (shmem_mapping(mapping))
+ return;
+
+ spin_lock_irq(&mapping->tree_lock);
+ /*
+ * Regular page slots are stabilized by the page lock even
+ * without the tree itself locked. These unlocked entries
+ * need verification under the tree lock.
+ */
+ radix_tree_delete_item(&mapping->page_tree, index, entry);
+ spin_unlock_irq(&mapping->tree_lock);
+}
/**
* do_invalidatepage - invalidate part or all of a page
@@ -209,6 +225,7 @@ void truncate_inode_pages_range(struct address_space *mapping,
unsigned int partial_start; /* inclusive */
unsigned int partial_end; /* exclusive */
struct pagevec pvec;
+ pgoff_t indices[PAGEVEC_SIZE];
pgoff_t index;
int i;
@@ -239,17 +256,23 @@ void truncate_inode_pages_range(struct address_space *mapping,
pagevec_init(&pvec, 0);
index = start;
- while (index < end && pagevec_lookup(&pvec, mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE))) {
+ while (index < end && pagevec_lookup_entries(&pvec, mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE),
+ indices)) {
mem_cgroup_uncharge_start();
for (i = 0; i < pagevec_count(&pvec); i++) {
struct page *page = pvec.pages[i];
/* We rely upon deletion not changing page->index */
- index = page->index;
+ index = indices[i];
if (index >= end)
break;
+ if (radix_tree_exceptional_entry(page)) {
+ clear_exceptional_entry(mapping, index, page);
+ continue;
+ }
+
if (!trylock_page(page))
continue;
WARN_ON(page->index != index);
@@ -260,6 +283,7 @@ void truncate_inode_pages_range(struct address_space *mapping,
truncate_inode_page(mapping, page);
unlock_page(page);
}
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
cond_resched();
@@ -308,14 +332,16 @@ void truncate_inode_pages_range(struct address_space *mapping,
index = start;
for ( ; ; ) {
cond_resched();
- if (!pagevec_lookup(&pvec, mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE))) {
+ if (!pagevec_lookup_entries(&pvec, mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE),
+ indices)) {
if (index == start)
break;
index = start;
continue;
}
- if (index == start && pvec.pages[0]->index >= end) {
+ if (index == start && indices[0] >= end) {
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
break;
}
@@ -324,16 +350,22 @@ void truncate_inode_pages_range(struct address_space *mapping,
struct page *page = pvec.pages[i];
/* We rely upon deletion not changing page->index */
- index = page->index;
+ index = indices[i];
if (index >= end)
break;
+ if (radix_tree_exceptional_entry(page)) {
+ clear_exceptional_entry(mapping, index, page);
+ continue;
+ }
+
lock_page(page);
WARN_ON(page->index != index);
wait_on_page_writeback(page);
truncate_inode_page(mapping, page);
unlock_page(page);
}
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
index++;
@@ -376,6 +408,7 @@ EXPORT_SYMBOL(truncate_inode_pages);
unsigned long invalidate_mapping_pages(struct address_space *mapping,
pgoff_t start, pgoff_t end)
{
+ pgoff_t indices[PAGEVEC_SIZE];
struct pagevec pvec;
pgoff_t index = start;
unsigned long ret;
@@ -391,17 +424,23 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
*/
pagevec_init(&pvec, 0);
- while (index <= end && pagevec_lookup(&pvec, mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
+ while (index <= end && pagevec_lookup_entries(&pvec, mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+ indices)) {
mem_cgroup_uncharge_start();
for (i = 0; i < pagevec_count(&pvec); i++) {
struct page *page = pvec.pages[i];
/* We rely upon deletion not changing page->index */
- index = page->index;
+ index = indices[i];
if (index > end)
break;
+ if (radix_tree_exceptional_entry(page)) {
+ clear_exceptional_entry(mapping, index, page);
+ continue;
+ }
+
if (!trylock_page(page))
continue;
WARN_ON(page->index != index);
@@ -415,6 +454,7 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
deactivate_page(page);
count += ret;
}
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
cond_resched();
@@ -482,6 +522,7 @@ static int do_launder_page(struct address_space *mapping, struct page *page)
int invalidate_inode_pages2_range(struct address_space *mapping,
pgoff_t start, pgoff_t end)
{
+ pgoff_t indices[PAGEVEC_SIZE];
struct pagevec pvec;
pgoff_t index;
int i;
@@ -492,17 +533,23 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
cleancache_invalidate_inode(mapping);
pagevec_init(&pvec, 0);
index = start;
- while (index <= end && pagevec_lookup(&pvec, mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
+ while (index <= end && pagevec_lookup_entries(&pvec, mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+ indices)) {
mem_cgroup_uncharge_start();
for (i = 0; i < pagevec_count(&pvec); i++) {
struct page *page = pvec.pages[i];
/* We rely upon deletion not changing page->index */
- index = page->index;
+ index = indices[i];
if (index > end)
break;
+ if (radix_tree_exceptional_entry(page)) {
+ clear_exceptional_entry(mapping, index, page);
+ continue;
+ }
+
lock_page(page);
WARN_ON(page->index != index);
if (page->mapping != mapping) {
@@ -540,6 +587,7 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
ret = ret2;
unlock_page(page);
}
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
cond_resched();
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 045c9a6dbd38..b88f0238b2be 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2061,13 +2061,27 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
unsigned long nr_reclaimed = 0;
unsigned long nr_to_reclaim = sc->nr_to_reclaim;
struct blk_plug plug;
- bool scan_adjusted = false;
+ bool scan_adjusted;
get_scan_count(lruvec, sc, nr);
/* Record the original scan target for proportional adjustments later */
memcpy(targets, nr, sizeof(nr));
+ /*
+ * Global reclaiming within direct reclaim at DEF_PRIORITY is a normal
+ * event that can occur when there is little memory pressure e.g.
+ * multiple streaming readers/writers. Hence, we do not abort scanning
+ * when the requested number of pages are reclaimed when scanning at
+ * DEF_PRIORITY on the assumption that the fact we are direct
+ * reclaiming implies that kswapd is not keeping up and it is best to
+ * do a batch of work at once. For memcg reclaim one check is made to
+ * abort proportional reclaim if either the file or anon lru has already
+ * dropped to zero at the first pass.
+ */
+ scan_adjusted = (global_reclaim(sc) && !current_is_kswapd() &&
+ sc->priority == DEF_PRIORITY);
+
blk_start_plug(&plug);
while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
nr[LRU_INACTIVE_FILE]) {
@@ -2088,17 +2102,8 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
continue;
/*
- * For global direct reclaim, reclaim only the number of pages
- * requested. Less care is taken to scan proportionally as it
- * is more important to minimise direct reclaim stall latency
- * than it is to properly age the LRU lists.
- */
- if (global_reclaim(sc) && !current_is_kswapd())
- break;
-
- /*
* For kswapd and memcg, reclaim at least the number of pages
- * requested. Ensure that the anon and file LRUs shrink
+ * requested. Ensure that the anon and file LRUs are scanned
* proportionally what was requested by get_scan_count(). We
* stop reclaiming one LRU and reduce the amount scanning
* proportional to the original scan target.
@@ -2106,6 +2111,15 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
nr_file = nr[LRU_INACTIVE_FILE] + nr[LRU_ACTIVE_FILE];
nr_anon = nr[LRU_INACTIVE_ANON] + nr[LRU_ACTIVE_ANON];
+ /*
+ * It's just vindictive to attack the larger once the smaller
+ * has gone to zero. And given the way we stop scanning the
+ * smaller below, this makes sure that we only make one nudge
+ * towards proportionality once we've got nr_to_reclaim.
+ */
+ if (!nr_file || !nr_anon)
+ break;
+
if (nr_file > nr_anon) {
unsigned long scan_target = targets[LRU_INACTIVE_ANON] +
targets[LRU_ACTIVE_ANON] + 1;
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index b851cc580853..fbda6b54baff 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -83,7 +83,7 @@ static bool batadv_is_on_batman_iface(const struct net_device *net_dev)
return true;
/* no more parents..stop recursion */
- if (net_dev->iflink == net_dev->ifindex)
+ if (net_dev->iflink == 0 || net_dev->iflink == net_dev->ifindex)
return false;
/* recurse over the parent device */
diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c
index 6e7a236525b6..06f19b9e159a 100644
--- a/net/ceph/crypto.c
+++ b/net/ceph/crypto.c
@@ -89,11 +89,82 @@ static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void)
static const u8 *aes_iv = (u8 *)CEPH_AES_IV;
+/*
+ * Should be used for buffers allocated with ceph_kvmalloc().
+ * Currently these are encrypt out-buffer (ceph_buffer) and decrypt
+ * in-buffer (msg front).
+ *
+ * Dispose of @sgt with teardown_sgtable().
+ *
+ * @prealloc_sg is to avoid memory allocation inside sg_alloc_table()
+ * in cases where a single sg is sufficient. No attempt to reduce the
+ * number of sgs by squeezing physically contiguous pages together is
+ * made though, for simplicity.
+ */
+static int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg,
+ const void *buf, unsigned int buf_len)
+{
+ struct scatterlist *sg;
+ const bool is_vmalloc = is_vmalloc_addr(buf);
+ unsigned int off = offset_in_page(buf);
+ unsigned int chunk_cnt = 1;
+ unsigned int chunk_len = PAGE_ALIGN(off + buf_len);
+ int i;
+ int ret;
+
+ if (buf_len == 0) {
+ memset(sgt, 0, sizeof(*sgt));
+ return -EINVAL;
+ }
+
+ if (is_vmalloc) {
+ chunk_cnt = chunk_len >> PAGE_SHIFT;
+ chunk_len = PAGE_SIZE;
+ }
+
+ if (chunk_cnt > 1) {
+ ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS);
+ if (ret)
+ return ret;
+ } else {
+ WARN_ON(chunk_cnt != 1);
+ sg_init_table(prealloc_sg, 1);
+ sgt->sgl = prealloc_sg;
+ sgt->nents = sgt->orig_nents = 1;
+ }
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *page;
+ unsigned int len = min(chunk_len - off, buf_len);
+
+ if (is_vmalloc)
+ page = vmalloc_to_page(buf);
+ else
+ page = virt_to_page(buf);
+
+ sg_set_page(sg, page, len, off);
+
+ off = 0;
+ buf += len;
+ buf_len -= len;
+ }
+ WARN_ON(buf_len != 0);
+
+ return 0;
+}
+
+static void teardown_sgtable(struct sg_table *sgt)
+{
+ if (sgt->orig_nents > 1)
+ sg_free_table(sgt);
+}
+
static int ceph_aes_encrypt(const void *key, int key_len,
void *dst, size_t *dst_len,
const void *src, size_t src_len)
{
- struct scatterlist sg_in[2], sg_out[1];
+ struct scatterlist sg_in[2], prealloc_sg;
+ struct sg_table sg_out;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
int ret;
@@ -109,16 +180,18 @@ static int ceph_aes_encrypt(const void *key, int key_len,
*dst_len = src_len + zero_padding;
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
sg_init_table(sg_in, 2);
sg_set_buf(&sg_in[0], src, src_len);
sg_set_buf(&sg_in[1], pad, zero_padding);
- sg_init_table(sg_out, 1);
- sg_set_buf(sg_out, dst, *dst_len);
+ ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
+ if (ret)
+ goto out_tfm;
+
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
+
/*
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
key, key_len, 1);
@@ -127,16 +200,22 @@ static int ceph_aes_encrypt(const void *key, int key_len,
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
pad, zero_padding, 1);
*/
- ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
+ ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
src_len + zero_padding);
- crypto_free_blkcipher(tfm);
- if (ret < 0)
+ if (ret < 0) {
pr_err("ceph_aes_crypt failed %d\n", ret);
+ goto out_sg;
+ }
/*
print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
dst, *dst_len, 1);
*/
- return 0;
+
+out_sg:
+ teardown_sgtable(&sg_out);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
@@ -144,7 +223,8 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
const void *src1, size_t src1_len,
const void *src2, size_t src2_len)
{
- struct scatterlist sg_in[3], sg_out[1];
+ struct scatterlist sg_in[3], prealloc_sg;
+ struct sg_table sg_out;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
int ret;
@@ -160,17 +240,19 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
*dst_len = src1_len + src2_len + zero_padding;
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
sg_init_table(sg_in, 3);
sg_set_buf(&sg_in[0], src1, src1_len);
sg_set_buf(&sg_in[1], src2, src2_len);
sg_set_buf(&sg_in[2], pad, zero_padding);
- sg_init_table(sg_out, 1);
- sg_set_buf(sg_out, dst, *dst_len);
+ ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
+ if (ret)
+ goto out_tfm;
+
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
+
/*
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
key, key_len, 1);
@@ -181,23 +263,30 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
pad, zero_padding, 1);
*/
- ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
+ ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
src1_len + src2_len + zero_padding);
- crypto_free_blkcipher(tfm);
- if (ret < 0)
+ if (ret < 0) {
pr_err("ceph_aes_crypt2 failed %d\n", ret);
+ goto out_sg;
+ }
/*
print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
dst, *dst_len, 1);
*/
- return 0;
+
+out_sg:
+ teardown_sgtable(&sg_out);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
static int ceph_aes_decrypt(const void *key, int key_len,
void *dst, size_t *dst_len,
const void *src, size_t src_len)
{
- struct scatterlist sg_in[1], sg_out[2];
+ struct sg_table sg_in;
+ struct scatterlist sg_out[2], prealloc_sg;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm };
char pad[16];
@@ -209,16 +298,16 @@ static int ceph_aes_decrypt(const void *key, int key_len,
if (IS_ERR(tfm))
return PTR_ERR(tfm);
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
- sg_init_table(sg_in, 1);
sg_init_table(sg_out, 2);
- sg_set_buf(sg_in, src, src_len);
sg_set_buf(&sg_out[0], dst, *dst_len);
sg_set_buf(&sg_out[1], pad, sizeof(pad));
+ ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
+ if (ret)
+ goto out_tfm;
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
/*
@@ -227,12 +316,10 @@ static int ceph_aes_decrypt(const void *key, int key_len,
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
src, src_len, 1);
*/
-
- ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
- crypto_free_blkcipher(tfm);
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
if (ret < 0) {
pr_err("ceph_aes_decrypt failed %d\n", ret);
- return ret;
+ goto out_sg;
}
if (src_len <= *dst_len)
@@ -250,7 +337,12 @@ static int ceph_aes_decrypt(const void *key, int key_len,
print_hex_dump(KERN_ERR, "dec out: ", DUMP_PREFIX_NONE, 16, 1,
dst, *dst_len, 1);
*/
- return 0;
+
+out_sg:
+ teardown_sgtable(&sg_in);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
static int ceph_aes_decrypt2(const void *key, int key_len,
@@ -258,7 +350,8 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
void *dst2, size_t *dst2_len,
const void *src, size_t src_len)
{
- struct scatterlist sg_in[1], sg_out[3];
+ struct sg_table sg_in;
+ struct scatterlist sg_out[3], prealloc_sg;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm };
char pad[16];
@@ -270,17 +363,17 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
if (IS_ERR(tfm))
return PTR_ERR(tfm);
- sg_init_table(sg_in, 1);
- sg_set_buf(sg_in, src, src_len);
sg_init_table(sg_out, 3);
sg_set_buf(&sg_out[0], dst1, *dst1_len);
sg_set_buf(&sg_out[1], dst2, *dst2_len);
sg_set_buf(&sg_out[2], pad, sizeof(pad));
+ ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
+ if (ret)
+ goto out_tfm;
crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
/*
@@ -289,12 +382,10 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
src, src_len, 1);
*/
-
- ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
- crypto_free_blkcipher(tfm);
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
if (ret < 0) {
pr_err("ceph_aes_decrypt failed %d\n", ret);
- return ret;
+ goto out_sg;
}
if (src_len <= *dst1_len)
@@ -324,7 +415,11 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
dst2, *dst2_len, 1);
*/
- return 0;
+out_sg:
+ teardown_sgtable(&sg_in);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c
index f2e15738534d..8f7bd56955b0 100644
--- a/net/ipv4/fib_rules.c
+++ b/net/ipv4/fib_rules.c
@@ -62,6 +62,10 @@ int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res)
else
res->tclassid = 0;
#endif
+
+ if (err == -ESRCH)
+ err = -ENETUNREACH;
+
return err;
}
EXPORT_SYMBOL_GPL(__fib_lookup);
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index c9244c8b8716..bdf2a257b142 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -217,6 +217,8 @@ static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident)
&ipv6_hdr(skb)->daddr))
continue;
#endif
+ } else {
+ continue;
}
if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index cb57aa862177..b27f6d34762b 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -962,8 +962,6 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
else
dev->flags &= ~IFF_POINTOPOINT;
- dev->iflink = p->link;
-
/* Precalculate GRE options length */
if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
if (t->parms.o_flags&GRE_CSUM)
@@ -1273,6 +1271,7 @@ static int ip6gre_tunnel_init(struct net_device *dev)
u64_stats_init(&ip6gre_tunnel_stats->syncp);
}
+ dev->iflink = tunnel->parms.link;
return 0;
}
@@ -1474,6 +1473,8 @@ static int ip6gre_tap_init(struct net_device *dev)
u64_stats_init(&ip6gre_tap_stats->syncp);
}
+ dev->iflink = tunnel->parms.link;
+
return 0;
}
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 912033957ad3..657639d39f70 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -272,9 +272,6 @@ static int ip6_tnl_create2(struct net_device *dev)
int err;
t = netdev_priv(dev);
- err = ip6_tnl_dev_init(dev);
- if (err < 0)
- goto out;
err = register_netdevice(dev);
if (err < 0)
@@ -1456,6 +1453,7 @@ ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
static const struct net_device_ops ip6_tnl_netdev_ops = {
+ .ndo_init = ip6_tnl_dev_init,
.ndo_uninit = ip6_tnl_dev_uninit,
.ndo_start_xmit = ip6_tnl_xmit,
.ndo_do_ioctl = ip6_tnl_ioctl,
@@ -1547,16 +1545,10 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev)
struct ip6_tnl *t = netdev_priv(dev);
struct net *net = dev_net(dev);
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
- int err = ip6_tnl_dev_init_gen(dev);
-
- if (err)
- return err;
t->parms.proto = IPPROTO_IPV6;
dev_hold(dev);
- ip6_tnl_link_config(t);
-
rcu_assign_pointer(ip6n->tnls_wc[0], t);
return 0;
}
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index 2d19272b8cee..9a5339fcb450 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -172,10 +172,6 @@ static int vti6_tnl_create2(struct net_device *dev)
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
int err;
- err = vti6_dev_init(dev);
- if (err < 0)
- goto out;
-
err = register_netdevice(dev);
if (err < 0)
goto out;
@@ -693,6 +689,7 @@ static int vti6_change_mtu(struct net_device *dev, int new_mtu)
}
static const struct net_device_ops vti6_netdev_ops = {
+ .ndo_init = vti6_dev_init,
.ndo_uninit = vti6_dev_uninit,
.ndo_start_xmit = vti6_tnl_xmit,
.ndo_do_ioctl = vti6_ioctl,
@@ -772,16 +769,10 @@ static int __net_init vti6_fb_tnl_dev_init(struct net_device *dev)
struct ip6_tnl *t = netdev_priv(dev);
struct net *net = dev_net(dev);
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
- int err = vti6_dev_init_gen(dev);
-
- if (err)
- return err;
t->parms.proto = IPPROTO_IPV6;
dev_hold(dev);
- vti6_link_config(t);
-
rcu_assign_pointer(ip6n->tnls_wc[0], t);
return 0;
}
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index b12b11b123ff..317b6dbf3190 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -195,10 +195,8 @@ static int ipip6_tunnel_create(struct net_device *dev)
struct sit_net *sitn = net_generic(net, sit_net_id);
int err;
- err = ipip6_tunnel_init(dev);
- if (err < 0)
- goto out;
- ipip6_tunnel_clone_6rd(dev, sitn);
+ memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
+ memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
if ((__force u16)t->parms.i_flags & SIT_ISATAP)
dev->priv_flags |= IFF_ISATAP;
@@ -207,7 +205,8 @@ static int ipip6_tunnel_create(struct net_device *dev)
if (err < 0)
goto out;
- strcpy(t->parms.name, dev->name);
+ ipip6_tunnel_clone_6rd(dev, sitn);
+
dev->rtnl_link_ops = &sit_link_ops;
dev_hold(dev);
@@ -1321,6 +1320,7 @@ static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu)
}
static const struct net_device_ops ipip6_netdev_ops = {
+ .ndo_init = ipip6_tunnel_init,
.ndo_uninit = ipip6_tunnel_uninit,
.ndo_start_xmit = sit_tunnel_xmit,
.ndo_do_ioctl = ipip6_tunnel_ioctl,
@@ -1367,9 +1367,7 @@ static int ipip6_tunnel_init(struct net_device *dev)
tunnel->dev = dev;
tunnel->net = dev_net(dev);
-
- memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
- memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
+ strcpy(tunnel->parms.name, dev->name);
ipip6_tunnel_bind_dev(dev);
dev->tstats = alloc_percpu(struct pcpu_sw_netstats);
@@ -1401,7 +1399,6 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
tunnel->dev = dev;
tunnel->net = dev_net(dev);
- strcpy(tunnel->parms.name, dev->name);
iph->version = 4;
iph->protocol = IPPROTO_IPV6;
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index 00b2a6d1c009..d65aea21ce81 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -1763,6 +1763,7 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
struct ipxhdr *ipx = NULL;
struct sk_buff *skb;
int copied, rc;
+ bool locked = true;
lock_sock(sk);
/* put the autobinding in */
@@ -1789,6 +1790,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
if (sock_flag(sk, SOCK_ZAPPED))
goto out;
+ release_sock(sk);
+ locked = false;
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &rc);
if (!skb)
@@ -1822,7 +1825,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
out_free:
skb_free_datagram(sk, skb);
out:
- release_sock(sk);
+ if (locked)
+ release_sock(sk);
return rc;
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index ea7013cb7e52..3f076b9c9308 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -815,7 +815,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
memset(&params, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems,
ifibss->chandef.chan->band,
sta_flags, ifibss->bssid, &csa_ie);
/* can't switch to destination channel, fail */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b127902361f4..bf7a1bbb975f 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1569,7 +1569,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
* ieee80211_parse_ch_switch_ie - parses channel switch IEs
* @sdata: the sdata of the interface which has received the frame
* @elems: parsed 802.11 elements received with the frame
- * @beacon: indicates if the frame was a beacon or probe response
* @current_band: indicates the current band
* @sta_flags: contains information about own capabilities and restrictions
* to decide which channel switch announcements can be accepted. Only the
@@ -1583,7 +1582,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
*/
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- struct ieee802_11_elems *elems, bool beacon,
+ struct ieee802_11_elems *elems,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 8f7fabc46c97..06f5de4e4fbb 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -760,10 +760,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
int i, flushed;
struct ps_data *ps;
struct cfg80211_chan_def chandef;
+ bool cancel_scan;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
- if (rcu_access_pointer(local->scan_sdata) == sdata)
+ cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
+ if (cancel_scan)
ieee80211_scan_cancel(local);
/*
@@ -973,6 +975,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_ps(local, -1);
+ if (cancel_scan)
+ flush_delayed_work(&local->scan_work);
+
if (local->open_count == 0) {
ieee80211_stop_device(local);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 5b919cab1de0..3d52d1d68431 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -885,7 +885,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
memset(&params, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
sta_flags, sdata->vif.addr,
&csa_ie);
if (err < 0)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 189eef014c4f..c9535a976b56 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1001,7 +1001,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
current_band = cbss->channel->band;
memset(&csa_ie, 0, sizeof(csa_ie));
- res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+ res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
ifmgd->flags,
ifmgd->associated->bssid, &csa_ie);
if (res < 0)
@@ -1086,7 +1086,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
else
mod_timer(&ifmgd->chswitch_timer,
- TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
+ TU_TO_EXP_TIME((csa_ie.count - 1) *
+ cbss->beacon_interval));
}
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 3e57f96c9666..095c16037bc5 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1679,11 +1679,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
sc = le16_to_cpu(hdr->seq_ctrl);
frag = sc & IEEE80211_SCTL_FRAG;
- if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
- is_multicast_ether_addr(hdr->addr1))) {
- /* not fragmented */
+ if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
+ goto out;
+
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ rx->local->dot11MulticastReceivedFrameCount++;
goto out;
}
+
I802_DEBUG_INC(rx->local->rx_handlers_fragments);
if (skb_linearize(rx->skb))
@@ -1776,10 +1779,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
out:
if (rx->sta)
rx->sta->rx_packets++;
- if (is_multicast_ether_addr(hdr->addr1))
- rx->local->dot11MulticastReceivedFrameCount++;
- else
- ieee80211_led_rx(rx->local);
+ ieee80211_led_rx(rx->local);
return RX_CONTINUE;
}
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 6ab009070084..efeba56c913b 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -22,7 +22,7 @@
#include "wme.h"
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- struct ieee802_11_elems *elems, bool beacon,
+ struct ieee802_11_elems *elems,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie)
@@ -91,19 +91,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
return -EINVAL;
}
- if (!beacon && sec_chan_offs) {
+ if (sec_chan_offs) {
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
- } else if (beacon && ht_oper) {
- secondary_channel_offset =
- ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
- /* If it's not a beacon, HT is enabled and the IE not present,
- * it's 20 MHz, 802.11-2012 8.5.2.6:
- * This element [the Secondary Channel Offset Element] is
- * present when switching to a 40 MHz channel. It may be
- * present when switching to a 20 MHz channel (in which
- * case the secondary channel offset is set to SCN).
- */
+ /* If the secondary channel offset IE is not present,
+ * we can't know what's the post-CSA offset, so the
+ * best we can do is use 20MHz.
+ */
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
}
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index de770ec39e51..cf9937743abb 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -636,7 +636,7 @@ ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index)
struct ip_set *set;
struct ip_set_net *inst = ip_set_pernet(net);
- if (index > inst->ip_set_max)
+ if (index >= inst->ip_set_max)
return IPSET_INVALID_ID;
nfnl_lock(NFNL_SUBSYS_IPSET);
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index a155d19a225e..6ff12a191400 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -45,7 +45,8 @@
#define NFULNL_NLBUFSIZ_DEFAULT NLMSG_GOODSIZE
#define NFULNL_TIMEOUT_DEFAULT 100 /* every second */
#define NFULNL_QTHRESH_DEFAULT 100 /* 100 packets */
-#define NFULNL_COPY_RANGE_MAX 0xFFFF /* max packet size is limited by 16-bit struct nfattr nfa_len field */
+/* max packet size is limited by 16-bit struct nfattr nfa_len field */
+#define NFULNL_COPY_RANGE_MAX (0xFFFF - NLA_HDRLEN)
#define PRINTR(x, args...) do { if (net_ratelimit()) \
printk(x, ## args); } while (0);
@@ -255,6 +256,8 @@ nfulnl_set_mode(struct nfulnl_instance *inst, u_int8_t mode,
case NFULNL_COPY_PACKET:
inst->copy_mode = mode;
+ if (range == 0)
+ range = NFULNL_COPY_RANGE_MAX;
inst->copy_range = min_t(unsigned int,
range, NFULNL_COPY_RANGE_MAX);
break;
@@ -346,26 +349,25 @@ nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size,
return skb;
}
-static int
+static void
__nfulnl_send(struct nfulnl_instance *inst)
{
- int status = -1;
-
if (inst->qlen > 1) {
struct nlmsghdr *nlh = nlmsg_put(inst->skb, 0, 0,
NLMSG_DONE,
sizeof(struct nfgenmsg),
0);
- if (!nlh)
+ if (WARN_ONCE(!nlh, "bad nlskb size: %u, tailroom %d\n",
+ inst->skb->len, skb_tailroom(inst->skb))) {
+ kfree_skb(inst->skb);
goto out;
+ }
}
- status = nfnetlink_unicast(inst->skb, inst->net, inst->peer_portid,
- MSG_DONTWAIT);
-
+ nfnetlink_unicast(inst->skb, inst->net, inst->peer_portid,
+ MSG_DONTWAIT);
+out:
inst->qlen = 0;
inst->skb = NULL;
-out:
- return status;
}
static void
@@ -652,7 +654,8 @@ nfulnl_log_packet(struct net *net,
+ nla_total_size(sizeof(u_int32_t)) /* gid */
+ nla_total_size(plen) /* prefix */
+ nla_total_size(sizeof(struct nfulnl_msg_packet_hw))
- + nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp));
+ + nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp))
+ + nla_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
if (in && skb_mac_header_was_set(skb)) {
size += nla_total_size(skb->dev->hard_header_len)
@@ -681,8 +684,7 @@ nfulnl_log_packet(struct net *net,
break;
case NFULNL_COPY_PACKET:
- if (inst->copy_range == 0
- || inst->copy_range > skb->len)
+ if (inst->copy_range > skb->len)
data_len = skb->len;
else
data_len = inst->copy_range;
@@ -695,8 +697,7 @@ nfulnl_log_packet(struct net *net,
goto unlock_and_release;
}
- if (inst->skb &&
- size > skb_tailroom(inst->skb) - sizeof(struct nfgenmsg)) {
+ if (inst->skb && size > skb_tailroom(inst->skb)) {
/* either the queue len is too high or we don't have
* enough room in the skb left. flush to userspace. */
__nfulnl_flush(inst);
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 82cb8236f8a1..ad979612238a 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -678,7 +678,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
family = ctx->afi->family;
/* Re-use the existing target if it's already loaded. */
- list_for_each_entry(nft_target, &nft_match_list, head) {
+ list_for_each_entry(nft_target, &nft_target_list, head) {
struct xt_target *target = nft_target->ops.data;
if (strcmp(target->name, tg_name) == 0 &&
diff --git a/net/rpmsg/rpmsg_proto.c b/net/rpmsg/rpmsg_proto.c
index 4534b6cc9545..6ec133bf10e0 100644
--- a/net/rpmsg/rpmsg_proto.c
+++ b/net/rpmsg/rpmsg_proto.c
@@ -47,9 +47,20 @@
struct rpmsg_socket {
struct sock sk;
struct rpmsg_channel *rpdev;
+ int rproc_id;
+ struct list_head elem;
bool unregister_rpdev;
};
+/* Connection and socket states */
+enum {
+ RPMSG_CONNECTED = 1,
+ RPMSG_OPEN,
+ RPMSG_LISTENING,
+ RPMSG_CLOSED,
+ RPMSG_ERROR,
+};
+
/* A two-level radix-tree-based scheme is used to maintain the rpmsg channels
* we're exposing to userland. The first radix tree maps vproc index id
* to its channels, and the second radix tree associates each channel
@@ -138,12 +149,12 @@ static int rpmsg_sock_connect(struct socket *sock, struct sockaddr *addr,
sa = (struct sockaddr_rpmsg *)addr;
+ mutex_lock(&rpmsg_channels_lock);
+
lock_sock(sk);
rpsk = container_of(sk, struct rpmsg_socket, sk);
- mutex_lock(&rpmsg_channels_lock);
-
/* find the set of channels exposed by this remote processor */
vrp_channels = radix_tree_lookup(&rpmsg_channels, sa->vproc_id);
if (!vrp_channels) {
@@ -158,17 +169,18 @@ static int rpmsg_sock_connect(struct socket *sock, struct sockaddr *addr,
goto out;
}
+ rpsk->rproc_id = sa->vproc_id;
rpsk->rpdev = rpdev;
/* bind this socket with its rpmsg endpoint */
- rpdev->ept->priv = sk;
+ list_add_tail(&rpsk->elem, rpdev->ept->priv);
/* XXX take care of disconnection state too */
sk->sk_state = RPMSG_CONNECTED;
out:
- mutex_unlock(&rpmsg_channels_lock);
release_sock(sk);
+ mutex_unlock(&rpmsg_channels_lock);
return err;
}
@@ -191,6 +203,12 @@ static int rpmsg_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
lock_sock(sk);
+ /* we don't support Tx on errored-out sockets */
+ if (sk->sk_state == RPMSG_ERROR) {
+ release_sock(sk);
+ return -ESHUTDOWN;
+ }
+
/* we don't support loopback at this point */
if (sk->sk_state != RPMSG_CONNECTED) {
release_sock(sk);
@@ -327,7 +345,7 @@ static int rpmsg_sock_getname(struct socket *sock, struct sockaddr *addr,
*len = sizeof(*sa);
if (peer) {
- sa->vproc_id = rpmsg_sock_get_proc_id(rpdev);
+ sa->vproc_id = rpsk->rproc_id;
sa->addr = rpdev->dst;
} else {
sa->vproc_id = RPMSG_LOCALHOST;
@@ -341,17 +359,41 @@ static int rpmsg_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct rpmsg_socket *rpsk = container_of(sk, struct rpmsg_socket, sk);
+ struct virtproc_info *vrp = NULL;
int ret;
if (!sk)
return 0;
- if (rpsk->unregister_rpdev) {
- ret = rpmsg_destroy_channel(rpsk->rpdev);
- if (ret)
- pr_err("rpmsg_destroy_channel failed for sk %p\n", sk);
+ mutex_lock(&rpmsg_channels_lock);
+ if (rpsk->unregister_rpdev) { /* Rx (bound) sockets */
+ /* The bound socket's rpmsg device will be removed by rpmsg bus
+ * core during recovery, but only after the published rpmsg
+ * channel is removed (device registration order). The check for
+ * valid vrp will ensure that rpmsg_destroy_channel will not be
+ * called if the release from userspace occurs first. However,
+ * the socket can be released much later than the recreated vrp
+ * as well, so an additional check for a sane socket state is
+ * also needed.
+ */
+ vrp = radix_tree_lookup(&rpmsg_vprocs, rpsk->rproc_id);
+ if (vrp && sk->sk_state != RPMSG_ERROR) {
+ rpsk->rpdev->ept->priv = NULL;
+ mutex_unlock(&rpmsg_channels_lock);
+ ret = rpmsg_destroy_channel(rpsk->rpdev);
+ if (ret) {
+ pr_err("rpmsg_destroy_channel failed for sk %p\n",
+ sk);
+ }
+ goto release;
+ }
+ } else { /* Tx (connected) sockets */
+ if (sk->sk_state != RPMSG_ERROR)
+ list_del(&rpsk->elem);
}
+ mutex_unlock(&rpmsg_channels_lock);
+release:
sock_put(sock->sk);
return 0;
@@ -398,6 +440,7 @@ rpmsg_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
rpsk->rpdev = rpdev;
rpsk->unregister_rpdev = true;
+ rpsk->rproc_id = sa->vproc_id;
/* bind this socket with its rpmsg endpoint */
rpdev->ept->priv = sk;
@@ -437,6 +480,7 @@ static int rpmsg_sock_create(struct net *net, struct socket *sock, int proto,
int kern)
{
struct sock *sk;
+ struct rpmsg_socket *rpsk;
if (sock->type != SOCK_SEQPACKET)
return -ESOCKTNOSUPPORT;
@@ -456,6 +500,11 @@ static int rpmsg_sock_create(struct net *net, struct socket *sock, int proto,
sk->sk_state = RPMSG_OPEN;
+ rpsk = container_of(sk, struct rpmsg_socket, sk);
+ INIT_LIST_HEAD(&rpsk->elem);
+ /* use RPMSG_LOCALHOST to serve as an invalid value */
+ rpsk->rproc_id = RPMSG_LOCALHOST;
+
return 0;
}
@@ -534,6 +583,7 @@ static int rpmsg_proto_probe(struct rpmsg_channel *rpdev)
int ret, dst = rpdev->dst, id;
struct radix_tree_root *vrp_channels;
struct virtproc_info *vrp;
+ struct list_head *sock_list = NULL;
id = rpmsg_sock_get_proc_id(rpdev);
@@ -573,12 +623,28 @@ static int rpmsg_proto_probe(struct rpmsg_channel *rpdev)
kfree(vrp_channels);
goto out;
}
+ } else {
+ ret = -ENODEV;
+ dev_err(dev, "multiple rpmsg-proto devices from the same rproc is not supported.\n");
+ goto out;
}
+ WARN_ON(!!rpdev->ept->priv);
+ sock_list = kzalloc(sizeof(*sock_list), GFP_KERNEL);
+ if (!sock_list) {
+ dev_err(dev, "failed to allocate list_head\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ INIT_LIST_HEAD(sock_list);
+
/* let's associate the new channel with its dst */
ret = radix_tree_insert(vrp_channels, dst, rpdev);
- if (ret)
+ if (ret) {
dev_err(dev, "failed to add rpmsg addr %d: %d\n", dst, ret);
+ kfree(sock_list);
+ }
+ rpdev->ept->priv = sock_list;
out:
mutex_unlock(&rpmsg_channels_lock);
@@ -591,6 +657,9 @@ static void rpmsg_proto_remove(struct rpmsg_channel *rpdev)
struct device *dev = &rpdev->dev;
int id, dst = rpdev->dst, src = rpdev->src;
struct radix_tree_root *vrp_channels;
+ struct list_head *sk_list;
+ struct rpmsg_socket *rpsk, *tmp;
+ struct sock *sk;
if (dst == RPMSG_ADDR_ANY)
return;
@@ -599,24 +668,54 @@ static void rpmsg_proto_remove(struct rpmsg_channel *rpdev)
mutex_lock(&rpmsg_channels_lock);
- vrp_channels = radix_tree_lookup(&rpmsg_channels, id);
- if (!vrp_channels) {
- dev_err(dev, "can't find channels for this vrp: %d\n", id);
- goto out;
- }
-
- /* Only remove non-reserved channels from the tree, as only these
- * were "probed". Note: bind is not causing a probe, and bound
+ /* Only remove non-reserved channels from the radix trees, as only these
+ * were "probed" (published from remote processor and added to radix
+ * trees). Note: bind is not causing a "true" probe, and bound
* sockets have src addresses < RPMSG_RESERVED_ADDRESSES.
*/
if (src >= RPMSG_RESERVED_ADDRESSES) {
+ vrp_channels = radix_tree_lookup(&rpmsg_channels, id);
+ if (!vrp_channels) {
+ dev_err(dev, "can't find channels for this vrp: %d\n",
+ id);
+ goto out;
+ }
+
mutex_lock(&rpmsg_vprocs_lock);
if (!radix_tree_delete(&rpmsg_vprocs, id))
dev_err(dev, "failed to delete id %d\n", id);
mutex_unlock(&rpmsg_vprocs_lock);
+ /* mark all connected sockets invalid and remove them
+ * from the rpdev's list.
+ */
+ sk_list = rpdev->ept->priv;
+ list_for_each_entry_safe(rpsk, tmp, sk_list, elem) {
+ rpsk->sk.sk_state = RPMSG_ERROR;
+ list_del(&rpsk->elem);
+ }
+ kfree(sk_list);
+ rpdev->ept->priv = NULL;
+
if (!radix_tree_delete(vrp_channels, dst))
dev_err(dev, "failed to delete rpmsg %d\n", dst);
+
+ if (!radix_tree_delete(&rpmsg_channels, id))
+ dev_err(dev, "failed to delete vrp_channels for id %d\n",
+ id);
+ kfree(vrp_channels);
+ } else {
+ /* mark the associated bound socket as invalid if it has not
+ * already been deleted by rpmsg_sock_release().
+ */
+ sk = rpdev->ept->priv;
+ if (sk) {
+ lock_sock(sk);
+ sk->sk_state = RPMSG_ERROR;
+ rpsk = container_of(sk, struct rpmsg_socket, sk);
+ rpsk->rpdev = NULL;
+ release_sock(sk);
+ }
}
out:
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 5d97d8fe4be7..d477d476714d 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1627,6 +1627,8 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack(
* ack chunk whose serial number matches that of the request.
*/
list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) {
+ if (sctp_chunk_pending(ack))
+ continue;
if (ack->subh.addip_hdr->serial == serial) {
sctp_chunk_hold(ack);
return ack;
diff --git a/net/sctp/auth.c b/net/sctp/auth.c
index 0e8529113dc5..fb7976aee61c 100644
--- a/net/sctp/auth.c
+++ b/net/sctp/auth.c
@@ -862,8 +862,6 @@ int sctp_auth_set_key(struct sctp_endpoint *ep,
list_add(&cur_key->key_list, sh_keys);
cur_key->key = key;
- sctp_auth_key_hold(key);
-
return 0;
nomem:
if (!replace)
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index 4de12afa13d4..7e8a16c77039 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -140,18 +140,9 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
} else {
/* Nothing to do. Next chunk in the packet, please. */
ch = (sctp_chunkhdr_t *) chunk->chunk_end;
-
/* Force chunk->skb->data to chunk->chunk_end. */
- skb_pull(chunk->skb,
- chunk->chunk_end - chunk->skb->data);
-
- /* Verify that we have at least chunk headers
- * worth of buffer left.
- */
- if (skb_headlen(chunk->skb) < sizeof(sctp_chunkhdr_t)) {
- sctp_chunk_free(chunk);
- chunk = queue->in_progress = NULL;
- }
+ skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data);
+ /* We are guaranteed to pull a SCTP header. */
}
}
@@ -187,24 +178,14 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
chunk->subh.v = NULL; /* Subheader is no longer valid. */
- if (chunk->chunk_end < skb_tail_pointer(chunk->skb)) {
+ if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <
+ skb_tail_pointer(chunk->skb)) {
/* This is not a singleton */
chunk->singleton = 0;
} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
- /* RFC 2960, Section 6.10 Bundling
- *
- * Partial chunks MUST NOT be placed in an SCTP packet.
- * If the receiver detects a partial chunk, it MUST drop
- * the chunk.
- *
- * Since the end of the chunk is past the end of our buffer
- * (which contains the whole packet, we can freely discard
- * the whole packet.
- */
- sctp_chunk_free(chunk);
- chunk = queue->in_progress = NULL;
-
- return NULL;
+ /* Discard inside state machine. */
+ chunk->pdiscard = 1;
+ chunk->chunk_end = skb_tail_pointer(chunk->skb);
} else {
/* We are at the end of the packet, so mark the chunk
* in case we need to send a SACK.
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index fee5552ddf92..43abb643f3a1 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -2609,6 +2609,9 @@ do_addr_param:
addr_param = param.v + sizeof(sctp_addip_param_t);
af = sctp_get_af_specific(param_type2af(param.p->type));
+ if (af == NULL)
+ break;
+
af->from_addr_param(&addr, addr_param,
htons(asoc->peer.port), 0);
@@ -3110,50 +3113,63 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
return SCTP_ERROR_NO_ERROR;
}
-/* Verify the ASCONF packet before we process it. */
-int sctp_verify_asconf(const struct sctp_association *asoc,
- struct sctp_paramhdr *param_hdr, void *chunk_end,
- struct sctp_paramhdr **errp) {
- sctp_addip_param_t *asconf_param;
+/* Verify the ASCONF packet before we process it. */
+bool sctp_verify_asconf(const struct sctp_association *asoc,
+ struct sctp_chunk *chunk, bool addr_param_needed,
+ struct sctp_paramhdr **errp)
+{
+ sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr;
union sctp_params param;
- int length, plen;
+ bool addr_param_seen = false;
- param.v = (sctp_paramhdr_t *) param_hdr;
- while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) {
- length = ntohs(param.p->length);
- *errp = param.p;
-
- if (param.v > chunk_end - length ||
- length < sizeof(sctp_paramhdr_t))
- return 0;
+ sctp_walk_params(param, addip, addip_hdr.params) {
+ size_t length = ntohs(param.p->length);
+ *errp = param.p;
switch (param.p->type) {
+ case SCTP_PARAM_ERR_CAUSE:
+ break;
+ case SCTP_PARAM_IPV4_ADDRESS:
+ if (length != sizeof(sctp_ipv4addr_param_t))
+ return false;
+ addr_param_seen = true;
+ break;
+ case SCTP_PARAM_IPV6_ADDRESS:
+ if (length != sizeof(sctp_ipv6addr_param_t))
+ return false;
+ addr_param_seen = true;
+ break;
case SCTP_PARAM_ADD_IP:
case SCTP_PARAM_DEL_IP:
case SCTP_PARAM_SET_PRIMARY:
- asconf_param = (sctp_addip_param_t *)param.v;
- plen = ntohs(asconf_param->param_hdr.length);
- if (plen < sizeof(sctp_addip_param_t) +
- sizeof(sctp_paramhdr_t))
- return 0;
+ /* In ASCONF chunks, these need to be first. */
+ if (addr_param_needed && !addr_param_seen)
+ return false;
+ length = ntohs(param.addip->param_hdr.length);
+ if (length < sizeof(sctp_addip_param_t) +
+ sizeof(sctp_paramhdr_t))
+ return false;
break;
case SCTP_PARAM_SUCCESS_REPORT:
case SCTP_PARAM_ADAPTATION_LAYER_IND:
if (length != sizeof(sctp_addip_param_t))
- return 0;
-
+ return false;
break;
default:
- break;
+ /* This is unkown to us, reject! */
+ return false;
}
-
- param.v += WORD_ROUND(length);
}
- if (param.v != chunk_end)
- return 0;
+ /* Remaining sanity checks. */
+ if (addr_param_needed && !addr_param_seen)
+ return false;
+ if (!addr_param_needed && addr_param_seen)
+ return false;
+ if (param.v != chunk->chunk_end)
+ return false;
- return 1;
+ return true;
}
/* Process an incoming ASCONF chunk with the next expected serial no. and
@@ -3162,16 +3178,17 @@ int sctp_verify_asconf(const struct sctp_association *asoc,
struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
struct sctp_chunk *asconf)
{
+ sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr;
+ bool all_param_pass = true;
+ union sctp_params param;
sctp_addiphdr_t *hdr;
union sctp_addr_param *addr_param;
sctp_addip_param_t *asconf_param;
struct sctp_chunk *asconf_ack;
-
__be16 err_code;
int length = 0;
int chunk_len;
__u32 serial;
- int all_param_pass = 1;
chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t);
hdr = (sctp_addiphdr_t *)asconf->skb->data;
@@ -3199,9 +3216,14 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
goto done;
/* Process the TLVs contained within the ASCONF chunk. */
- while (chunk_len > 0) {
+ sctp_walk_params(param, addip, addip_hdr.params) {
+ /* Skip preceeding address parameters. */
+ if (param.p->type == SCTP_PARAM_IPV4_ADDRESS ||
+ param.p->type == SCTP_PARAM_IPV6_ADDRESS)
+ continue;
+
err_code = sctp_process_asconf_param(asoc, asconf,
- asconf_param);
+ param.addip);
/* ADDIP 4.1 A7)
* If an error response is received for a TLV parameter,
* all TLVs with no response before the failed TLV are
@@ -3209,28 +3231,20 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
* the failed response are considered unsuccessful unless
* a specific success indication is present for the parameter.
*/
- if (SCTP_ERROR_NO_ERROR != err_code)
- all_param_pass = 0;
-
+ if (err_code != SCTP_ERROR_NO_ERROR)
+ all_param_pass = false;
if (!all_param_pass)
- sctp_add_asconf_response(asconf_ack,
- asconf_param->crr_id, err_code,
- asconf_param);
+ sctp_add_asconf_response(asconf_ack, param.addip->crr_id,
+ err_code, param.addip);
/* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add
* an IP address sends an 'Out of Resource' in its response, it
* MUST also fail any subsequent add or delete requests bundled
* in the ASCONF.
*/
- if (SCTP_ERROR_RSRC_LOW == err_code)
+ if (err_code == SCTP_ERROR_RSRC_LOW)
goto done;
-
- /* Move to the next ASCONF param. */
- length = ntohs(asconf_param->param_hdr.length);
- asconf_param = (void *)asconf_param + length;
- chunk_len -= length;
}
-
done:
asoc->peer.addip_serial++;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 7194fe8589b0..3e287a3fa03b 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -170,6 +170,9 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk,
{
__u16 chunk_length = ntohs(chunk->chunk_hdr->length);
+ /* Previously already marked? */
+ if (unlikely(chunk->pdiscard))
+ return 0;
if (unlikely(chunk_length < required_length))
return 0;
@@ -3591,9 +3594,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
struct sctp_chunk *asconf_ack = NULL;
struct sctp_paramhdr *err_param = NULL;
sctp_addiphdr_t *hdr;
- union sctp_addr_param *addr_param;
__u32 serial;
- int length;
if (!sctp_vtag_verify(chunk, asoc)) {
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
@@ -3618,17 +3619,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
hdr = (sctp_addiphdr_t *)chunk->skb->data;
serial = ntohl(hdr->serial);
- addr_param = (union sctp_addr_param *)hdr->params;
- length = ntohs(addr_param->p.length);
- if (length < sizeof(sctp_paramhdr_t))
- return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
- (void *)addr_param, commands);
-
/* Verify the ASCONF chunk before processing it. */
- if (!sctp_verify_asconf(asoc,
- (sctp_paramhdr_t *)((void *)addr_param + length),
- (void *)chunk->chunk_end,
- &err_param))
+ if (!sctp_verify_asconf(asoc, chunk, true, &err_param))
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
(void *)err_param, commands);
@@ -3745,10 +3737,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net,
rcvd_serial = ntohl(addip_hdr->serial);
/* Verify the ASCONF-ACK chunk before processing it. */
- if (!sctp_verify_asconf(asoc,
- (sctp_paramhdr_t *)addip_hdr->params,
- (void *)asconf_ack->chunk_end,
- &err_param))
+ if (!sctp_verify_asconf(asoc, asconf_ack, false, &err_param))
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
(void *)err_param, commands);
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 0fcbe90f3ef2..12528e9ac4c2 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1369,8 +1369,7 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
/* enable small pop, introduce 400ms delay in turning off */
snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
- SGTL5000_SMALL_POP,
- SGTL5000_SMALL_POP);
+ SGTL5000_SMALL_POP, 1);
/* disable short cut detector */
snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 2f8c88931f69..bd7a344bf8c5 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -275,7 +275,7 @@
#define SGTL5000_BIAS_CTRL_MASK 0x000e
#define SGTL5000_BIAS_CTRL_SHIFT 1
#define SGTL5000_BIAS_CTRL_WIDTH 3
-#define SGTL5000_SMALL_POP 0x0001
+#define SGTL5000_SMALL_POP 0
/*
* SGTL5000_CHIP_MIC_CTRL
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 0307909483cd..d1e451faeaa5 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -1210,7 +1210,7 @@ static void aic31xx_pdata_from_of(struct aic31xx_priv *aic31xx)
}
#endif /* CONFIG_OF */
-void aic31xx_device_init(struct aic31xx_priv *aic31xx)
+static int aic31xx_device_init(struct aic31xx_priv *aic31xx)
{
int ret, i;
@@ -1229,7 +1229,7 @@ void aic31xx_device_init(struct aic31xx_priv *aic31xx)
"aic31xx-reset-pin");
if (ret < 0) {
dev_err(aic31xx->dev, "not able to acquire gpio\n");
- return;
+ return ret;
}
}
@@ -1242,6 +1242,7 @@ void aic31xx_device_init(struct aic31xx_priv *aic31xx)
if (ret != 0)
dev_err(aic31xx->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
}
static int aic31xx_i2c_probe(struct i2c_client *i2c,
@@ -1272,7 +1273,9 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
aic31xx->pdata.codec_type = id->driver_data;
- aic31xx_device_init(aic31xx);
+ ret = aic31xx_device_init(aic31xx);
+ if (ret)
+ return ret;
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx,
aic31xx_dai_driver,
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 53c03aff762e..0502e3f17412 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1341,6 +1341,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
file, blocks, pos - firmware->size);
out_fw:
+ regmap_async_complete(regmap);
release_firmware(firmware);
wm_adsp_buf_free(&buf_list);
out:
diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c
index 35bc89f176fa..8152fd5ae8a0 100644
--- a/sound/soc/omap/omap-hdmi-audio.c
+++ b/sound/soc/omap/omap-hdmi-audio.c
@@ -62,7 +62,13 @@ static int hdmi_dai_startup(struct snd_pcm_substream *substream,
ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
if (ret < 0) {
- dev_err(dai->dev, "could not apply constraint\n");
+ dev_err(dai->dev, "could not apply period constraint\n");
+ return ret;
+ }
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128);
+ if (ret < 0) {
+ dev_err(dai->dev, "could not apply buffer constraint\n");
return ret;
}
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 1967f44e7cd4..9d0c59ce6de8 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -1785,8 +1785,7 @@ static const struct snd_soc_dai_ops fsi_dai_ops = {
static struct snd_pcm_hardware fsi_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 743de5e3b1e1..37fcd93ed1fd 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -626,8 +626,7 @@ static void rsnd_dai_remove(struct platform_device *pdev,
static struct snd_pcm_hardware rsnd_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 02733ded2cb1..e28704e1274a 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1258,13 +1258,36 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
}
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
+
+/* Set FE's runtime_update state; the state is protected via PCM stream lock
+ * for avoiding the race with trigger callback.
+ * If the state is unset and a trigger is pending while the previous operation,
+ * process the pending trigger action here.
+ */
+static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
+ int stream, enum snd_soc_dpcm_update state)
+{
+ struct snd_pcm_substream *substream =
+ snd_soc_dpcm_get_substream(fe, stream);
+
+ snd_pcm_stream_lock_irq(substream);
+ if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
+ dpcm_fe_dai_do_trigger(substream,
+ fe->dpcm[stream].trigger_pending - 1);
+ fe->dpcm[stream].trigger_pending = 0;
+ }
+ fe->dpcm[stream].runtime_update = state;
+ snd_pcm_stream_unlock_irq(substream);
+}
+
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
{
struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
struct snd_pcm_runtime *runtime = fe_substream->runtime;
int stream = fe_substream->stream, ret = 0;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
ret = dpcm_be_dai_startup(fe, fe_substream->stream);
if (ret < 0) {
@@ -1286,13 +1309,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
dpcm_set_fe_runtime(fe_substream);
snd_pcm_limit_hw_rates(runtime);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
unwind:
dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
be_err:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -1339,7 +1362,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* shutdown the BEs */
dpcm_be_dai_shutdown(fe, substream->stream);
@@ -1353,7 +1376,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
}
@@ -1401,7 +1424,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
int err, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
@@ -1416,7 +1439,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
err = dpcm_be_dai_hw_free(fe, stream);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return 0;
@@ -1509,7 +1532,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
int ret, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
memcpy(&fe->dpcm[substream->stream].hw_params, params,
sizeof(struct snd_pcm_hw_params));
@@ -1532,7 +1555,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
}
@@ -1646,7 +1669,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
}
EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
-static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream, ret;
@@ -1720,6 +1743,23 @@ out:
return ret;
}
+static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int stream = substream->stream;
+
+ /* if FE's runtime_update is already set, we're in race;
+ * process this trigger later at exit
+ */
+ if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) {
+ fe->dpcm[stream].trigger_pending = cmd + 1;
+ return 0; /* delayed, assuming it's successful */
+ }
+
+ /* we're alone, let's trigger */
+ return dpcm_fe_dai_do_trigger(substream, cmd);
+}
+
int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
@@ -1763,7 +1803,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* there is no point preparing this FE if there are no BEs */
if (list_empty(&fe->dpcm[stream].be_clients)) {
@@ -1790,7 +1830,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
@@ -1937,11 +1977,11 @@ static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_startup(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -1950,11 +1990,11 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_shutdown(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index f4b12c216f1c..5a723df670b4 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -885,6 +885,11 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
return changed;
}
+static void kctl_private_value_free(struct snd_kcontrol *kctl)
+{
+ kfree((void *)kctl->private_value);
+}
+
static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
int validx, int bUnitID)
{
@@ -919,6 +924,7 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
return -ENOMEM;
}
+ kctl->private_free = kctl_private_value_free;
err = snd_ctl_add(mixer->chip->card, kctl);
if (err < 0)
return err;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index c64a3d96db22..827d40441ec7 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1142,6 +1142,20 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
+
+ /* Marantz/Denon devices with USB DAC functionality need a delay
+ * after each class compliant request
+ */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x154e) &&
+ (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+
+ switch (le16_to_cpu(dev->descriptor.idProduct)) {
+ case 0x3005: /* Marantz HD-DAC1 */
+ case 0x3006: /* Marantz SA-14S1 */
+ mdelay(20);
+ break;
+ }
+ }
}
/*
diff --git a/ti_config_fragments/all_valid_socs.cfg b/ti_config_fragments/all_valid_socs.cfg
new file mode 100644
index 000000000000..5ef0526408c3
--- /dev/null
+++ b/ti_config_fragments/all_valid_socs.cfg
@@ -0,0 +1,25 @@
+##################################################
+# TI SoCs supported by this release config options
+#
+# IMPORTANT NOTE: Always refer to the appropriate
+# Release Note for accurate information on the
+# specific SoC.
+##################################################
+
+# Supported ARM CPUs
+CONFIG_ARCH_MULTI_V6=n
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_MULTI_V6_V7=n
+CONFIG_CPU_V6=n
+
+# Enable CONFIG_SMP
+CONFIG_SMP=y
+
+# Supported SoCs
+CONFIG_ARCH_OMAP2=n
+CONFIG_ARCH_OMAP3=n
+CONFIG_ARCH_OMAP4=n
+CONFIG_SOC_OMAP5=y
+CONFIG_SOC_AM33XX=y
+CONFIG_SOC_AM43XX=y
+CONFIG_SOC_DRA7XX=y
diff --git a/ti_config_fragments/connectivity.cfg b/ti_config_fragments/connectivity.cfg
index 436d48ecc51e..1745a20cd5da 100644
--- a/ti_config_fragments/connectivity.cfg
+++ b/ti_config_fragments/connectivity.cfg
@@ -127,3 +127,12 @@ CONFIG_MTD_M25P80=m
#EXTCON
CONFIG_EXTCON_GPIO=y
+
+# Networking
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_NF_NAT_IPV4=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_BRIDGE=m
diff --git a/ti_config_fragments/defconfig_fragment b/ti_config_fragments/defconfig_fragment
index 854d3c0fb8e7..6181b357a2e1 100644
--- a/ti_config_fragments/defconfig_fragment
+++ b/ti_config_fragments/defconfig_fragment
@@ -1,5 +1,6 @@
use-kernel-config=omap2plus_defconfig
config-fragment=ti_config_fragments/baseport.cfg
+config-fragment=ti_config_fragments/all_valid_socs.cfg
config-fragment=ti_config_fragments/power.cfg
config-fragment=ti_config_fragments/connectivity.cfg
config-fragment=ti_config_fragments/ipc.cfg
diff --git a/ti_config_fragments/dra7_only.cfg b/ti_config_fragments/dra7_only.cfg
new file mode 100644
index 000000000000..1e2d0cfefe6a
--- /dev/null
+++ b/ti_config_fragments/dra7_only.cfg
@@ -0,0 +1,24 @@
+##################################################
+# TI DRA7 specific config options
+##################################################
+
+# Supported ARM CPUs
+CONFIG_ARCH_MULTI_V6=n
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_MULTI_V6_V7=n
+CONFIG_CPU_V6=n
+
+# Enable CONFIG_SMP
+CONFIG_SMP=y
+
+# Disable Socs other than DRA7
+CONFIG_ARCH_OMAP2=n
+CONFIG_ARCH_OMAP3=n
+CONFIG_ARCH_OMAP4=n
+CONFIG_SOC_OMAP5=n
+CONFIG_SOC_AM33XX=n
+CONFIG_SOC_AM43XX=n
+CONFIG_SOC_DRA7XX=y
+
+# Enable LPAE (Large Physical Address Extensions)
+CONFIG_ARM_LPAE=y
diff --git a/ti_config_fragments/system_test.cfg b/ti_config_fragments/system_test.cfg
index fe5c08322d39..ba70e66b6792 100644
--- a/ti_config_fragments/system_test.cfg
+++ b/ti_config_fragments/system_test.cfg
@@ -10,9 +10,15 @@ CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=n
CONFIG_DEBUG_INFO=y
CONFIG_RTC_DEBUG=y
CONFIG_TIGON3=m
+CONFIG_THERMAL_EMULATION=y
# Enable Devfreq for co-processor driver testing
CONFIG_PM_DEVFREQ=y
CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
CONFIG_DEVFREQ_GOV_PERFORMANCE=y
CONFIG_DEVFREQ_GOV_POWERSAVE=y
CONFIG_DEVFREQ_GOV_USERSPACE=y
+
+CONFIG_SATA_AHCI=m
+CONFIG_SKGE=m
+CONFIG_E1000=m
+CONFIG_E1000E=m
diff --git a/ti_config_fragments/system_test_defconfig_fragment b/ti_config_fragments/system_test_defconfig_fragment
index aad7bbaf37ca..ed78066b3e7f 100644
--- a/ti_config_fragments/system_test_defconfig_fragment
+++ b/ti_config_fragments/system_test_defconfig_fragment
@@ -1,5 +1,6 @@
use-kernel-config=omap2plus_defconfig
config-fragment=ti_config_fragments/baseport.cfg
+config-fragment=ti_config_fragments/all_valid_socs.cfg
config-fragment=ti_config_fragments/power.cfg
config-fragment=ti_config_fragments/connectivity.cfg
config-fragment=ti_config_fragments/ipc.cfg
diff --git a/ti_config_fragments/wlan.cfg b/ti_config_fragments/wlan.cfg
index d1cc785e3c75..06d7c7407b57 100644
--- a/ti_config_fragments/wlan.cfg
+++ b/ti_config_fragments/wlan.cfg
@@ -17,11 +17,3 @@ CONFIG_CRYPTO_TEST=m
CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_CCM=y
-
-
-CONFIG_NF_CONNTRACK=y
-CONFIG_NF_CONNTRACK_IPV4=y
-CONFIG_IP_NF_IPTABLES=y
-CONFIG_IP_NF_FILTER=y
-CONFIG_NF_NAT_IPV4=y
-CONFIG_IP_NF_TARGET_MASQUERADE=y